@teincfood/core 0.7.9 → 0.8.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.
Files changed (191) hide show
  1. package/README.md +26 -21
  2. package/dist/adapters/auth.d.ts +8 -0
  3. package/dist/adapters/auth.js +36 -0
  4. package/dist/adapters/devices.d.ts +3 -1
  5. package/dist/adapters/devices.js +187 -3
  6. package/dist/adapters/filesystem.d.ts +4 -0
  7. package/dist/adapters/kv.d.ts +10 -0
  8. package/dist/adapters/kv.js +23 -7
  9. package/dist/adapters/local-node.d.ts +4 -6
  10. package/dist/adapters/local-node.js +9 -32
  11. package/dist/adapters/notify.d.ts +12 -0
  12. package/dist/adapters/notify.js +33 -0
  13. package/dist/adapters/query-keys.d.ts +4 -0
  14. package/dist/adapters/query-keys.js +4 -0
  15. package/dist/adapters/services.d.ts +96 -15
  16. package/dist/adapters/services.js +241 -4
  17. package/dist/auth/session.d.ts +66 -0
  18. package/dist/auth/session.js +172 -0
  19. package/dist/catalog/hooks.d.ts +10 -0
  20. package/dist/catalog/hooks.js +82 -11
  21. package/dist/db/migrations.js +274 -0
  22. package/dist/db/operations.d.ts +53 -2
  23. package/dist/db/operations.js +177 -8
  24. package/dist/domain/business/index.d.ts +5 -0
  25. package/dist/domain/business/index.js +5 -0
  26. package/dist/domain/orders/lifecycle.d.ts +6 -0
  27. package/dist/domain/orders/lifecycle.js +6 -0
  28. package/dist/domain/orders/service.d.ts +24 -0
  29. package/dist/domain/orders/service.js +242 -0
  30. package/dist/domain/payments/index.d.ts +6 -0
  31. package/dist/domain/payments/index.js +5 -0
  32. package/dist/domain/reference/index.d.ts +8 -0
  33. package/dist/domain/reference/index.js +8 -0
  34. package/dist/e2e/harness.d.ts +1 -0
  35. package/dist/e2e/harness.js +61 -0
  36. package/dist/hooks/analytics.hooks.d.ts +25 -0
  37. package/dist/hooks/analytics.hooks.js +214 -0
  38. package/dist/hooks/business-hours.hooks.d.ts +8 -0
  39. package/dist/hooks/business-hours.hooks.js +45 -0
  40. package/dist/hooks/dashboard.hooks.d.ts +30 -0
  41. package/dist/hooks/dashboard.hooks.js +132 -0
  42. package/dist/hooks/delivery.hooks.d.ts +225 -0
  43. package/dist/hooks/delivery.hooks.js +80 -0
  44. package/dist/hooks/fleet.hooks.d.ts +37 -0
  45. package/dist/hooks/fleet.hooks.js +229 -0
  46. package/dist/hooks/image.hooks.d.ts +20 -0
  47. package/dist/hooks/image.hooks.js +62 -0
  48. package/dist/hooks/kiosk.hooks.d.ts +22 -5
  49. package/dist/hooks/kiosk.hooks.js +74 -5
  50. package/dist/hooks/members.hooks.d.ts +48 -0
  51. package/dist/hooks/members.hooks.js +302 -0
  52. package/dist/hooks/offline-orders.hooks.d.ts +9 -0
  53. package/dist/hooks/offline-orders.hooks.js +51 -0
  54. package/dist/hooks/orders.hooks.d.ts +18 -39
  55. package/dist/hooks/orders.hooks.js +124 -343
  56. package/dist/hooks/places.hooks.d.ts +21 -0
  57. package/dist/hooks/places.hooks.js +112 -0
  58. package/dist/hooks/realtime-stubs.d.ts +28 -0
  59. package/dist/hooks/realtime-stubs.js +85 -0
  60. package/dist/hooks/rider.hooks.d.ts +470 -0
  61. package/dist/hooks/rider.hooks.js +373 -0
  62. package/dist/hooks/use-infinite-list.d.ts +243 -0
  63. package/dist/hooks/use-infinite-list.js +58 -0
  64. package/dist/hooks/wallet.hooks.d.ts +219 -0
  65. package/dist/hooks/wallet.hooks.js +56 -0
  66. package/dist/image-cache/service.d.ts +8 -0
  67. package/dist/image-cache/service.js +15 -3
  68. package/dist/index.d.ts +56 -6
  69. package/dist/index.js +73 -6
  70. package/dist/local-node/adapters.d.ts +87 -0
  71. package/dist/local-node/adapters.js +4 -0
  72. package/dist/local-node/index.d.ts +5 -0
  73. package/dist/local-node/index.js +5 -0
  74. package/dist/local-node/security.d.ts +25 -0
  75. package/dist/local-node/security.js +149 -0
  76. package/dist/local-node/service.d.ts +83 -0
  77. package/dist/local-node/service.js +706 -0
  78. package/dist/local-node/store.d.ts +50 -0
  79. package/dist/local-node/store.js +284 -0
  80. package/dist/local-node/types.d.ts +92 -0
  81. package/dist/local-node/types.js +4 -0
  82. package/dist/printer/receipt.d.ts +4 -0
  83. package/dist/printer/receipt.js +75 -0
  84. package/dist/printer/service.js +1 -1
  85. package/dist/providers/query-client.d.ts +13 -0
  86. package/dist/providers/query-client.js +58 -0
  87. package/dist/realtime/phoenix-socket.js +30 -1
  88. package/dist/realtime/useEventsConnection.js +11 -2
  89. package/dist/reference/api.d.ts +2 -0
  90. package/dist/reference/api.js +2 -0
  91. package/dist/reference/capabilities.d.ts +12 -7
  92. package/dist/reference/capabilities.js +27 -13
  93. package/dist/reference/hooks.d.ts +34 -0
  94. package/dist/reference/hooks.js +124 -0
  95. package/dist/reference/menu-apply.d.ts +29 -0
  96. package/dist/reference/menu-apply.js +289 -0
  97. package/dist/reference/menu-record.d.ts +69 -0
  98. package/dist/reference/menu-record.js +163 -0
  99. package/dist/reference/operations.d.ts +3 -0
  100. package/dist/reference/operations.js +35 -1
  101. package/dist/reference/service.js +36 -9
  102. package/dist/reference/types.d.ts +1 -1
  103. package/dist/repositories/order.repository.d.ts +7 -0
  104. package/dist/repositories/order.repository.js +15 -2
  105. package/dist/repositories/pos.repository.d.ts +4 -0
  106. package/dist/repositories/pos.repository.js +4 -0
  107. package/dist/sync/backoff.d.ts +21 -0
  108. package/dist/sync/backoff.js +42 -0
  109. package/dist/sync/command-builder.js +10 -2
  110. package/dist/sync/command-queue.service.d.ts +12 -0
  111. package/dist/sync/command-queue.service.js +81 -16
  112. package/dist/sync/device-lease.service.d.ts +3 -3
  113. package/dist/sync/device-lease.service.js +80 -20
  114. package/dist/sync/engine.d.ts +3 -1
  115. package/dist/sync/engine.js +44 -4
  116. package/dist/sync/full-sync.d.ts +6 -0
  117. package/dist/sync/full-sync.js +72 -0
  118. package/dist/sync/health-probe.d.ts +24 -0
  119. package/dist/sync/health-probe.js +138 -0
  120. package/dist/sync/offline-capable.js +25 -0
  121. package/dist/sync/online-gates.d.ts +17 -0
  122. package/dist/sync/online-gates.js +55 -0
  123. package/dist/sync/order-number.d.ts +4 -4
  124. package/dist/sync/order-number.js +33 -13
  125. package/dist/sync/order-snapshots.service.js +18 -3
  126. package/dist/sync/pending-orders.service.d.ts +18 -0
  127. package/dist/sync/pending-orders.service.js +67 -1
  128. package/dist/sync/reconciliation/index.d.ts +5 -0
  129. package/dist/sync/reconciliation/index.js +5 -0
  130. package/dist/sync/snapshots/entity.d.ts +6 -0
  131. package/dist/sync/snapshots/entity.js +6 -0
  132. package/dist/sync/sync-jobs.service.d.ts +18 -0
  133. package/dist/sync/sync-jobs.service.js +539 -0
  134. package/dist/sync/transition-endpoints.d.ts +11 -0
  135. package/dist/sync/transition-endpoints.js +30 -0
  136. package/dist/sync/transport-local-node.js +3 -2
  137. package/dist/sync/transport-rest.d.ts +7 -0
  138. package/dist/sync/transport-rest.js +84 -26
  139. package/dist/sync/use-sync-reconciliation.js +15 -1
  140. package/dist/test-helpers/memory-driver.js +182 -4
  141. package/dist/test-helpers/seed-hours.d.ts +1 -0
  142. package/dist/test-helpers/seed-hours.js +23 -0
  143. package/dist/types/api/business.api.d.ts +42 -0
  144. package/dist/types/api/menu.api.d.ts +15 -0
  145. package/dist/types/api/seller.api.d.ts +20 -0
  146. package/dist/types/places.d.ts +19 -0
  147. package/dist/types/places.js +2 -0
  148. package/dist/types/rider.d.ts +67 -0
  149. package/dist/types/rider.js +2 -0
  150. package/dist/utils/attempt-stats.d.ts +23 -0
  151. package/dist/utils/attempt-stats.js +61 -0
  152. package/dist/utils/business-hours.d.ts +37 -0
  153. package/dist/utils/business-hours.js +96 -0
  154. package/dist/utils/debug-log.d.ts +23 -0
  155. package/dist/utils/debug-log.js +153 -0
  156. package/dist/utils/order-filter.d.ts +1 -1
  157. package/dist/utils/order-filter.js +1 -1
  158. package/dist/utils/order-transitions.d.ts +30 -0
  159. package/dist/utils/order-transitions.js +185 -0
  160. package/dist/utils/sync-logger.js +5 -4
  161. package/dist/validation/offline.d.ts +46 -0
  162. package/dist/validation/offline.js +79 -0
  163. package/package.json +6 -1
  164. package/dist/pricing/engine.test.d.ts +0 -1
  165. package/dist/pricing/engine.test.js +0 -142
  166. package/dist/reference/lww.test.d.ts +0 -1
  167. package/dist/reference/lww.test.js +0 -23
  168. package/dist/reference/operations.test.d.ts +0 -1
  169. package/dist/reference/operations.test.js +0 -66
  170. package/dist/standalone.integration.test.d.ts +0 -1
  171. package/dist/standalone.integration.test.js +0 -235
  172. package/dist/sync/device-lease.test.d.ts +0 -1
  173. package/dist/sync/device-lease.test.js +0 -38
  174. package/dist/sync/offline-integration.test.d.ts +0 -1
  175. package/dist/sync/offline-integration.test.js +0 -52
  176. package/dist/sync/order-number.test.d.ts +0 -1
  177. package/dist/sync/order-number.test.js +0 -51
  178. package/dist/sync/perms.test.d.ts +0 -1
  179. package/dist/sync/perms.test.js +0 -15
  180. package/dist/utils/currency.test.d.ts +0 -1
  181. package/dist/utils/currency.test.js +0 -17
  182. package/dist/utils/hlc.test.d.ts +0 -1
  183. package/dist/utils/hlc.test.js +0 -29
  184. package/dist/utils/phone.test.d.ts +0 -1
  185. package/dist/utils/phone.test.js +0 -19
  186. package/dist/utils/responsive.d.ts +0 -13
  187. package/dist/utils/responsive.js +0 -18
  188. package/dist/utils/uuid.test.d.ts +0 -1
  189. package/dist/utils/uuid.test.js +0 -13
  190. package/dist/utils/validation.test.d.ts +0 -1
  191. package/dist/utils/validation.test.js +0 -23
package/README.md CHANGED
@@ -2,15 +2,18 @@
2
2
 
3
3
  TeincFood shared offline-first core — single source of truth for mobile (`TeincFoodBusiness`) and desktop (`TeincFoodDesktop`).
4
4
 
5
- ## What lives here (v0.1.0 backbone)
5
+ Current: `0.7.31`. UI → hooks → repositories → core → SQLite → SyncEngine (cloud → LAN mesh → queue) → reconciliation → UI.
6
+
7
+ ## What lives here
6
8
 
7
9
  - `types/api/*` — snake_case API contracts matching backend
8
- - `utils/*` — currency, error, format, uuid, order-number (async), etc.
9
- - `sync/*` — engine, command-queue, transports, routing-policy, pending-orders
10
+ - `utils/*` — currency, error, format, uuid, order-number (DD-RRR-CCC), order-transitions, etc.
11
+ - `sync/*` — engine, command-queue, transports (`rest`, `local_node`), pending-orders, order-snapshots, reconciliation
10
12
  - `db/*` — migrations + operations (driver-agnostic, takes a `SqliteDriver`)
11
- - `reference/*` — versioned reference-data cache
12
- - `repositories/*` — order/pos repositories
13
- - `adapters/*` — platform seams (KV, SQLite, HTTP, etc.)
13
+ - `reference/*` — versioned reference-data cache (per-key HLC LWW)
14
+ - `repositories/*` — order/pos repositories (explicit verbs, no `order.update`)
15
+ - `local-node/*` — **LAN mesh**: lib-owned crypto/protocol/orchestration (`docs/MESH.md`)
16
+ - `adapters/*` — platform seams (KV, SQLite, HTTP, local-node, connectivity, devices, auth, business, …)
14
17
 
15
18
  ## Adapters
16
19
 
@@ -18,27 +21,29 @@ Apps supply platform adapters via `createTeincCore({ storage, db, http, ... })`:
18
21
 
19
22
  - `KVStorage` — async `getItem/setItem/removeItem` (MMKV-wrapped on mobile, `plugin-store` on desktop)
20
23
  - `SqliteDriver` — `execAsync/runAsync/getFirstAsync/getAllAsync/withTransactionAsync`
21
- - `HttpClient` — axios instance with auth interceptors (token plumbing stays app-side)
22
- - `LanTransport` — `{ isAvailable(), execute(command) }`
24
+ - `HttpClient` — axios/fetch instance with auth interceptors (token plumbing stays app-side)
25
+ - `LocalNodeDeps` — `transport {createServer, createClient}`, `discovery {startPublishing, startScanning, ...}`, `getAuth/getBusiness/getDeviceId`. Core derives the LAN secret and owns the mesh lifecycle; apps only provide sockets/mDNS/TLS primitives.
23
26
 
24
- ## Usage
27
+ ## LAN mesh
25
28
 
26
- ```ts
27
- import { createTeincCore } from "@teincfood/core";
28
- const core = createTeincCore({ storage, db, http });
29
- ```
29
+ No election: every `isLocalNodeEnabled` device is server + client. Spec: `docs/MESH.md`.
30
+ Two-process harness (real mDNS + TLS, no apps): `scripts/node-mesh/README.md`.
30
31
 
31
- Apps re-export types via `export * from "@teincfood/core"` barrels to avoid churn.
32
+ ## Docs
32
33
 
33
- ## Publishing
34
+ - `docs/MESH.md` — LAN mesh, single source of truth (crypto, wire protocol, lifecycle, order flow)
35
+ - `docs/OFFLINE_FIRST_STRATEGY.md` — offline-first lifecycles (offline→online, Phoenix, mesh)
36
+ - `docs/CORE_ARCHITECTURE.md` — module map and responsibilities
37
+ - `docs/AUDIT.md` — historical `0.7.10` pre-implementation snapshot (superseded where it conflicts with `MESH.md`)
38
+
39
+ ## Checks
34
40
 
35
41
  ```bash
36
- yarn build
37
- npm publish --access public
42
+ npx tsc --noEmit # 0 errors
43
+ npm test # 101/101 (mesh + security vectors + sync)
38
44
  ```
39
45
 
40
- ## Roadmap
46
+ ## Publishing
41
47
 
42
- - v0.2 services/stores/hooks
43
- - v0.3 LAN protocol + orchestrator + restart/diagnostics
44
- - v0.4 phoenix/events
48
+ Apps consume the packed tarball (`teincfood-core-<version>.tgz` refreshed into
49
+ `TeincFoodBusiness`/`TeincFoodDesktop`) or `npm publish --access public`.
@@ -11,5 +11,13 @@ export declare const useAuthStore: {
11
11
  user: AuthUser | null;
12
12
  };
13
13
  };
14
+ export declare function subscribeAuth(listener: () => void): () => void;
14
15
  export declare function setAuthProvider(fn: () => string | null): void;
16
+ export declare function setAuthRefreshProvider(fn: () => string | null): void;
17
+ export declare function setAuthTokensProvider(fn: (at: string, rt: string) => void): void;
18
+ export declare function setAuthClearProvider(fn: () => void): void;
15
19
  export declare function getAuthToken(): string | null;
20
+ export declare function getRefreshToken(): string | null;
21
+ export declare function setAuthTokens(at: string, rt: string): void;
22
+ export declare function clearAuth(): void;
23
+ export declare function notifyAuthChanged(): void;
@@ -9,9 +9,45 @@ export const useAuthStore = {
9
9
  getState: () => ({ user }),
10
10
  };
11
11
  let getAccessToken = null;
12
+ let getRefresh = null;
13
+ let setTokens = null;
14
+ let clearFn = null;
15
+ const authListeners = new Set();
16
+ export function subscribeAuth(listener) {
17
+ authListeners.add(listener);
18
+ return () => authListeners.delete(listener);
19
+ }
20
+ function notifyAuth() {
21
+ authListeners.forEach((l) => l());
22
+ }
12
23
  export function setAuthProvider(fn) {
13
24
  getAccessToken = fn;
14
25
  }
26
+ export function setAuthRefreshProvider(fn) {
27
+ getRefresh = fn;
28
+ }
29
+ export function setAuthTokensProvider(fn) {
30
+ setTokens = fn;
31
+ }
32
+ export function setAuthClearProvider(fn) {
33
+ clearFn = fn;
34
+ }
15
35
  export function getAuthToken() {
16
36
  return getAccessToken ? getAccessToken() : null;
17
37
  }
38
+ export function getRefreshToken() {
39
+ return getRefresh ? getRefresh() : null;
40
+ }
41
+ export function setAuthTokens(at, rt) {
42
+ if (setTokens)
43
+ setTokens(at, rt);
44
+ notifyAuth();
45
+ }
46
+ export function clearAuth() {
47
+ if (clearFn)
48
+ clearFn();
49
+ notifyAuth();
50
+ }
51
+ export function notifyAuthChanged() {
52
+ notifyAuth();
53
+ }
@@ -1,5 +1,7 @@
1
1
  /**
2
- * Devices service stub.
2
+ * Devices service — stable per-device UUID for DD lease.
3
+ * Persists via KV sync so DD stays same for life of device (until wipe).
3
4
  */
4
5
  export declare function setAppInstanceId(id: string): void;
5
6
  export declare function getAppInstanceId(): string;
7
+ export declare function getAppInstanceIdAsync(): Promise<string>;
@@ -1,10 +1,194 @@
1
1
  /**
2
- * Devices service stub.
2
+ * Devices service — stable per-device UUID for DD lease.
3
+ * Persists via KV sync so DD stays same for life of device (until wipe).
3
4
  */
4
- let appInstanceId = "core-device-id";
5
+ import { getItemSync, setItemSync, getItem, setItem } from "./kv";
6
+ import { generateUUID } from "../utils/uuid";
7
+ let appInstanceId = null;
8
+ const APP_ID_KEY = "teinc_app_instance_id";
9
+ // For Tauri desktop, try to restore from localStorage synchronously at import to avoid async race generating a new UUID on every cargo tauri dev restart
10
+ try {
11
+ if (typeof localStorage !== "undefined") {
12
+ const ls = localStorage.getItem(APP_ID_KEY);
13
+ if (ls) {
14
+ let v = ls;
15
+ try {
16
+ if (v.startsWith('"'))
17
+ v = JSON.parse(v);
18
+ }
19
+ catch { }
20
+ try {
21
+ if (v.startsWith('"') && v.endsWith('"'))
22
+ v = JSON.parse(v);
23
+ }
24
+ catch { }
25
+ if (v.length > 8)
26
+ appInstanceId = v;
27
+ }
28
+ }
29
+ }
30
+ catch { }
5
31
  export function setAppInstanceId(id) {
6
32
  appInstanceId = id;
33
+ try {
34
+ setItemSync(APP_ID_KEY, id);
35
+ }
36
+ catch { }
7
37
  }
8
38
  export function getAppInstanceId() {
9
- return appInstanceId;
39
+ if (appInstanceId)
40
+ return appInstanceId;
41
+ // Check localStorage first (covers Tauri webview where KVDriver async may race)
42
+ try {
43
+ const ls = typeof localStorage !== "undefined" ? localStorage.getItem(APP_ID_KEY) : null;
44
+ if (ls) {
45
+ let v = ls;
46
+ try {
47
+ if (v.startsWith('"'))
48
+ v = JSON.parse(v);
49
+ }
50
+ catch { }
51
+ try {
52
+ if (v.startsWith('"') && v.endsWith('"'))
53
+ v = JSON.parse(v);
54
+ }
55
+ catch { }
56
+ if (v.length > 8) {
57
+ appInstanceId = v;
58
+ return v;
59
+ }
60
+ }
61
+ }
62
+ catch { }
63
+ try {
64
+ const stored = getItemSync(APP_ID_KEY);
65
+ if (stored && typeof stored === "string" && stored.length > 8) {
66
+ let v = stored;
67
+ try {
68
+ if (v.startsWith('"') && v.endsWith('"'))
69
+ v = JSON.parse(v);
70
+ }
71
+ catch { }
72
+ if (v.length > 8) {
73
+ appInstanceId = v;
74
+ try {
75
+ if (typeof localStorage !== "undefined")
76
+ localStorage.setItem(APP_ID_KEY, JSON.stringify(v));
77
+ }
78
+ catch { }
79
+ return v;
80
+ }
81
+ }
82
+ }
83
+ catch { }
84
+ const id = generateUUID();
85
+ appInstanceId = id;
86
+ try {
87
+ setItemSync(APP_ID_KEY, id);
88
+ }
89
+ catch { }
90
+ try {
91
+ if (typeof localStorage !== "undefined")
92
+ localStorage.setItem(APP_ID_KEY, JSON.stringify(id));
93
+ }
94
+ catch { }
95
+ return id;
96
+ }
97
+ export async function getAppInstanceIdAsync() {
98
+ if (appInstanceId)
99
+ return appInstanceId;
100
+ // Try localStorage directly first (covers desktop webview where KVDriver's async getItem may race with Tauri store load)
101
+ try {
102
+ const ls = typeof localStorage !== "undefined" ? localStorage.getItem(APP_ID_KEY) : null;
103
+ if (ls) {
104
+ let v = ls;
105
+ try {
106
+ if (v.startsWith('"'))
107
+ v = JSON.parse(v);
108
+ }
109
+ catch { }
110
+ // Handle double-stringified "\"xxx\""
111
+ try {
112
+ if (v.startsWith('"') && v.endsWith('"'))
113
+ v = JSON.parse(v);
114
+ }
115
+ catch { }
116
+ if (v.length > 8) {
117
+ appInstanceId = v;
118
+ return v;
119
+ }
120
+ }
121
+ }
122
+ catch { }
123
+ try {
124
+ const stored = await getItem(APP_ID_KEY);
125
+ try {
126
+ const { syncLogger } = require("../utils/sync-logger");
127
+ syncLogger.debug("Devices", "getAppInstanceIdAsync getItem", { stored, len: stored?.length });
128
+ }
129
+ catch { }
130
+ if (stored && typeof stored === "string" && stored.length > 8) {
131
+ // Handle double-stringified case "\"xxx\"" from older builds
132
+ let v = stored;
133
+ try {
134
+ if (v.startsWith('"') && v.endsWith('"'))
135
+ v = JSON.parse(v);
136
+ }
137
+ catch { }
138
+ if (v.length > 8) {
139
+ appInstanceId = v; // also prime localStorage for sync fallback
140
+ try {
141
+ if (typeof localStorage !== "undefined")
142
+ localStorage.setItem(APP_ID_KEY, JSON.stringify(v));
143
+ }
144
+ catch { }
145
+ return v;
146
+ }
147
+ }
148
+ }
149
+ catch { }
150
+ // fallback to sync check
151
+ try {
152
+ const s = getItemSync(APP_ID_KEY);
153
+ try {
154
+ const { syncLogger } = require("../utils/sync-logger");
155
+ syncLogger.debug("Devices", "getAppInstanceIdAsync getItemSync", { s, len: s?.length });
156
+ }
157
+ catch { }
158
+ if (s && typeof s === "string" && s.length > 8) {
159
+ let v = s;
160
+ try {
161
+ if (v.startsWith('"') && v.endsWith('"'))
162
+ v = JSON.parse(v);
163
+ }
164
+ catch { }
165
+ if (v.length > 8) {
166
+ appInstanceId = v;
167
+ return v;
168
+ }
169
+ }
170
+ }
171
+ catch { }
172
+ const id = generateUUID();
173
+ appInstanceId = id;
174
+ try {
175
+ const { syncLogger } = require("../utils/sync-logger");
176
+ syncLogger.info("Devices", "generated new appInstanceId", { id });
177
+ }
178
+ catch { }
179
+ try {
180
+ await setItem(APP_ID_KEY, id);
181
+ }
182
+ catch {
183
+ try {
184
+ setItemSync(APP_ID_KEY, id);
185
+ }
186
+ catch { }
187
+ }
188
+ try {
189
+ if (typeof localStorage !== "undefined")
190
+ localStorage.setItem(APP_ID_KEY, JSON.stringify(id));
191
+ }
192
+ catch { }
193
+ return id;
10
194
  }
@@ -17,6 +17,10 @@ export interface FileSystemAdapter {
17
17
  }): Promise<void>;
18
18
  readDirectoryAsync(path: string): Promise<string[]>;
19
19
  getDocumentDirectory(): string;
20
+ readAsStringAsync?(path: string, opts?: {
21
+ encoding?: string;
22
+ }): Promise<string>;
23
+ readAsBase64Async?(path: string): Promise<string>;
20
24
  }
21
25
  export declare function setFileSystemAdapter(a: FileSystemAdapter | null): void;
22
26
  export declare function getFileSystemAdapter(): FileSystemAdapter | null;
@@ -6,6 +6,16 @@ export interface KVDriver {
6
6
  getItem(key: string): Promise<string | null> | string | null;
7
7
  setItem(key: string, value: string): Promise<void> | void;
8
8
  removeItem(key: string): Promise<void> | void;
9
+ /**
10
+ * Optional synchronous access. Sync callers (e.g. order-number callout
11
+ * sequencing) MUST read persisted state — an async-only driver makes them
12
+ * fall back to the ephemeral memory map, silently resetting sequences on
13
+ * every launch. Drivers backed by async storage (Tauri store) implement
14
+ * these over a synchronously-readable cache (localStorage + memory).
15
+ */
16
+ getItemSync?(key: string): string | null;
17
+ setItemSync?(key: string, value: string): void;
18
+ removeItemSync?(key: string): void;
9
19
  }
10
20
  export declare function setKVDriver(d: KVDriver): void;
11
21
  export declare function getKVDriver(): KVDriver;
@@ -49,17 +49,25 @@ export async function removeItem(key) {
49
49
  await kvRemoveItem(key);
50
50
  }
51
51
  // Synchronous fallbacks for legacy callers that used MMKV sync API
52
- // 0.7.1: delegate to driver when driver supports sync (MMKV), otherwise memory
52
+ // 0.7.1: delegate to driver when driver supports sync (MMKV), otherwise memory.
53
+ // Drivers with async storage expose getItemSync/setItemSync over a sync-readable
54
+ // cache — prefer those, else a Promise-returning getItem degrades to the
55
+ // ephemeral memory map (sequences reset every launch).
53
56
  export function getItemSync(key) {
54
57
  let raw;
55
58
  if (driver) {
56
59
  try {
57
- const v = driver.getItem(key);
58
- // if driver returns Promise, we can't await sync — fall back to memory
59
- if (v instanceof Promise)
60
- raw = memory.get(key) ?? null;
61
- else
62
- raw = v ?? memory.get(key) ?? null;
60
+ if (driver.getItemSync) {
61
+ raw = driver.getItemSync(key) ?? memory.get(key) ?? null;
62
+ }
63
+ else {
64
+ const v = driver.getItem(key);
65
+ // if driver returns Promise, we can't await sync — fall back to memory
66
+ if (v instanceof Promise)
67
+ raw = memory.get(key) ?? null;
68
+ else
69
+ raw = v ?? memory.get(key) ?? null;
70
+ }
63
71
  }
64
72
  catch {
65
73
  raw = memory.get(key) ?? null;
@@ -82,6 +90,10 @@ export function setItemSync(key, value) {
82
90
  memory.set(key, str);
83
91
  if (driver) {
84
92
  try {
93
+ if (driver.setItemSync) {
94
+ driver.setItemSync(key, str);
95
+ return;
96
+ }
85
97
  const r = driver.setItem(key, str);
86
98
  // if async, also keep memory
87
99
  if (r instanceof Promise)
@@ -94,6 +106,10 @@ export function removeItemSync(key) {
94
106
  memory.delete(key);
95
107
  if (driver) {
96
108
  try {
109
+ if (driver.removeItemSync) {
110
+ driver.removeItemSync(key);
111
+ return;
112
+ }
97
113
  const r = driver.removeItem(key);
98
114
  if (r instanceof Promise)
99
115
  r.catch(() => { });
@@ -1,12 +1,10 @@
1
1
  /**
2
- * Local Node stubs — minimal interfaces so core compiles without RN.
3
- * Host apps with LAN support replace these via setters.
2
+ * Local Node adapters — re-export core-owned implementation for compat.
3
+ * Host apps should import from @teincfood/core/local-node instead.
4
4
  */
5
5
  import type { Command, DomainEvent } from "../sync/types";
6
- export interface LocalNodeClient {
7
- execute(command: Command): Promise<DomainEvent[]>;
8
- publishEvent?(event: DomainEvent): void | Promise<void>;
9
- }
6
+ import type { LocalNodeClient as CoreClient } from "../local-node/adapters";
7
+ export type LocalNodeClient = CoreClient;
10
8
  export interface ElectedNode {
11
9
  deviceId: string;
12
10
  }
@@ -1,36 +1,13 @@
1
1
  /**
2
- * Local Node stubs — minimal interfaces so core compiles without RN.
3
- * Host apps with LAN support replace these via setters.
2
+ * Local Node adapters — re-export core-owned implementation for compat.
3
+ * Host apps should import from @teincfood/core/local-node instead.
4
4
  */
5
- let isLocalEnabled = false;
6
- let isConnected = false;
7
- let isLocalNodeFlag = false;
8
- let electedNode = null;
9
- let client = null;
5
+ import { localNodeService as coreService } from "../local-node/service";
6
+ import { useLocalNodeStore as coreStore } from "../local-node/store";
10
7
  export function setLocalNodeState(s) {
11
- if (s.isLocalNodeEnabled !== undefined)
12
- isLocalEnabled = s.isLocalNodeEnabled;
13
- if (s.isConnectedToNode !== undefined)
14
- isConnected = s.isConnectedToNode;
15
- if (s.isLocalNode !== undefined)
16
- isLocalNodeFlag = s.isLocalNode;
17
- if (s.electedNode !== undefined)
18
- electedNode = s.electedNode;
19
- if (s.client !== undefined)
20
- client = s.client;
8
+ // Compat: no-op — core store/service is source of truth.
9
+ // Business/Desktop bootstrap now calls initLocalNodeService(deps).
10
+ void s;
21
11
  }
22
- export const localNodeService = {
23
- isLocalNode: () => isLocalNodeFlag,
24
- isConnectedToNode: () => isConnected,
25
- getElectedNode: () => electedNode,
26
- getClient: () => client,
27
- broadcastToClients: (_event) => { },
28
- handleNodeCommand: async (_command) => null,
29
- };
30
- export const useLocalNodeStore = {
31
- getState: () => ({
32
- isLocalNodeEnabled: isLocalEnabled,
33
- isConnectedToNode: isConnected,
34
- electedNode,
35
- }),
36
- };
12
+ export const localNodeService = coreService;
13
+ export const useLocalNodeStore = coreStore;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Notify adapter — platform toast injection (sonner on desktop,
3
+ * sonner-native on mobile). Core hooks never import a toast library.
4
+ */
5
+ interface NotifyHandlers {
6
+ success: (message: string) => void;
7
+ error: (message: string) => void;
8
+ }
9
+ export declare function setNotifyHandlers(next: Partial<NotifyHandlers>): void;
10
+ export declare function notifySuccess(message: string): void;
11
+ export declare function notifyError(message: string): void;
12
+ export {};
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Notify adapter — platform toast injection (sonner on desktop,
3
+ * sonner-native on mobile). Core hooks never import a toast library.
4
+ */
5
+ let handlers = {
6
+ success: (m) => {
7
+ try {
8
+ console.debug("[notify] success", m);
9
+ }
10
+ catch { }
11
+ },
12
+ error: (m) => {
13
+ try {
14
+ console.debug("[notify] error", m);
15
+ }
16
+ catch { }
17
+ },
18
+ };
19
+ export function setNotifyHandlers(next) {
20
+ handlers = { ...handlers, ...next };
21
+ }
22
+ export function notifySuccess(message) {
23
+ try {
24
+ handlers.success(message);
25
+ }
26
+ catch { }
27
+ }
28
+ export function notifyError(message) {
29
+ try {
30
+ handlers.error(message);
31
+ }
32
+ catch { }
33
+ }
@@ -25,3 +25,7 @@ export declare const MENU_KEYS: {
25
25
  export declare const REFERENCE_KEYS: {
26
26
  all: readonly ["reference"];
27
27
  };
28
+ export declare const SELLER_DASHBOARD_KEYS: {
29
+ all: readonly ["seller-dashboard"];
30
+ full: (businessId?: string) => readonly unknown[];
31
+ };
@@ -25,3 +25,7 @@ export const MENU_KEYS = {
25
25
  export const REFERENCE_KEYS = {
26
26
  all: ["reference"],
27
27
  };
28
+ export const SELLER_DASHBOARD_KEYS = {
29
+ all: ["seller-dashboard"],
30
+ full: (businessId) => ["seller-dashboard", "full", businessId].filter((x) => x !== undefined),
31
+ };