@sincpro/mobile 0.1.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 +74 -0
- package/dist/adapters/Bluetooth.adapter.d.ts +14 -0
- package/dist/adapters/Bluetooth.adapter.js +100 -0
- package/dist/adapters/Geo.adapter.d.ts +8 -0
- package/dist/adapters/Geo.adapter.js +30 -0
- package/dist/adapters/JsonSerializer.adapter.d.ts +28 -0
- package/dist/adapters/JsonSerializer.adapter.js +47 -0
- package/dist/adapters/Network.adapter.d.ts +4 -0
- package/dist/adapters/Network.adapter.js +13 -0
- package/dist/adapters/ReceiptExporter.adapter.d.ts +19 -0
- package/dist/adapters/ReceiptExporter.adapter.js +142 -0
- package/dist/adapters/repositories/database_table.repository.d.ts +13 -0
- package/dist/adapters/repositories/database_table.repository.js +29 -0
- package/dist/adapters/repositories/domain_event.repository.d.ts +32 -0
- package/dist/adapters/repositories/domain_event.repository.js +147 -0
- package/dist/adapters/repositories/domain_event_dead_letter.repository.d.ts +23 -0
- package/dist/adapters/repositories/domain_event_dead_letter.repository.js +81 -0
- package/dist/adapters/repositories/setting.repository.d.ts +10 -0
- package/dist/adapters/repositories/setting.repository.js +21 -0
- package/dist/adapters/webview.adapter.d.ts +10 -0
- package/dist/adapters/webview.adapter.js +166 -0
- package/dist/domain/connectivity/bluetooth.d.ts +19 -0
- package/dist/domain/connectivity/bluetooth.js +1 -0
- package/dist/domain/connectivity/events.d.ts +17 -0
- package/dist/domain/connectivity/events.js +17 -0
- package/dist/domain/connectivity/geo.d.ts +4 -0
- package/dist/domain/connectivity/geo.js +1 -0
- package/dist/domain/connectivity/index.d.ts +3 -0
- package/dist/domain/connectivity/index.js +3 -0
- package/dist/domain/connectivity/network.d.ts +7 -0
- package/dist/domain/connectivity/network.js +1 -0
- package/dist/domain/database/database.d.ts +16 -0
- package/dist/domain/database/database.js +1 -0
- package/dist/domain/database/index.d.ts +2 -0
- package/dist/domain/database/index.js +2 -0
- package/dist/domain/database/repository.d.ts +19 -0
- package/dist/domain/database/repository.js +1 -0
- package/dist/domain/entity/entity.d.ts +109 -0
- package/dist/domain/entity/entity.js +246 -0
- package/dist/domain/entity/entity_collection.d.ts +396 -0
- package/dist/domain/entity/entity_collection.js +824 -0
- package/dist/domain/entity/index.d.ts +3 -0
- package/dist/domain/entity/index.js +3 -0
- package/dist/domain/entity/value_object.d.ts +12 -0
- package/dist/domain/entity/value_object.js +39 -0
- package/dist/domain/event_sourcing/domain_event.d.ts +58 -0
- package/dist/domain/event_sourcing/domain_event.js +164 -0
- package/dist/domain/event_sourcing/event.d.ts +25 -0
- package/dist/domain/event_sourcing/event.js +25 -0
- package/dist/domain/event_sourcing/event_handler.d.ts +7 -0
- package/dist/domain/event_sourcing/event_handler.js +13 -0
- package/dist/domain/event_sourcing/index.d.ts +2 -0
- package/dist/domain/event_sourcing/index.js +2 -0
- package/dist/domain/events.d.ts +33 -0
- package/dist/domain/events.js +32 -0
- package/dist/domain/icon.d.ts +1 -0
- package/dist/domain/icon.js +1 -0
- package/dist/domain/index.d.ts +9 -0
- package/dist/domain/index.js +9 -0
- package/dist/domain/print/driver_registry.d.ts +4 -0
- package/dist/domain/print/driver_registry.js +29 -0
- package/dist/domain/print/events.d.ts +13 -0
- package/dist/domain/print/events.js +13 -0
- package/dist/domain/print/index.d.ts +2 -0
- package/dist/domain/print/index.js +1 -0
- package/dist/domain/print/printer.d.ts +35 -0
- package/dist/domain/print/printer.js +1 -0
- package/dist/domain/receipt.d.ts +31 -0
- package/dist/domain/receipt.js +1 -0
- package/dist/domain/repositories.d.ts +6 -0
- package/dist/domain/repositories.js +7 -0
- package/dist/domain/settings.d.ts +7 -0
- package/dist/domain/settings.js +1 -0
- package/dist/domain/webview/events.d.ts +11 -0
- package/dist/domain/webview/events.js +11 -0
- package/dist/domain/webview/index.d.ts +1 -0
- package/dist/domain/webview/index.js +1 -0
- package/dist/domain/webview/webview.d.ts +57 -0
- package/dist/domain/webview/webview.js +9 -0
- package/dist/entrypoints/cron/Cron.d.ts +13 -0
- package/dist/entrypoints/cron/Cron.js +57 -0
- package/dist/entrypoints/cron/checkNetworkStatus.cron.d.ts +3 -0
- package/dist/entrypoints/cron/checkNetworkStatus.cron.js +10 -0
- package/dist/entrypoints/db/index.d.ts +2 -0
- package/dist/entrypoints/db/index.js +2 -0
- package/dist/entrypoints/db/migrations.d.ts +10 -0
- package/dist/entrypoints/db/migrations.js +115 -0
- package/dist/entrypoints/db/repositories.d.ts +8 -0
- package/dist/entrypoints/db/repositories.js +34 -0
- package/dist/entrypoints/queue/QueueProcessor.d.ts +12 -0
- package/dist/entrypoints/queue/QueueProcessor.js +75 -0
- package/dist/entrypoints/queue/activateDomain.subscriber.d.ts +7 -0
- package/dist/entrypoints/queue/activateDomain.subscriber.js +15 -0
- package/dist/entrypoints/queue/newAppSettings.handler.d.ts +7 -0
- package/dist/entrypoints/queue/newAppSettings.handler.js +17 -0
- package/dist/entrypoints/queue/printImage.subscriber.d.ts +7 -0
- package/dist/entrypoints/queue/printImage.subscriber.js +14 -0
- package/dist/entrypoints/queue/processWebViewMessage.subscriber.d.ts +7 -0
- package/dist/entrypoints/queue/processWebViewMessage.subscriber.js +23 -0
- package/dist/entrypoints/ui/AppShell.d.ts +15 -0
- package/dist/entrypoints/ui/AppShell.js +58 -0
- package/dist/entrypoints/ui/common_provider.d.ts +35 -0
- package/dist/entrypoints/ui/common_provider.js +244 -0
- package/dist/entrypoints/ui/domain_switcher.d.ts +19 -0
- package/dist/entrypoints/ui/domain_switcher.js +22 -0
- package/dist/entrypoints/ui/theme.d.ts +15 -0
- package/dist/entrypoints/ui/theme.js +16 -0
- package/dist/exceptions.d.ts +22 -0
- package/dist/exceptions.js +60 -0
- package/dist/framework/base_module.d.ts +14 -0
- package/dist/framework/base_module.js +40 -0
- package/dist/framework/createApp.d.ts +6 -0
- package/dist/framework/createApp.js +9 -0
- package/dist/framework/domain_module.d.ts +13 -0
- package/dist/framework/domain_module.js +18 -0
- package/dist/framework/kernel.d.ts +18 -0
- package/dist/framework/kernel.js +60 -0
- package/dist/framework/orchestrator.d.ts +29 -0
- package/dist/framework/orchestrator.js +118 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +11 -0
- package/dist/infrastructure/database/connector.d.ts +13 -0
- package/dist/infrastructure/database/connector.js +165 -0
- package/dist/infrastructure/database/index.d.ts +2 -0
- package/dist/infrastructure/database/index.js +2 -0
- package/dist/infrastructure/database/mapped.d.ts +128 -0
- package/dist/infrastructure/database/mapped.js +174 -0
- package/dist/infrastructure/database/utils.d.ts +10 -0
- package/dist/infrastructure/database/utils.js +35 -0
- package/dist/infrastructure/logger.d.ts +45 -0
- package/dist/infrastructure/logger.js +125 -0
- package/dist/infrastructure/ui/ToastHost.d.ts +1 -0
- package/dist/infrastructure/ui/ToastHost.js +19 -0
- package/dist/infrastructure/ui/UIEventBus.d.ts +12 -0
- package/dist/infrastructure/ui/UIEventBus.js +65 -0
- package/dist/infrastructure/ui/errorHandler.d.ts +1 -0
- package/dist/infrastructure/ui/errorHandler.js +20 -0
- package/dist/infrastructure/ui/events.d.ts +1 -0
- package/dist/infrastructure/ui/events.js +1 -0
- package/dist/infrastructure/workers/CronWorker.d.ts +42 -0
- package/dist/infrastructure/workers/CronWorker.js +143 -0
- package/dist/infrastructure/workers/EventBus.d.ts +67 -0
- package/dist/infrastructure/workers/EventBus.js +279 -0
- package/dist/infrastructure/workers/index.d.ts +2 -0
- package/dist/infrastructure/workers/index.js +2 -0
- package/dist/services/bluetooth.service.d.ts +14 -0
- package/dist/services/bluetooth.service.js +72 -0
- package/dist/services/database_table.service.d.ts +8 -0
- package/dist/services/database_table.service.js +19 -0
- package/dist/services/dead_letter_queue.service.d.ts +38 -0
- package/dist/services/dead_letter_queue.service.js +99 -0
- package/dist/services/event.service.d.ts +7 -0
- package/dist/services/event.service.js +24 -0
- package/dist/services/network.service.d.ts +7 -0
- package/dist/services/network.service.js +43 -0
- package/dist/services/printer.service.d.ts +25 -0
- package/dist/services/printer.service.js +220 -0
- package/dist/services/webview.service.d.ts +17 -0
- package/dist/services/webview.service.js +59 -0
- package/dist/tools/utils/Initials.d.ts +1 -0
- package/dist/tools/utils/Initials.js +12 -0
- package/dist/tools/utils/collections.d.ts +120 -0
- package/dist/tools/utils/collections.js +158 -0
- package/dist/tools/utils/date.d.ts +70 -0
- package/dist/tools/utils/date.js +126 -0
- package/dist/tools/utils/maps.d.ts +4 -0
- package/dist/tools/utils/maps.js +20 -0
- package/dist/tools/utils/monetary.d.ts +3 -0
- package/dist/tools/utils/monetary.js +31 -0
- package/dist/tools/utils/quantity.d.ts +11 -0
- package/dist/tools/utils/quantity.js +44 -0
- package/dist/tools/utils/searchTools.d.ts +39 -0
- package/dist/tools/utils/searchTools.js +56 -0
- package/dist/tools/utils/serializer.d.ts +2 -0
- package/dist/tools/utils/serializer.js +44 -0
- package/dist/ui/components/atoms/DebugBanner.d.ts +2 -0
- package/dist/ui/components/atoms/DebugBanner.js +15 -0
- package/dist/ui/components/atoms/index.d.ts +1 -0
- package/dist/ui/components/atoms/index.js +1 -0
- package/dist/ui/components/molecules/BluetoothDeviceSelectorModal.d.ts +8 -0
- package/dist/ui/components/molecules/BluetoothDeviceSelectorModal.js +61 -0
- package/dist/ui/components/molecules/DeadLetterErrorBlock.d.ts +8 -0
- package/dist/ui/components/molecules/DeadLetterErrorBlock.js +14 -0
- package/dist/ui/components/molecules/EventTimelineItem.d.ts +7 -0
- package/dist/ui/components/molecules/EventTimelineItem.js +65 -0
- package/dist/ui/components/molecules/ProcessToast.d.ts +5 -0
- package/dist/ui/components/molecules/ProcessToast.js +51 -0
- package/dist/ui/components/molecules/ProcessToastProvider.d.ts +4 -0
- package/dist/ui/components/molecules/ProcessToastProvider.js +5 -0
- package/dist/ui/components/molecules/index.d.ts +5 -0
- package/dist/ui/components/molecules/index.js +5 -0
- package/dist/ui/components/organisms/BluetoothPrinterSelector.d.ts +7 -0
- package/dist/ui/components/organisms/BluetoothPrinterSelector.js +92 -0
- package/dist/ui/components/organisms/DatabaseInfoRow.d.ts +11 -0
- package/dist/ui/components/organisms/DatabaseInfoRow.js +8 -0
- package/dist/ui/components/organisms/DeadLetterQueueRow.d.ts +7 -0
- package/dist/ui/components/organisms/DeadLetterQueueRow.js +72 -0
- package/dist/ui/components/organisms/EventRow.d.ts +7 -0
- package/dist/ui/components/organisms/EventRow.js +90 -0
- package/dist/ui/components/organisms/InjectableWebView.d.ts +35 -0
- package/dist/ui/components/organisms/InjectableWebView.js +169 -0
- package/dist/ui/components/organisms/Receipt.d.ts +11 -0
- package/dist/ui/components/organisms/Receipt.js +207 -0
- package/dist/ui/components/organisms/TableInfoInfoRow.d.ts +9 -0
- package/dist/ui/components/organisms/TableInfoInfoRow.js +19 -0
- package/dist/ui/components/organisms/index.d.ts +7 -0
- package/dist/ui/components/organisms/index.js +7 -0
- package/dist/ui/index.d.ts +4 -0
- package/dist/ui/index.js +4 -0
- package/dist/ui/layouts/router_layouts.d.ts +17 -0
- package/dist/ui/layouts/router_layouts.js +20 -0
- package/dist/ui/screens/database/database.context.d.ts +32 -0
- package/dist/ui/screens/database/database.context.js +158 -0
- package/dist/ui/screens/database/database.json_detail.d.ts +2 -0
- package/dist/ui/screens/database/database.json_detail.js +19 -0
- package/dist/ui/screens/database/database.list.d.ts +1 -0
- package/dist/ui/screens/database/database.list.js +41 -0
- package/dist/ui/screens/database/database.table_rows.d.ts +2 -0
- package/dist/ui/screens/database/database.table_rows.js +10 -0
- package/dist/ui/screens/dead_letter_queue/dead_letter_queue.context.d.ts +34 -0
- package/dist/ui/screens/dead_letter_queue/dead_letter_queue.context.js +166 -0
- package/dist/ui/screens/dead_letter_queue/dead_letter_queue.list.d.ts +2 -0
- package/dist/ui/screens/dead_letter_queue/dead_letter_queue.list.js +24 -0
- package/dist/ui/screens/events/events.context.d.ts +25 -0
- package/dist/ui/screens/events/events.context.js +113 -0
- package/dist/ui/screens/events/events.list.d.ts +1 -0
- package/dist/ui/screens/events/events.list.js +26 -0
- package/dist/ui/screens/events/index.d.ts +1 -0
- package/dist/ui/screens/events/index.js +1 -0
- package/dist/ui/screens/index.d.ts +3 -0
- package/dist/ui/screens/index.js +3 -0
- package/package.json +125 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { cron } from "../entrypoints/cron/Cron";
|
|
2
|
+
import { QueueProcessor } from "../entrypoints/queue/QueueProcessor";
|
|
3
|
+
import logger from "../infrastructure/logger";
|
|
4
|
+
import { AppState } from "react-native";
|
|
5
|
+
class Orchestrator {
|
|
6
|
+
kernel = null;
|
|
7
|
+
authenticated = false;
|
|
8
|
+
activeDomains = new Set();
|
|
9
|
+
sharedKeys = new Set();
|
|
10
|
+
appStateSubscription = null;
|
|
11
|
+
appStateCallbacks = [];
|
|
12
|
+
mutex = Promise.resolve();
|
|
13
|
+
configure(kernel) {
|
|
14
|
+
this.kernel = kernel;
|
|
15
|
+
this.authenticated = false;
|
|
16
|
+
this.sharedKeys = new Set(kernel.sharedKeys());
|
|
17
|
+
this.activeDomains = new Set(kernel.keys());
|
|
18
|
+
}
|
|
19
|
+
requireKernel() {
|
|
20
|
+
if (!this.kernel) {
|
|
21
|
+
throw new Error("Orchestrator not configured. Call createApp() first.");
|
|
22
|
+
}
|
|
23
|
+
return this.kernel;
|
|
24
|
+
}
|
|
25
|
+
async withMutex(operation) {
|
|
26
|
+
const previous = this.mutex;
|
|
27
|
+
let release;
|
|
28
|
+
this.mutex = new Promise((resolve) => (release = resolve));
|
|
29
|
+
try {
|
|
30
|
+
await previous;
|
|
31
|
+
return await operation();
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
release();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async orchestrateQueue() {
|
|
38
|
+
await QueueProcessor.sync(this.activeDomains, this.authenticated, this.requireKernel().subscribersByKey());
|
|
39
|
+
}
|
|
40
|
+
async orchestrateCron() {
|
|
41
|
+
await cron.sync(this.activeDomains, this.authenticated, this.requireKernel().cronsByKey());
|
|
42
|
+
}
|
|
43
|
+
async restoreConsistency() {
|
|
44
|
+
await this.orchestrateQueue();
|
|
45
|
+
await this.orchestrateCron();
|
|
46
|
+
}
|
|
47
|
+
initAppStateWatcher() {
|
|
48
|
+
if (this.appStateSubscription)
|
|
49
|
+
return;
|
|
50
|
+
const subscription = AppState.addEventListener("change", async (state) => {
|
|
51
|
+
if (state === "active") {
|
|
52
|
+
await this.restoreConsistency();
|
|
53
|
+
}
|
|
54
|
+
this.appStateCallbacks.forEach((callback) => callback(state));
|
|
55
|
+
});
|
|
56
|
+
this.appStateSubscription = () => subscription.remove();
|
|
57
|
+
}
|
|
58
|
+
async bootstrap() {
|
|
59
|
+
await this.requireKernel().bootstrap();
|
|
60
|
+
QueueProcessor.start();
|
|
61
|
+
await this.orchestrateQueue();
|
|
62
|
+
await this.orchestrateCron();
|
|
63
|
+
this.initAppStateWatcher();
|
|
64
|
+
logger.info("[Orchestrator] Infrastructure ready");
|
|
65
|
+
}
|
|
66
|
+
async authenticateSession() {
|
|
67
|
+
this.authenticated = true;
|
|
68
|
+
await this.orchestrateQueue();
|
|
69
|
+
await this.orchestrateCron();
|
|
70
|
+
}
|
|
71
|
+
async restoreSession() {
|
|
72
|
+
this.authenticated = true;
|
|
73
|
+
await this.orchestrateQueue();
|
|
74
|
+
await this.orchestrateCron();
|
|
75
|
+
}
|
|
76
|
+
async unauthenticateSession(cleanData = true) {
|
|
77
|
+
this.authenticated = false;
|
|
78
|
+
await this.orchestrateQueue();
|
|
79
|
+
await this.orchestrateCron();
|
|
80
|
+
if (cleanData) {
|
|
81
|
+
await QueueProcessor.clearQueue();
|
|
82
|
+
await this.requireKernel().restartDatabase();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async enableDomain(key) {
|
|
86
|
+
if (this.activeDomains.has(key))
|
|
87
|
+
return;
|
|
88
|
+
this.activeDomains.add(key);
|
|
89
|
+
await this.withMutex(async () => {
|
|
90
|
+
await this.orchestrateQueue();
|
|
91
|
+
await this.orchestrateCron();
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
async disableDomain(key) {
|
|
95
|
+
if (this.sharedKeys.has(key))
|
|
96
|
+
return;
|
|
97
|
+
if (!this.activeDomains.has(key))
|
|
98
|
+
return;
|
|
99
|
+
this.activeDomains.delete(key);
|
|
100
|
+
await this.withMutex(async () => {
|
|
101
|
+
await this.orchestrateQueue();
|
|
102
|
+
await this.orchestrateCron();
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
isSessionAuthenticated() {
|
|
106
|
+
return this.authenticated;
|
|
107
|
+
}
|
|
108
|
+
subscribeToAppState(callback) {
|
|
109
|
+
this.appStateCallbacks.push(callback);
|
|
110
|
+
return () => {
|
|
111
|
+
const index = this.appStateCallbacks.indexOf(callback);
|
|
112
|
+
if (index > -1) {
|
|
113
|
+
this.appStateCallbacks.splice(index, 1);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
export const orchestrator = new Orchestrator();
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type { AppShellConfig } from "./entrypoints/ui/AppShell";
|
|
2
|
+
export { createAppShell } from "./entrypoints/ui/AppShell";
|
|
3
|
+
export type { TimezoneLocale } from "./entrypoints/ui/common_provider";
|
|
4
|
+
export { CommonProvider, useCommon } from "./entrypoints/ui/common_provider";
|
|
5
|
+
export type { DomainApp } from "./entrypoints/ui/domain_switcher";
|
|
6
|
+
export { ActiveDomainApp, DomainSwitcherProvider, useDomainSwitcher, } from "./entrypoints/ui/domain_switcher";
|
|
7
|
+
export type { DeepPartial, ThemeTokens } from "./entrypoints/ui/theme";
|
|
8
|
+
export { createTheme } from "./entrypoints/ui/theme";
|
|
9
|
+
export { BaseModule, baseModule } from "./framework/base_module";
|
|
10
|
+
export type { AppConfig } from "./framework/createApp";
|
|
11
|
+
export { createApp } from "./framework/createApp";
|
|
12
|
+
export { DomainModule } from "./framework/domain_module";
|
|
13
|
+
export { Kernel } from "./framework/kernel";
|
|
14
|
+
export { orchestrator } from "./framework/orchestrator";
|
|
15
|
+
export { installGlobalErrorHandler } from "./infrastructure/ui/errorHandler";
|
|
16
|
+
export { PlainLayout, TabNavigatorLayout } from "./ui/layouts/router_layouts";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { createAppShell } from "./entrypoints/ui/AppShell";
|
|
2
|
+
export { CommonProvider, useCommon } from "./entrypoints/ui/common_provider";
|
|
3
|
+
export { ActiveDomainApp, DomainSwitcherProvider, useDomainSwitcher, } from "./entrypoints/ui/domain_switcher";
|
|
4
|
+
export { createTheme } from "./entrypoints/ui/theme";
|
|
5
|
+
export { BaseModule, baseModule } from "./framework/base_module";
|
|
6
|
+
export { createApp } from "./framework/createApp";
|
|
7
|
+
export { DomainModule } from "./framework/domain_module";
|
|
8
|
+
export { Kernel } from "./framework/kernel";
|
|
9
|
+
export { orchestrator } from "./framework/orchestrator";
|
|
10
|
+
export { installGlobalErrorHandler } from "./infrastructure/ui/errorHandler";
|
|
11
|
+
export { PlainLayout, TabNavigatorLayout } from "./ui/layouts/router_layouts";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { SQLiteDatabase, SQLiteRunResult, SQLiteVariadicBindParams } from "expo-sqlite";
|
|
2
|
+
export declare const DB_NAME = "distribution.db";
|
|
3
|
+
export declare function getDatabase(): Promise<SQLiteDatabase>;
|
|
4
|
+
export declare function getDatabaseSync(): SQLiteDatabase;
|
|
5
|
+
export declare const DBCursor: {
|
|
6
|
+
mutateDatabase(query: string, ...params: SQLiteVariadicBindParams): Promise<SQLiteRunResult>;
|
|
7
|
+
getFirstAsync<T>(query: string, ...params: SQLiteVariadicBindParams): Promise<T | null>;
|
|
8
|
+
getAllAsync<T>(query: string, ...params: SQLiteVariadicBindParams): Promise<T[]>;
|
|
9
|
+
execAsync(query: string): Promise<void>;
|
|
10
|
+
getFirstSync<T>(query: string, ...params: SQLiteVariadicBindParams): T | null;
|
|
11
|
+
getAllSync<T>(query: string, ...params: SQLiteVariadicBindParams): T[];
|
|
12
|
+
mutateDatabaseSync(query: string, ...params: SQLiteVariadicBindParams): SQLiteRunResult;
|
|
13
|
+
};
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import logger, { loggerRepositories } from "../../infrastructure/logger";
|
|
2
|
+
import { Mutex } from "async-mutex";
|
|
3
|
+
import { openDatabaseAsync, openDatabaseSync, } from "expo-sqlite";
|
|
4
|
+
export const DB_NAME = "distribution.db";
|
|
5
|
+
let dbInstance = null;
|
|
6
|
+
const dbLock = new Mutex();
|
|
7
|
+
const SKIP_LOG_TABLES = ["event_queue", "dead_letter_queue"];
|
|
8
|
+
function shouldLogQuery(query) {
|
|
9
|
+
const lowerQuery = query.toLowerCase();
|
|
10
|
+
return !SKIP_LOG_TABLES.some((table) => lowerQuery.includes(table));
|
|
11
|
+
}
|
|
12
|
+
async function initDatabase() {
|
|
13
|
+
logger.info("Initializing database connection...");
|
|
14
|
+
const db = await openDatabaseAsync(DB_NAME);
|
|
15
|
+
await db.execAsync("PRAGMA journal_mode = WAL;");
|
|
16
|
+
await db.execAsync("PRAGMA foreign_keys = ON;");
|
|
17
|
+
return db;
|
|
18
|
+
}
|
|
19
|
+
export async function getDatabase() {
|
|
20
|
+
if (!dbInstance) {
|
|
21
|
+
dbInstance = await initDatabase();
|
|
22
|
+
}
|
|
23
|
+
return dbInstance;
|
|
24
|
+
}
|
|
25
|
+
function initDatabaseSync() {
|
|
26
|
+
logger.info("Initializing database connection (sync)...");
|
|
27
|
+
const db = openDatabaseSync(DB_NAME);
|
|
28
|
+
db.execSync("PRAGMA journal_mode = WAL;");
|
|
29
|
+
db.execSync("PRAGMA foreign_keys = ON;");
|
|
30
|
+
return db;
|
|
31
|
+
}
|
|
32
|
+
export function getDatabaseSync() {
|
|
33
|
+
if (!dbInstance) {
|
|
34
|
+
dbInstance = initDatabaseSync();
|
|
35
|
+
}
|
|
36
|
+
return dbInstance;
|
|
37
|
+
}
|
|
38
|
+
async function withDB(fn) {
|
|
39
|
+
return dbLock.runExclusive(async () => fn(await getDatabase()));
|
|
40
|
+
}
|
|
41
|
+
function withDBSync(fn) {
|
|
42
|
+
return fn(getDatabaseSync());
|
|
43
|
+
}
|
|
44
|
+
export const DBCursor = {
|
|
45
|
+
async mutateDatabase(query, ...params) {
|
|
46
|
+
const safeQuery = `${query.trim()};`;
|
|
47
|
+
return withDB(async (db) => {
|
|
48
|
+
try {
|
|
49
|
+
if (shouldLogQuery(safeQuery)) {
|
|
50
|
+
loggerRepositories.info(`Database mutation: ${safeQuery}]`);
|
|
51
|
+
}
|
|
52
|
+
const result = await db.runAsync(safeQuery, ...params);
|
|
53
|
+
if (shouldLogQuery(safeQuery)) {
|
|
54
|
+
loggerRepositories.debug("Database mutation result:", result.lastInsertRowId);
|
|
55
|
+
}
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
loggerRepositories.error("Database mutation error:", error);
|
|
60
|
+
loggerRepositories.error("SQL:", safeQuery, params);
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
},
|
|
65
|
+
async getFirstAsync(query, ...params) {
|
|
66
|
+
const safeQuery = `${query.trim()};`;
|
|
67
|
+
return withDB(async (db) => {
|
|
68
|
+
try {
|
|
69
|
+
if (shouldLogQuery(safeQuery)) {
|
|
70
|
+
loggerRepositories.info("Database query:", safeQuery, params);
|
|
71
|
+
}
|
|
72
|
+
return await db.getFirstAsync(safeQuery, ...params);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
loggerRepositories.error("Database query error:", error);
|
|
76
|
+
loggerRepositories.error("SQL:", safeQuery, params);
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
},
|
|
81
|
+
async getAllAsync(query, ...params) {
|
|
82
|
+
const safeQuery = `${query.trim()};`;
|
|
83
|
+
return withDB(async (db) => {
|
|
84
|
+
try {
|
|
85
|
+
if (shouldLogQuery(safeQuery)) {
|
|
86
|
+
loggerRepositories.info("Database query:", safeQuery, params);
|
|
87
|
+
}
|
|
88
|
+
return await db.getAllAsync(safeQuery, ...params);
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
loggerRepositories.error("Database query error:", error);
|
|
92
|
+
loggerRepositories.error("SQL:", safeQuery, params);
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
},
|
|
97
|
+
async execAsync(query) {
|
|
98
|
+
const safeQuery = `${query.trim()};`;
|
|
99
|
+
return withDB(async (db) => {
|
|
100
|
+
try {
|
|
101
|
+
if (shouldLogQuery(safeQuery)) {
|
|
102
|
+
loggerRepositories.info("Executing database command:", safeQuery);
|
|
103
|
+
}
|
|
104
|
+
await db.execAsync(safeQuery);
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
loggerRepositories.error("Database command error:", error);
|
|
108
|
+
loggerRepositories.error("SQL:", safeQuery);
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
},
|
|
113
|
+
getFirstSync(query, ...params) {
|
|
114
|
+
const safeQuery = `${query.trim()};`;
|
|
115
|
+
return withDBSync((db) => {
|
|
116
|
+
try {
|
|
117
|
+
if (shouldLogQuery(safeQuery)) {
|
|
118
|
+
loggerRepositories.debug("Sync database query:", safeQuery);
|
|
119
|
+
}
|
|
120
|
+
return db.getFirstSync(safeQuery, ...params);
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
loggerRepositories.error("Sync database query error:", error);
|
|
124
|
+
loggerRepositories.error("SQL:", safeQuery, params);
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
},
|
|
129
|
+
getAllSync(query, ...params) {
|
|
130
|
+
const safeQuery = `${query.trim()};`;
|
|
131
|
+
return withDBSync((db) => {
|
|
132
|
+
try {
|
|
133
|
+
if (shouldLogQuery(safeQuery)) {
|
|
134
|
+
loggerRepositories.debug("Sync database query:", safeQuery);
|
|
135
|
+
}
|
|
136
|
+
return db.getAllSync(safeQuery, ...params);
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
loggerRepositories.error("Sync database query error:", error);
|
|
140
|
+
loggerRepositories.error("SQL:", safeQuery, params);
|
|
141
|
+
throw error;
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
},
|
|
145
|
+
mutateDatabaseSync(query, ...params) {
|
|
146
|
+
const safeQuery = `${query.trim()};`;
|
|
147
|
+
return withDBSync((db) => {
|
|
148
|
+
try {
|
|
149
|
+
if (shouldLogQuery(safeQuery)) {
|
|
150
|
+
loggerRepositories.debug("Sync database mutation:", safeQuery);
|
|
151
|
+
}
|
|
152
|
+
const result = db.runSync(safeQuery, ...params);
|
|
153
|
+
if (shouldLogQuery(safeQuery)) {
|
|
154
|
+
loggerRepositories.debug("Sync mutation result:", result.lastInsertRowId);
|
|
155
|
+
}
|
|
156
|
+
return result;
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
loggerRepositories.error("Sync database mutation error:", error);
|
|
160
|
+
loggerRepositories.error("SQL:", safeQuery, params);
|
|
161
|
+
throw error;
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
},
|
|
165
|
+
};
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import type { RepositoriesContainer } from "../../entrypoints/db/repositories";
|
|
2
|
+
/**
|
|
3
|
+
* Initializes the repository facade for use with the @mapped decorator.
|
|
4
|
+
*
|
|
5
|
+
* This function MUST be called during application bootstrap, after all repositories
|
|
6
|
+
* are loaded but before any entity with @mapped decorator is instantiated.
|
|
7
|
+
*
|
|
8
|
+
* @param repos - The repository facade object containing all domain repositories
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* // In your app bootstrap (e.g., InfrastructureOrchestrator)
|
|
12
|
+
* import { initializeRepositoryFacade } from "../../infrastructure/database";
|
|
13
|
+
* import { repos } from "../../entrypoints/db";
|
|
14
|
+
*
|
|
15
|
+
* async function bootstrap() {
|
|
16
|
+
* await initDatabase();
|
|
17
|
+
* initializeRepositoryFacade(repos); // ← Initialize after DB and repos are ready
|
|
18
|
+
* await runMigrations();
|
|
19
|
+
* }
|
|
20
|
+
*/
|
|
21
|
+
export declare function initializeRepositoryFacade(repos: RepositoriesContainer): void;
|
|
22
|
+
/**
|
|
23
|
+
* Resolves an entity or collection from repository by foreign key value.
|
|
24
|
+
*
|
|
25
|
+
* @param repositoryKey - Repository enum key (EDistributionRepository, ECommonRepository)
|
|
26
|
+
* @param fkValue - Foreign key: single ID (uuid/remoteId) or array of IDs
|
|
27
|
+
* @param remote - Use remoteId lookup instead of uuid (default: false)
|
|
28
|
+
* @returns Single entity, EntityCollection, or undefined if not found
|
|
29
|
+
* @throws DomainAppError if repository key doesn't exist in registry
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* // Local entity by uuid
|
|
33
|
+
* const customer = resolveEntity(EDistributionRepository.CUSTOMER, "uuid-123");
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* // Remote entity by remoteId
|
|
37
|
+
* const product = resolveEntity(EDistributionRepository.PRODUCT, 456, true);
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* // Collection by array of IDs
|
|
41
|
+
* const payments = resolveEntity(EDistributionRepository.PAYMENT, [1, 2, 3], true);
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* // Usage in Entity to refresh from DB
|
|
45
|
+
* async getRefreshedEntity(repositoryKey: string): Promise<this> {
|
|
46
|
+
* return resolveEntity(repositoryKey, this.uuid) as this;
|
|
47
|
+
* }
|
|
48
|
+
*/
|
|
49
|
+
export declare function resolveEntity(repositoryKey: string, fkValue: any, remote?: boolean): any;
|
|
50
|
+
/**
|
|
51
|
+
* Decorator for automatic runtime resolution of database relations using synchronous queries.
|
|
52
|
+
*
|
|
53
|
+
* This decorator enables entities to resolve their relationships automatically using getters,
|
|
54
|
+
* eliminating the need for manual hydration functions and avoiding circular import issues.
|
|
55
|
+
*
|
|
56
|
+
* **Important:** This decorator does NOT cache results. Every access to the getter will
|
|
57
|
+
* query the database to ensure you always get fresh, up-to-date data.
|
|
58
|
+
*
|
|
59
|
+
* It works by:
|
|
60
|
+
* 1. Detecting the foreign key type (scalar vs array) automatically
|
|
61
|
+
* 2. Calling the appropriate repository sync method (findByIdSync or findByIdsSync)
|
|
62
|
+
* 3. Returning fresh data on every access (no caching)
|
|
63
|
+
*
|
|
64
|
+
* @param repositoryKey - The repository key from the enum (EDistributionRepository or ECommonRepository)
|
|
65
|
+
* @param foreignKey - The name of the property on the entity that contains the foreign key (ID or array of IDs)
|
|
66
|
+
*
|
|
67
|
+
* @param remote - Optional flag indicating if the foreign keys are remote IDs.
|
|
68
|
+
* @returns A decorator function that can be applied to entity getters
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* // One-to-One relationship (hasOne)
|
|
72
|
+
* import { mapped } from "../../infrastructure/database";
|
|
73
|
+
* import { EDistributionDomainRepository } from "../../entrypoints/db";
|
|
74
|
+
*
|
|
75
|
+
* export class SaleOrder {
|
|
76
|
+
* customerId: number;
|
|
77
|
+
*
|
|
78
|
+
* @mapped(EDistributionDomainRepository.CUSTOMER, "customerId")
|
|
79
|
+
* get customer() {
|
|
80
|
+
* return undefined; // This value is replaced by the decorator at runtime
|
|
81
|
+
* }
|
|
82
|
+
* }
|
|
83
|
+
*
|
|
84
|
+
* // Usage:
|
|
85
|
+
* const order = new SaleOrder({ id: 1, customerId: 123 });
|
|
86
|
+
* console.log(order.customer.name); // Queries database on every access
|
|
87
|
+
* console.log(order.customer.vat); // Queries database again (fresh data)
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* // One-to-Many relationship (hasMany)
|
|
91
|
+
* export class SaleOrder {
|
|
92
|
+
* paymentIds: number[];
|
|
93
|
+
*
|
|
94
|
+
* @mapped(EDistributionRepository.PAYMENT, "paymentIds")
|
|
95
|
+
* get payments() {
|
|
96
|
+
* return []; // This value is replaced by the decorator at runtime
|
|
97
|
+
* }
|
|
98
|
+
* }
|
|
99
|
+
*
|
|
100
|
+
* // Usage:
|
|
101
|
+
* const order = new SaleOrder({ id: 1, paymentIds: [10, 20, 30] });
|
|
102
|
+
* order.payments.forEach(payment => console.log(payment.amount)); // Fresh data on every access
|
|
103
|
+
*
|
|
104
|
+
* @remarks
|
|
105
|
+
* **Requirements:**
|
|
106
|
+
* - The target repository MUST implement `findByIdSync()` for scalar foreign keys
|
|
107
|
+
* - The target repository MUST implement `findByIdsSync()` for array foreign keys
|
|
108
|
+
* - The database must be initialized before accessing decorated properties
|
|
109
|
+
*
|
|
110
|
+
* **Advantages:**
|
|
111
|
+
* - ✅ No circular import issues (uses enum-based repository keys resolved at runtime)
|
|
112
|
+
* - ✅ Automatic type detection (scalar vs array)
|
|
113
|
+
* - ✅ Always returns fresh data from the database (no stale cache)
|
|
114
|
+
* - ✅ Clean, declarative syntax
|
|
115
|
+
* - ✅ Type-safe when using repository enums
|
|
116
|
+
* - ✅ No memory overhead from caching
|
|
117
|
+
* - ✅ No domain knowledge needed - just use the enum
|
|
118
|
+
*
|
|
119
|
+
* **Limitations:**
|
|
120
|
+
* - ⚠️ Blocks the main thread on every access (uses synchronous SQLite queries)
|
|
121
|
+
* - ⚠️ Performance cost on repeated access (queries database each time)
|
|
122
|
+
* - ⚠️ Only works for simple ID-based lookups (no complex filters or joins)
|
|
123
|
+
* - ⚠️ Repository must support sync operations (optional methods in IRepository)
|
|
124
|
+
*
|
|
125
|
+
* @see {@link IRepository.findByIdSync} - Required repository method for hasOne relations
|
|
126
|
+
* @see {@link IRepository.findByIdsSync} - Required repository method for hasMany relations
|
|
127
|
+
*/
|
|
128
|
+
export declare function mapped(repositoryKey: string, foreignKey: string, remote?: boolean): (target: any, propertyKey: string, descriptor?: PropertyDescriptor) => void;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { DomainAppError } from "../../exceptions";
|
|
2
|
+
import { loggerRepositories } from "../../infrastructure/logger";
|
|
3
|
+
/**
|
|
4
|
+
* Repository facade reference that will be injected after initialization.
|
|
5
|
+
* This avoids circular import issues by delaying the repository resolution.
|
|
6
|
+
*/
|
|
7
|
+
let repositoryFacade = null;
|
|
8
|
+
/**
|
|
9
|
+
* Initializes the repository facade for use with the @mapped decorator.
|
|
10
|
+
*
|
|
11
|
+
* This function MUST be called during application bootstrap, after all repositories
|
|
12
|
+
* are loaded but before any entity with @mapped decorator is instantiated.
|
|
13
|
+
*
|
|
14
|
+
* @param repos - The repository facade object containing all domain repositories
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* // In your app bootstrap (e.g., InfrastructureOrchestrator)
|
|
18
|
+
* import { initializeRepositoryFacade } from "../../infrastructure/database";
|
|
19
|
+
* import { repos } from "../../entrypoints/db";
|
|
20
|
+
*
|
|
21
|
+
* async function bootstrap() {
|
|
22
|
+
* await initDatabase();
|
|
23
|
+
* initializeRepositoryFacade(repos); // ← Initialize after DB and repos are ready
|
|
24
|
+
* await runMigrations();
|
|
25
|
+
* }
|
|
26
|
+
*/
|
|
27
|
+
export function initializeRepositoryFacade(repos) {
|
|
28
|
+
repositoryFacade = repos;
|
|
29
|
+
loggerRepositories.info("[Mapped] Repository facade initialized for @mapped decorator");
|
|
30
|
+
}
|
|
31
|
+
function resolveEntityInternal(context, repositoryKey, fkValue, remote = false) {
|
|
32
|
+
if (!repositoryFacade) {
|
|
33
|
+
throw new DomainAppError(`[${context}] Repository facade not initialized. ` +
|
|
34
|
+
"Call initializeRepositoryFacade(repos) during app bootstrap.");
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
repositoryFacade.get(repositoryKey);
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
throw new DomainAppError(`[${context}] ${error.message}`);
|
|
41
|
+
}
|
|
42
|
+
return repositoryFacade.resolveRelation(repositoryKey, fkValue, remote);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Resolves an entity or collection from repository by foreign key value.
|
|
46
|
+
*
|
|
47
|
+
* @param repositoryKey - Repository enum key (EDistributionRepository, ECommonRepository)
|
|
48
|
+
* @param fkValue - Foreign key: single ID (uuid/remoteId) or array of IDs
|
|
49
|
+
* @param remote - Use remoteId lookup instead of uuid (default: false)
|
|
50
|
+
* @returns Single entity, EntityCollection, or undefined if not found
|
|
51
|
+
* @throws DomainAppError if repository key doesn't exist in registry
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* // Local entity by uuid
|
|
55
|
+
* const customer = resolveEntity(EDistributionRepository.CUSTOMER, "uuid-123");
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* // Remote entity by remoteId
|
|
59
|
+
* const product = resolveEntity(EDistributionRepository.PRODUCT, 456, true);
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* // Collection by array of IDs
|
|
63
|
+
* const payments = resolveEntity(EDistributionRepository.PAYMENT, [1, 2, 3], true);
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* // Usage in Entity to refresh from DB
|
|
67
|
+
* async getRefreshedEntity(repositoryKey: string): Promise<this> {
|
|
68
|
+
* return resolveEntity(repositoryKey, this.uuid) as this;
|
|
69
|
+
* }
|
|
70
|
+
*/
|
|
71
|
+
export function resolveEntity(repositoryKey, fkValue, remote = false) {
|
|
72
|
+
return resolveEntityInternal("resolveEntity", repositoryKey, fkValue, remote);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Decorator for automatic runtime resolution of database relations using synchronous queries.
|
|
76
|
+
*
|
|
77
|
+
* This decorator enables entities to resolve their relationships automatically using getters,
|
|
78
|
+
* eliminating the need for manual hydration functions and avoiding circular import issues.
|
|
79
|
+
*
|
|
80
|
+
* **Important:** This decorator does NOT cache results. Every access to the getter will
|
|
81
|
+
* query the database to ensure you always get fresh, up-to-date data.
|
|
82
|
+
*
|
|
83
|
+
* It works by:
|
|
84
|
+
* 1. Detecting the foreign key type (scalar vs array) automatically
|
|
85
|
+
* 2. Calling the appropriate repository sync method (findByIdSync or findByIdsSync)
|
|
86
|
+
* 3. Returning fresh data on every access (no caching)
|
|
87
|
+
*
|
|
88
|
+
* @param repositoryKey - The repository key from the enum (EDistributionRepository or ECommonRepository)
|
|
89
|
+
* @param foreignKey - The name of the property on the entity that contains the foreign key (ID or array of IDs)
|
|
90
|
+
*
|
|
91
|
+
* @param remote - Optional flag indicating if the foreign keys are remote IDs.
|
|
92
|
+
* @returns A decorator function that can be applied to entity getters
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* // One-to-One relationship (hasOne)
|
|
96
|
+
* import { mapped } from "../../infrastructure/database";
|
|
97
|
+
* import { EDistributionDomainRepository } from "../../entrypoints/db";
|
|
98
|
+
*
|
|
99
|
+
* export class SaleOrder {
|
|
100
|
+
* customerId: number;
|
|
101
|
+
*
|
|
102
|
+
* @mapped(EDistributionDomainRepository.CUSTOMER, "customerId")
|
|
103
|
+
* get customer() {
|
|
104
|
+
* return undefined; // This value is replaced by the decorator at runtime
|
|
105
|
+
* }
|
|
106
|
+
* }
|
|
107
|
+
*
|
|
108
|
+
* // Usage:
|
|
109
|
+
* const order = new SaleOrder({ id: 1, customerId: 123 });
|
|
110
|
+
* console.log(order.customer.name); // Queries database on every access
|
|
111
|
+
* console.log(order.customer.vat); // Queries database again (fresh data)
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* // One-to-Many relationship (hasMany)
|
|
115
|
+
* export class SaleOrder {
|
|
116
|
+
* paymentIds: number[];
|
|
117
|
+
*
|
|
118
|
+
* @mapped(EDistributionRepository.PAYMENT, "paymentIds")
|
|
119
|
+
* get payments() {
|
|
120
|
+
* return []; // This value is replaced by the decorator at runtime
|
|
121
|
+
* }
|
|
122
|
+
* }
|
|
123
|
+
*
|
|
124
|
+
* // Usage:
|
|
125
|
+
* const order = new SaleOrder({ id: 1, paymentIds: [10, 20, 30] });
|
|
126
|
+
* order.payments.forEach(payment => console.log(payment.amount)); // Fresh data on every access
|
|
127
|
+
*
|
|
128
|
+
* @remarks
|
|
129
|
+
* **Requirements:**
|
|
130
|
+
* - The target repository MUST implement `findByIdSync()` for scalar foreign keys
|
|
131
|
+
* - The target repository MUST implement `findByIdsSync()` for array foreign keys
|
|
132
|
+
* - The database must be initialized before accessing decorated properties
|
|
133
|
+
*
|
|
134
|
+
* **Advantages:**
|
|
135
|
+
* - ✅ No circular import issues (uses enum-based repository keys resolved at runtime)
|
|
136
|
+
* - ✅ Automatic type detection (scalar vs array)
|
|
137
|
+
* - ✅ Always returns fresh data from the database (no stale cache)
|
|
138
|
+
* - ✅ Clean, declarative syntax
|
|
139
|
+
* - ✅ Type-safe when using repository enums
|
|
140
|
+
* - ✅ No memory overhead from caching
|
|
141
|
+
* - ✅ No domain knowledge needed - just use the enum
|
|
142
|
+
*
|
|
143
|
+
* **Limitations:**
|
|
144
|
+
* - ⚠️ Blocks the main thread on every access (uses synchronous SQLite queries)
|
|
145
|
+
* - ⚠️ Performance cost on repeated access (queries database each time)
|
|
146
|
+
* - ⚠️ Only works for simple ID-based lookups (no complex filters or joins)
|
|
147
|
+
* - ⚠️ Repository must support sync operations (optional methods in IRepository)
|
|
148
|
+
*
|
|
149
|
+
* @see {@link IRepository.findByIdSync} - Required repository method for hasOne relations
|
|
150
|
+
* @see {@link IRepository.findByIdsSync} - Required repository method for hasMany relations
|
|
151
|
+
*/
|
|
152
|
+
export function mapped(repositoryKey, foreignKey, remote = false) {
|
|
153
|
+
return function (target, propertyKey, descriptor) {
|
|
154
|
+
loggerRepositories.warn(`[Mapped] Setting up mapped relation for ${target.constructor.name}.${propertyKey} ` +
|
|
155
|
+
`using repository "${repositoryKey}" and foreign key "${foreignKey}"`);
|
|
156
|
+
const getter = function () {
|
|
157
|
+
const fkValue = this[foreignKey];
|
|
158
|
+
if (!fkValue) {
|
|
159
|
+
return undefined;
|
|
160
|
+
}
|
|
161
|
+
return resolveEntityInternal("mapped", repositoryKey, fkValue, remote);
|
|
162
|
+
};
|
|
163
|
+
if (descriptor) {
|
|
164
|
+
descriptor.get = getter;
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
Object.defineProperty(target, propertyKey, {
|
|
168
|
+
get: getter,
|
|
169
|
+
enumerable: true,
|
|
170
|
+
configurable: true,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Constructs a SQL placeholder string and corresponding values for use in a query based on the provided field and data.
|
|
3
|
+
*
|
|
4
|
+
* @param {string} field - The database field name to be used in the placeholder condition.
|
|
5
|
+
* @param {any} data - The data used to generate placeholders and corresponding values. Can be an array, object, set, or a single value.
|
|
6
|
+
* @return {[string, string]} - A tuple where the first element is the formatted placeholder condition string,
|
|
7
|
+
* and the second element is the corresponding values. IE: `["field IN (?, ?, ?)", [value1, value2, value3]]`.
|
|
8
|
+
*/
|
|
9
|
+
export declare function getPlaceholders(field: string, data: any | any[]): [string, string];
|
|
10
|
+
export declare function generateUUID(): string;
|