@teincfood/core 0.1.1 → 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.js +1 -2
- package/dist/types/api/wallet.api.js +1 -2
- package/dist/types/pos.types.js +1 -2
- 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/index.js
CHANGED
|
@@ -1,117 +1,64 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* @teincfood/core — shared offline-first backbone
|
|
4
3
|
*
|
|
5
4
|
* Entry point. Re-exports types, sync engine, DB, reference service, repos and adapters.
|
|
6
5
|
* Host apps call `createTeincCore` once at bootstrap to wire platform adapters.
|
|
7
6
|
*/
|
|
8
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
-
if (k2 === undefined) k2 = k;
|
|
10
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
-
}
|
|
14
|
-
Object.defineProperty(o, k2, desc);
|
|
15
|
-
}) : (function(o, m, k, k2) {
|
|
16
|
-
if (k2 === undefined) k2 = k;
|
|
17
|
-
o[k2] = m[k];
|
|
18
|
-
}));
|
|
19
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
-
}) : function(o, v) {
|
|
22
|
-
o["default"] = v;
|
|
23
|
-
});
|
|
24
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
25
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
26
|
-
};
|
|
27
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
28
|
-
var ownKeys = function(o) {
|
|
29
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
30
|
-
var ar = [];
|
|
31
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
32
|
-
return ar;
|
|
33
|
-
};
|
|
34
|
-
return ownKeys(o);
|
|
35
|
-
};
|
|
36
|
-
return function (mod) {
|
|
37
|
-
if (mod && mod.__esModule) return mod;
|
|
38
|
-
var result = {};
|
|
39
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
40
|
-
__setModuleDefault(result, mod);
|
|
41
|
-
return result;
|
|
42
|
-
};
|
|
43
|
-
})();
|
|
44
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
-
exports.referenceSyncService = exports.posRepository = exports.orderRepository = exports.orderSnapshotsService = exports.isLocalOrderNumber = exports.generateLocalCalloutNumber = exports.generateLocalOrderNumber = exports.buildCommand = exports.commandQueueService = exports.SyncEngine = exports.getSyncDatabase = exports.openSyncDatabase = exports.dbOps = void 0;
|
|
46
|
-
exports.createTeincCore = createTeincCore;
|
|
47
|
-
exports.getCoreEngine = getCoreEngine;
|
|
48
7
|
// ── Types ──
|
|
49
|
-
|
|
50
|
-
|
|
8
|
+
export * from "./types/api";
|
|
9
|
+
export * from "./types/pos.types";
|
|
51
10
|
// ── Utils ──
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
11
|
+
export * from "./utils/constants";
|
|
12
|
+
export * from "./utils/currency";
|
|
13
|
+
export * from "./utils/error";
|
|
14
|
+
export * from "./utils/uuid";
|
|
15
|
+
export * from "./utils/sync-logger";
|
|
57
16
|
// ── DB ──
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
Object.defineProperty(exports, "openSyncDatabase", { enumerable: true, get: function () { return connection_1.openSyncDatabase; } });
|
|
62
|
-
Object.defineProperty(exports, "getSyncDatabase", { enumerable: true, get: function () { return connection_1.getSyncDatabase; } });
|
|
17
|
+
export * from "./db/migrations";
|
|
18
|
+
export * as dbOps from "./db/operations";
|
|
19
|
+
export { openSyncDatabase, getSyncDatabase } from "./db/connection";
|
|
63
20
|
// ── Sync ──
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
var order_number_1 = require("./sync/order-number");
|
|
72
|
-
Object.defineProperty(exports, "generateLocalOrderNumber", { enumerable: true, get: function () { return order_number_1.generateLocalOrderNumber; } });
|
|
73
|
-
Object.defineProperty(exports, "generateLocalCalloutNumber", { enumerable: true, get: function () { return order_number_1.generateLocalCalloutNumber; } });
|
|
74
|
-
Object.defineProperty(exports, "isLocalOrderNumber", { enumerable: true, get: function () { return order_number_1.isLocalOrderNumber; } });
|
|
75
|
-
var order_snapshots_service_1 = require("./sync/order-snapshots.service");
|
|
76
|
-
Object.defineProperty(exports, "orderSnapshotsService", { enumerable: true, get: function () { return order_snapshots_service_1.orderSnapshotsService; } });
|
|
77
|
-
__exportStar(require("./sync/pending-orders.service"), exports);
|
|
21
|
+
export * from "./sync/types";
|
|
22
|
+
export { SyncEngine } from "./sync/engine";
|
|
23
|
+
export { commandQueueService } from "./sync/command-queue.service";
|
|
24
|
+
export { buildCommand } from "./sync/command-builder";
|
|
25
|
+
export { generateLocalOrderNumber, generateLocalCalloutNumber, isLocalOrderNumber } from "./sync/order-number";
|
|
26
|
+
export { orderSnapshotsService } from "./sync/order-snapshots.service";
|
|
27
|
+
export * from "./sync/pending-orders.service";
|
|
78
28
|
// ── Repositories ──
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
var pos_repository_1 = require("./repositories/pos.repository");
|
|
82
|
-
Object.defineProperty(exports, "posRepository", { enumerable: true, get: function () { return pos_repository_1.posRepository; } });
|
|
29
|
+
export { orderRepository } from "./repositories/order.repository";
|
|
30
|
+
export { posRepository } from "./repositories/pos.repository";
|
|
83
31
|
// ── Reference ──
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
Object.defineProperty(exports, "referenceSyncService", { enumerable: true, get: function () { return service_1.referenceSyncService; } });
|
|
32
|
+
export * from "./reference/types";
|
|
33
|
+
export { referenceSyncService } from "./reference/service";
|
|
87
34
|
// ── Adapters (for host apps to supply) ──
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
35
|
+
export * from "./adapters/sqlite";
|
|
36
|
+
export * from "./adapters/kv";
|
|
37
|
+
export * from "./adapters/connectivity";
|
|
38
|
+
export * from "./adapters/http";
|
|
39
|
+
export * from "./adapters/local-node";
|
|
93
40
|
// ── Init helper ──
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
41
|
+
import { setSqliteDriver } from "./adapters/sqlite";
|
|
42
|
+
import { setKVDriver } from "./adapters/kv";
|
|
43
|
+
import { setConnectivityState } from "./adapters/connectivity";
|
|
44
|
+
import { setHttpClient } from "./adapters/http";
|
|
45
|
+
import { SyncEngine } from "./sync/engine";
|
|
46
|
+
import { restTransport } from "./sync/transport-rest";
|
|
47
|
+
import { localNodeTransport } from "./sync/transport-local-node";
|
|
101
48
|
let engine = null;
|
|
102
|
-
function createTeincCore(config = {}) {
|
|
49
|
+
export function createTeincCore(config = {}) {
|
|
103
50
|
if (config.sqliteDriver)
|
|
104
|
-
|
|
51
|
+
setSqliteDriver(config.sqliteDriver);
|
|
105
52
|
if (config.kvDriver)
|
|
106
|
-
|
|
53
|
+
setKVDriver(config.kvDriver);
|
|
107
54
|
if (config.httpClient)
|
|
108
|
-
|
|
55
|
+
setHttpClient(config.httpClient);
|
|
109
56
|
if (config.initialConnectivity)
|
|
110
|
-
|
|
111
|
-
engine = new
|
|
57
|
+
setConnectivityState(config.initialConnectivity);
|
|
58
|
+
engine = new SyncEngine({ transports: [restTransport, localNodeTransport] });
|
|
112
59
|
return { engine };
|
|
113
60
|
}
|
|
114
|
-
function getCoreEngine() {
|
|
61
|
+
export function getCoreEngine() {
|
|
115
62
|
if (!engine)
|
|
116
63
|
throw new Error("TeincFood core not initialised. Call createTeincCore() first.");
|
|
117
64
|
return engine;
|
package/dist/reference/api.js
CHANGED
|
@@ -1,24 +1,19 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* API service for reference data synchronization.
|
|
4
3
|
*
|
|
5
4
|
* These endpoints are expected on the backend. If they are not available,
|
|
6
5
|
* the ReferenceSyncService falls back to legacy menu endpoints.
|
|
7
6
|
*/
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
exports.fetchReferenceChanges = fetchReferenceChanges;
|
|
12
|
-
const http_1 = require("../adapters/http");
|
|
13
|
-
async function fetchReferenceVersions(businessId) {
|
|
14
|
-
const response = await http_1.api.get(`/businesses/${businessId}/reference-versions`);
|
|
7
|
+
import { api } from "../adapters/http";
|
|
8
|
+
export async function fetchReferenceVersions(businessId) {
|
|
9
|
+
const response = await api.get(`/businesses/${businessId}/reference-versions`);
|
|
15
10
|
return response.data.data;
|
|
16
11
|
}
|
|
17
|
-
async function fetchReferenceData(businessId, keys) {
|
|
18
|
-
const response = await
|
|
12
|
+
export async function fetchReferenceData(businessId, keys) {
|
|
13
|
+
const response = await api.get(`/businesses/${businessId}/reference-data`, { params: { keys: keys.join(",") } });
|
|
19
14
|
return response.data.data;
|
|
20
15
|
}
|
|
21
|
-
async function fetchReferenceChanges(businessId, key, fromVersion) {
|
|
22
|
-
const response = await
|
|
16
|
+
export async function fetchReferenceChanges(businessId, key, fromVersion) {
|
|
17
|
+
const response = await api.get(`/businesses/${businessId}/reference-changes`, { params: { key, from_version: fromVersion } });
|
|
23
18
|
return response.data.data;
|
|
24
19
|
}
|
package/dist/reference/events.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* Reference data change events.
|
|
4
3
|
*
|
|
@@ -17,24 +16,17 @@
|
|
|
17
16
|
* `local-node/service.ts` can depend on this module without creating an
|
|
18
17
|
* import cycle (engine → transport → local-node/service → reference/events).
|
|
19
18
|
*/
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const auth_1 = require("../adapters/auth");
|
|
28
|
-
const business_1 = require("../adapters/business");
|
|
29
|
-
const devices_1 = require("../adapters/devices");
|
|
30
|
-
const uuid_1 = require("../utils/uuid");
|
|
31
|
-
const sync_logger_1 = require("../utils/sync-logger");
|
|
32
|
-
const service_1 = require("./service");
|
|
33
|
-
exports.REFERENCE_CHANGED_EVENT_TYPE = "reference_data.changed";
|
|
19
|
+
import { useAuthStore } from "../adapters/auth";
|
|
20
|
+
import { useBusinessStore } from "../adapters/business";
|
|
21
|
+
import { getAppInstanceId } from "../adapters/devices";
|
|
22
|
+
import { generateEventId } from "../utils/uuid";
|
|
23
|
+
import { syncLogger } from "../utils/sync-logger";
|
|
24
|
+
import { referenceSyncService } from "./service";
|
|
25
|
+
export const REFERENCE_CHANGED_EVENT_TYPE = "reference_data.changed";
|
|
34
26
|
/** Injected Sync Engine singleton (set by the reconciliation bridge). */
|
|
35
27
|
let engine = null;
|
|
36
28
|
/** Register the Sync Engine used to publish reference change events. */
|
|
37
|
-
function registerReferenceEventEngine(syncEngine) {
|
|
29
|
+
export function registerReferenceEventEngine(syncEngine) {
|
|
38
30
|
engine = syncEngine;
|
|
39
31
|
}
|
|
40
32
|
/** Invalidate TanStack Query caches after reference data is re-synced. */
|
|
@@ -44,7 +36,7 @@ let invalidator = null;
|
|
|
44
36
|
* data changes. Returns an unregister function. Mirrors
|
|
45
37
|
* `registerPendingOrdersInvalidator`.
|
|
46
38
|
*/
|
|
47
|
-
function registerReferenceInvalidator(fn) {
|
|
39
|
+
export function registerReferenceInvalidator(fn) {
|
|
48
40
|
invalidator = fn;
|
|
49
41
|
return () => {
|
|
50
42
|
if (invalidator === fn)
|
|
@@ -56,18 +48,18 @@ function registerReferenceInvalidator(fn) {
|
|
|
56
48
|
* one) re-syncs the affected keys. Called after a successful mutation of a
|
|
57
49
|
* reference-backed entity.
|
|
58
50
|
*/
|
|
59
|
-
function emitReferenceChanged(businessId, keys) {
|
|
51
|
+
export function emitReferenceChanged(businessId, keys) {
|
|
60
52
|
if (!businessId || keys.length === 0)
|
|
61
53
|
return;
|
|
62
54
|
if (!engine)
|
|
63
55
|
return;
|
|
64
|
-
const user =
|
|
56
|
+
const user = useAuthStore.getState().user;
|
|
65
57
|
const event = {
|
|
66
|
-
event_id:
|
|
67
|
-
event_type:
|
|
58
|
+
event_id: generateEventId(),
|
|
59
|
+
event_type: REFERENCE_CHANGED_EVENT_TYPE,
|
|
68
60
|
event_version: "1.0",
|
|
69
61
|
timestamp: new Date().toISOString(),
|
|
70
|
-
device_id:
|
|
62
|
+
device_id: getAppInstanceId(),
|
|
71
63
|
user_id: user?.id ?? "anonymous",
|
|
72
64
|
entity_id: businessId,
|
|
73
65
|
payload: { business_id: businessId, keys },
|
|
@@ -81,27 +73,27 @@ function emitReferenceChanged(businessId, keys) {
|
|
|
81
73
|
* by a single-flight flag. Errors are logged, never thrown, so a failed
|
|
82
74
|
* reference sync can never crash the LAN event loop.
|
|
83
75
|
*/
|
|
84
|
-
async function applyReferenceChangeEvent(event, origin = "local") {
|
|
76
|
+
export async function applyReferenceChangeEvent(event, origin = "local") {
|
|
85
77
|
try {
|
|
86
|
-
if (event.event_type !==
|
|
78
|
+
if (event.event_type !== REFERENCE_CHANGED_EVENT_TYPE)
|
|
87
79
|
return;
|
|
88
80
|
const payload = event.payload;
|
|
89
81
|
const businessId = payload?.business_id;
|
|
90
82
|
const keys = payload?.keys;
|
|
91
83
|
if (!businessId || !keys || keys.length === 0)
|
|
92
84
|
return;
|
|
93
|
-
|
|
85
|
+
syncLogger.info("ReferenceChange", "Reference data changed", {
|
|
94
86
|
origin,
|
|
95
87
|
business_id: businessId,
|
|
96
88
|
keys,
|
|
97
89
|
});
|
|
98
|
-
const result = await
|
|
90
|
+
const result = await referenceSyncService.sync({
|
|
99
91
|
businessId,
|
|
100
92
|
keys,
|
|
101
93
|
incremental: true,
|
|
102
94
|
});
|
|
103
95
|
if (result.failed.length > 0) {
|
|
104
|
-
|
|
96
|
+
syncLogger.warn("ReferenceChange", "Reference sync had failures", {
|
|
105
97
|
origin,
|
|
106
98
|
failed: result.failed,
|
|
107
99
|
errors: result.errors,
|
|
@@ -110,7 +102,7 @@ async function applyReferenceChangeEvent(event, origin = "local") {
|
|
|
110
102
|
invalidator?.();
|
|
111
103
|
}
|
|
112
104
|
catch (error) {
|
|
113
|
-
|
|
105
|
+
syncLogger.error("ReferenceChange", "Failed to apply reference change event", {
|
|
114
106
|
error: String(error),
|
|
115
107
|
event_type: event.event_type,
|
|
116
108
|
});
|
|
@@ -121,23 +113,23 @@ async function applyReferenceChangeEvent(event, origin = "local") {
|
|
|
121
113
|
* that missed the change event (offline, not on the LAN) — the next time they
|
|
122
114
|
* get internet they attempt to re-sync all default reference keys.
|
|
123
115
|
*/
|
|
124
|
-
async function syncReferenceDataOnReconnect() {
|
|
125
|
-
const businessId =
|
|
116
|
+
export async function syncReferenceDataOnReconnect() {
|
|
117
|
+
const businessId = useBusinessStore.getState().activeBusiness?.id;
|
|
126
118
|
if (!businessId)
|
|
127
119
|
return;
|
|
128
120
|
try {
|
|
129
|
-
const result = await
|
|
121
|
+
const result = await referenceSyncService.sync({
|
|
130
122
|
businessId,
|
|
131
123
|
incremental: true,
|
|
132
124
|
});
|
|
133
125
|
if (result.failed.length > 0) {
|
|
134
|
-
|
|
126
|
+
syncLogger.warn("ReferenceChange", "Reconnect sync had failures", {
|
|
135
127
|
failed: result.failed,
|
|
136
128
|
});
|
|
137
129
|
}
|
|
138
130
|
}
|
|
139
131
|
catch (error) {
|
|
140
|
-
|
|
132
|
+
syncLogger.error("ReferenceChange", "Reconnect sync failed", {
|
|
141
133
|
error: String(error),
|
|
142
134
|
});
|
|
143
135
|
}
|
|
@@ -1,15 +1,7 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* SQLite operations for the business-scoped reference data cache.
|
|
4
3
|
*/
|
|
5
|
-
|
|
6
|
-
exports.getReferenceData = getReferenceData;
|
|
7
|
-
exports.getReferenceVersion = getReferenceVersion;
|
|
8
|
-
exports.setReferenceData = setReferenceData;
|
|
9
|
-
exports.deleteReferenceData = deleteReferenceData;
|
|
10
|
-
exports.deleteReferenceDataForBusiness = deleteReferenceDataForBusiness;
|
|
11
|
-
exports.getAllReferenceDataKeys = getAllReferenceDataKeys;
|
|
12
|
-
async function getReferenceData(db, businessId, key) {
|
|
4
|
+
export async function getReferenceData(db, businessId, key) {
|
|
13
5
|
const row = await db.getFirstAsync("SELECT * FROM reference_data WHERE business_id = ? AND key = ?;", businessId, key);
|
|
14
6
|
if (!row)
|
|
15
7
|
return null;
|
|
@@ -20,11 +12,11 @@ async function getReferenceData(db, businessId, key) {
|
|
|
20
12
|
downloadedAt: row.downloaded_at,
|
|
21
13
|
};
|
|
22
14
|
}
|
|
23
|
-
async function getReferenceVersion(db, businessId, key) {
|
|
15
|
+
export async function getReferenceVersion(db, businessId, key) {
|
|
24
16
|
const row = await db.getFirstAsync("SELECT version FROM reference_data WHERE business_id = ? AND key = ?;", businessId, key);
|
|
25
17
|
return row?.version ?? null;
|
|
26
18
|
}
|
|
27
|
-
async function setReferenceData(db, businessId, record) {
|
|
19
|
+
export async function setReferenceData(db, businessId, record) {
|
|
28
20
|
await db.runAsync(`
|
|
29
21
|
INSERT INTO reference_data (business_id, key, version, payload, downloaded_at)
|
|
30
22
|
VALUES (?, ?, ?, ?, ?)
|
|
@@ -34,13 +26,13 @@ async function setReferenceData(db, businessId, record) {
|
|
|
34
26
|
downloaded_at = excluded.downloaded_at;
|
|
35
27
|
`, businessId, record.key, record.version, JSON.stringify(record.payload), record.downloadedAt);
|
|
36
28
|
}
|
|
37
|
-
async function deleteReferenceData(db, businessId, key) {
|
|
29
|
+
export async function deleteReferenceData(db, businessId, key) {
|
|
38
30
|
await db.runAsync("DELETE FROM reference_data WHERE business_id = ? AND key = ?;", businessId, key);
|
|
39
31
|
}
|
|
40
|
-
async function deleteReferenceDataForBusiness(db, businessId) {
|
|
32
|
+
export async function deleteReferenceDataForBusiness(db, businessId) {
|
|
41
33
|
await db.runAsync("DELETE FROM reference_data WHERE business_id = ?;", businessId);
|
|
42
34
|
}
|
|
43
|
-
async function getAllReferenceDataKeys(db, businessId) {
|
|
35
|
+
export async function getAllReferenceDataKeys(db, businessId) {
|
|
44
36
|
const rows = await db.getAllAsync("SELECT key FROM reference_data WHERE business_id = ? ORDER BY key;", businessId);
|
|
45
37
|
return rows.map((r) => r.key);
|
|
46
38
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* Reference Data Synchronization Service.
|
|
4
3
|
*
|
|
@@ -8,15 +7,13 @@
|
|
|
8
7
|
* This service is separate from the Sync Engine, which handles transactional
|
|
9
8
|
* commands and events.
|
|
10
9
|
*/
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const operations_1 = require("./operations");
|
|
19
|
-
const api_1 = require("./api");
|
|
10
|
+
import { openSyncDatabase } from "../db/connection";
|
|
11
|
+
import { syncLogger } from "../utils/sync-logger";
|
|
12
|
+
import { generateUUID } from "../utils/uuid";
|
|
13
|
+
import { menuService, businessService, rolesService } from "../adapters/services";
|
|
14
|
+
import { cacheImageBatch, pruneImageCache } from "../adapters/image-cache";
|
|
15
|
+
import { getReferenceData, getReferenceVersion, setReferenceData, } from "./operations";
|
|
16
|
+
import { fetchReferenceVersions, fetchReferenceData, fetchReferenceChanges, } from "./api";
|
|
20
17
|
const DEFAULT_SYNC_KEYS = [
|
|
21
18
|
"menu:items",
|
|
22
19
|
"menu:categories",
|
|
@@ -41,7 +38,7 @@ class ReferenceSyncService {
|
|
|
41
38
|
};
|
|
42
39
|
}
|
|
43
40
|
this.isSyncing = true;
|
|
44
|
-
|
|
41
|
+
syncLogger.info("ReferenceSync", "Starting reference data sync", {
|
|
45
42
|
businessId: options.businessId,
|
|
46
43
|
keys: options.keys,
|
|
47
44
|
});
|
|
@@ -52,16 +49,16 @@ class ReferenceSyncService {
|
|
|
52
49
|
errors: {},
|
|
53
50
|
};
|
|
54
51
|
try {
|
|
55
|
-
const db = await
|
|
52
|
+
const db = await openSyncDatabase();
|
|
56
53
|
const keys = options.keys ?? DEFAULT_SYNC_KEYS;
|
|
57
54
|
// Attempt version-aware sync. If the backend does not support versioning
|
|
58
55
|
// endpoints, fall back to legacy full-fetch per key.
|
|
59
56
|
let versions = null;
|
|
60
57
|
try {
|
|
61
|
-
versions = await
|
|
58
|
+
versions = await fetchReferenceVersions(options.businessId);
|
|
62
59
|
}
|
|
63
60
|
catch (error) {
|
|
64
|
-
|
|
61
|
+
syncLogger.warn("ReferenceSync", "Version endpoint unavailable, using fallback", {
|
|
65
62
|
error: String(error),
|
|
66
63
|
});
|
|
67
64
|
}
|
|
@@ -78,7 +75,7 @@ class ReferenceSyncService {
|
|
|
78
75
|
catch (error) {
|
|
79
76
|
result.failed.push(key);
|
|
80
77
|
result.errors[key] = String(error);
|
|
81
|
-
|
|
78
|
+
syncLogger.error("ReferenceSync", `Failed to sync ${key}`, {
|
|
82
79
|
error: String(error),
|
|
83
80
|
});
|
|
84
81
|
}
|
|
@@ -93,7 +90,7 @@ class ReferenceSyncService {
|
|
|
93
90
|
finally {
|
|
94
91
|
this.isSyncing = false;
|
|
95
92
|
}
|
|
96
|
-
|
|
93
|
+
syncLogger.info("ReferenceSync", "Reference data sync complete", {
|
|
97
94
|
synced: result.synced,
|
|
98
95
|
skipped: result.skipped,
|
|
99
96
|
failed: result.failed,
|
|
@@ -102,7 +99,7 @@ class ReferenceSyncService {
|
|
|
102
99
|
}
|
|
103
100
|
async syncKey(db, key, options, versions) {
|
|
104
101
|
const { businessId } = options;
|
|
105
|
-
const localVersion = await
|
|
102
|
+
const localVersion = await getReferenceVersion(db, businessId, key);
|
|
106
103
|
// A key the backend does not version (e.g. during a gradual rollout) is
|
|
107
104
|
// fetched with a full fetch + local hash so it is still cached offline.
|
|
108
105
|
const serverVersion = versions?.find((v) => v.key === key)?.version;
|
|
@@ -110,7 +107,7 @@ class ReferenceSyncService {
|
|
|
110
107
|
// In non-incremental mode without force, we still re-sync if version
|
|
111
108
|
// info says there is a newer version on the server.
|
|
112
109
|
if (serverVersion && serverVersion === localVersion) {
|
|
113
|
-
|
|
110
|
+
syncLogger.debug("ReferenceSync", `Up to date: ${key}`, {
|
|
114
111
|
version: localVersion,
|
|
115
112
|
});
|
|
116
113
|
return false;
|
|
@@ -121,19 +118,19 @@ class ReferenceSyncService {
|
|
|
121
118
|
if (versions && serverVersion) {
|
|
122
119
|
if (localVersion && serverVersion !== localVersion) {
|
|
123
120
|
try {
|
|
124
|
-
const changeSet = await
|
|
121
|
+
const changeSet = await fetchReferenceChanges(businessId, key, localVersion);
|
|
125
122
|
payload = changeSet.changes;
|
|
126
123
|
version = changeSet.to_version;
|
|
127
124
|
}
|
|
128
125
|
catch {
|
|
129
126
|
// Fall back to full fetch if changes endpoint fails.
|
|
130
|
-
const data = await
|
|
127
|
+
const data = await fetchReferenceData(businessId, [key]);
|
|
131
128
|
payload = data[key];
|
|
132
129
|
version = serverVersion;
|
|
133
130
|
}
|
|
134
131
|
}
|
|
135
132
|
else {
|
|
136
|
-
const data = await
|
|
133
|
+
const data = await fetchReferenceData(businessId, [key]);
|
|
137
134
|
payload = data[key];
|
|
138
135
|
version = serverVersion;
|
|
139
136
|
}
|
|
@@ -145,12 +142,12 @@ class ReferenceSyncService {
|
|
|
145
142
|
payload = fallback.payload;
|
|
146
143
|
version = fallback.version;
|
|
147
144
|
}
|
|
148
|
-
await
|
|
145
|
+
await setReferenceData(db, businessId, {
|
|
149
146
|
key,
|
|
150
147
|
// Defensive: never write a NULL version. If the backend ever omits the
|
|
151
148
|
// version, generate one so the row is still usable; the next version
|
|
152
149
|
// comparison will simply see a mismatch and re-sync.
|
|
153
|
-
version: version ??
|
|
150
|
+
version: version ?? generateUUID(),
|
|
154
151
|
payload,
|
|
155
152
|
downloadedAt: new Date().toISOString(),
|
|
156
153
|
});
|
|
@@ -159,49 +156,49 @@ class ReferenceSyncService {
|
|
|
159
156
|
async fetchFallback(key, businessId) {
|
|
160
157
|
switch (key) {
|
|
161
158
|
case "menu:items": {
|
|
162
|
-
const response = await
|
|
159
|
+
const response = await menuService.fetchBusinessMenuItems(businessId, 1);
|
|
163
160
|
return {
|
|
164
161
|
payload: response,
|
|
165
162
|
version: this.hashPayload(response),
|
|
166
163
|
};
|
|
167
164
|
}
|
|
168
165
|
case "menu:categories": {
|
|
169
|
-
const response = await
|
|
166
|
+
const response = await menuService.fetchBusinessCategories(businessId);
|
|
170
167
|
return {
|
|
171
168
|
payload: response,
|
|
172
169
|
version: this.hashPayload(response),
|
|
173
170
|
};
|
|
174
171
|
}
|
|
175
172
|
case "roles": {
|
|
176
|
-
const response = await
|
|
173
|
+
const response = await rolesService.fetchBusinessRoles();
|
|
177
174
|
return {
|
|
178
175
|
payload: response,
|
|
179
176
|
version: this.hashPayload(response),
|
|
180
177
|
};
|
|
181
178
|
}
|
|
182
179
|
case "members": {
|
|
183
|
-
const response = await
|
|
180
|
+
const response = await businessService.fetchBusinessMembers(businessId);
|
|
184
181
|
return {
|
|
185
182
|
payload: response,
|
|
186
183
|
version: this.hashPayload(response),
|
|
187
184
|
};
|
|
188
185
|
}
|
|
189
186
|
case "tax-rules": {
|
|
190
|
-
const response = await
|
|
187
|
+
const response = await businessService.fetchBusinessTaxRules(businessId);
|
|
191
188
|
return {
|
|
192
189
|
payload: response,
|
|
193
190
|
version: this.hashPayload(response),
|
|
194
191
|
};
|
|
195
192
|
}
|
|
196
193
|
case "business:profile": {
|
|
197
|
-
const response = await
|
|
194
|
+
const response = await businessService.fetchBusinessById(businessId);
|
|
198
195
|
return {
|
|
199
196
|
payload: response,
|
|
200
197
|
version: this.hashPayload(response),
|
|
201
198
|
};
|
|
202
199
|
}
|
|
203
200
|
case "operating-hours": {
|
|
204
|
-
const response = await
|
|
201
|
+
const response = await businessService.fetchOperatingHours(businessId);
|
|
205
202
|
return {
|
|
206
203
|
payload: response,
|
|
207
204
|
version: this.hashPayload(response),
|
|
@@ -221,12 +218,12 @@ class ReferenceSyncService {
|
|
|
221
218
|
try {
|
|
222
219
|
const urls = [];
|
|
223
220
|
if (keys.includes("menu:items")) {
|
|
224
|
-
const cached = await
|
|
221
|
+
const cached = await getReferenceData(db, businessId, "menu:items");
|
|
225
222
|
const items = cached?.payload?.data ?? [];
|
|
226
223
|
urls.push(...items.map((item) => item.image_url));
|
|
227
224
|
}
|
|
228
225
|
if (keys.includes("business:profile")) {
|
|
229
|
-
const cached = await
|
|
226
|
+
const cached = await getReferenceData(db, businessId, "business:profile");
|
|
230
227
|
const business = cached?.payload?.data;
|
|
231
228
|
if (business) {
|
|
232
229
|
urls.push(business.logo_url, business.cover_image_url);
|
|
@@ -234,18 +231,18 @@ class ReferenceSyncService {
|
|
|
234
231
|
}
|
|
235
232
|
if (urls.length === 0)
|
|
236
233
|
return;
|
|
237
|
-
const cachedImages = await
|
|
234
|
+
const cachedImages = await cacheImageBatch(urls, {
|
|
238
235
|
skipIfCached: true,
|
|
239
236
|
});
|
|
240
|
-
|
|
237
|
+
syncLogger.debug("ReferenceSync", "Reference images cached", {
|
|
241
238
|
businessId,
|
|
242
239
|
keys,
|
|
243
240
|
count: cachedImages.length,
|
|
244
241
|
});
|
|
245
|
-
await
|
|
242
|
+
await pruneImageCache(urls);
|
|
246
243
|
}
|
|
247
244
|
catch (error) {
|
|
248
|
-
|
|
245
|
+
syncLogger.warn("ReferenceSync", "Failed to cache reference images", {
|
|
249
246
|
error: String(error),
|
|
250
247
|
});
|
|
251
248
|
}
|
|
@@ -263,8 +260,8 @@ class ReferenceSyncService {
|
|
|
263
260
|
return `local-${Math.abs(hash).toString(16)}`;
|
|
264
261
|
}
|
|
265
262
|
async getCached(businessId, key) {
|
|
266
|
-
const db = await
|
|
267
|
-
return
|
|
263
|
+
const db = await openSyncDatabase();
|
|
264
|
+
return getReferenceData(db, businessId, key);
|
|
268
265
|
}
|
|
269
266
|
}
|
|
270
|
-
|
|
267
|
+
export const referenceSyncService = new ReferenceSyncService();
|
package/dist/reference/types.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* Types for reference data synchronization.
|
|
4
3
|
*
|
|
@@ -9,4 +8,4 @@
|
|
|
9
8
|
* It is synchronized separately from transactional data through the
|
|
10
9
|
* ReferenceSyncService.
|
|
11
10
|
*/
|
|
12
|
-
|
|
11
|
+
export {};
|