@akylas/nativescript-app-utils 2.2.1 → 2.2.3
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/CHANGELOG.md +8 -0
- package/package.json +2 -2
- package/platforms/android/nativescript_app_utils.aar +0 -0
- package/worker/BaseWorker.d.ts +47 -0
- package/worker/BaseWorker.js +126 -0
- package/worker/BaseWorkerHandler.d.ts +31 -0
- package/worker/BaseWorkerHandler.js +143 -0
- package/worker/queue.d.ts +7 -0
- package/worker/queue.js +49 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [2.2.3](https://github.com/akylas/nativescript-app-utils/compare/v2.2.2...v2.2.3) (2025-01-31)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @akylas/nativescript-app-utils
|
|
9
|
+
|
|
10
|
+
## [2.2.2](https://github.com/akylas/nativescript-app-utils/compare/v2.2.1...v2.2.2) (2025-01-27)
|
|
11
|
+
|
|
12
|
+
**Note:** Version bump only for package @akylas/nativescript-app-utils
|
|
13
|
+
|
|
6
14
|
## [2.2.1](https://github.com/akylas/nativescript-app-utils/compare/v2.2.0...v2.2.1) (2024-12-18)
|
|
7
15
|
|
|
8
16
|
### Bug Fixes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akylas/nativescript-app-utils",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.3",
|
|
4
4
|
"description": "Provides API for changing the styles of SystemUI (StatusBar, NavigationBar...) on iOS.",
|
|
5
5
|
"main": "index",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -66,5 +66,5 @@
|
|
|
66
66
|
"dependencies": {
|
|
67
67
|
"@nativescript-community/ui-share-file": "1.3.3"
|
|
68
68
|
},
|
|
69
|
-
"gitHead": "
|
|
69
|
+
"gitHead": "a4acea396bce45159bcc13904cec638a444bc618"
|
|
70
70
|
}
|
|
Binary file
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Optional } from '@nativescript/core/utils/typescript-utils';
|
|
2
|
+
import { EventData, Observable } from '@nativescript/core';
|
|
3
|
+
export type WorkerEventType = 'event' | 'error' | 'started' | 'terminate';
|
|
4
|
+
export interface WorkerPostOptions {
|
|
5
|
+
id?: number;
|
|
6
|
+
messageData?: string;
|
|
7
|
+
}
|
|
8
|
+
export type WorkerPostEvent = {
|
|
9
|
+
type: string;
|
|
10
|
+
} & WorkerPostOptions;
|
|
11
|
+
export interface WorkerEvent {
|
|
12
|
+
data: {
|
|
13
|
+
messageData?: any;
|
|
14
|
+
error?: Error;
|
|
15
|
+
nativeData?: {
|
|
16
|
+
[k: string]: any;
|
|
17
|
+
};
|
|
18
|
+
type: string;
|
|
19
|
+
id?: number;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export interface IWorker extends BaseWorker {
|
|
23
|
+
onmessage: Function;
|
|
24
|
+
postMessage(event: WorkerPostEvent): any;
|
|
25
|
+
}
|
|
26
|
+
export declare abstract class BaseWorker extends Observable {
|
|
27
|
+
protected context: any;
|
|
28
|
+
constructor(context: any);
|
|
29
|
+
onmessage: Function;
|
|
30
|
+
postMessage(event: WorkerPostEvent): void;
|
|
31
|
+
abstract receivedMessage(event: WorkerEvent): any;
|
|
32
|
+
receivedMessageBase(event: WorkerEvent): boolean;
|
|
33
|
+
messagePromises: {
|
|
34
|
+
[key: string]: {
|
|
35
|
+
resolve: Function;
|
|
36
|
+
reject: Function;
|
|
37
|
+
timeoutTimer: number;
|
|
38
|
+
}[];
|
|
39
|
+
};
|
|
40
|
+
postPromiseMessage<T = any>(type: string, messageData: any, id?: number, timeout?: number, nativeData?: any): Promise<T>;
|
|
41
|
+
stop(error?: any, id?: any): Promise<void>;
|
|
42
|
+
notify<T extends Optional<EventData & {
|
|
43
|
+
error?: Error;
|
|
44
|
+
}, 'object'>>(data: T): void;
|
|
45
|
+
notifyAndAwait<T = any>(eventName: any, data: any): Promise<T>;
|
|
46
|
+
sendError(error: any): Promise<void>;
|
|
47
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { time } from '@nativescript/core/profiling';
|
|
2
|
+
import { getWorkerContextValue, setWorkerContextValue } from '@akylas/nativescript-app-utils';
|
|
3
|
+
import { Observable } from '@nativescript/core';
|
|
4
|
+
export class BaseWorker extends Observable {
|
|
5
|
+
constructor(context) {
|
|
6
|
+
super();
|
|
7
|
+
this.context = context;
|
|
8
|
+
this.messagePromises = {};
|
|
9
|
+
context.onmessage = (event) => {
|
|
10
|
+
// DEV_LOG && console.log(TAG, 'onmessage', Date.now(), event);
|
|
11
|
+
if (typeof event.data.messageData === 'string') {
|
|
12
|
+
try {
|
|
13
|
+
event.data.messageData = JSON.parse(event.data.messageData);
|
|
14
|
+
}
|
|
15
|
+
catch (error) { }
|
|
16
|
+
}
|
|
17
|
+
if (Array.isArray(event.data.nativeData)) {
|
|
18
|
+
event.data.nativeData = event.data.nativeData.reduce((acc, key) => {
|
|
19
|
+
const actualKey = key.split('$$$')[1];
|
|
20
|
+
acc[actualKey] = getWorkerContextValue(key);
|
|
21
|
+
setWorkerContextValue(key, null);
|
|
22
|
+
return acc;
|
|
23
|
+
}, {});
|
|
24
|
+
}
|
|
25
|
+
if (typeof event.data.error === 'string') {
|
|
26
|
+
try {
|
|
27
|
+
event.data.error = JSON.parse(event.data.error);
|
|
28
|
+
}
|
|
29
|
+
catch (error) { }
|
|
30
|
+
}
|
|
31
|
+
const handled = this.receivedMessageBase(event);
|
|
32
|
+
if (!handled) {
|
|
33
|
+
this.receivedMessage(event);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
this.postMessage({
|
|
37
|
+
type: 'started'
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
postMessage(event) {
|
|
41
|
+
global.postMessage(event);
|
|
42
|
+
}
|
|
43
|
+
receivedMessageBase(event) {
|
|
44
|
+
const data = event.data;
|
|
45
|
+
const id = data.id;
|
|
46
|
+
// DEV_LOG && console.log(TAG, 'receivedMessage', data.type, id, id && this.messagePromises.hasOwnProperty(id), Object.keys(this.messagePromises), data);
|
|
47
|
+
if (data.type === 'terminate') {
|
|
48
|
+
this.context.close();
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
if (id && this.messagePromises.hasOwnProperty(id)) {
|
|
52
|
+
this.messagePromises[id].forEach(function (executor) {
|
|
53
|
+
executor.timeoutTimer && clearTimeout(executor.timeoutTimer);
|
|
54
|
+
const messageData = data.messageData;
|
|
55
|
+
if (!!messageData?.error) {
|
|
56
|
+
executor.reject(messageData.error);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
executor.resolve(messageData);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
delete this.messagePromises[id];
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
postPromiseMessage(type, messageData, id = 0, timeout = 0, nativeData) {
|
|
68
|
+
return new Promise((resolve, reject) => {
|
|
69
|
+
id = id || time();
|
|
70
|
+
// DEV_LOG && console.warn(TAG, 'postPromiseMessage', type, id, timeout, messageData);
|
|
71
|
+
if (id || timeout) {
|
|
72
|
+
this.messagePromises[id] = this.messagePromises[id] || [];
|
|
73
|
+
let timeoutTimer;
|
|
74
|
+
if (timeout > 0) {
|
|
75
|
+
timeoutTimer = setTimeout(() => {
|
|
76
|
+
// we need to try catch because the simple fact of creating a new Error actually throws.
|
|
77
|
+
// so we will get an uncaughtException
|
|
78
|
+
try {
|
|
79
|
+
reject(new Error('timeout'));
|
|
80
|
+
}
|
|
81
|
+
catch { }
|
|
82
|
+
delete this.messagePromises[id];
|
|
83
|
+
}, timeout);
|
|
84
|
+
}
|
|
85
|
+
this.messagePromises[id].push({ reject, resolve, timeoutTimer });
|
|
86
|
+
}
|
|
87
|
+
const mData = {
|
|
88
|
+
id,
|
|
89
|
+
messageData: JSON.stringify(messageData),
|
|
90
|
+
type
|
|
91
|
+
};
|
|
92
|
+
// DEV_LOG && console.log(TAG, 'postMessage', mData, this.messagePromises[id]);
|
|
93
|
+
this.postMessage(mData);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
async stop(error, id) {
|
|
97
|
+
this.postMessage({
|
|
98
|
+
type: 'terminate'
|
|
99
|
+
});
|
|
100
|
+
this.context.close();
|
|
101
|
+
}
|
|
102
|
+
notify(data) {
|
|
103
|
+
// DEV_LOG && console.log(TAG, 'notify', data.eventName);
|
|
104
|
+
//we are a fake observable
|
|
105
|
+
if (data.error) {
|
|
106
|
+
// Error is not really serializable so we need custom handling
|
|
107
|
+
const { nativeException, ...error } = data.error;
|
|
108
|
+
data.error = { message: data.error.toString(), stack: data.error.stack, ...data.error };
|
|
109
|
+
}
|
|
110
|
+
this.postMessage({
|
|
111
|
+
messageData: JSON.stringify(data),
|
|
112
|
+
type: 'event'
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
async notifyAndAwait(eventName, data) {
|
|
116
|
+
return this.postPromiseMessage('event', { data, eventName });
|
|
117
|
+
}
|
|
118
|
+
async sendError(error) {
|
|
119
|
+
const { nativeException, ...realError } = error;
|
|
120
|
+
this.postMessage({
|
|
121
|
+
messageData: JSON.stringify({ error: { message: error.toString(), stack: error.stack, ...error } }),
|
|
122
|
+
type: 'error'
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=BaseWorker.js.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Observable } from '@nativescript/core';
|
|
2
|
+
import type { BaseWorker, WorkerEventType, WorkerPostEvent } from './BaseWorker';
|
|
3
|
+
import Queue from './queue';
|
|
4
|
+
export default abstract class BaseWorkerHandler<T extends BaseWorker> extends Observable {
|
|
5
|
+
private createWorker;
|
|
6
|
+
constructor(createWorker: () => Worker);
|
|
7
|
+
worker: T;
|
|
8
|
+
messagePromises: {
|
|
9
|
+
[key: string]: {
|
|
10
|
+
resolve: Function;
|
|
11
|
+
reject: Function;
|
|
12
|
+
timeoutTimer: number;
|
|
13
|
+
}[];
|
|
14
|
+
};
|
|
15
|
+
abstract onWorkerEvent(eventData: any): any;
|
|
16
|
+
onWorkerMessage(event: {
|
|
17
|
+
data: {
|
|
18
|
+
id?: number;
|
|
19
|
+
type: WorkerEventType;
|
|
20
|
+
messageData?: string;
|
|
21
|
+
nativeDatas?: {
|
|
22
|
+
[k: string]: any;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
}): Promise<void>;
|
|
26
|
+
abstract handleError(error: any): any;
|
|
27
|
+
abstract handleWorkerError(error: any): any;
|
|
28
|
+
queue: Queue;
|
|
29
|
+
internalSendMessageToWorker(data: WorkerPostEvent): Promise<unknown>;
|
|
30
|
+
sendMessageToWorker<T = any>(type: string, messageData?: any, id?: number, error?: any, isResponse?: boolean, timeout?: number, nativeData?: any): Promise<T>;
|
|
31
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { setWorkerContextValue } from '@akylas/nativescript-app-utils';
|
|
2
|
+
import { Observable } from '@nativescript/core';
|
|
3
|
+
import { time } from '@nativescript/core/profiling';
|
|
4
|
+
import Queue from './queue';
|
|
5
|
+
export default class BaseWorkerHandler extends Observable {
|
|
6
|
+
constructor(createWorker) {
|
|
7
|
+
super();
|
|
8
|
+
this.createWorker = createWorker;
|
|
9
|
+
this.messagePromises = {};
|
|
10
|
+
this.queue = new Queue();
|
|
11
|
+
}
|
|
12
|
+
async onWorkerMessage(event) {
|
|
13
|
+
// DEV_LOG && console.log('onWorkerMessage', event);
|
|
14
|
+
const data = event.data;
|
|
15
|
+
const id = data.id;
|
|
16
|
+
try {
|
|
17
|
+
let messageData = data.messageData;
|
|
18
|
+
if (typeof messageData === 'string') {
|
|
19
|
+
try {
|
|
20
|
+
messageData = JSON.parse(messageData);
|
|
21
|
+
}
|
|
22
|
+
catch (error) { }
|
|
23
|
+
}
|
|
24
|
+
// DEV_LOG && console.error(TAG, 'onWorkerMessage', id, data.type, id && this.messagePromises.hasOwnProperty(id), Object.keys(this.messagePromises), messageData);
|
|
25
|
+
if (id && this.messagePromises.hasOwnProperty(id)) {
|
|
26
|
+
this.messagePromises[id].forEach(function (executor) {
|
|
27
|
+
executor.timeoutTimer && clearTimeout(executor.timeoutTimer);
|
|
28
|
+
executor.resolve(messageData);
|
|
29
|
+
});
|
|
30
|
+
delete this.messagePromises[id];
|
|
31
|
+
}
|
|
32
|
+
const eventData = messageData;
|
|
33
|
+
switch (data.type) {
|
|
34
|
+
case 'event':
|
|
35
|
+
await this.onWorkerEvent(eventData);
|
|
36
|
+
break;
|
|
37
|
+
case 'error':
|
|
38
|
+
this.handleWorkerError(eventData.error);
|
|
39
|
+
// showError(CustomError.fromObject(eventData.error));
|
|
40
|
+
break;
|
|
41
|
+
case 'started':
|
|
42
|
+
this.notify({ eventName: 'worker_started' });
|
|
43
|
+
break;
|
|
44
|
+
case 'terminate':
|
|
45
|
+
// console.info('worker stopped');
|
|
46
|
+
this.worker = null;
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
console.error(error, error.stack);
|
|
52
|
+
this.handleError(error);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async internalSendMessageToWorker(data) {
|
|
56
|
+
return this.queue.add(async () => {
|
|
57
|
+
// DEV_LOG && console.warn('internalSendMessageToWorker', data.type, !!this.worker);
|
|
58
|
+
if (!this.worker) {
|
|
59
|
+
await new Promise((resolve, reject) => {
|
|
60
|
+
this.once('worker_started', () => {
|
|
61
|
+
if (timeoutTimer) {
|
|
62
|
+
clearTimeout(timeoutTimer);
|
|
63
|
+
}
|
|
64
|
+
resolve();
|
|
65
|
+
});
|
|
66
|
+
const worker = (this.worker = this.createWorker());
|
|
67
|
+
worker.onmessage = this.onWorkerMessage.bind(this);
|
|
68
|
+
worker.onerror = (e) => {
|
|
69
|
+
reject(e);
|
|
70
|
+
this.worker = null;
|
|
71
|
+
};
|
|
72
|
+
const timeoutTimer = setTimeout(() => {
|
|
73
|
+
reject(new Error('failed_to_start_worker'));
|
|
74
|
+
}, 2000);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
this.worker.postMessage(data);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
async sendMessageToWorker(type, messageData, id, error, isResponse = false, timeout = 0, nativeData) {
|
|
81
|
+
// DEV_LOG && console.info('Sync', 'sendMessageToWorker', type, id, timeout, isResponse, !isResponse && (id || timeout), messageData, nativeData, this.worker);
|
|
82
|
+
if (!isResponse && (id || timeout)) {
|
|
83
|
+
return new Promise(async (resolve, reject) => {
|
|
84
|
+
// const id = Date.now().valueOf();
|
|
85
|
+
id = id || time();
|
|
86
|
+
this.messagePromises[id] = this.messagePromises[id] || [];
|
|
87
|
+
let timeoutTimer;
|
|
88
|
+
if (timeout > 0) {
|
|
89
|
+
timeoutTimer = setTimeout(() => {
|
|
90
|
+
// we need to try catch because the simple fact of creating a new Error actually throws.
|
|
91
|
+
// so we will get an uncaughtException
|
|
92
|
+
try {
|
|
93
|
+
reject(new Error('timeout'));
|
|
94
|
+
}
|
|
95
|
+
catch { }
|
|
96
|
+
delete this.messagePromises[id];
|
|
97
|
+
}, timeout);
|
|
98
|
+
}
|
|
99
|
+
this.messagePromises[id].push({ reject, resolve, timeoutTimer });
|
|
100
|
+
const keys = Object.keys(nativeData);
|
|
101
|
+
const nativeDataKeysPrefix = Date.now() + '$$$';
|
|
102
|
+
keys.forEach((k) => {
|
|
103
|
+
setWorkerContextValue(nativeDataKeysPrefix + k, nativeData[k]._native || nativeData[k]);
|
|
104
|
+
});
|
|
105
|
+
const data = {
|
|
106
|
+
error: !!error ? JSON.stringify(error.toJSON() ? error.toJSON() : { message: error.toString(), ...error }) : undefined,
|
|
107
|
+
id,
|
|
108
|
+
nativeDataKeysPrefix,
|
|
109
|
+
messageData: !!messageData ? JSON.stringify(messageData) : undefined,
|
|
110
|
+
nativeData: keys.map((k) => nativeDataKeysPrefix + k),
|
|
111
|
+
type
|
|
112
|
+
};
|
|
113
|
+
// DEV_LOG && console.info('Sync', 'postMessage', JSON.stringify(data));
|
|
114
|
+
try {
|
|
115
|
+
await this.internalSendMessageToWorker(data);
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
reject(error);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
// DEV_LOG && console.info('Sync', 'postMessage', 'test');
|
|
124
|
+
const keys = Object.keys(nativeData);
|
|
125
|
+
const nativeDataKeysPrefix = Date.now() + '$$$';
|
|
126
|
+
keys.forEach((k) => {
|
|
127
|
+
setWorkerContextValue(nativeDataKeysPrefix + k, nativeData[k]._native || nativeData[k]);
|
|
128
|
+
});
|
|
129
|
+
const data = {
|
|
130
|
+
error: !!error ? JSON.stringify({ message: error.toString(), ...error }) : undefined,
|
|
131
|
+
id,
|
|
132
|
+
nativeDataKeysPrefix,
|
|
133
|
+
messageData: !!messageData ? JSON.stringify(messageData) : undefined,
|
|
134
|
+
nativeData: keys.map((k) => nativeDataKeysPrefix + k),
|
|
135
|
+
type
|
|
136
|
+
};
|
|
137
|
+
// DEV_LOG && console.info('Sync', 'postMessage', JSON.stringify(data));
|
|
138
|
+
await this.internalSendMessageToWorker(data);
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=BaseWorkerHandler.js.map
|
package/worker/queue.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Observable } from '@nativescript/core';
|
|
2
|
+
export default class Queue extends Observable {
|
|
3
|
+
constructor() {
|
|
4
|
+
super(...arguments);
|
|
5
|
+
this.workingOnPromise = false;
|
|
6
|
+
this.queue = [];
|
|
7
|
+
}
|
|
8
|
+
add(promise) {
|
|
9
|
+
return new Promise((resolve, reject) => {
|
|
10
|
+
this.queue.push({
|
|
11
|
+
promise,
|
|
12
|
+
resolve,
|
|
13
|
+
reject
|
|
14
|
+
});
|
|
15
|
+
this.dequeue();
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
dequeue() {
|
|
19
|
+
if (this.workingOnPromise) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
const item = this.queue.shift();
|
|
23
|
+
if (!item) {
|
|
24
|
+
this.notify({ eventName: 'done' });
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
this.workingOnPromise = true;
|
|
29
|
+
item.promise()
|
|
30
|
+
.then((value) => {
|
|
31
|
+
this.workingOnPromise = false;
|
|
32
|
+
item.resolve(value);
|
|
33
|
+
this.dequeue();
|
|
34
|
+
})
|
|
35
|
+
.catch((err) => {
|
|
36
|
+
this.workingOnPromise = false;
|
|
37
|
+
item.reject(err);
|
|
38
|
+
this.dequeue();
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
this.workingOnPromise = false;
|
|
43
|
+
item.reject(err);
|
|
44
|
+
this.dequeue();
|
|
45
|
+
}
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=queue.js.map
|