@auxilium/datalynk-client 1.4.1 → 1.5.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.
- package/README.md +36 -0
- package/bin/datalynk-migrate.mjs +86 -0
- package/bin/datalynk-models.mjs +3 -3
- package/bin/utils.mjs +163 -0
- package/dist/api.d.ts +96 -5
- package/dist/api.d.ts.map +1 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/index.cjs +548 -845
- package/dist/index.mjs +546 -843
- package/dist/login-prompt.d.ts +4 -1
- package/dist/login-prompt.d.ts.map +1 -1
- package/dist/slice.d.ts +22 -5
- package/dist/slice.d.ts.map +1 -1
- package/dist/themes.d.ts +1 -1
- package/dist/themes.d.ts.map +1 -1
- package/dist/utils.d.ts +95 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/webrtc.d.ts +1 -1
- package/dist/webrtc.d.ts.map +1 -1
- package/package.json +11 -7
package/README.md
CHANGED
|
@@ -318,6 +318,30 @@ api.request(..., {offline: true}).then((resp: T | void) => {
|
|
|
318
318
|
}
|
|
319
319
|
});
|
|
320
320
|
```
|
|
321
|
+
|
|
322
|
+
#### Connection status
|
|
323
|
+
`online$` remains available for a simple boolean check. Use `status$` when the application needs to react differently to connectivity, authentication, and API failures:
|
|
324
|
+
|
|
325
|
+
```ts
|
|
326
|
+
api.status$.subscribe(status => {
|
|
327
|
+
switch(status) {
|
|
328
|
+
case 'online':
|
|
329
|
+
// The API is responding normally
|
|
330
|
+
break;
|
|
331
|
+
case 'offline':
|
|
332
|
+
// The network request could not reach the API
|
|
333
|
+
break;
|
|
334
|
+
case 'unauthorized':
|
|
335
|
+
// The token expired or the API rejected it; prompt for login
|
|
336
|
+
break;
|
|
337
|
+
case 'unavailable':
|
|
338
|
+
// The server returned a failure or a non-JSON API response
|
|
339
|
+
break;
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
Expired and rejected tokens are cleared from `token$`. Malformed responses reject with `UnexpectedApiResponseError`. Requests only enter the offline queue when `{offline: true}` is supplied; other requests reject so callers can handle the relevant status. `api.offline` reflects Datalynk availability, not only browser connectivity, so it can be `true` while `navigator.onLine` is still `true` during API/MySQL recovery. Offline-enabled slices use their local IndexedDB cache in either case.
|
|
321
345
|
</details>
|
|
322
346
|
|
|
323
347
|
<details>
|
|
@@ -530,3 +554,15 @@ session.disconnect();
|
|
|
530
554
|
```
|
|
531
555
|
|
|
532
556
|
</details>
|
|
557
|
+
|
|
558
|
+
## Request-owned API recovery (1.5.0)
|
|
559
|
+
|
|
560
|
+
When a request receives a server-side response failure (HTTP 5xx, malformed/non-JSON API response, or SQLSTATE error), the client now:
|
|
561
|
+
|
|
562
|
+
1. immediately sets detailed status to `unavailable` (`online === false`, `offline === true`),
|
|
563
|
+
2. retries the exact failed HTTP payload twice immediately with no artificial delay,
|
|
564
|
+
3. if both immediate retries fail, waits 30 seconds after the previous attempt finishes and retries the same payload again,
|
|
565
|
+
4. repeats that sequential 30-second recovery attempt until the exact request returns a valid successful API response,
|
|
566
|
+
5. only then returns to `online` and allows queued/normal traffic to resume.
|
|
567
|
+
|
|
568
|
+
The original failed caller rejects immediately after the client enters `unavailable`, allowing an offline-enabled `Slice` operation to fall back to its local IndexedDB cache while recovery continues in the background. Only one recovery request can be active at a time. Heartbeat/version checks cannot override request-owned recovery, and other normal requests are blocked from the network while recovery is active. HTTP 401 remains `unauthorized`, ordinary 4xx errors still reject without taking the whole client offline, and transport/fetch failures continue to use the network `offline` path.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/bin/node
|
|
2
|
+
|
|
3
|
+
import {ArgParser, command as $, commandSync as $Sync} from './utils.mjs';
|
|
4
|
+
|
|
5
|
+
const user = $Sync`whoami`;
|
|
6
|
+
const argParser = new ArgParser('datalynk-migration', 'Migrate datalynk database from one MySQL server to another', [
|
|
7
|
+
{name: 'databases', desc: 'Databases to sync', extras: true},
|
|
8
|
+
{name: 'tunnelFrom', desc: 'Create an SSH tunnel to donor\'s jumpbox', flags: ['--tunnel-from'], default: `${user}@tools.auxiliumgroup.com`},
|
|
9
|
+
{name: 'tunnelTo', desc: 'Create an SSH tunnel to recipient\'s jumpbox', flags: ['--tunnel-to']},
|
|
10
|
+
{name: 'fromMysql', desc: 'Donor MySQL connection string', flags: ['-f', '--from-mysql'], default: `${user}@10.5.0.101:3006`},
|
|
11
|
+
{name: 'toMysql', desc: 'Recipient MySQL connection string', flags: ['-t', '--to-mysql'], default: `${user}@localhost:3306`},
|
|
12
|
+
{name: 'connection', desc: 'Update HUB connection string', flags: ['-h', '--hub'], default: ''},
|
|
13
|
+
{name: 'audits', desc: 'Migrate spoke audit tables', flags: ['-a', '--audits'], default: false},
|
|
14
|
+
{name: 'dump', desc: 'Dump the data but do not import', flags: ['-d', '--dump'], default: false},
|
|
15
|
+
{name: 'drop', desc: 'Drop database before importing', flags: ['-x', '--drop'], default: false},
|
|
16
|
+
{name: 'noCleanup', desc: 'Keep generated dumps', flags: ['-n', '--no-cleanup'], default: false},
|
|
17
|
+
]);
|
|
18
|
+
const args = argParser.parse(process.argv.slice(2));
|
|
19
|
+
const fromMysql = splitHost(args['fromMysql']);
|
|
20
|
+
const toMysql = splitHost(args['toMysql']);
|
|
21
|
+
|
|
22
|
+
function splitHost(target) {
|
|
23
|
+
return /(?:(?<protocall>.+):\/\/\/?)?(?:(?<user>[^:@]+)(?::(?<password>.+))?@)?(?<host>[^:/#?]+)(?::(?<port>\d+))?(?<path>\/[^#?]+)?(?:#(?<fragment>[^?]+))?(?:\?(?<query>.+))?/g.exec(target);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function ssh(target) {
|
|
27
|
+
const remote = splitHost(target);
|
|
28
|
+
return `ssh -p ${remote['port'] ?? 22} ${remote['user']}${remote['password'] ? `:${remote['password']}` : ''}${remote['user'] ? '@' : ''}${remote['host']}`
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Help prompt
|
|
32
|
+
if(args['help']) {
|
|
33
|
+
console.log(argParser.help());
|
|
34
|
+
process.exit();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Create any required tunnels
|
|
38
|
+
if(args['tunnelFrom']) {
|
|
39
|
+
console.log(`Creating tunnel: 2000 -> ${fromMysql.groups.host}:${fromMysql.groups.port}`);
|
|
40
|
+
$`${ssh(args['tunnelFrom'])} -L 20000:${fromMysql.groups.host}:${fromMysql.groups.port}`;
|
|
41
|
+
}
|
|
42
|
+
if(args['tunnelTo']) {
|
|
43
|
+
console.log(`Creating tunnel: 2001 -> ${toMysql.groups.host}:${toMysql.groups.port}`);
|
|
44
|
+
$`${ssh(args['tunnelTo'])} -L 20001:${toMysql.groups.host}:${toMysql.groups.port}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Iterate through databases in need of migration
|
|
48
|
+
for(const db of args['databases']) {
|
|
49
|
+
console.log(`\nDatabase: ${db}`);
|
|
50
|
+
|
|
51
|
+
if(args['drop']); //TODO: Drop databases
|
|
52
|
+
// TODO: Dump section
|
|
53
|
+
// switch(db) {
|
|
54
|
+
// case 'community':
|
|
55
|
+
//
|
|
56
|
+
// }
|
|
57
|
+
if(!args['dump']); // TODO: Import section
|
|
58
|
+
|
|
59
|
+
if(!args['noCleanup']);// TODO: Optional Cleanup
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if(args['hub']) {
|
|
63
|
+
process.stdout.write('Updating Hub connections... ');
|
|
64
|
+
const conn = await mysql.createConnection({host: toMysql.groups.host, port: toMysql.groups.port, user: toMysql.groups.user, password: toMysql.groups.password, database: 'hub'});
|
|
65
|
+
try {
|
|
66
|
+
await conn.query('SET autocommit=1');
|
|
67
|
+
const [rows] = await conn.query('SELECT * FROM connection');
|
|
68
|
+
for (const row of rows) {
|
|
69
|
+
let temp = JSON.parse(row.config);
|
|
70
|
+
temp[0] = temp[0]
|
|
71
|
+
.replace('10.5.0.70', args.connection_host)
|
|
72
|
+
.replace('10.5.0.60', args.connection_host)
|
|
73
|
+
.replace('db.auxilium.world', args.connection_host)
|
|
74
|
+
.replace('mysqlrouter;port=6446', args.connection_host);
|
|
75
|
+
temp[1] = args.sql_user;
|
|
76
|
+
temp[2] = args.sql_password;
|
|
77
|
+
|
|
78
|
+
// Update the row in the table
|
|
79
|
+
const updatedConfig = JSON.stringify(temp);
|
|
80
|
+
await conn.query('UPDATE connection SET config = ? WHERE id = ?', [updatedConfig, row.id]);
|
|
81
|
+
}
|
|
82
|
+
console.log('Done!');
|
|
83
|
+
} finally {
|
|
84
|
+
await conn.end();
|
|
85
|
+
}
|
|
86
|
+
}
|
package/bin/datalynk-models.mjs
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
import fs from 'fs';
|
|
4
4
|
import {join} from 'path';
|
|
5
5
|
import {Api} from '../dist/index.mjs';
|
|
6
|
-
import {ask} from '
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
import {ask, kebabCase, pascalCase} from './utils.mjs';
|
|
7
|
+
|
|
8
|
+
const packageFile = JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
|
|
9
9
|
|
|
10
10
|
// Shorthand for getting spoke credentials
|
|
11
11
|
async function getCredentials(spoke, login, password) {
|
package/bin/utils.mjs
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import {exec, execSync} from 'node:child_process';
|
|
2
|
+
import readline from 'node:readline';
|
|
3
|
+
|
|
4
|
+
export function command(parts, ...values) {
|
|
5
|
+
const value = parts.reduce((result, part, index) => result + part + (values[index] || ''), '');
|
|
6
|
+
return new Promise((resolve, reject) => exec(value, (error, stdout, stderr) => {
|
|
7
|
+
if(error || stderr) reject(error || stderr);
|
|
8
|
+
else resolve(stdout);
|
|
9
|
+
}));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function commandSync(parts, ...values) {
|
|
13
|
+
return execSync(parts.reduce((result, part, index) => result + part + (values[index] || ''), ''), {encoding: 'utf8'});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function ask(prompt, hide = false) {
|
|
17
|
+
const terminal = readline.createInterface({input: process.stdin, output: process.stdout, terminal: true});
|
|
18
|
+
return new Promise(resolve => {
|
|
19
|
+
if(!hide) {
|
|
20
|
+
terminal.question(prompt, answer => {
|
|
21
|
+
resolve(answer);
|
|
22
|
+
terminal.close();
|
|
23
|
+
});
|
|
24
|
+
} else {
|
|
25
|
+
let input = '';
|
|
26
|
+
const onKeypress = (character, key) => {
|
|
27
|
+
if(key && key.name === 'return') {
|
|
28
|
+
terminal.input.setRawMode(false);
|
|
29
|
+
terminal.input.removeListener('keypress', onKeypress);
|
|
30
|
+
terminal.close();
|
|
31
|
+
resolve(input);
|
|
32
|
+
} else {
|
|
33
|
+
if(key && key.name === 'backspace') {
|
|
34
|
+
if(input.length > 0) input = input.slice(0, -1);
|
|
35
|
+
} else {
|
|
36
|
+
input += character;
|
|
37
|
+
}
|
|
38
|
+
terminal.output.write(`\r${prompt}${'*'.repeat(input.length)} `);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
terminal.input.on('keypress', onKeypress);
|
|
42
|
+
terminal.input.setRawMode(true);
|
|
43
|
+
terminal.input.resume();
|
|
44
|
+
terminal.output.write(prompt);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function wordSegments(value = '') {
|
|
50
|
+
if(!value) return [];
|
|
51
|
+
return value
|
|
52
|
+
.replace(/([a-z])([A-Z])/g, '$1 $2')
|
|
53
|
+
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
|
|
54
|
+
.replace(/([0-9]+)([a-zA-Z])/g, '$1 $2')
|
|
55
|
+
.replace(/([a-zA-Z])([0-9]+)/g, '$1 $2')
|
|
56
|
+
.replace(/[_\-\s]+/g, ' ')
|
|
57
|
+
.trim()
|
|
58
|
+
.split(/\s+/)
|
|
59
|
+
.filter(Boolean);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function kebabCase(value) {
|
|
63
|
+
if(!value) return '';
|
|
64
|
+
return wordSegments(value).map(word => word.toLowerCase()).join('-');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function pascalCase(value) {
|
|
68
|
+
if(!value) return '';
|
|
69
|
+
return wordSegments(value)
|
|
70
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
71
|
+
.join('');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export class ArgParser {
|
|
75
|
+
commands = [];
|
|
76
|
+
args = [];
|
|
77
|
+
flags = [];
|
|
78
|
+
defaults;
|
|
79
|
+
|
|
80
|
+
constructor(name, desc, argList = [], examples = []) {
|
|
81
|
+
this.name = name;
|
|
82
|
+
this.desc = desc;
|
|
83
|
+
this.argList = argList;
|
|
84
|
+
|
|
85
|
+
this.commands = argList.filter(arg => arg instanceof ArgParser);
|
|
86
|
+
this.args = argList.filter(arg => !(arg instanceof ArgParser) && !arg.flags?.length);
|
|
87
|
+
this.flags = [
|
|
88
|
+
...argList.filter(arg => !(arg instanceof ArgParser) && arg.flags && arg.flags.length),
|
|
89
|
+
{name: 'help', desc: "Display command's help message", flags: ['-h', '--help'], default: false},
|
|
90
|
+
];
|
|
91
|
+
this.defaults = argList.reduce((acc, arg) => ({...acc, [arg.name]: arg.extras ? [] : (arg.default ?? null)}), {});
|
|
92
|
+
|
|
93
|
+
this.examples = [
|
|
94
|
+
...examples,
|
|
95
|
+
`[OPTIONS] ${this.args.map(arg => (arg.optional ? `[${arg.name.toUpperCase()}]` : arg.name.toUpperCase()) + (arg.extras ? '...' : '')).join(' ')}`,
|
|
96
|
+
this.commands.length ? '[OPTIONS] COMMAND' : null,
|
|
97
|
+
`--help ${this.commands.length ? '[COMMAND]' : ''}`,
|
|
98
|
+
].filter(example => !!example);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
parse(args) {
|
|
102
|
+
let extras = [];
|
|
103
|
+
let parsed = {...this.defaults, _error: []};
|
|
104
|
+
let queue = [...args];
|
|
105
|
+
while(queue.length) {
|
|
106
|
+
let arg = queue.splice(0, 1)[0];
|
|
107
|
+
if(arg[0] == '-') {
|
|
108
|
+
if(arg[1] != '-' && arg.length > 2) {
|
|
109
|
+
queue = [...arg.substring(2).split('').map(a => `-${a}`), ...queue];
|
|
110
|
+
arg = `-${arg[1]}`;
|
|
111
|
+
}
|
|
112
|
+
const combined = arg.split('=');
|
|
113
|
+
const argDef = this.flags.find(flag => flag.flags?.includes(combined[0] || arg));
|
|
114
|
+
if(argDef == null) {
|
|
115
|
+
extras.push(arg);
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
const value = combined[1] != null ? combined[1] :
|
|
119
|
+
argDef.default === false ? true :
|
|
120
|
+
argDef.default === true ? false :
|
|
121
|
+
queue.splice(queue.findIndex(q => q[0] != '-'), 1)[0] ||
|
|
122
|
+
argDef.default;
|
|
123
|
+
if(value == null) parsed._error.push(`Option missing value: ${argDef.name || combined[0]}`);
|
|
124
|
+
parsed[argDef.name] = value;
|
|
125
|
+
} else {
|
|
126
|
+
const command = this.commands.find(candidate => candidate.name == arg);
|
|
127
|
+
if(command) {
|
|
128
|
+
const parsedCommand = command.parse(queue.splice(0, queue.length));
|
|
129
|
+
Object.keys(parsedCommand).forEach(key => {
|
|
130
|
+
if(parsed[key] != parsedCommand[key] && parsedCommand[key] == command.defaults[key])
|
|
131
|
+
delete parsedCommand[key];
|
|
132
|
+
});
|
|
133
|
+
parsed = {...parsed, ...parsedCommand, _command: command.name};
|
|
134
|
+
} else extras.push(arg);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
this.args.filter(arg => !arg.extras).forEach(arg => {
|
|
138
|
+
if(!arg.optional && !extras.length) parsed._error.push(`Argument missing: ${arg.name.toUpperCase()}`);
|
|
139
|
+
if(extras.length) parsed[arg.name] = extras.splice(0, 1)[0];
|
|
140
|
+
});
|
|
141
|
+
const extraKey = this.args.find(arg => arg.extras)?.name || '_extra';
|
|
142
|
+
parsed[extraKey] = extras;
|
|
143
|
+
return parsed;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
help(opts = {}) {
|
|
147
|
+
const spacer = text => Array(24 - text.length || 1).fill(' ').join('');
|
|
148
|
+
if(opts.command) {
|
|
149
|
+
const parser = this.commands.find(candidate => candidate.name == opts.command);
|
|
150
|
+
if(!parser) throw new Error(`${opts.command.toUpperCase()} is not a command`);
|
|
151
|
+
return parser.help({...opts, command: undefined});
|
|
152
|
+
}
|
|
153
|
+
let message = `\n\n${opts.message || this.desc}`;
|
|
154
|
+
message += '\n\nUsage:\t' + this.examples.map(example => `${this.name} ${example}`).join('\n\t');
|
|
155
|
+
if(this.args.length) message += '\n\n\t' + this.args.map(arg => `${arg.name.toUpperCase()}${spacer(arg.name)}${arg.desc}`).join('\n\t');
|
|
156
|
+
message += '\n\nOptions:\n\t' + this.flags.map(flag => {
|
|
157
|
+
const flags = flag.flags?.join(', ') || '';
|
|
158
|
+
return `${flags}${spacer(flags)}${flag.desc}`;
|
|
159
|
+
}).join('\n\t');
|
|
160
|
+
if(this.commands.length) message += '\n\nCommands:\n\t' + this.commands.map(command => `${command.name}${spacer(command.name)}${command.desc}`).join('\n\t');
|
|
161
|
+
return `${message}\n\n`;
|
|
162
|
+
}
|
|
163
|
+
}
|
package/dist/api.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Database } from '
|
|
1
|
+
import { Database } from './utils';
|
|
2
2
|
import { BehaviorSubject } from 'rxjs';
|
|
3
3
|
import { Auth } from './auth';
|
|
4
4
|
import { PWA } from './pwa';
|
|
@@ -34,7 +34,12 @@ export type ApiOptions = {
|
|
|
34
34
|
manifest?: any;
|
|
35
35
|
/** Name of application */
|
|
36
36
|
name?: string;
|
|
37
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* Slice IDs to persist in the local IndexedDB cache for offline use.
|
|
39
|
+
*
|
|
40
|
+
* These slices can fall back to local data when the browser loses network
|
|
41
|
+
* connectivity or when Datalynk enters an `unavailable` recovery state.
|
|
42
|
+
*/
|
|
38
43
|
offline?: number[];
|
|
39
44
|
/** Display an offline banner */
|
|
40
45
|
offlineBanner?: boolean | 'top' | 'bottom';
|
|
@@ -79,7 +84,12 @@ export type ApiOptions = {
|
|
|
79
84
|
export interface ApiRequestOptions {
|
|
80
85
|
/** Skip bundling & caching */
|
|
81
86
|
noOptimize?: boolean;
|
|
82
|
-
/**
|
|
87
|
+
/**
|
|
88
|
+
* Allow this request to be queued while normal API access is unavailable.
|
|
89
|
+
*
|
|
90
|
+
* Queued requests do not receive an immediate response payload. Unauthorized
|
|
91
|
+
* or expired sessions are never written to the replay queue.
|
|
92
|
+
*/
|
|
83
93
|
offline?: boolean;
|
|
84
94
|
/** Skip the token translating step */
|
|
85
95
|
raw?: boolean;
|
|
@@ -97,6 +107,26 @@ export interface ApiError {
|
|
|
97
107
|
/** Stack trace */
|
|
98
108
|
trace?: any;
|
|
99
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Current ability to communicate with the Datalynk API.
|
|
112
|
+
*
|
|
113
|
+
* - `online` - normal API requests are available.
|
|
114
|
+
* - `offline` - the API could not be reached over the network.
|
|
115
|
+
* - `unauthorized` - the current token is expired or was rejected.
|
|
116
|
+
* - `unavailable` - the API responded with a recoverable server/API failure and
|
|
117
|
+
* the exact failed request owns recovery until it succeeds again.
|
|
118
|
+
*
|
|
119
|
+
* `offline` and `unavailable` are intentionally distinct. A browser may still
|
|
120
|
+
* have Internet access while Datalynk is `unavailable` because, for example,
|
|
121
|
+
* MySQL or an API route is failing.
|
|
122
|
+
*/
|
|
123
|
+
export type ApiConnectionStatus = 'online' | 'offline' | 'unauthorized' | 'unavailable';
|
|
124
|
+
/** The server answered, but not with a valid Datalynk API response. */
|
|
125
|
+
export declare class UnexpectedApiResponseError extends Error {
|
|
126
|
+
readonly response: Response;
|
|
127
|
+
readonly body: string;
|
|
128
|
+
constructor(response: Response, body: string);
|
|
129
|
+
}
|
|
100
130
|
/**
|
|
101
131
|
* Connect to Datalynk & send requests
|
|
102
132
|
*/
|
|
@@ -110,6 +140,13 @@ export declare class Api {
|
|
|
110
140
|
private bundleOngoing;
|
|
111
141
|
/** Track online state */
|
|
112
142
|
private heartbeat;
|
|
143
|
+
private authenticationInvalid;
|
|
144
|
+
private tokenExpiryTimeout;
|
|
145
|
+
/** Request-specific recovery after a server response proves an API call is broken. */
|
|
146
|
+
private recovery;
|
|
147
|
+
/** Retry the failed request twice immediately, then this long after each failed recovery response. */
|
|
148
|
+
private recoveryRetryInterval;
|
|
149
|
+
private readonly recoveryImmediateRetries;
|
|
113
150
|
/** LocalStorage key for persisting logins */
|
|
114
151
|
private localStorageKey;
|
|
115
152
|
/** Pending requests cache */
|
|
@@ -146,11 +183,45 @@ export declare class Api {
|
|
|
146
183
|
/** Get session info from JWT payload */
|
|
147
184
|
get jwtPayload(): JwtPayload | null;
|
|
148
185
|
private onlineOverride;
|
|
186
|
+
private initialOnline;
|
|
187
|
+
/**
|
|
188
|
+
* Detailed API connection state.
|
|
189
|
+
*
|
|
190
|
+
* Subscribe to this when callers need to distinguish physical/network
|
|
191
|
+
* offline state from authentication failure or server/API unavailability.
|
|
192
|
+
*/
|
|
193
|
+
status$: BehaviorSubject<ApiConnectionStatus>;
|
|
194
|
+
/**
|
|
195
|
+
* Backwards-compatible boolean connection state.
|
|
196
|
+
*
|
|
197
|
+
* `false` includes `offline`, `unauthorized`, and `unavailable` states.
|
|
198
|
+
*/
|
|
149
199
|
online$: BehaviorSubject<boolean>;
|
|
150
|
-
/**
|
|
200
|
+
/** Current detailed Datalynk API connection state. */
|
|
201
|
+
get status(): ApiConnectionStatus;
|
|
202
|
+
/**
|
|
203
|
+
* Whether normal Datalynk network requests are currently available.
|
|
204
|
+
*
|
|
205
|
+
* This becomes `false` for network outages, rejected/expired sessions, and
|
|
206
|
+
* request-owned server recovery. It therefore describes Datalynk
|
|
207
|
+
* availability rather than only `navigator.onLine`.
|
|
208
|
+
*/
|
|
151
209
|
get online(): boolean | null;
|
|
210
|
+
/**
|
|
211
|
+
* Whether normal Datalynk API access is currently unavailable.
|
|
212
|
+
*
|
|
213
|
+
* This is the inverse of {@link online}. It can be `true` while the browser
|
|
214
|
+
* still has Internet access, for example during MySQL/HTTP 5xx recovery.
|
|
215
|
+
*/
|
|
152
216
|
get offline(): boolean;
|
|
153
|
-
/**
|
|
217
|
+
/**
|
|
218
|
+
* Override the boolean connection state.
|
|
219
|
+
*
|
|
220
|
+
* Set `true` or `false` to force the corresponding state. Set `null` to
|
|
221
|
+
* remove the override and resume normal connection checking. This is a
|
|
222
|
+
* manual override and can supersede the current recovery state, so normal
|
|
223
|
+
* applications should generally observe {@link status} instead of forcing it.
|
|
224
|
+
*/
|
|
154
225
|
set online(value: boolean | null);
|
|
155
226
|
/** Logged in spoke */
|
|
156
227
|
get spoke(): string;
|
|
@@ -171,7 +242,27 @@ export declare class Api {
|
|
|
171
242
|
*/
|
|
172
243
|
constructor(origin: string, options?: ApiOptions);
|
|
173
244
|
private _request;
|
|
245
|
+
/** Execute exactly one HTTP API attempt. Recovery uses this directly to avoid recursive retry loops. */
|
|
246
|
+
private _requestOnce;
|
|
247
|
+
/** Only server-response failures own global recovery; auth and ordinary 4xx errors do not. */
|
|
248
|
+
private isRecoverableResponseError;
|
|
249
|
+
private isMysqlError;
|
|
250
|
+
/**
|
|
251
|
+
* A returned server failure immediately makes the client unavailable. The exact
|
|
252
|
+
* failed request is then retried twice back-to-back. After that, retries are
|
|
253
|
+
* scheduled 30 seconds after each completed failed attempt, never with overlap.
|
|
254
|
+
*/
|
|
255
|
+
private beginRecovery;
|
|
256
|
+
private runImmediateRecovery;
|
|
257
|
+
private tryRecoveryOnce;
|
|
258
|
+
private scheduleRecovery;
|
|
259
|
+
private finishRecovery;
|
|
260
|
+
private cancelRecovery;
|
|
174
261
|
private checkConnection;
|
|
262
|
+
private isTokenExpired;
|
|
263
|
+
private scheduleTokenExpiry;
|
|
264
|
+
private markUnauthorized;
|
|
265
|
+
private setConnectionStatus;
|
|
175
266
|
private offlineBanner;
|
|
176
267
|
private startHeartbeat;
|
|
177
268
|
private stopHeartbeat;
|
package/dist/api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
package/dist/auth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,GAAG,EAAC,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAC,eAAe,EAAS,MAAM,MAAM,CAAC;AAE7C,OAAO,EAAC,WAAW,EAAE,kBAAkB,EAAC,MAAM,gBAAgB,CAAC;AAE/D,mBAAmB;AACnB,MAAM,MAAM,IAAI,GAAG;IAClB,6BAA6B;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,4BAA4B;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,cAAc;IACd,EAAE,EAAE,MAAM,CAAC;IACX,8CAA8C;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8CAA8C;IAC9C,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,mDAAmD;IACnD,UAAU,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,OAAO,EAAE,MAAM,CAAC;IAChB,kCAAkC;IAClC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,kCAAkC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,iCAAiC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,6CAA6C;IAC7C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8BAA8B;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2CAA2C;IAC3C,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,kDAAkD;IAClD,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,6CAA6C;IAC7C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sDAAsD;IACtD,QAAQ,EAAE,MAAM,CAAC;IACjB,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,8BAA8B;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,4BAA4B;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,4CAA4C;IAC5C,KAAK,EAAE,OAAO,CAAC;IACf,kCAAkC;IAClC,QAAQ,EAAE,OAAO,CAAC;IAClB,4BAA4B;IAC5B,KAAK,EAAE,MAAM,CAAC;CACd,CAAA;AAED;;GAEG;AACH,qBAAa,IAAI;IAcJ,OAAO,CAAC,QAAQ,CAAC,GAAG;IAbhC,OAAO,CAAC,YAAY,CAAC,CAAM;IAE3B,oCAAoC;IACpC,KAAK,2CAA2D;IAEhE,mBAAmB;IACnB,IAAI,IAAI,IAGO,IAAI,GAAG,IAAI,GAAG,SAAS,CAHM;IAE5C,4BAA4B;IAC5B,IAAI,IAAI,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,SAAS,EAA4B;IAElE,IAAI,KAAK,kBAAiD;gBAE7B,GAAG,EAAE,GAAG;
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
|