@teincfood/core 0.1.0 → 0.1.2
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/adapters/auth.js +2 -6
- package/dist/adapters/business.js +2 -6
- package/dist/adapters/connectivity.js +6 -14
- package/dist/adapters/devices.js +2 -6
- package/dist/adapters/http.js +4 -10
- package/dist/adapters/image-cache.js +2 -6
- package/dist/adapters/kv-types.js +1 -2
- package/dist/adapters/kv.js +11 -24
- package/dist/adapters/local-node.js +3 -7
- package/dist/adapters/pending-orders.js +1 -4
- package/dist/adapters/query-keys.js +4 -7
- package/dist/adapters/services.js +9 -14
- package/dist/adapters/sqlite.js +3 -8
- package/dist/db/connection.js +15 -20
- package/dist/db/migrations.js +1 -4
- package/dist/db/operations.js +19 -36
- package/dist/index.js +40 -93
- package/dist/reference/api.js +7 -12
- package/dist/reference/events.js +25 -33
- package/dist/reference/operations.js +6 -14
- package/dist/reference/service.js +36 -39
- package/dist/reference/types.js +1 -2
- package/dist/repositories/order.repository.js +15 -18
- package/dist/repositories/pos.repository.js +6 -9
- package/dist/sync/command-builder.js +8 -12
- package/dist/sync/command-queue.service.js +35 -38
- package/dist/sync/engine.js +38 -42
- package/dist/sync/order-number.js +6 -11
- package/dist/sync/order-snapshots.service.js +19 -22
- package/dist/sync/pending-orders.service.js +27 -33
- package/dist/sync/transport-local-node.js +16 -20
- package/dist/sync/transport-rest.js +18 -22
- package/dist/sync/types.js +1 -2
- package/dist/sync/use-sync-reconciliation.js +37 -40
- package/dist/types/api/auth.api.js +1 -2
- package/dist/types/api/business.api.js +1 -2
- package/dist/types/api/conversation.api.js +1 -2
- package/dist/types/api/error.api.js +1 -2
- package/dist/types/api/fleet.api.js +1 -2
- package/dist/types/api/index.js +11 -27
- package/dist/types/api/kiosk.api.js +1 -2
- package/dist/types/api/menu.api.js +1 -2
- package/dist/types/api/pagination.js +1 -2
- package/dist/types/api/rider.api.js +1 -2
- package/dist/types/api/seller.api.d.ts +8 -0
- package/dist/types/api/seller.api.js +1 -2
- package/dist/types/api/wallet.api.js +1 -2
- package/dist/types/pos.types.d.ts +65 -18
- package/dist/types/pos.types.js +1 -5
- package/dist/utils/constants.js +24 -27
- package/dist/utils/copyToClipBoard.js +2 -7
- package/dist/utils/currency.js +8 -16
- package/dist/utils/error.js +4 -11
- package/dist/utils/formatCurrency.js +1 -4
- package/dist/utils/formatDate.js +2 -6
- package/dist/utils/phone.js +6 -13
- package/dist/utils/responsive.js +1 -4
- package/dist/utils/storage.js +14 -28
- package/dist/utils/sync-logger.js +1 -4
- package/dist/utils/uuid.js +4 -10
- package/dist/utils/validation.js +11 -24
- package/package.json +12 -1
package/dist/adapters/auth.js
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* Auth store stub.
|
|
4
3
|
*/
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.useAuthStore = void 0;
|
|
7
|
-
exports.setAuthUser = setAuthUser;
|
|
8
4
|
let user = null;
|
|
9
|
-
function setAuthUser(u) {
|
|
5
|
+
export function setAuthUser(u) {
|
|
10
6
|
user = u;
|
|
11
7
|
}
|
|
12
|
-
|
|
8
|
+
export const useAuthStore = {
|
|
13
9
|
getState: () => ({ user }),
|
|
14
10
|
};
|
|
@@ -1,14 +1,10 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* Business store stub.
|
|
4
3
|
*/
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.useBusinessStore = void 0;
|
|
7
|
-
exports.setActiveBusiness = setActiveBusiness;
|
|
8
4
|
let activeBusiness = null;
|
|
9
|
-
function setActiveBusiness(b) {
|
|
5
|
+
export function setActiveBusiness(b) {
|
|
10
6
|
activeBusiness = b;
|
|
11
7
|
}
|
|
12
|
-
|
|
8
|
+
export const useBusinessStore = {
|
|
13
9
|
getState: () => ({ activeBusiness }),
|
|
14
10
|
};
|
|
@@ -1,38 +1,30 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* Connectivity adapter — replaces zustand connectivity store.
|
|
4
3
|
* Host apps inject real connectivity state; core defaults to offline-unknown.
|
|
5
4
|
*/
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.connectivityStoreShim = void 0;
|
|
8
|
-
exports.getConnectivityState = getConnectivityState;
|
|
9
|
-
exports.setConnectivityState = setConnectivityState;
|
|
10
|
-
exports.setCloudReachable = setCloudReachable;
|
|
11
|
-
exports.setOnline = setOnline;
|
|
12
|
-
exports.subscribeConnectivity = subscribeConnectivity;
|
|
13
5
|
let state = {
|
|
14
6
|
isOnline: true,
|
|
15
7
|
isCloudReachable: "unknown",
|
|
16
8
|
};
|
|
17
9
|
const listeners = new Set();
|
|
18
|
-
function getConnectivityState() {
|
|
10
|
+
export function getConnectivityState() {
|
|
19
11
|
return { ...state };
|
|
20
12
|
}
|
|
21
|
-
function setConnectivityState(patch) {
|
|
13
|
+
export function setConnectivityState(patch) {
|
|
22
14
|
state = { ...state, ...patch };
|
|
23
15
|
listeners.forEach((l) => l({ ...state }));
|
|
24
16
|
}
|
|
25
|
-
function setCloudReachable(value) {
|
|
17
|
+
export function setCloudReachable(value) {
|
|
26
18
|
setConnectivityState({ isCloudReachable: value });
|
|
27
19
|
}
|
|
28
|
-
function setOnline(value) {
|
|
20
|
+
export function setOnline(value) {
|
|
29
21
|
setConnectivityState({ isOnline: value });
|
|
30
22
|
}
|
|
31
|
-
function subscribeConnectivity(listener) {
|
|
23
|
+
export function subscribeConnectivity(listener) {
|
|
32
24
|
listeners.add(listener);
|
|
33
25
|
return () => listeners.delete(listener);
|
|
34
26
|
}
|
|
35
27
|
// Compatibility shim for code that previously used useConnectivityStore.getState()
|
|
36
|
-
|
|
28
|
+
export const connectivityStoreShim = {
|
|
37
29
|
getState: getConnectivityState,
|
|
38
30
|
};
|
package/dist/adapters/devices.js
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* Devices service stub.
|
|
4
3
|
*/
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.setAppInstanceId = setAppInstanceId;
|
|
7
|
-
exports.getAppInstanceId = getAppInstanceId;
|
|
8
4
|
let appInstanceId = "core-device-id";
|
|
9
|
-
function setAppInstanceId(id) {
|
|
5
|
+
export function setAppInstanceId(id) {
|
|
10
6
|
appInstanceId = id;
|
|
11
7
|
}
|
|
12
|
-
function getAppInstanceId() {
|
|
8
|
+
export function getAppInstanceId() {
|
|
13
9
|
return appInstanceId;
|
|
14
10
|
}
|
package/dist/adapters/http.js
CHANGED
|
@@ -1,13 +1,8 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* HTTP adapter — provides ApiError and a minimal api client interface.
|
|
4
3
|
* Host apps inject a real HTTP client (axios/fetch) via setHttpClient().
|
|
5
4
|
*/
|
|
6
|
-
|
|
7
|
-
exports.api = exports.ApiError = void 0;
|
|
8
|
-
exports.setHttpClient = setHttpClient;
|
|
9
|
-
exports.getHttpClient = getHttpClient;
|
|
10
|
-
class ApiError extends Error {
|
|
5
|
+
export class ApiError extends Error {
|
|
11
6
|
constructor(message, status, code, data) {
|
|
12
7
|
super(message);
|
|
13
8
|
this.name = "ApiError";
|
|
@@ -16,19 +11,18 @@ class ApiError extends Error {
|
|
|
16
11
|
this.data = data;
|
|
17
12
|
}
|
|
18
13
|
}
|
|
19
|
-
exports.ApiError = ApiError;
|
|
20
14
|
let client = null;
|
|
21
|
-
function setHttpClient(c) {
|
|
15
|
+
export function setHttpClient(c) {
|
|
22
16
|
client = c;
|
|
23
17
|
}
|
|
24
|
-
function getHttpClient() {
|
|
18
|
+
export function getHttpClient() {
|
|
25
19
|
if (!client) {
|
|
26
20
|
throw new Error("HTTP client not injected. Call setHttpClient(client) before making API calls.");
|
|
27
21
|
}
|
|
28
22
|
return client;
|
|
29
23
|
}
|
|
30
24
|
// Default export compatible with `import { api } from "@/services/api"`
|
|
31
|
-
|
|
25
|
+
export const api = {
|
|
32
26
|
get: (...args) => getHttpClient().get(...args),
|
|
33
27
|
post: (...args) => getHttpClient().post(...args),
|
|
34
28
|
put: (...args) => getHttpClient().put(...args),
|
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* Image cache stub — no-op in core library.
|
|
4
3
|
* Host apps with file-system access inject real impl.
|
|
5
4
|
*/
|
|
6
|
-
|
|
7
|
-
exports.cacheImageBatch = cacheImageBatch;
|
|
8
|
-
exports.pruneImageCache = pruneImageCache;
|
|
9
|
-
async function cacheImageBatch(_urls, _opts) {
|
|
5
|
+
export async function cacheImageBatch(_urls, _opts) {
|
|
10
6
|
return [];
|
|
11
7
|
}
|
|
12
|
-
async function pruneImageCache(_urls) { }
|
|
8
|
+
export async function pruneImageCache(_urls) { }
|
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1
|
+
export {};
|
package/dist/adapters/kv.js
CHANGED
|
@@ -1,26 +1,13 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* KV storage adapter — async get/set/remove with in-memory fallback for tests.
|
|
4
3
|
* Host apps can inject a persistent backend via setKVDriver().
|
|
5
4
|
*/
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.setKVDriver = setKVDriver;
|
|
8
|
-
exports.getKVDriver = getKVDriver;
|
|
9
|
-
exports.kvGetItem = kvGetItem;
|
|
10
|
-
exports.kvSetItem = kvSetItem;
|
|
11
|
-
exports.kvRemoveItem = kvRemoveItem;
|
|
12
|
-
exports.getItem = getItem;
|
|
13
|
-
exports.setItem = setItem;
|
|
14
|
-
exports.removeItem = removeItem;
|
|
15
|
-
exports.getItemSync = getItemSync;
|
|
16
|
-
exports.setItemSync = setItemSync;
|
|
17
|
-
exports.removeItemSync = removeItemSync;
|
|
18
5
|
const memory = new Map();
|
|
19
6
|
let driver = null;
|
|
20
|
-
function setKVDriver(d) {
|
|
7
|
+
export function setKVDriver(d) {
|
|
21
8
|
driver = d;
|
|
22
9
|
}
|
|
23
|
-
function getKVDriver() {
|
|
10
|
+
export function getKVDriver() {
|
|
24
11
|
if (driver)
|
|
25
12
|
return driver;
|
|
26
13
|
return {
|
|
@@ -33,18 +20,18 @@ function getKVDriver() {
|
|
|
33
20
|
},
|
|
34
21
|
};
|
|
35
22
|
}
|
|
36
|
-
async function kvGetItem(key) {
|
|
23
|
+
export async function kvGetItem(key) {
|
|
37
24
|
const v = await getKVDriver().getItem(key);
|
|
38
25
|
return v ?? null;
|
|
39
26
|
}
|
|
40
|
-
async function kvSetItem(key, value) {
|
|
27
|
+
export async function kvSetItem(key, value) {
|
|
41
28
|
await getKVDriver().setItem(key, value);
|
|
42
29
|
}
|
|
43
|
-
async function kvRemoveItem(key) {
|
|
30
|
+
export async function kvRemoveItem(key) {
|
|
44
31
|
await getKVDriver().removeItem(key);
|
|
45
32
|
}
|
|
46
33
|
// Typed JSON helpers (mirrors old storage.ts sync API but async)
|
|
47
|
-
async function getItem(key) {
|
|
34
|
+
export async function getItem(key) {
|
|
48
35
|
const raw = await kvGetItem(key);
|
|
49
36
|
if (raw == null)
|
|
50
37
|
return null;
|
|
@@ -55,14 +42,14 @@ async function getItem(key) {
|
|
|
55
42
|
return null;
|
|
56
43
|
}
|
|
57
44
|
}
|
|
58
|
-
async function setItem(key, value) {
|
|
45
|
+
export async function setItem(key, value) {
|
|
59
46
|
await kvSetItem(key, JSON.stringify(value));
|
|
60
47
|
}
|
|
61
|
-
async function removeItem(key) {
|
|
48
|
+
export async function removeItem(key) {
|
|
62
49
|
await kvRemoveItem(key);
|
|
63
50
|
}
|
|
64
51
|
// Synchronous fallbacks for legacy callers that used MMKV sync API
|
|
65
|
-
function getItemSync(key) {
|
|
52
|
+
export function getItemSync(key) {
|
|
66
53
|
const raw = memory.get(key);
|
|
67
54
|
if (raw == null)
|
|
68
55
|
return null;
|
|
@@ -73,9 +60,9 @@ function getItemSync(key) {
|
|
|
73
60
|
return null;
|
|
74
61
|
}
|
|
75
62
|
}
|
|
76
|
-
function setItemSync(key, value) {
|
|
63
|
+
export function setItemSync(key, value) {
|
|
77
64
|
memory.set(key, JSON.stringify(value));
|
|
78
65
|
}
|
|
79
|
-
function removeItemSync(key) {
|
|
66
|
+
export function removeItemSync(key) {
|
|
80
67
|
memory.delete(key);
|
|
81
68
|
}
|
|
@@ -1,17 +1,13 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* Local Node stubs — minimal interfaces so core compiles without RN.
|
|
4
3
|
* Host apps with LAN support replace these via setters.
|
|
5
4
|
*/
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.useLocalNodeStore = exports.localNodeService = void 0;
|
|
8
|
-
exports.setLocalNodeState = setLocalNodeState;
|
|
9
5
|
let isLocalEnabled = false;
|
|
10
6
|
let isConnected = false;
|
|
11
7
|
let isLocalNodeFlag = false;
|
|
12
8
|
let electedNode = null;
|
|
13
9
|
let client = null;
|
|
14
|
-
function setLocalNodeState(s) {
|
|
10
|
+
export function setLocalNodeState(s) {
|
|
15
11
|
if (s.isLocalNodeEnabled !== undefined)
|
|
16
12
|
isLocalEnabled = s.isLocalNodeEnabled;
|
|
17
13
|
if (s.isConnectedToNode !== undefined)
|
|
@@ -23,7 +19,7 @@ function setLocalNodeState(s) {
|
|
|
23
19
|
if (s.client !== undefined)
|
|
24
20
|
client = s.client;
|
|
25
21
|
}
|
|
26
|
-
|
|
22
|
+
export const localNodeService = {
|
|
27
23
|
isLocalNode: () => isLocalNodeFlag,
|
|
28
24
|
isConnectedToNode: () => isConnected,
|
|
29
25
|
getElectedNode: () => electedNode,
|
|
@@ -31,7 +27,7 @@ exports.localNodeService = {
|
|
|
31
27
|
broadcastToClients: (_event) => { },
|
|
32
28
|
handleNodeCommand: async (_command) => null,
|
|
33
29
|
};
|
|
34
|
-
|
|
30
|
+
export const useLocalNodeStore = {
|
|
35
31
|
getState: () => ({
|
|
36
32
|
isLocalNodeEnabled: isLocalEnabled,
|
|
37
33
|
isConnectedToNode: isConnected,
|
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* Pending orders store stub.
|
|
4
3
|
*/
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.usePendingOrdersStore = void 0;
|
|
7
4
|
let orders = [];
|
|
8
5
|
let hydrated = false;
|
|
9
|
-
|
|
6
|
+
export const usePendingOrdersStore = {
|
|
10
7
|
getState: () => ({
|
|
11
8
|
hydrated,
|
|
12
9
|
orders,
|
|
@@ -1,20 +1,17 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* Query key stubs for reconciliation bridge.
|
|
4
3
|
* Real host apps expose these; core only needs shape for invalidateQueries.
|
|
5
4
|
*/
|
|
6
|
-
|
|
7
|
-
exports.REFERENCE_KEYS = exports.MENU_KEYS = exports.SELLER_KEYS = exports.POS_KEYS = void 0;
|
|
8
|
-
exports.POS_KEYS = {
|
|
5
|
+
export const POS_KEYS = {
|
|
9
6
|
all: ["pos"],
|
|
10
7
|
orders: (businessId) => ["pos", "orders", businessId],
|
|
11
8
|
};
|
|
12
|
-
|
|
9
|
+
export const SELLER_KEYS = {
|
|
13
10
|
all: ["seller"],
|
|
14
11
|
};
|
|
15
|
-
|
|
12
|
+
export const MENU_KEYS = {
|
|
16
13
|
all: ["menu"],
|
|
17
14
|
};
|
|
18
|
-
|
|
15
|
+
export const REFERENCE_KEYS = {
|
|
19
16
|
all: ["reference"],
|
|
20
17
|
};
|
|
@@ -1,17 +1,12 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* Service stubs for pos/seller/kiosk/menu/business/roles.
|
|
4
3
|
* Host apps can replace via setServiceImpls; core defaults to no-op that throws helpful error.
|
|
5
4
|
*/
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.useLocationStore = exports.rolesService = exports.businessService = exports.menuService = exports.kioskService = exports.sellerService = exports.posService = void 0;
|
|
8
|
-
exports.getRegionConfig = getRegionConfig;
|
|
9
|
-
exports.formatCurrencyByRegion = formatCurrencyByRegion;
|
|
10
5
|
function notImpl(name) {
|
|
11
6
|
throw new Error(`Service ${name} not injected. Wire via adapters/services.ts or setHttpClient.`);
|
|
12
7
|
}
|
|
13
8
|
// pos.service
|
|
14
|
-
|
|
9
|
+
export const posService = {
|
|
15
10
|
createPOSOrder: async (_businessId, _payload) => {
|
|
16
11
|
notImpl("posService.createPOSOrder");
|
|
17
12
|
},
|
|
@@ -23,7 +18,7 @@ exports.posService = {
|
|
|
23
18
|
},
|
|
24
19
|
};
|
|
25
20
|
// seller.service
|
|
26
|
-
|
|
21
|
+
export const sellerService = {
|
|
27
22
|
acceptOrder: async (_orderId) => {
|
|
28
23
|
notImpl("sellerService.acceptOrder");
|
|
29
24
|
},
|
|
@@ -47,13 +42,13 @@ exports.sellerService = {
|
|
|
47
42
|
},
|
|
48
43
|
};
|
|
49
44
|
// kiosk.service
|
|
50
|
-
|
|
45
|
+
export const kioskService = {
|
|
51
46
|
assignKioskOrderToRider: async (_orderId, _riderId) => {
|
|
52
47
|
notImpl("kioskService.assignKioskOrderToRider");
|
|
53
48
|
},
|
|
54
49
|
};
|
|
55
50
|
// menu.service etc. for reference fallback
|
|
56
|
-
|
|
51
|
+
export const menuService = {
|
|
57
52
|
fetchBusinessMenuItems: async (_businessId, _page) => {
|
|
58
53
|
notImpl("menuService.fetchBusinessMenuItems");
|
|
59
54
|
},
|
|
@@ -61,7 +56,7 @@ exports.menuService = {
|
|
|
61
56
|
notImpl("menuService.fetchBusinessCategories");
|
|
62
57
|
},
|
|
63
58
|
};
|
|
64
|
-
|
|
59
|
+
export const businessService = {
|
|
65
60
|
fetchBusinessMembers: async (_businessId) => {
|
|
66
61
|
notImpl("businessService.fetchBusinessMembers");
|
|
67
62
|
},
|
|
@@ -75,19 +70,19 @@ exports.businessService = {
|
|
|
75
70
|
notImpl("businessService.fetchOperatingHours");
|
|
76
71
|
},
|
|
77
72
|
};
|
|
78
|
-
|
|
73
|
+
export const rolesService = {
|
|
79
74
|
fetchBusinessRoles: async () => {
|
|
80
75
|
notImpl("rolesService.fetchBusinessRoles");
|
|
81
76
|
},
|
|
82
77
|
};
|
|
83
78
|
// region pricing + location stubs for currency utils
|
|
84
|
-
function getRegionConfig(_countryCode) {
|
|
79
|
+
export function getRegionConfig(_countryCode) {
|
|
85
80
|
return { currencyCode: "GHS" };
|
|
86
81
|
}
|
|
87
|
-
function formatCurrencyByRegion(amountMinor, _config) {
|
|
82
|
+
export function formatCurrencyByRegion(amountMinor, _config) {
|
|
88
83
|
return `GH₵${(amountMinor / 100).toFixed(2)}`;
|
|
89
84
|
}
|
|
90
|
-
|
|
85
|
+
export const useLocationStore = {
|
|
91
86
|
getState: () => ({ currentLocation: null }),
|
|
92
87
|
// hook form: selector
|
|
93
88
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
package/dist/adapters/sqlite.js
CHANGED
|
@@ -1,23 +1,18 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* SQLite driver adapter — decouples core from expo-sqlite.
|
|
4
3
|
* Host apps inject a concrete driver via setSqliteDriver().
|
|
5
4
|
*/
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.setSqliteDriver = setSqliteDriver;
|
|
8
|
-
exports.getSqliteDriver = getSqliteDriver;
|
|
9
|
-
exports.hasSqliteDriver = hasSqliteDriver;
|
|
10
5
|
let driver = null;
|
|
11
|
-
function setSqliteDriver(d) {
|
|
6
|
+
export function setSqliteDriver(d) {
|
|
12
7
|
driver = d;
|
|
13
8
|
}
|
|
14
|
-
function getSqliteDriver() {
|
|
9
|
+
export function getSqliteDriver() {
|
|
15
10
|
if (!driver) {
|
|
16
11
|
throw new Error("SQLite driver not injected. Call setSqliteDriver(driver) before openSyncDatabase(). " +
|
|
17
12
|
"Provide an object implementing SqliteDriver (see src/adapters/sqlite.ts).");
|
|
18
13
|
}
|
|
19
14
|
return driver;
|
|
20
15
|
}
|
|
21
|
-
function hasSqliteDriver() {
|
|
16
|
+
export function hasSqliteDriver() {
|
|
22
17
|
return driver !== null;
|
|
23
18
|
}
|
package/dist/db/connection.js
CHANGED
|
@@ -1,50 +1,45 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* SQLite connection for the offline-first command outbox and event journal.
|
|
4
3
|
* Delegates to injected SqliteDriver (see src/adapters/sqlite.ts).
|
|
5
4
|
*/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
exports.closeSyncDatabase = closeSyncDatabase;
|
|
10
|
-
const sync_logger_1 = require("../utils/sync-logger");
|
|
11
|
-
const migrations_1 = require("./migrations");
|
|
12
|
-
const sqlite_1 = require("../adapters/sqlite");
|
|
5
|
+
import { syncLogger } from "../utils/sync-logger";
|
|
6
|
+
import { MIGRATIONS } from "./migrations";
|
|
7
|
+
import { getSqliteDriver, hasSqliteDriver } from "../adapters/sqlite";
|
|
13
8
|
const DB_NAME = "teincfood_sync.db";
|
|
14
9
|
let dbInstance = null;
|
|
15
10
|
let initPromise = null;
|
|
16
|
-
async function openSyncDatabase() {
|
|
11
|
+
export async function openSyncDatabase() {
|
|
17
12
|
if (dbInstance)
|
|
18
13
|
return dbInstance;
|
|
19
14
|
if (initPromise)
|
|
20
15
|
return initPromise;
|
|
21
|
-
if (!
|
|
16
|
+
if (!hasSqliteDriver()) {
|
|
22
17
|
throw new Error("SQLite driver not injected. Call setSqliteDriver(driver) before openSyncDatabase().");
|
|
23
18
|
}
|
|
24
|
-
const driver =
|
|
19
|
+
const driver = getSqliteDriver();
|
|
25
20
|
initPromise = (async () => {
|
|
26
21
|
await runMigrations(driver);
|
|
27
22
|
dbInstance = driver;
|
|
28
|
-
|
|
23
|
+
syncLogger.info("SQLite", "Database opened and migrated", {
|
|
29
24
|
name: DB_NAME,
|
|
30
25
|
});
|
|
31
26
|
return driver;
|
|
32
27
|
})();
|
|
33
28
|
initPromise = initPromise.catch((error) => {
|
|
34
29
|
initPromise = null;
|
|
35
|
-
|
|
30
|
+
syncLogger.error("SQLite", "Failed to open sync database", {
|
|
36
31
|
error: String(error),
|
|
37
32
|
});
|
|
38
33
|
throw error;
|
|
39
34
|
});
|
|
40
35
|
return initPromise;
|
|
41
36
|
}
|
|
42
|
-
function getSyncDatabase() {
|
|
37
|
+
export function getSyncDatabase() {
|
|
43
38
|
if (!dbInstance) {
|
|
44
39
|
// If driver injected but not yet opened, return driver directly to avoid "not initialized"
|
|
45
40
|
// during early replay (cold start). Migration will run on openSyncDatabase.
|
|
46
|
-
if (
|
|
47
|
-
return
|
|
41
|
+
if (hasSqliteDriver()) {
|
|
42
|
+
return getSqliteDriver();
|
|
48
43
|
}
|
|
49
44
|
throw new Error("Sync database not initialized. Call openSyncDatabase() first.");
|
|
50
45
|
}
|
|
@@ -58,13 +53,13 @@ async function runMigrations(db) {
|
|
|
58
53
|
applied_at TEXT NOT NULL
|
|
59
54
|
);
|
|
60
55
|
`);
|
|
61
|
-
for (const migration of
|
|
56
|
+
for (const migration of MIGRATIONS) {
|
|
62
57
|
const row = await db.getFirstAsync("SELECT version FROM migrations WHERE version = ?;", migration.version);
|
|
63
58
|
if (row)
|
|
64
59
|
continue;
|
|
65
60
|
await migration.up(db);
|
|
66
61
|
await db.runAsync("INSERT INTO migrations (version, applied_at) VALUES (?, ?);", migration.version, new Date().toISOString());
|
|
67
|
-
|
|
62
|
+
syncLogger.info("SQLite", "Applied migration", {
|
|
68
63
|
version: migration.version,
|
|
69
64
|
});
|
|
70
65
|
}
|
|
@@ -76,12 +71,12 @@ async function runMigrations(db) {
|
|
|
76
71
|
await doMigrate();
|
|
77
72
|
}
|
|
78
73
|
}
|
|
79
|
-
async function closeSyncDatabase() {
|
|
74
|
+
export async function closeSyncDatabase() {
|
|
80
75
|
if (dbInstance) {
|
|
81
76
|
if (dbInstance.closeAsync)
|
|
82
77
|
await dbInstance.closeAsync();
|
|
83
78
|
dbInstance = null;
|
|
84
79
|
initPromise = null;
|
|
85
|
-
|
|
80
|
+
syncLogger.info("SQLite", "Database closed");
|
|
86
81
|
}
|
|
87
82
|
}
|
package/dist/db/migrations.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* SQLite migrations for the offline-first command outbox and event journal.
|
|
4
3
|
*/
|
|
5
|
-
|
|
6
|
-
exports.MIGRATIONS = void 0;
|
|
7
|
-
exports.MIGRATIONS = [
|
|
4
|
+
export const MIGRATIONS = [
|
|
8
5
|
{
|
|
9
6
|
version: 1,
|
|
10
7
|
up: async (db) => {
|
package/dist/db/operations.js
CHANGED
|
@@ -1,25 +1,8 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* Low-level SQLite operations for the command outbox and event journal.
|
|
4
3
|
*/
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
exports.getPendingCommands = getPendingCommands;
|
|
8
|
-
exports.updateCommandStatus = updateCommandStatus;
|
|
9
|
-
exports.markCommandInFlight = markCommandInFlight;
|
|
10
|
-
exports.deleteCommand = deleteCommand;
|
|
11
|
-
exports.clearCompletedCommands = clearCompletedCommands;
|
|
12
|
-
exports.insertEvent = insertEvent;
|
|
13
|
-
exports.getEventsForEntity = getEventsForEntity;
|
|
14
|
-
exports.getUnsyncedEvents = getUnsyncedEvents;
|
|
15
|
-
exports.markEventsSynced = markEventsSynced;
|
|
16
|
-
exports.upsertEntitySnapshot = upsertEntitySnapshot;
|
|
17
|
-
exports.getEntitySnapshot = getEntitySnapshot;
|
|
18
|
-
exports.getEntitySnapshotsByType = getEntitySnapshotsByType;
|
|
19
|
-
exports.deleteEntitySnapshot = deleteEntitySnapshot;
|
|
20
|
-
exports.pruneEntitySnapshots = pruneEntitySnapshots;
|
|
21
|
-
const sync_logger_1 = require("../utils/sync-logger");
|
|
22
|
-
async function insertCommand(db, input) {
|
|
4
|
+
import { syncLogger } from "../utils/sync-logger";
|
|
5
|
+
export async function insertCommand(db, input) {
|
|
23
6
|
const { command, status = "pending" } = input;
|
|
24
7
|
const now = new Date().toISOString();
|
|
25
8
|
const payload = JSON.stringify(command.payload);
|
|
@@ -38,10 +21,10 @@ async function insertCommand(db, input) {
|
|
|
38
21
|
if (!row) {
|
|
39
22
|
throw new Error(`Failed to insert command ${command.command_id}`);
|
|
40
23
|
}
|
|
41
|
-
|
|
24
|
+
syncLogger.command(command, "queued");
|
|
42
25
|
return rowToPersistedCommand(row);
|
|
43
26
|
}
|
|
44
|
-
async function getPendingCommands(db, limit = 100) {
|
|
27
|
+
export async function getPendingCommands(db, limit = 100) {
|
|
45
28
|
const rows = await db.getAllAsync(`
|
|
46
29
|
SELECT * FROM commands
|
|
47
30
|
WHERE status IN ('pending', 'failed')
|
|
@@ -50,7 +33,7 @@ async function getPendingCommands(db, limit = 100) {
|
|
|
50
33
|
`, limit);
|
|
51
34
|
return rows.map(rowToPersistedCommand);
|
|
52
35
|
}
|
|
53
|
-
async function updateCommandStatus(db, commandId, status, error) {
|
|
36
|
+
export async function updateCommandStatus(db, commandId, status, error) {
|
|
54
37
|
const now = new Date().toISOString();
|
|
55
38
|
if (status === "failed") {
|
|
56
39
|
await db.runAsync(`
|
|
@@ -66,9 +49,9 @@ async function updateCommandStatus(db, commandId, status, error) {
|
|
|
66
49
|
else {
|
|
67
50
|
await db.runAsync("UPDATE commands SET status = ?, error = ?, updated_at = ? WHERE command_id = ?;", status, error ?? null, now, commandId);
|
|
68
51
|
}
|
|
69
|
-
|
|
52
|
+
syncLogger.debug("CommandOutbox", "Status updated", { commandId, status });
|
|
70
53
|
}
|
|
71
|
-
async function markCommandInFlight(db, commandId) {
|
|
54
|
+
export async function markCommandInFlight(db, commandId) {
|
|
72
55
|
const now = new Date().toISOString();
|
|
73
56
|
await db.runAsync(`
|
|
74
57
|
UPDATE commands
|
|
@@ -76,14 +59,14 @@ async function markCommandInFlight(db, commandId) {
|
|
|
76
59
|
WHERE command_id = ?;
|
|
77
60
|
`, now, now, commandId);
|
|
78
61
|
}
|
|
79
|
-
async function deleteCommand(db, commandId) {
|
|
62
|
+
export async function deleteCommand(db, commandId) {
|
|
80
63
|
await db.runAsync("DELETE FROM commands WHERE command_id = ?;", commandId);
|
|
81
64
|
}
|
|
82
|
-
async function clearCompletedCommands(db, olderThanHours = 24) {
|
|
65
|
+
export async function clearCompletedCommands(db, olderThanHours = 24) {
|
|
83
66
|
const cutoff = new Date(Date.now() - olderThanHours * 60 * 60 * 1000).toISOString();
|
|
84
67
|
await db.runAsync("DELETE FROM commands WHERE status = 'completed' AND updated_at < ?;", cutoff);
|
|
85
68
|
}
|
|
86
|
-
async function insertEvent(db, input) {
|
|
69
|
+
export async function insertEvent(db, input) {
|
|
87
70
|
const { event, origin, syncedToCloud = false } = input;
|
|
88
71
|
await db.runAsync(`
|
|
89
72
|
INSERT INTO events (
|
|
@@ -92,9 +75,9 @@ async function insertEvent(db, input) {
|
|
|
92
75
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
93
76
|
ON CONFLICT(event_id) DO NOTHING;
|
|
94
77
|
`, event.event_id, event.event_type, event.event_version, event.entity_id, event.user_id, event.device_id, JSON.stringify(event.payload), origin, syncedToCloud ? 1 : 0, event.timestamp, new Date().toISOString());
|
|
95
|
-
|
|
78
|
+
syncLogger.event(event, "persisted", { origin, syncedToCloud });
|
|
96
79
|
}
|
|
97
|
-
async function getEventsForEntity(db, entityId, limit = 100) {
|
|
80
|
+
export async function getEventsForEntity(db, entityId, limit = 100) {
|
|
98
81
|
const rows = await db.getAllAsync(`
|
|
99
82
|
SELECT * FROM events
|
|
100
83
|
WHERE entity_id = ?
|
|
@@ -103,7 +86,7 @@ async function getEventsForEntity(db, entityId, limit = 100) {
|
|
|
103
86
|
`, entityId, limit);
|
|
104
87
|
return rows.map(rowToDomainEvent);
|
|
105
88
|
}
|
|
106
|
-
async function getUnsyncedEvents(db, origin = "local", limit = 100) {
|
|
89
|
+
export async function getUnsyncedEvents(db, origin = "local", limit = 100) {
|
|
107
90
|
const rows = await db.getAllAsync(`
|
|
108
91
|
SELECT * FROM events
|
|
109
92
|
WHERE origin = ? AND synced_to_cloud = 0
|
|
@@ -112,7 +95,7 @@ async function getUnsyncedEvents(db, origin = "local", limit = 100) {
|
|
|
112
95
|
`, origin, limit);
|
|
113
96
|
return rows.map(rowToDomainEvent);
|
|
114
97
|
}
|
|
115
|
-
async function markEventsSynced(db, eventIds) {
|
|
98
|
+
export async function markEventsSynced(db, eventIds) {
|
|
116
99
|
if (eventIds.length === 0)
|
|
117
100
|
return;
|
|
118
101
|
const placeholders = eventIds.map(() => "?").join(",");
|
|
@@ -128,7 +111,7 @@ function rowToEntitySnapshot(row) {
|
|
|
128
111
|
payload: JSON.parse(row.payload),
|
|
129
112
|
};
|
|
130
113
|
}
|
|
131
|
-
async function upsertEntitySnapshot(db, input) {
|
|
114
|
+
export async function upsertEntitySnapshot(db, input) {
|
|
132
115
|
const { entityType, entityId, businessId = null, lastEventId, lastEventTimestamp, payload, } = input;
|
|
133
116
|
const existing = await db.getFirstAsync("SELECT last_event_timestamp FROM entity_snapshots WHERE entity_type = ? AND entity_id = ?;", entityType, entityId);
|
|
134
117
|
if (existing && existing.last_event_timestamp > lastEventTimestamp) {
|
|
@@ -144,18 +127,18 @@ async function upsertEntitySnapshot(db, input) {
|
|
|
144
127
|
payload = excluded.payload;
|
|
145
128
|
`, entityType, entityId, businessId, lastEventId, lastEventTimestamp, JSON.stringify(payload));
|
|
146
129
|
}
|
|
147
|
-
async function getEntitySnapshot(db, entityType, entityId) {
|
|
130
|
+
export async function getEntitySnapshot(db, entityType, entityId) {
|
|
148
131
|
const row = await db.getFirstAsync("SELECT * FROM entity_snapshots WHERE entity_type = ? AND entity_id = ?;", entityType, entityId);
|
|
149
132
|
return row ? rowToEntitySnapshot(row) : null;
|
|
150
133
|
}
|
|
151
|
-
async function getEntitySnapshotsByType(db, entityType) {
|
|
134
|
+
export async function getEntitySnapshotsByType(db, entityType) {
|
|
152
135
|
const rows = await db.getAllAsync("SELECT * FROM entity_snapshots WHERE entity_type = ? ORDER BY last_event_timestamp DESC;", entityType);
|
|
153
136
|
return rows.map(rowToEntitySnapshot);
|
|
154
137
|
}
|
|
155
|
-
async function deleteEntitySnapshot(db, entityType, entityId) {
|
|
138
|
+
export async function deleteEntitySnapshot(db, entityType, entityId) {
|
|
156
139
|
await db.runAsync("DELETE FROM entity_snapshots WHERE entity_type = ? AND entity_id = ?;", entityType, entityId);
|
|
157
140
|
}
|
|
158
|
-
async function pruneEntitySnapshots(db, entityType, cutoffIso) {
|
|
141
|
+
export async function pruneEntitySnapshots(db, entityType, cutoffIso) {
|
|
159
142
|
await db.runAsync("DELETE FROM entity_snapshots WHERE entity_type = ? AND last_event_timestamp < ?;", entityType, cutoffIso);
|
|
160
143
|
}
|
|
161
144
|
function rowToPersistedCommand(row) {
|