@sincpro/mobile 0.4.1 → 0.4.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/dist/entrypoints/cron/EBuiltinCron.d.ts +4 -0
- package/dist/entrypoints/cron/EBuiltinCron.js +5 -0
- package/dist/entrypoints/cron/checkNetworkStatus.cron.js +2 -1
- package/dist/entrypoints/db/migrations.js +0 -18
- package/dist/entrypoints/ui/common_provider.d.ts +1 -0
- package/dist/entrypoints/ui/common_provider.js +14 -6
- package/dist/framework/createApp.d.ts +8 -0
- package/dist/framework/createApp.js +3 -1
- package/dist/framework/kernel.d.ts +2 -1
- package/dist/framework/kernel.js +8 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/infrastructure/telemetry/telemetry_flush_worker.js +3 -2
- package/dist/infrastructure/workers/CronWorker.d.ts +10 -2
- package/dist/infrastructure/workers/CronWorker.js +18 -5
- package/dist/infrastructure/workers/index.d.ts +1 -1
- package/package.json +2 -2
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { EBuiltinCron } from "../../entrypoints/cron/EBuiltinCron";
|
|
1
2
|
import { loggerCronJobs } from "../../infrastructure/logger";
|
|
2
3
|
import { CronWorker } from "../../infrastructure/workers";
|
|
3
4
|
import { networkUseCases } from "../../services/network.service";
|
|
@@ -6,5 +7,5 @@ async function checkNetworkStatus() {
|
|
|
6
7
|
await networkUseCases.getNetworkStatus();
|
|
7
8
|
loggerCronJobs.info("Finished checking network status");
|
|
8
9
|
}
|
|
9
|
-
const cronCheckNetworkStatus = new CronWorker(
|
|
10
|
+
const cronCheckNetworkStatus = new CronWorker(EBuiltinCron.CHECK_NETWORK, checkNetworkStatus, 2.5);
|
|
10
11
|
export default cronCheckNetworkStatus;
|
|
@@ -155,23 +155,6 @@ async function createSpansQueueTable() {
|
|
|
155
155
|
ON ${DATABASE_TABLES.SPANS_QUEUE}(trace_id);
|
|
156
156
|
`);
|
|
157
157
|
}
|
|
158
|
-
// Adds events and links columns to spans_queue for existing installs.
|
|
159
|
-
// Fresh installs get them from createSpansQueueTable above; ALTER TABLE is a
|
|
160
|
-
// no-op on those (caught and ignored — duplicate column error is expected).
|
|
161
|
-
async function addSpansQueueEventsLinks() {
|
|
162
|
-
try {
|
|
163
|
-
await DBCursor.execAsync(`ALTER TABLE ${DATABASE_TABLES.SPANS_QUEUE} ADD COLUMN events TEXT NOT NULL DEFAULT '[]'`);
|
|
164
|
-
}
|
|
165
|
-
catch {
|
|
166
|
-
// column already exists on fresh installs
|
|
167
|
-
}
|
|
168
|
-
try {
|
|
169
|
-
await DBCursor.execAsync(`ALTER TABLE ${DATABASE_TABLES.SPANS_QUEUE} ADD COLUMN links TEXT NOT NULL DEFAULT '[]'`);
|
|
170
|
-
}
|
|
171
|
-
catch {
|
|
172
|
-
// column already exists on fresh installs
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
158
|
const MIGRATIONS = [
|
|
176
159
|
{ name: DATABASE_TABLES.SETTINGS, migrationFn: createSettingsTable },
|
|
177
160
|
{ name: DATABASE_TABLES.EVENT_QUEUE, migrationFn: createEventQueueTable },
|
|
@@ -183,6 +166,5 @@ const MIGRATIONS = [
|
|
|
183
166
|
},
|
|
184
167
|
{ name: DATABASE_TABLES.TELEMETRY_QUEUE, migrationFn: createTelemetryQueueTable },
|
|
185
168
|
{ name: DATABASE_TABLES.SPANS_QUEUE, migrationFn: createSpansQueueTable },
|
|
186
|
-
{ name: "spans_queue_v2_events_links", migrationFn: addSpansQueueEventsLinks },
|
|
187
169
|
];
|
|
188
170
|
export default MIGRATIONS;
|
|
@@ -25,6 +25,7 @@ interface ICommonContext {
|
|
|
25
25
|
geoError: string | null;
|
|
26
26
|
checkGeoPermission: () => Promise<void>;
|
|
27
27
|
requestGeoPermission: () => Promise<boolean>;
|
|
28
|
+
locale: string | null;
|
|
28
29
|
timezone: string | null;
|
|
29
30
|
updateTimezone: (tz: TimezoneLocale) => Promise<void>;
|
|
30
31
|
colorScheme: "light" | "dark" | "system";
|
|
@@ -23,6 +23,7 @@ export function CommonProvider({ children, lightTheme, darkTheme }) {
|
|
|
23
23
|
const [hasGeoPermission, setHasGeoPermission] = useState(false);
|
|
24
24
|
const [geoIsLoading, setGeoIsLoading] = useState(false);
|
|
25
25
|
const [geoError, setGeoError] = useState(null);
|
|
26
|
+
const [locale, setLocale] = useState(null);
|
|
26
27
|
const [timezone, setTimezone] = useState(null);
|
|
27
28
|
const [colorScheme, setColorSchemeState] = useState("system");
|
|
28
29
|
const errorTimeoutRef = useRef(null);
|
|
@@ -85,6 +86,7 @@ export function CommonProvider({ children, lightTheme, darkTheme }) {
|
|
|
85
86
|
await SettingsRepository.saveOneSetting(TIMEZONE_SETTING_KEY, tz.timezone);
|
|
86
87
|
await SettingsRepository.saveOneSetting(LOCALE_SETTING_KEY, tz.locale);
|
|
87
88
|
setTimezone(tz.timezone);
|
|
89
|
+
setLocale(tz.locale);
|
|
88
90
|
}
|
|
89
91
|
catch (error) {
|
|
90
92
|
logger.warn(error);
|
|
@@ -127,16 +129,20 @@ export function CommonProvider({ children, lightTheme, darkTheme }) {
|
|
|
127
129
|
return () => sub.remove();
|
|
128
130
|
}, [colorScheme, applyTheme]);
|
|
129
131
|
useEffect(() => {
|
|
130
|
-
async function
|
|
132
|
+
async function loadTimezoneAndLocale() {
|
|
131
133
|
try {
|
|
132
|
-
const timezoneResult = await
|
|
134
|
+
const [timezoneResult, localeResult] = await Promise.all([
|
|
135
|
+
SettingsRepository.getSettingByName(TIMEZONE_SETTING_KEY),
|
|
136
|
+
SettingsRepository.getSettingByName(LOCALE_SETTING_KEY),
|
|
137
|
+
]);
|
|
133
138
|
setTimezone(timezoneResult);
|
|
139
|
+
setLocale(localeResult);
|
|
134
140
|
}
|
|
135
141
|
catch (error) {
|
|
136
|
-
logger.warn("Failed to load timezone", error);
|
|
142
|
+
logger.warn("Failed to load timezone/locale", error);
|
|
137
143
|
}
|
|
138
144
|
}
|
|
139
|
-
|
|
145
|
+
loadTimezoneAndLocale();
|
|
140
146
|
}, []);
|
|
141
147
|
useEffect(() => {
|
|
142
148
|
loggerUseCases.debug("[CommonProvider] Setting up UIEventBus listeners");
|
|
@@ -191,12 +197,12 @@ export function CommonProvider({ children, lightTheme, darkTheme }) {
|
|
|
191
197
|
function handleCronStart(payload) {
|
|
192
198
|
if (!payload || typeof payload !== "object" || !("task" in payload))
|
|
193
199
|
return;
|
|
194
|
-
const { task } = payload;
|
|
200
|
+
const { task, label } = payload;
|
|
195
201
|
setCronTasks((prev) => {
|
|
196
202
|
const next = new Map(prev);
|
|
197
203
|
next.set(task, {
|
|
198
204
|
id: task,
|
|
199
|
-
label: task,
|
|
205
|
+
label: label ?? task,
|
|
200
206
|
type: "cron",
|
|
201
207
|
status: "running",
|
|
202
208
|
startedAt: Date.now(),
|
|
@@ -255,6 +261,7 @@ export function CommonProvider({ children, lightTheme, darkTheme }) {
|
|
|
255
261
|
geoError,
|
|
256
262
|
checkGeoPermission,
|
|
257
263
|
requestGeoPermission,
|
|
264
|
+
locale,
|
|
258
265
|
timezone,
|
|
259
266
|
updateTimezone,
|
|
260
267
|
colorScheme,
|
|
@@ -272,6 +279,7 @@ export function CommonProvider({ children, lightTheme, darkTheme }) {
|
|
|
272
279
|
geoError,
|
|
273
280
|
checkGeoPermission,
|
|
274
281
|
requestGeoPermission,
|
|
282
|
+
locale,
|
|
275
283
|
timezone,
|
|
276
284
|
updateTimezone,
|
|
277
285
|
colorScheme,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { TelemetryConfig } from "../infrastructure/telemetry";
|
|
2
|
+
import type { CronWorkerOpts } from "../infrastructure/workers";
|
|
2
3
|
import type { DomainModule } from "./domain_module";
|
|
3
4
|
import { Kernel } from "./kernel";
|
|
4
5
|
export interface AppConfig {
|
|
@@ -8,5 +9,12 @@ export interface AppConfig {
|
|
|
8
9
|
* after migrations complete — so the `telemetry_queue` table is guaranteed to exist.
|
|
9
10
|
*/
|
|
10
11
|
telemetry?: TelemetryConfig;
|
|
12
|
+
/**
|
|
13
|
+
* Override display options for any cron by taskName.
|
|
14
|
+
* Useful for showing/hiding toasts on internal crons (CHECK_NETWORK, TELEMETRY_FLUSH, etc.).
|
|
15
|
+
* @example
|
|
16
|
+
* crons: { CHECK_NETWORK: { showToast: true, label: "Verificando conexión" } }
|
|
17
|
+
*/
|
|
18
|
+
crons?: Record<string, Partial<CronWorkerOpts>>;
|
|
11
19
|
}
|
|
12
20
|
export declare function createApp(config?: AppConfig): Promise<Kernel>;
|
|
@@ -4,8 +4,10 @@ import { Kernel } from "./kernel";
|
|
|
4
4
|
import { orchestrator } from "./orchestrator";
|
|
5
5
|
export async function createApp(config = {}) {
|
|
6
6
|
const kernel = new Kernel([baseModule, ...(config.domains ?? [])]);
|
|
7
|
+
if (config.crons)
|
|
8
|
+
kernel.applyCronConfig(config.crons);
|
|
7
9
|
orchestrator.configure(kernel);
|
|
8
|
-
await orchestrator.bootstrap();
|
|
10
|
+
await orchestrator.bootstrap();
|
|
9
11
|
if (config.telemetry) {
|
|
10
12
|
await initTelemetry(config.telemetry);
|
|
11
13
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { IMigration } from "../domain/database";
|
|
2
2
|
import type { Subscriber } from "../domain/event_sourcing";
|
|
3
|
-
import type { CronWorker } from "../infrastructure/workers";
|
|
3
|
+
import type { CronWorker, CronWorkerOpts } from "../infrastructure/workers";
|
|
4
4
|
import type { DomainModule } from "./domain_module";
|
|
5
5
|
export declare class Kernel {
|
|
6
6
|
private readonly modules;
|
|
@@ -12,6 +12,7 @@ export declare class Kernel {
|
|
|
12
12
|
persistedTables(): string[];
|
|
13
13
|
subscribersByKey(): Record<string, Subscriber[]>;
|
|
14
14
|
cronsByKey(): Record<string, CronWorker[]>;
|
|
15
|
+
applyCronConfig(config: Record<string, Partial<CronWorkerOpts>>): void;
|
|
15
16
|
runMigrations(): Promise<void>;
|
|
16
17
|
bootstrap(): Promise<void>;
|
|
17
18
|
restartDatabase(): Promise<void>;
|
package/dist/framework/kernel.js
CHANGED
|
@@ -36,6 +36,14 @@ export class Kernel {
|
|
|
36
36
|
}
|
|
37
37
|
return map;
|
|
38
38
|
}
|
|
39
|
+
applyCronConfig(config) {
|
|
40
|
+
for (const crons of Object.values(this.cronsByKey())) {
|
|
41
|
+
for (const cron of crons) {
|
|
42
|
+
if (config[cron.taskName])
|
|
43
|
+
cron.configure(config[cron.taskName]);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
39
47
|
async runMigrations() {
|
|
40
48
|
for (const { name, migrationFn } of this.migrations()) {
|
|
41
49
|
logger.info(`Running migration: ${name}`);
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { EBuiltinCron } from "./entrypoints/cron/EBuiltinCron";
|
|
1
2
|
export { createAppShell } from "./entrypoints/ui/AppShell";
|
|
2
3
|
export { CommonProvider, useCommon } from "./entrypoints/ui/common_provider";
|
|
3
4
|
export { ActiveDomainApp, DomainSwitcherProvider, useDomainSwitcher, } from "./entrypoints/ui/domain_switcher";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { InternetIsDownEvent, InternetIsUpEvent, } from "../../domain/connectivity/events";
|
|
2
|
+
import { EBuiltinCron } from "../../entrypoints/cron/EBuiltinCron";
|
|
2
3
|
import { UIEventBus } from "../../infrastructure/ui/UIEventBus";
|
|
3
|
-
import { CronWorker } from "../../infrastructure/workers
|
|
4
|
+
import { CronWorker } from "../../infrastructure/workers";
|
|
4
5
|
/** Default background-flush interval (minutes). ≥15 → real OS background task. */
|
|
5
6
|
export const DEFAULT_BACKGROUND_INTERVAL_MIN = 15;
|
|
6
7
|
/**
|
|
@@ -49,7 +50,7 @@ export class TelemetryFlushWorker {
|
|
|
49
50
|
// offline backlog drains promptly when the OS runs the task.
|
|
50
51
|
const intervalMin = this.opts.backgroundIntervalMin ?? DEFAULT_BACKGROUND_INTERVAL_MIN;
|
|
51
52
|
if (intervalMin > 0) {
|
|
52
|
-
this.cron = new CronWorker(
|
|
53
|
+
this.cron = new CronWorker(EBuiltinCron.TELEMETRY_FLUSH, () => this.flush.run({ resetBackoff: true }), intervalMin);
|
|
53
54
|
this.cron.start().catch(() => { });
|
|
54
55
|
}
|
|
55
56
|
}
|
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
export type CronJob = () => Promise<void>;
|
|
2
|
+
export interface CronWorkerOpts {
|
|
3
|
+
requiresAuth?: boolean;
|
|
4
|
+
label?: string;
|
|
5
|
+
showToast?: boolean;
|
|
6
|
+
}
|
|
2
7
|
export declare class CronWorker {
|
|
3
8
|
readonly taskName: string;
|
|
4
9
|
private readonly job;
|
|
5
10
|
private readonly intervalMin;
|
|
6
|
-
readonly requiresAuth: boolean;
|
|
7
11
|
private stopFn?;
|
|
8
12
|
private isRegistering;
|
|
9
13
|
private isExecuting;
|
|
10
14
|
private registrationError?;
|
|
11
|
-
|
|
15
|
+
readonly requiresAuth: boolean;
|
|
16
|
+
label?: string;
|
|
17
|
+
private showToast;
|
|
18
|
+
constructor(taskName: string, job: CronJob, intervalMin?: number, opts?: CronWorkerOpts);
|
|
19
|
+
configure(opts: Partial<CronWorkerOpts>): void;
|
|
12
20
|
/**
|
|
13
21
|
* Registers a task with a specified interval and execution logic.
|
|
14
22
|
* If the interval is less than 15 minutes, a repeating job is created using a defined stop function.
|
|
@@ -6,16 +6,26 @@ export class CronWorker {
|
|
|
6
6
|
taskName;
|
|
7
7
|
job;
|
|
8
8
|
intervalMin;
|
|
9
|
-
requiresAuth;
|
|
10
9
|
stopFn;
|
|
11
10
|
isRegistering = false;
|
|
12
11
|
isExecuting = false;
|
|
13
12
|
registrationError;
|
|
14
|
-
|
|
13
|
+
requiresAuth;
|
|
14
|
+
label;
|
|
15
|
+
showToast;
|
|
16
|
+
constructor(taskName, job, intervalMin = 15, opts = {}) {
|
|
15
17
|
this.taskName = taskName;
|
|
16
18
|
this.job = job;
|
|
17
19
|
this.intervalMin = intervalMin;
|
|
18
|
-
this.requiresAuth = requiresAuth;
|
|
20
|
+
this.requiresAuth = opts.requiresAuth ?? false;
|
|
21
|
+
this.label = opts.label;
|
|
22
|
+
this.showToast = opts.showToast ?? false;
|
|
23
|
+
}
|
|
24
|
+
configure(opts) {
|
|
25
|
+
if (opts.label !== undefined)
|
|
26
|
+
this.label = opts.label;
|
|
27
|
+
if (opts.showToast !== undefined)
|
|
28
|
+
this.showToast = opts.showToast;
|
|
19
29
|
}
|
|
20
30
|
/**
|
|
21
31
|
* Registers a task with a specified interval and execution logic.
|
|
@@ -106,7 +116,9 @@ export class CronWorker {
|
|
|
106
116
|
async runWithEvents(job) {
|
|
107
117
|
this.isExecuting = true;
|
|
108
118
|
const startTime = Date.now();
|
|
109
|
-
|
|
119
|
+
if (this.showToast) {
|
|
120
|
+
UIEventBus.emit("CRON_START", { task: this.taskName, label: this.label });
|
|
121
|
+
}
|
|
110
122
|
try {
|
|
111
123
|
await Promise.resolve(job());
|
|
112
124
|
const duration = Date.now() - startTime;
|
|
@@ -119,7 +131,8 @@ export class CronWorker {
|
|
|
119
131
|
}
|
|
120
132
|
finally {
|
|
121
133
|
this.isExecuting = false;
|
|
122
|
-
|
|
134
|
+
if (this.showToast)
|
|
135
|
+
UIEventBus.emit("CRON_END", { task: this.taskName });
|
|
123
136
|
}
|
|
124
137
|
}
|
|
125
138
|
/**
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { CronJob, CronWorker } from "./CronWorker";
|
|
1
|
+
export { CronJob, CronWorker, CronWorkerOpts } from "./CronWorker";
|
|
2
2
|
export { EventBus, EventName } from "./EventBus";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sincpro/mobile",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"description": "Sincpro Mobile framework core: offline-first infra (DB, queue/events, cron), DDD patterns, adapters, AppShell and module-driven composition for React Native",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"author": "Gutierrez, Andres <andres.gutierrez@sincpro.com.bo>",
|
|
28
28
|
"license": "MIT",
|
|
29
29
|
"dependencies": {
|
|
30
|
+
"@opentelemetry/api": "^1.9.0",
|
|
30
31
|
"@opentelemetry/core": "^2.8.0",
|
|
31
32
|
"@opentelemetry/resources": "^2.8.0",
|
|
32
33
|
"@opentelemetry/sdk-trace-base": "^2.8.0",
|
|
@@ -39,7 +40,6 @@
|
|
|
39
40
|
"uuid": "^13.0.0"
|
|
40
41
|
},
|
|
41
42
|
"peerDependencies": {
|
|
42
|
-
"@opentelemetry/api": ">=1.3.0 <1.10.0",
|
|
43
43
|
"@react-navigation/native": ">=7",
|
|
44
44
|
"@react-navigation/native-stack": ">=7",
|
|
45
45
|
"@sincpro/mobile-ui": "^0.7.2",
|