oorja 2.1.8 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +1,10 @@
1
1
  import { Command } from '@oclif/core';
2
- import { determineENV, setENVAccessToken } from '../lib/config.js';
2
+ import { Config } from '../lib/config.js';
3
3
  export class SignOut extends Command {
4
4
  static description = `Sign-out of oorja. Clears saved auth-token`;
5
5
  async run() {
6
- const env = determineENV();
7
- setENVAccessToken(env, '');
6
+ const config = new Config(this.config.configDir);
7
+ config.setAccessToken('');
8
8
  console.log('Sign-out complete');
9
9
  }
10
10
  }
@@ -11,10 +11,10 @@ export default class TeleTypeCommand extends Command {
11
11
  new_space: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
12
  };
13
13
  static args: {
14
- space: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
14
+ streamKey: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
15
15
  };
16
16
  run(): Promise<void>;
17
- private streamToLink;
17
+ private streamUsingStreamKey;
18
18
  private createRoomAndStream;
19
19
  private clearstdin;
20
20
  }
@@ -1,27 +1,29 @@
1
1
  import inquirer from 'inquirer';
2
2
  import { Command, Flags, Args } from '@oclif/core';
3
3
  import ora from 'ora';
4
- import * as os from 'os';
4
+ import { hostname } from 'os';
5
5
  import chalk from 'chalk';
6
- import { ROOM_LINK_SAMPLE } from '../../lib/config.js';
7
- import { getApp } from '../../lib/oorja/index.js';
8
- import { printExitMessage, promptRoomLink } from '../../lib/utils.js';
9
- const DEFAULT_SHELL = os.platform() === 'win32' ? 'powershell.exe' : process.env.SHELL || 'bash';
6
+ import { Config, STREAM_KEY_SAMPLE } from '../../lib/config.js';
7
+ import { App, parseStreamKey } from '../../lib/oorja/index.js';
8
+ import { printExitMessage, promptStreamKey } from '../../lib/utils.js';
9
+ import { Unauthorized } from '../../lib/connect/errors.js';
10
+ import { exit } from '../../lib/exit.js';
10
11
  export default class TeleTypeCommand extends Command {
11
12
  static order = 1;
12
13
  static aliases = ['tty'];
13
14
  static description = `Launch a terminal streaming session in oorja.`;
14
15
  static examples = [
15
16
  `${chalk.blueBright('$ teletype')}
16
- Will prompt to choose streaming destination - existing space or create a new one.
17
+ Will prompt to choose streaming destination - either enter a stream key for an existing space or create a new space.
17
18
 
18
19
  `,
19
- `${chalk.blueBright(`$ teletype '${ROOM_LINK_SAMPLE}'`)}
20
- Will stream to the space specified by secret link, you must have joined the space before streaming.
20
+ `${chalk.blueBright(`$ teletype '${STREAM_KEY_SAMPLE}'`)}
21
+ Will stream to the space using the secret stream-key. NOTE: stream-keys are personal (generated for you in the teletype app at oorja.io), do not accept them from other people, nor should
22
+ you share your stream-keys with others.
21
23
 
22
24
  `,
23
25
  `${chalk.blueBright('$ teletype -m')}
24
- Will also allow participants to write to your terminal!
26
+ Will also allow participants to write to your terminal! Collaboration mode must be explicitly enabled.
25
27
 
26
28
  `,
27
29
  ];
@@ -30,7 +32,7 @@ Will also allow participants to write to your terminal!
30
32
  shell: Flags.string({
31
33
  char: 's',
32
34
  description: 'shell to use. e.g. bash, fish',
33
- default: DEFAULT_SHELL,
35
+ default: 'bash',
34
36
  }),
35
37
  multiplex: Flags.boolean({
36
38
  char: 'm',
@@ -44,61 +46,64 @@ Will also allow participants to write to your terminal!
44
46
  }),
45
47
  };
46
48
  static args = {
47
- space: Args.string({}),
49
+ streamKey: Args.string({}),
48
50
  };
49
51
  async run() {
50
52
  const { args, flags: { shell, multiplex, new_space }, } = await this.parse(TeleTypeCommand);
51
- if (args.space) {
52
- await this.streamToLink({ shell, multiplex, roomLink: args.space });
53
- process.exit(0);
53
+ const config = new Config(this.config.configDir);
54
+ const app = new App(config);
55
+ if (args.streamKey) {
56
+ await this.streamUsingStreamKey(app, { shell, multiplex, streamKey: args.streamKey });
57
+ exit(0);
54
58
  }
55
59
  if (new_space) {
56
- await this.createRoomAndStream({ shell, multiplex });
57
- process.exit(0);
60
+ await this.createRoomAndStream(app, { shell, multiplex });
61
+ exit(0);
58
62
  }
59
63
  console.log('(use -h for description and options) \n');
60
64
  // room not known, prompt
61
- const SPACE = 'To an existing space (you have the link)';
62
- const NEW = 'New space';
65
+ const STREAM_USING_STREAM_KEY = 'Stream using stream-key (You can acquire your stream key from teletype app within the space)';
66
+ const STREAM_TO_NEW_SPACE = 'New space';
63
67
  const { answer } = await inquirer.prompt([
64
68
  {
65
69
  type: 'list',
66
70
  name: 'answer',
67
71
  message: 'Choose streaming destination',
68
- choices: [NEW, SPACE],
72
+ choices: [STREAM_TO_NEW_SPACE, STREAM_USING_STREAM_KEY],
69
73
  },
70
74
  ]);
71
75
  switch (answer) {
72
- case SPACE:
73
- await this.streamToLink({ shell, multiplex });
76
+ case STREAM_USING_STREAM_KEY:
77
+ await this.streamUsingStreamKey(app, { shell, multiplex });
74
78
  break;
75
- case NEW:
76
- await this.createRoomAndStream({ shell, multiplex });
79
+ case STREAM_TO_NEW_SPACE:
80
+ await this.createRoomAndStream(app, { shell, multiplex });
77
81
  break;
78
82
  }
79
- process.exit(0);
83
+ exit(0);
80
84
  }
81
- async streamToLink(options) {
82
- const roomLink = options.roomLink || (await promptRoomLink());
83
- if (!roomLink) {
84
- printExitMessage(chalk.redBright('Space link not provided :('));
85
- process.exit();
85
+ async streamUsingStreamKey(app, options) {
86
+ const streamKey = options.streamKey || (await promptStreamKey());
87
+ if (!streamKey) {
88
+ printExitMessage(chalk.redBright('stream-key not provided :('));
89
+ exit();
86
90
  }
87
- const app = await getApp({ roomLink });
88
- const roomKey = app.getRoomKey(roomLink);
91
+ const streamKeyStruct = parseStreamKey(streamKey);
92
+ const oorja = await app.init(streamKeyStruct);
93
+ const roomKey = oorja.getRoomKey(streamKeyStruct);
89
94
  this.clearstdin();
90
- await app.teletype({ roomKey, ...options, process });
95
+ await oorja.teletype({ roomKey, ...options, process });
91
96
  }
92
- async createRoomAndStream({ shell, multiplex }) {
93
- const app = await getApp();
97
+ async createRoomAndStream(app, { shell, multiplex }) {
98
+ const oorja = await app.init();
94
99
  const spinner = ora({
95
100
  text: chalk.bold('Creating space with TeleType app'),
96
101
  discardStdin: false,
97
102
  }).start();
98
103
  const now = new Date();
99
- const { roomKey } = await app
104
+ const { roomKey } = await oorja
100
105
  .createRoom({
101
- roomName: `Teletype session - ${os.hostname()} @ ${now.getHours()}:${now.getMinutes().toString().padStart(2, '0')}`,
106
+ roomName: `Teletype session - ${hostname()} @ ${now.getHours()}:${now.getMinutes().toString().padStart(2, '0')}`,
102
107
  apps: {
103
108
  defaultFocus: '39',
104
109
  appList: [
@@ -111,15 +116,21 @@ Will also allow participants to write to your terminal!
111
116
  },
112
117
  })
113
118
  .catch((e) => {
114
- printExitMessage('Failed to create space.');
115
- process.exit(9);
119
+ if (e instanceof Unauthorized) {
120
+ printExitMessage('Failed to create space. Are you sure your access-token is valid?');
121
+ }
122
+ else {
123
+ printExitMessage('Failed to create space. Try again later or try updating your oorja-cli');
124
+ }
125
+ exit(9);
126
+ return Promise.reject();
116
127
  });
117
128
  spinner.succeed(chalk.bold('Space created')).clear();
118
- const link = app.linkForRoom(roomKey);
129
+ const link = oorja.linkForRoom(roomKey);
119
130
  console.log(`\n${chalk.bold(chalk.blueBright(link))}\n`);
120
131
  console.log(chalk.bold("^^ You'll be streaming here ^^"));
121
132
  this.clearstdin();
122
- return await app.teletype({ roomKey, shell, multiplex, process });
133
+ return await oorja.teletype({ roomKey, shell, multiplex, process });
123
134
  }
124
135
  clearstdin() {
125
136
  process.stdin.read();
@@ -1,18 +1,23 @@
1
- import { URL } from 'url';
2
- export declare const CLI_VERSION = 2.6;
3
- import Conf from 'conf';
4
- export declare const config: Conf<string>;
1
+ export declare const CLI_VERSION = 2.7;
5
2
  export type env = 'local' | 'prod';
3
+ export declare class Config {
4
+ streamKeyAuth: boolean;
5
+ private configPath;
6
+ private config;
7
+ constructor(configDir: string);
8
+ private loadConfig;
9
+ private saveConfig;
10
+ getEnv: () => env;
11
+ getAccessToken: () => string;
12
+ setAccessToken: (token: string) => void;
13
+ }
6
14
  export type ConnectConfig = {
15
+ useHttps: boolean;
7
16
  host: string;
8
- token: string;
9
17
  };
10
18
  export declare const getConnectConfig: (env: env, region: string) => ConnectConfig;
11
- export declare const ROOM_LINK_SAMPLE = "https://oorja.io/spaces?id=foo#key";
12
- export declare const INVALID_ROOM_LINK_MESSAGE: string;
13
- export declare const determineENV: (roomURL?: URL) => env;
14
- export declare const getENVAccessToken: (env: env) => string;
15
- export declare const setENVAccessToken: (env: env, token: string) => void;
19
+ export declare const STREAM_KEY_SAMPLE = "sk-xxxx:space-id#encryption-secret";
20
+ export declare const INVALID_STREAM_KEY_MESSAGE: string;
16
21
  export type oorjaConfig = {
17
22
  host: string;
18
23
  };
@@ -1,57 +1,70 @@
1
1
  import chalk from 'chalk';
2
- export const CLI_VERSION = 2.6;
3
- import Conf from 'conf';
4
- import { printExitMessage } from './utils.js';
5
- export const config = new Conf({
6
- projectName: 'oorja',
7
- schema: {
8
- env: {
9
- type: 'string',
10
- },
11
- 'staging-access-token': {
12
- type: 'string',
13
- },
14
- 'prod-access-token': {
15
- type: 'string',
16
- },
17
- },
18
- });
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
3
+ import path from 'path';
4
+ export const CLI_VERSION = 2.7;
5
+ export class Config {
6
+ streamKeyAuth = false;
7
+ configPath;
8
+ config = {};
9
+ constructor(configDir) {
10
+ this.configPath = path.join(configDir, 'config.json');
11
+ this.loadConfig();
12
+ }
13
+ loadConfig() {
14
+ try {
15
+ const data = readFileSync(this.configPath, 'utf8');
16
+ this.config = JSON.parse(data);
17
+ }
18
+ catch (error) {
19
+ if (error.code === 'ENOENT') {
20
+ // Config file doesn't exist, create it with default values
21
+ const dir = path.dirname(this.configPath);
22
+ if (!existsSync(dir)) {
23
+ mkdirSync(dir, { recursive: true });
24
+ }
25
+ this.saveConfig();
26
+ }
27
+ else {
28
+ console.error(chalk.redBright('Error loading config:'), error.message, this.configPath);
29
+ }
30
+ }
31
+ }
32
+ async saveConfig() {
33
+ try {
34
+ writeFileSync(this.configPath, JSON.stringify(this.config, null, 2), 'utf8');
35
+ }
36
+ catch (error) {
37
+ console.error(chalk.redBright('Error saving config:'), error.message);
38
+ }
39
+ }
40
+ getEnv = () => {
41
+ return this.config['env'] ?? 'prod';
42
+ };
43
+ getAccessToken = () => {
44
+ return this.config[`${this.getEnv()}-access-token`] || '';
45
+ };
46
+ setAccessToken = (token) => {
47
+ this.config[`${this.getEnv()}-access-token`] = token;
48
+ this.saveConfig();
49
+ };
50
+ }
19
51
  export const getConnectConfig = (env, region) => {
20
52
  const getHost = (env) => {
21
53
  switch (env) {
22
54
  case 'local':
23
- return 'connect-staging.oorja.io';
55
+ return 'localhost:4000';
24
56
  case 'prod':
25
57
  return region ? `${region}.connect.oorja.io` : 'connect.oorja.io';
26
58
  }
27
59
  };
60
+ const host = getHost(env);
28
61
  return {
29
- host: getHost(env),
30
- token: getENVAccessToken(env),
62
+ useHttps: host.includes('oorja.io'),
63
+ host,
31
64
  };
32
65
  };
33
- export const ROOM_LINK_SAMPLE = 'https://oorja.io/spaces?id=foo#key';
34
- export const INVALID_ROOM_LINK_MESSAGE = `${chalk.redBright('invalid url ')}🤔. It should look like: ${chalk.blue(ROOM_LINK_SAMPLE)}`;
35
- export const determineENV = (roomURL) => {
36
- if (!roomURL)
37
- return config.get('env') || 'prod';
38
- switch (roomURL.host) {
39
- case 'oorja.io':
40
- case 'teletype.oorja.io':
41
- return 'prod';
42
- case 'localhost:3000':
43
- return 'local';
44
- default:
45
- printExitMessage(INVALID_ROOM_LINK_MESSAGE);
46
- process.exit(1);
47
- }
48
- };
49
- export const getENVAccessToken = (env) => {
50
- return config.get(`${env}-access-token`) || '';
51
- };
52
- export const setENVAccessToken = (env, token) => {
53
- config.set(`${env}-access-token`, token);
54
- };
66
+ export const STREAM_KEY_SAMPLE = 'sk-xxxx:space-id#encryption-secret';
67
+ export const INVALID_STREAM_KEY_MESSAGE = `${chalk.redBright('invalid stream-key ')}🤔. It should look like: ${chalk.blue(STREAM_KEY_SAMPLE)}`;
55
68
  export const getoorjaConfig = (env) => {
56
69
  let host;
57
70
  switch (env) {
@@ -9,29 +9,30 @@ export declare class ConnectClient {
9
9
  private headers;
10
10
  private timeout;
11
11
  private socket?;
12
- constructor(env: env, region: string);
12
+ private accessToken;
13
+ constructor(env: env, region: string, token: string);
14
+ setAccessToken: (token: string) => void;
13
15
  private _fetch;
14
16
  fetchCliManifest: () => Promise<CliManifest>;
15
- fetchSessionUser: () => Promise<User>;
17
+ fetchSessionUser: (v2?: boolean) => Promise<User>;
16
18
  createRoom: ({ roomName, apps }: CreateRoomOptions) => Promise<Room>;
17
19
  createAnonymousUser: () => Promise<string>;
18
- accessTokenFromRoomParticipantOTP: (roomId: string, otp: string) => Promise<string>;
19
20
  fetchRoom: (roomId: string) => Promise<Room>;
20
21
  establishSocket: () => Promise<void>;
21
- joinChannel: ({ channel, params, onJoin, onClose, onError, onMessage, handleSessionJoin, handleSessionLeave, }: JoinChannelOptions<any>) => Channel;
22
+ joinChannel: ({ channel, params, onJoin, onClose, onError, onMessage, handleSessionJoin, handleSessionLeave, }: JoinChannelOptions<unknown, unknown>) => Channel;
22
23
  destroy: () => Promise<unknown>;
23
24
  }
24
25
  export type CreateRoomOptions = {
25
26
  roomName: string;
26
27
  apps: RoomApps;
27
28
  };
28
- export type JoinChannelOptions<T> = {
29
+ export type JoinChannelOptions<Params, Metas> = {
29
30
  channel: string;
30
- params: T;
31
+ params: Params;
31
32
  onJoin?: () => void;
32
33
  onError?: (reason: any) => void;
33
34
  onClose?: (payload: any, ref: any, joinRef: any) => void;
34
35
  onMessage: (payload: any) => void;
35
- handleSessionJoin: (session: string | undefined, _ignore: any, metas: any) => void;
36
- handleSessionLeave: (session: string | undefined) => void;
36
+ handleSessionJoin: (session: string, currentMetas: undefined | Metas, newMetas: Metas) => void;
37
+ handleSessionLeave: (session: string, currentMetas: undefined | Metas, leftMetas: Metas) => void;
37
38
  };
@@ -8,6 +8,7 @@ import { Unauthorized, BadRequest } from './errors.js';
8
8
  import { Socket, Presence } from 'phoenix';
9
9
  import camelcaseKeys from 'camelcase-keys';
10
10
  import { printExitMessage } from '../utils.js';
11
+ import { exit } from '../exit.js';
11
12
  export class ApiClientError extends Error {
12
13
  }
13
14
  export class ConnectClient {
@@ -16,16 +17,22 @@ export class ConnectClient {
16
17
  headers;
17
18
  timeout;
18
19
  socket;
19
- constructor(env, region) {
20
+ accessToken;
21
+ constructor(env, region, token) {
20
22
  const config = getConnectConfig(env, region);
21
- this.baseURL = connectBaseURL(config.host);
23
+ this.baseURL = connectBaseURL(config.host, config.useHttps);
22
24
  this.headers = {
23
- 'x-access-token': config.token || '',
25
+ 'x-access-token': token || '',
24
26
  'Content-Type': 'application/json',
25
27
  };
28
+ this.accessToken = token || '';
26
29
  this.timeout = 5000;
27
30
  this.config = config;
28
31
  }
32
+ setAccessToken = (token) => {
33
+ this.accessToken = token;
34
+ this.headers['x-access-token'] = token;
35
+ };
29
36
  // Private helper to encapsulate fetch with timeout and consistent error handling.
30
37
  async _fetch(path, options = {}) {
31
38
  const controller = new AbortController();
@@ -54,7 +61,7 @@ export class ConnectClient {
54
61
  }
55
62
  fetchCliManifest = async () => {
56
63
  try {
57
- const response = await this._fetch('/cli');
64
+ const response = await this._fetch('/v1/cli');
58
65
  const data = await response.json();
59
66
  return camelcaseKeys(data);
60
67
  }
@@ -62,9 +69,10 @@ export class ConnectClient {
62
69
  return handleError(error);
63
70
  }
64
71
  };
65
- fetchSessionUser = async () => {
72
+ fetchSessionUser = async (v2 = false) => {
73
+ const path = v2 ? '/v2/session/user_profile' : '/v1/session/user';
66
74
  try {
67
- const response = await this._fetch('/session/user');
75
+ const response = await this._fetch(path);
68
76
  const data = await response.json();
69
77
  return defaultParser(data.data);
70
78
  }
@@ -81,7 +89,7 @@ export class ConnectClient {
81
89
  },
82
90
  };
83
91
  try {
84
- const response = await this._fetch('/rooms', {
92
+ const response = await this._fetch('/v1/rooms', {
85
93
  method: 'POST',
86
94
  body: JSON.stringify(body),
87
95
  });
@@ -94,7 +102,7 @@ export class ConnectClient {
94
102
  };
95
103
  createAnonymousUser = async () => {
96
104
  try {
97
- const response = await this._fetch('/session/anon', { method: 'POST' });
105
+ const response = await this._fetch('/v1/session/anon', { method: 'POST' });
98
106
  const data = await response.json();
99
107
  return data.access_token;
100
108
  }
@@ -102,25 +110,9 @@ export class ConnectClient {
102
110
  return handleError(error);
103
111
  }
104
112
  };
105
- accessTokenFromRoomParticipantOTP = async (roomId, otp) => {
106
- try {
107
- const response = await this._fetch('/access_tokens/from_room_participant_otp', {
108
- method: 'POST',
109
- body: JSON.stringify({
110
- room_id: roomId,
111
- otp: otp,
112
- }),
113
- });
114
- const data = await response.json();
115
- return data.data.token;
116
- }
117
- catch (error) {
118
- return handleError(error);
119
- }
120
- };
121
113
  fetchRoom = async (roomId) => {
122
114
  try {
123
- const response = await this._fetch(`/rooms/${roomId}`);
115
+ const response = await this._fetch(`/v1/rooms/${roomId}`);
124
116
  const data = await response.json();
125
117
  return defaultParser(data.data);
126
118
  }
@@ -129,7 +121,7 @@ export class ConnectClient {
129
121
  }
130
122
  };
131
123
  establishSocket = async () => {
132
- const protocolPrefix = `wss://`;
124
+ const protocolPrefix = this.config.useHttps ? 'wss://' : 'ws://';
133
125
  const host = this.config.host;
134
126
  const encodeMessage = function (rawdata, callback) {
135
127
  return callback(encoder.encode(rawdata));
@@ -141,7 +133,7 @@ export class ConnectClient {
141
133
  let initialConnection = false;
142
134
  this.socket = new Socket(`${protocolPrefix}${host}/socket`, {
143
135
  params: {
144
- access_token: this.config.token,
136
+ access_token: this.accessToken,
145
137
  },
146
138
  transport: WebSocket,
147
139
  heartbeatIntervalMs: 7500,
@@ -159,9 +151,8 @@ export class ConnectClient {
159
151
  return;
160
152
  }
161
153
  printExitMessage('connection error');
162
- process.exit(2);
154
+ exit(2);
163
155
  });
164
- // @ts-ignore
165
156
  this.socket.connect();
166
157
  });
167
158
  };
@@ -173,16 +164,20 @@ export class ConnectClient {
173
164
  chan.onError(onError);
174
165
  if (onClose)
175
166
  chan.onClose(onClose);
176
- let presences = [];
177
167
  chan.on('new_msg', (msg) => {
178
168
  onMessage(msg);
179
169
  });
180
- chan.on('presence_state', (response) => {
181
- Presence.syncState(presences, response, handleSessionJoin, undefined);
182
- presences = response;
170
+ const presence = new Presence(chan);
171
+ // https://hexdocs.pm/phoenix/js/index.html#handling-individual-presence-join-and-leave-events
172
+ presence.onJoin((key, current, newPres) => {
173
+ if (!current) {
174
+ handleSessionJoin(key, current, newPres);
175
+ }
183
176
  });
184
- chan.on('presence_diff', (newPresence) => {
185
- presences = Presence.syncDiff(presences, newPresence, handleSessionJoin, handleSessionLeave);
177
+ presence.onLeave((key, current, leftPres) => {
178
+ if (current.metas.length === 0) {
179
+ handleSessionLeave(key, current, leftPres);
180
+ }
186
181
  });
187
182
  chan
188
183
  .join()
@@ -197,7 +192,7 @@ export class ConnectClient {
197
192
  return;
198
193
  }
199
194
  printExitMessage('error on channel');
200
- process.exit(3);
195
+ exit(3);
201
196
  });
202
197
  return chan;
203
198
  };
@@ -214,7 +209,7 @@ export class ConnectClient {
214
209
  });
215
210
  };
216
211
  }
217
- const connectBaseURL = (host) => `https://${host}/api/v1`;
212
+ const connectBaseURL = (host, useHttps) => `http${useHttps ? 's' : ''}://${host}/api`;
218
213
  const handleError = (error) => {
219
214
  // This function was originally designed for AxiosError.
220
215
  // The custom _fetch method now throws an object with a `response` key
@@ -0,0 +1 @@
1
+ export declare function exit(exitCode?: number, streams?: any): void;
@@ -0,0 +1,44 @@
1
+ // because flushing stdout / graceful exit doesn't seem possible in nodejs?
2
+ // without this error-codes/logs just before exit is called won't show up..
3
+ /*
4
+ * exit
5
+ * https://github.com/cowboy/node-exit
6
+ *
7
+ * Copyright (c) 2013 "Cowboy" Ben Alman
8
+ * Licensed under the MIT license.
9
+ */
10
+ 'use strict';
11
+ export function exit(exitCode = 0, streams) {
12
+ if (!streams) {
13
+ streams = [process.stdout, process.stderr];
14
+ }
15
+ var drainCount = 0;
16
+ // Actually exit if all streams are drained.
17
+ function tryToExit() {
18
+ if (drainCount === streams.length) {
19
+ process.exit(exitCode);
20
+ }
21
+ }
22
+ streams.forEach(function (stream) {
23
+ // Count drained streams now, but monitor non-drained streams.
24
+ if (stream.bufferSize === 0) {
25
+ drainCount++;
26
+ }
27
+ else {
28
+ stream.write('', 'utf-8', function () {
29
+ drainCount++;
30
+ tryToExit();
31
+ });
32
+ }
33
+ // Prevent further writing.
34
+ stream.write = function () { };
35
+ });
36
+ // If all streams were already drained, exit now.
37
+ tryToExit();
38
+ // In Windows, when run as a Node.js child process, a script utilizing
39
+ // this library might just exit with a 0 exit code, regardless. This code,
40
+ // despite the fact that it looks a bit crazy, appears to fix that.
41
+ process.on('exit', function () {
42
+ process.exit(exitCode);
43
+ });
44
+ }
@@ -1,5 +1,7 @@
1
1
  import haversine from 'haversine-distance';
2
2
  import { printExitMessage } from '../utils.js';
3
+ import { exit } from '../exit.js';
4
+ import { geoMap } from './geoMap.js';
3
5
  export class OorjaClientError extends Error {
4
6
  }
5
7
  const _maybeError = (response) => {
@@ -24,18 +26,21 @@ class Client {
24
26
  }
25
27
  const _client = new Client();
26
28
  export const getRegion = async () => {
27
- const response = await _client.get('https://static.oorja.io/nudge', {
29
+ const response = await _client.get('https://oorja.io/nudge', {
28
30
  method: 'GET',
29
31
  });
30
32
  if (response.status !== 200) {
31
33
  printExitMessage('There seems to be an issue with the network');
32
- process.exit(1);
34
+ exit(1);
35
+ return Promise.reject();
33
36
  }
34
37
  const settings = (await response.json());
38
+ const country = (response.headers.get('cdn-requestcountrycode') || 'us').toUpperCase().trim();
39
+ const info = geoMap[country] || geoMap['US'];
35
40
  try {
36
41
  const clientLocation = {
37
- lat: parseFloat(response.headers.get('oorja-lat')),
38
- lng: parseFloat(response.headers.get('oorja-lon')),
42
+ lat: parseFloat(info[0]),
43
+ lng: parseFloat(info[1]),
39
44
  };
40
45
  const distances = settings.regions.map((region) => {
41
46
  return {
@@ -48,6 +53,7 @@ export const getRegion = async () => {
48
53
  }
49
54
  catch {
50
55
  printExitMessage('error determining region');
51
- process.exit(1);
56
+ exit(1);
57
+ return Promise.reject();
52
58
  }
53
59
  };