@teincfood/core 0.7.8 → 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 (198) hide show
  1. package/README.md +26 -21
  2. package/dist/adapters/auth.d.ts +10 -0
  3. package/dist/adapters/auth.js +43 -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 +99 -15
  16. package/dist/adapters/services.js +244 -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 +19 -39
  55. package/dist/hooks/orders.hooks.js +126 -321
  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 +60 -5
  69. package/dist/index.js +78 -5
  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/events.service.d.ts +18 -0
  88. package/dist/realtime/events.service.js +76 -0
  89. package/dist/realtime/order-events.service.d.ts +6 -0
  90. package/dist/realtime/order-events.service.js +17 -0
  91. package/dist/realtime/phoenix-socket.d.ts +55 -0
  92. package/dist/realtime/phoenix-socket.js +269 -0
  93. package/dist/realtime/useEventsConnection.d.ts +1 -0
  94. package/dist/realtime/useEventsConnection.js +50 -0
  95. package/dist/reference/api.d.ts +2 -0
  96. package/dist/reference/api.js +2 -0
  97. package/dist/reference/capabilities.d.ts +12 -7
  98. package/dist/reference/capabilities.js +28 -13
  99. package/dist/reference/hooks.d.ts +34 -0
  100. package/dist/reference/hooks.js +124 -0
  101. package/dist/reference/menu-apply.d.ts +29 -0
  102. package/dist/reference/menu-apply.js +289 -0
  103. package/dist/reference/menu-record.d.ts +69 -0
  104. package/dist/reference/menu-record.js +163 -0
  105. package/dist/reference/operations.d.ts +3 -0
  106. package/dist/reference/operations.js +35 -1
  107. package/dist/reference/service.js +36 -9
  108. package/dist/reference/types.d.ts +1 -1
  109. package/dist/repositories/order.repository.d.ts +8 -0
  110. package/dist/repositories/order.repository.js +24 -2
  111. package/dist/repositories/pos.repository.d.ts +4 -0
  112. package/dist/repositories/pos.repository.js +4 -0
  113. package/dist/sync/backoff.d.ts +21 -0
  114. package/dist/sync/backoff.js +42 -0
  115. package/dist/sync/command-builder.js +10 -2
  116. package/dist/sync/command-queue.service.d.ts +12 -0
  117. package/dist/sync/command-queue.service.js +115 -18
  118. package/dist/sync/device-lease.service.d.ts +3 -3
  119. package/dist/sync/device-lease.service.js +80 -20
  120. package/dist/sync/engine.d.ts +3 -1
  121. package/dist/sync/engine.js +44 -4
  122. package/dist/sync/full-sync.d.ts +6 -0
  123. package/dist/sync/full-sync.js +72 -0
  124. package/dist/sync/health-probe.d.ts +24 -0
  125. package/dist/sync/health-probe.js +138 -0
  126. package/dist/sync/offline-capable.js +25 -0
  127. package/dist/sync/online-gates.d.ts +17 -0
  128. package/dist/sync/online-gates.js +55 -0
  129. package/dist/sync/order-number.d.ts +4 -4
  130. package/dist/sync/order-number.js +33 -13
  131. package/dist/sync/order-snapshots.service.d.ts +1 -0
  132. package/dist/sync/order-snapshots.service.js +47 -2
  133. package/dist/sync/pending-orders.service.d.ts +18 -0
  134. package/dist/sync/pending-orders.service.js +148 -3
  135. package/dist/sync/reconciliation/index.d.ts +5 -0
  136. package/dist/sync/reconciliation/index.js +5 -0
  137. package/dist/sync/snapshots/entity.d.ts +6 -0
  138. package/dist/sync/snapshots/entity.js +6 -0
  139. package/dist/sync/sync-jobs.service.d.ts +18 -0
  140. package/dist/sync/sync-jobs.service.js +539 -0
  141. package/dist/sync/transition-endpoints.d.ts +11 -0
  142. package/dist/sync/transition-endpoints.js +30 -0
  143. package/dist/sync/transport-local-node.js +3 -2
  144. package/dist/sync/transport-rest.d.ts +7 -0
  145. package/dist/sync/transport-rest.js +90 -25
  146. package/dist/sync/use-sync-reconciliation.js +58 -16
  147. package/dist/test-helpers/memory-driver.js +182 -4
  148. package/dist/test-helpers/seed-hours.d.ts +1 -0
  149. package/dist/test-helpers/seed-hours.js +23 -0
  150. package/dist/types/api/business.api.d.ts +42 -0
  151. package/dist/types/api/menu.api.d.ts +15 -0
  152. package/dist/types/api/seller.api.d.ts +20 -0
  153. package/dist/types/places.d.ts +19 -0
  154. package/dist/types/places.js +2 -0
  155. package/dist/types/rider.d.ts +67 -0
  156. package/dist/types/rider.js +2 -0
  157. package/dist/utils/attempt-stats.d.ts +23 -0
  158. package/dist/utils/attempt-stats.js +61 -0
  159. package/dist/utils/business-hours.d.ts +37 -0
  160. package/dist/utils/business-hours.js +96 -0
  161. package/dist/utils/debug-log.d.ts +23 -0
  162. package/dist/utils/debug-log.js +153 -0
  163. package/dist/utils/order-filter.d.ts +1 -1
  164. package/dist/utils/order-filter.js +1 -1
  165. package/dist/utils/order-transitions.d.ts +30 -0
  166. package/dist/utils/order-transitions.js +185 -0
  167. package/dist/utils/sync-logger.js +5 -4
  168. package/dist/validation/offline.d.ts +46 -0
  169. package/dist/validation/offline.js +79 -0
  170. package/package.json +6 -1
  171. package/dist/pricing/engine.test.d.ts +0 -1
  172. package/dist/pricing/engine.test.js +0 -142
  173. package/dist/reference/lww.test.d.ts +0 -1
  174. package/dist/reference/lww.test.js +0 -23
  175. package/dist/reference/operations.test.d.ts +0 -1
  176. package/dist/reference/operations.test.js +0 -66
  177. package/dist/standalone.integration.test.d.ts +0 -1
  178. package/dist/standalone.integration.test.js +0 -235
  179. package/dist/sync/device-lease.test.d.ts +0 -1
  180. package/dist/sync/device-lease.test.js +0 -38
  181. package/dist/sync/offline-integration.test.d.ts +0 -1
  182. package/dist/sync/offline-integration.test.js +0 -52
  183. package/dist/sync/order-number.test.d.ts +0 -1
  184. package/dist/sync/order-number.test.js +0 -51
  185. package/dist/sync/perms.test.d.ts +0 -1
  186. package/dist/sync/perms.test.js +0 -15
  187. package/dist/utils/currency.test.d.ts +0 -1
  188. package/dist/utils/currency.test.js +0 -17
  189. package/dist/utils/hlc.test.d.ts +0 -1
  190. package/dist/utils/hlc.test.js +0 -29
  191. package/dist/utils/phone.test.d.ts +0 -1
  192. package/dist/utils/phone.test.js +0 -19
  193. package/dist/utils/responsive.d.ts +0 -13
  194. package/dist/utils/responsive.js +0 -18
  195. package/dist/utils/uuid.test.d.ts +0 -1
  196. package/dist/utils/uuid.test.js +0 -13
  197. package/dist/utils/validation.test.d.ts +0 -1
  198. package/dist/utils/validation.test.js +0 -23
@@ -2,25 +2,16 @@
2
2
  * Service stubs for pos/seller/kiosk/menu/business/roles.
3
3
  * Host apps can replace via setServiceImpls; core defaults to no-op that throws helpful error.
4
4
  */
5
+ import type { SellerOrdersResponse, SellerOrderDetailResponse, BusinessDeliveriesResponse } from "../types/api/seller.api";
6
+ import type { PlaceSuggestion, PlaceDetails } from "../types/places";
5
7
  export declare const posService: {
6
- createPOSOrder: (_businessId: string, _payload: unknown) => Promise<{
7
- data: unknown;
8
- }>;
9
8
  fetchPOSOrderPreview: (_businessId: string, _payload: unknown) => Promise<unknown>;
10
- fetchBusinessOrders: (_businessId: string, _params?: unknown) => Promise<unknown>;
9
+ fetchBusinessOrders: (_businessId: string, _params?: Record<string, string>) => Promise<SellerOrdersResponse>;
11
10
  };
12
11
  export declare const sellerService: {
13
- fetchBusinessOrders: (_businessId: string, _params?: Record<string, unknown>) => Promise<{
14
- data: unknown[];
15
- pagination: unknown;
16
- }>;
17
- fetchSellerOrder: (_orderId: string) => Promise<{
18
- data: unknown;
19
- }>;
20
- fetchBusinessDeliveries: (_businessId: string, _params?: Record<string, unknown>) => Promise<{
21
- data: unknown[];
22
- pagination: unknown;
23
- }>;
12
+ fetchBusinessOrders: (_businessId: string, _params?: Record<string, unknown>) => Promise<SellerOrdersResponse>;
13
+ fetchSellerOrder: (_orderId: string) => Promise<SellerOrderDetailResponse>;
14
+ fetchBusinessDeliveries: (_businessId: string, _params?: Record<string, unknown>) => Promise<BusinessDeliveriesResponse>;
24
15
  acceptOrder: (_orderId: string) => Promise<{
25
16
  data: unknown;
26
17
  }>;
@@ -40,6 +31,9 @@ export declare const sellerService: {
40
31
  data: unknown;
41
32
  }>;
42
33
  confirmHandover: (_deliveryId: string) => Promise<unknown>;
34
+ confirmCashPayment: (_orderId: string) => Promise<{
35
+ data: unknown;
36
+ }>;
43
37
  };
44
38
  export declare const kioskService: {
45
39
  assignKioskOrderToRider: (_orderId: string, _riderId: string) => Promise<unknown>;
@@ -51,10 +45,23 @@ export declare const menuService: {
51
45
  updateMenuItem: (_itemId: string, _payload: Record<string, unknown>) => Promise<unknown>;
52
46
  deleteMenuItem: (_itemId: string) => Promise<unknown>;
53
47
  createCategory: (_businessId: string, _payload: Record<string, unknown>) => Promise<unknown>;
48
+ updateCategory: (_categoryId: string, _payload: Record<string, unknown>) => Promise<unknown>;
49
+ deleteCategory: (_categoryId: string) => Promise<unknown>;
54
50
  };
55
51
  export declare const businessService: {
56
52
  fetchBusinessMembers: (_businessId: string) => Promise<unknown>;
53
+ inviteBusinessMember: (_businessId: string, _data: Record<string, unknown>) => Promise<unknown>;
54
+ removeBusinessMember: (_businessId: string, _userId: string) => Promise<unknown>;
55
+ fetchBusinessMemberInvitations: (_businessId: string) => Promise<unknown>;
56
+ revokeBusinessMemberInvitation: (_businessId: string, _invitationId: string) => Promise<unknown>;
57
+ deleteBusinessMemberInvitation: (_businessId: string, _invitationId: string) => Promise<unknown>;
58
+ fetchUserInvitations: () => Promise<unknown>;
59
+ leaveBusiness: (_invitationId: string) => Promise<unknown>;
60
+ acceptBusinessInvitation: (_data: Record<string, unknown>) => Promise<unknown>;
57
61
  fetchBusinessTaxRules: (_businessId: string) => Promise<unknown>;
62
+ createBusinessTaxRule: (_businessId: string, _data: Record<string, unknown>) => Promise<unknown>;
63
+ updateTaxRule: (_id: string, _data: Record<string, unknown>) => Promise<unknown>;
64
+ deleteTaxRule: (_id: string) => Promise<unknown>;
58
65
  fetchBusinessById: (_businessId: string) => Promise<unknown>;
59
66
  fetchOperatingHours: (_businessId: string) => Promise<unknown>;
60
67
  fetchMyBusinessMembership: (_businessId: string) => Promise<unknown>;
@@ -62,6 +69,83 @@ export declare const businessService: {
62
69
  export declare const rolesService: {
63
70
  fetchBusinessRoles: () => Promise<unknown>;
64
71
  };
72
+ export declare const walletService: {
73
+ fetchAllBalances: () => Promise<unknown>;
74
+ fetchBusinessWalletBalance: (_businessId: string) => Promise<unknown>;
75
+ fetchWalletHistory: (_params: {
76
+ businessId?: string;
77
+ page?: number;
78
+ pageSize?: number;
79
+ }) => Promise<unknown>;
80
+ };
81
+ export declare const deliveryService: {
82
+ fetchBusinessDeliveries: (_businessId: string, _params?: Record<string, unknown>) => Promise<unknown>;
83
+ fetchDeliveryDetail: (_deliveryId: string) => Promise<unknown>;
84
+ };
85
+ export declare const dashboardService: {
86
+ fetchSellerDashboard: (_businessId: string) => Promise<unknown>;
87
+ };
88
+ export declare const analyticsService: {
89
+ fetchSellerAnalytics: (_businessId: string, _period?: string) => Promise<unknown>;
90
+ fetchRevenueSummary: (_businessId: string, _period?: string) => Promise<unknown>;
91
+ };
92
+ export declare const placesService: {
93
+ searchPlaces: (_query: string, _lat?: number, _lng?: number) => Promise<PlaceSuggestion[]>;
94
+ getPlaceDetails: (_placeId: string) => Promise<PlaceDetails | null>;
95
+ clearCache: () => Promise<void>;
96
+ };
97
+ export declare const fleetService: {
98
+ fetchFleetRiders: (_businessId?: string, _page?: number, _pageSize?: number, _filters?: unknown) => Promise<unknown>;
99
+ fetchFleetRider: (_riderId: string, _businessId?: string) => Promise<unknown>;
100
+ inviteRider: (_data: Record<string, unknown>, _businessId?: string) => Promise<unknown>;
101
+ approveRider: (_riderId: string, _businessId?: string) => Promise<unknown>;
102
+ suspendRider: (_riderId: string, _businessId?: string) => Promise<unknown>;
103
+ activateRider: (_riderId: string, _businessId?: string) => Promise<unknown>;
104
+ fetchRiderKyc: (_riderId: string, _businessId?: string) => Promise<unknown>;
105
+ fetchJoinRequests: (_businessId?: string) => Promise<unknown>;
106
+ approveJoinRequest: (_requestId: string, _businessId?: string) => Promise<unknown>;
107
+ rejectJoinRequest: (_requestId: string, _businessId?: string) => Promise<unknown>;
108
+ fetchInvitations: (_businessId?: string) => Promise<unknown>;
109
+ revokeInvitation: (_invitationId: string, _businessId?: string) => Promise<unknown>;
110
+ fetchFleetVehicles: (_businessId?: string) => Promise<unknown>;
111
+ addVehicle: (_data: Record<string, unknown>, _businessId?: string) => Promise<unknown>;
112
+ removeVehicle: (_vehicleId: string, _businessId?: string) => Promise<unknown>;
113
+ fetchFleetDashboard: (_businessId?: string) => Promise<unknown>;
114
+ };
115
+ export declare const riderService: {
116
+ fetchRiderProfile: () => Promise<unknown>;
117
+ updateRiderProfile: (_data: Record<string, unknown>) => Promise<unknown>;
118
+ updateOnlineStatus: (_isOnline: boolean) => Promise<unknown>;
119
+ fetchRiderMetrics: () => Promise<unknown>;
120
+ fetchRiderDashboard: () => Promise<unknown>;
121
+ fetchRiderStatistics: () => Promise<unknown>;
122
+ fetchAvailableOrders: () => Promise<unknown>;
123
+ acceptDelivery: (_orderId: string) => Promise<unknown>;
124
+ selfAssignDelivery: (_orderId: string) => Promise<unknown>;
125
+ fetchActiveDelivery: () => Promise<unknown>;
126
+ fetchRiderDeliveries: (_page: number, _pageSize: number, _filters?: Record<string, unknown>) => Promise<unknown>;
127
+ fetchDeliveryDetail: (_deliveryId: string) => Promise<unknown>;
128
+ markDeliveryInTransit: (_deliveryId: string) => Promise<unknown>;
129
+ markDeliveryArrived: (_deliveryId: string) => Promise<unknown>;
130
+ markDeliveryDelivered: (_deliveryId: string) => Promise<unknown>;
131
+ markDeliveryFailed: (_deliveryId: string) => Promise<unknown>;
132
+ markDeliveryReturned: (_deliveryId: string) => Promise<unknown>;
133
+ confirmDeliveryPickup: (_deliveryId: string) => Promise<unknown>;
134
+ confirmDeliveryHandover: (_deliveryId: string) => Promise<unknown>;
135
+ fetchRiderEarnings: () => Promise<unknown>;
136
+ fetchEarningsBreakdown: () => Promise<unknown>;
137
+ fetchRiderEarningsSummary: (_period: string) => Promise<unknown>;
138
+ fetchWalletHistory: (_page?: number, _pageSize?: number) => Promise<unknown>;
139
+ fetchVehicleInfo: () => Promise<unknown>;
140
+ createVehicle: (_data: unknown) => Promise<unknown>;
141
+ fetchDeliveryZones: () => Promise<unknown>;
142
+ updateDeliveryZones: (_zones: unknown) => Promise<unknown>;
143
+ toggleDeliveryZone: (_zoneId: string, _enabled: boolean) => Promise<unknown>;
144
+ fetchNavigationSettings: () => Promise<unknown>;
145
+ updateNavigationSettings: (_settings: unknown) => Promise<unknown>;
146
+ fetchNavigationApps: () => Promise<unknown>;
147
+ fetchVehicleTypes: () => Promise<unknown>;
148
+ };
65
149
  export declare function getRegionConfig(_countryCode?: string): {
66
150
  currencyCode: string;
67
151
  };
@@ -5,11 +5,10 @@
5
5
  function notImpl(name) {
6
6
  throw new Error(`Service ${name} not injected. Wire via adapters/services.ts or setHttpClient.`);
7
7
  }
8
- // pos.service
8
+ // pos.service — NOTE: no createPOSOrder slot. Order creation has exactly one
9
+ // path: createLocalOrder (domain/orders/service). Direct-REST creation was
10
+ // removed so the operating-hours gate can never be bypassed.
9
11
  export const posService = {
10
- createPOSOrder: async (_businessId, _payload) => {
11
- notImpl("posService.createPOSOrder");
12
- },
13
12
  fetchPOSOrderPreview: async (_businessId, _payload) => {
14
13
  notImpl("posService.fetchPOSOrderPreview");
15
14
  },
@@ -49,6 +48,9 @@ export const sellerService = {
49
48
  confirmHandover: async (_deliveryId) => {
50
49
  notImpl("sellerService.confirmHandover");
51
50
  },
51
+ confirmCashPayment: async (_orderId) => {
52
+ notImpl("sellerService.confirmCashPayment");
53
+ },
52
54
  };
53
55
  // kiosk.service
54
56
  export const kioskService = {
@@ -76,14 +78,53 @@ export const menuService = {
76
78
  createCategory: async (_businessId, _payload) => {
77
79
  notImpl("menuService.createCategory");
78
80
  },
81
+ updateCategory: async (_categoryId, _payload) => {
82
+ notImpl("menuService.updateCategory");
83
+ },
84
+ deleteCategory: async (_categoryId) => {
85
+ notImpl("menuService.deleteCategory");
86
+ },
79
87
  };
80
88
  export const businessService = {
81
89
  fetchBusinessMembers: async (_businessId) => {
82
90
  notImpl("businessService.fetchBusinessMembers");
83
91
  },
92
+ inviteBusinessMember: async (_businessId, _data) => {
93
+ notImpl("businessService.inviteBusinessMember");
94
+ },
95
+ removeBusinessMember: async (_businessId, _userId) => {
96
+ notImpl("businessService.removeBusinessMember");
97
+ },
98
+ fetchBusinessMemberInvitations: async (_businessId) => {
99
+ notImpl("businessService.fetchBusinessMemberInvitations");
100
+ },
101
+ revokeBusinessMemberInvitation: async (_businessId, _invitationId) => {
102
+ notImpl("businessService.revokeBusinessMemberInvitation");
103
+ },
104
+ deleteBusinessMemberInvitation: async (_businessId, _invitationId) => {
105
+ notImpl("businessService.deleteBusinessMemberInvitation");
106
+ },
107
+ fetchUserInvitations: async () => {
108
+ notImpl("businessService.fetchUserInvitations");
109
+ },
110
+ leaveBusiness: async (_invitationId) => {
111
+ notImpl("businessService.leaveBusiness");
112
+ },
113
+ acceptBusinessInvitation: async (_data) => {
114
+ notImpl("businessService.acceptBusinessInvitation");
115
+ },
84
116
  fetchBusinessTaxRules: async (_businessId) => {
85
117
  notImpl("businessService.fetchBusinessTaxRules");
86
118
  },
119
+ createBusinessTaxRule: async (_businessId, _data) => {
120
+ notImpl("businessService.createBusinessTaxRule");
121
+ },
122
+ updateTaxRule: async (_id, _data) => {
123
+ notImpl("businessService.updateTaxRule");
124
+ },
125
+ deleteTaxRule: async (_id) => {
126
+ notImpl("businessService.deleteTaxRule");
127
+ },
87
128
  fetchBusinessById: async (_businessId) => {
88
129
  notImpl("businessService.fetchBusinessById");
89
130
  },
@@ -99,6 +140,205 @@ export const rolesService = {
99
140
  notImpl("rolesService.fetchBusinessRoles");
100
141
  },
101
142
  };
143
+ // wallet.service — balances + history (hosts inject axios impls)
144
+ export const walletService = {
145
+ fetchAllBalances: async () => {
146
+ notImpl("walletService.fetchAllBalances");
147
+ },
148
+ fetchBusinessWalletBalance: async (_businessId) => {
149
+ notImpl("walletService.fetchBusinessWalletBalance");
150
+ },
151
+ fetchWalletHistory: async (_params) => {
152
+ notImpl("walletService.fetchWalletHistory");
153
+ },
154
+ };
155
+ // delivery.service — business deliveries (hosts inject axios impls)
156
+ export const deliveryService = {
157
+ fetchBusinessDeliveries: async (_businessId, _params) => {
158
+ notImpl("deliveryService.fetchBusinessDeliveries");
159
+ },
160
+ fetchDeliveryDetail: async (_deliveryId) => {
161
+ notImpl("deliveryService.fetchDeliveryDetail");
162
+ },
163
+ };
164
+ // dashboard.service — seller dashboard (hosts inject axios impls)
165
+ export const dashboardService = {
166
+ fetchSellerDashboard: async (_businessId) => {
167
+ notImpl("dashboardService.fetchSellerDashboard");
168
+ },
169
+ };
170
+ // analytics.service — seller analytics + revenue (hosts inject axios impls)
171
+ export const analyticsService = {
172
+ fetchSellerAnalytics: async (_businessId, _period) => {
173
+ notImpl("analyticsService.fetchSellerAnalytics");
174
+ },
175
+ fetchRevenueSummary: async (_businessId, _period) => {
176
+ notImpl("analyticsService.fetchRevenueSummary");
177
+ },
178
+ };
179
+ // places.service — place autocomplete/details (hosts inject axios impls)
180
+ export const placesService = {
181
+ searchPlaces: async (_query, _lat, _lng) => {
182
+ notImpl("placesService.searchPlaces");
183
+ return [];
184
+ },
185
+ getPlaceDetails: async (_placeId) => {
186
+ notImpl("placesService.getPlaceDetails");
187
+ return null;
188
+ },
189
+ clearCache: async () => {
190
+ notImpl("placesService.clearCache");
191
+ },
192
+ };
193
+ // fleet.service — fleet management (hosts inject axios impls)
194
+ export const fleetService = {
195
+ fetchFleetRiders: async (_businessId, _page, _pageSize, _filters) => {
196
+ notImpl("fleetService.fetchFleetRiders");
197
+ },
198
+ fetchFleetRider: async (_riderId, _businessId) => {
199
+ notImpl("fleetService.fetchFleetRider");
200
+ },
201
+ inviteRider: async (_data, _businessId) => {
202
+ notImpl("fleetService.inviteRider");
203
+ },
204
+ approveRider: async (_riderId, _businessId) => {
205
+ notImpl("fleetService.approveRider");
206
+ },
207
+ suspendRider: async (_riderId, _businessId) => {
208
+ notImpl("fleetService.suspendRider");
209
+ },
210
+ activateRider: async (_riderId, _businessId) => {
211
+ notImpl("fleetService.activateRider");
212
+ },
213
+ fetchRiderKyc: async (_riderId, _businessId) => {
214
+ notImpl("fleetService.fetchRiderKyc");
215
+ },
216
+ fetchJoinRequests: async (_businessId) => {
217
+ notImpl("fleetService.fetchJoinRequests");
218
+ },
219
+ approveJoinRequest: async (_requestId, _businessId) => {
220
+ notImpl("fleetService.approveJoinRequest");
221
+ },
222
+ rejectJoinRequest: async (_requestId, _businessId) => {
223
+ notImpl("fleetService.rejectJoinRequest");
224
+ },
225
+ fetchInvitations: async (_businessId) => {
226
+ notImpl("fleetService.fetchInvitations");
227
+ },
228
+ revokeInvitation: async (_invitationId, _businessId) => {
229
+ notImpl("fleetService.revokeInvitation");
230
+ },
231
+ fetchFleetVehicles: async (_businessId) => {
232
+ notImpl("fleetService.fetchFleetVehicles");
233
+ },
234
+ addVehicle: async (_data, _businessId) => {
235
+ notImpl("fleetService.addVehicle");
236
+ },
237
+ removeVehicle: async (_vehicleId, _businessId) => {
238
+ notImpl("fleetService.removeVehicle");
239
+ },
240
+ fetchFleetDashboard: async (_businessId) => {
241
+ notImpl("fleetService.fetchFleetDashboard");
242
+ },
243
+ };
244
+ export const riderService = {
245
+ fetchRiderProfile: async () => {
246
+ notImpl("riderService.fetchRiderProfile");
247
+ },
248
+ updateRiderProfile: async (_data) => {
249
+ notImpl("riderService.updateRiderProfile");
250
+ },
251
+ updateOnlineStatus: async (_isOnline) => {
252
+ notImpl("riderService.updateOnlineStatus");
253
+ },
254
+ fetchRiderMetrics: async () => {
255
+ notImpl("riderService.fetchRiderMetrics");
256
+ },
257
+ fetchRiderDashboard: async () => {
258
+ notImpl("riderService.fetchRiderDashboard");
259
+ },
260
+ fetchRiderStatistics: async () => {
261
+ notImpl("riderService.fetchRiderStatistics");
262
+ },
263
+ fetchAvailableOrders: async () => {
264
+ notImpl("riderService.fetchAvailableOrders");
265
+ },
266
+ acceptDelivery: async (_orderId) => {
267
+ notImpl("riderService.acceptDelivery");
268
+ },
269
+ selfAssignDelivery: async (_orderId) => {
270
+ notImpl("riderService.selfAssignDelivery");
271
+ },
272
+ fetchActiveDelivery: async () => {
273
+ notImpl("riderService.fetchActiveDelivery");
274
+ },
275
+ fetchRiderDeliveries: async (_page, _pageSize, _filters) => {
276
+ notImpl("riderService.fetchRiderDeliveries");
277
+ },
278
+ fetchDeliveryDetail: async (_deliveryId) => {
279
+ notImpl("riderService.fetchDeliveryDetail");
280
+ },
281
+ markDeliveryInTransit: async (_deliveryId) => {
282
+ notImpl("riderService.markDeliveryInTransit");
283
+ },
284
+ markDeliveryArrived: async (_deliveryId) => {
285
+ notImpl("riderService.markDeliveryArrived");
286
+ },
287
+ markDeliveryDelivered: async (_deliveryId) => {
288
+ notImpl("riderService.markDeliveryDelivered");
289
+ },
290
+ markDeliveryFailed: async (_deliveryId) => {
291
+ notImpl("riderService.markDeliveryFailed");
292
+ },
293
+ markDeliveryReturned: async (_deliveryId) => {
294
+ notImpl("riderService.markDeliveryReturned");
295
+ },
296
+ confirmDeliveryPickup: async (_deliveryId) => {
297
+ notImpl("riderService.confirmDeliveryPickup");
298
+ },
299
+ confirmDeliveryHandover: async (_deliveryId) => {
300
+ notImpl("riderService.confirmDeliveryHandover");
301
+ },
302
+ fetchRiderEarnings: async () => {
303
+ notImpl("riderService.fetchRiderEarnings");
304
+ },
305
+ fetchEarningsBreakdown: async () => {
306
+ notImpl("riderService.fetchEarningsBreakdown");
307
+ },
308
+ fetchRiderEarningsSummary: async (_period) => {
309
+ notImpl("riderService.fetchRiderEarningsSummary");
310
+ },
311
+ fetchWalletHistory: async (_page, _pageSize) => {
312
+ notImpl("riderService.fetchWalletHistory");
313
+ },
314
+ fetchVehicleInfo: async () => {
315
+ notImpl("riderService.fetchVehicleInfo");
316
+ },
317
+ createVehicle: async (_data) => {
318
+ notImpl("riderService.createVehicle");
319
+ },
320
+ fetchDeliveryZones: async () => {
321
+ notImpl("riderService.fetchDeliveryZones");
322
+ },
323
+ updateDeliveryZones: async (_zones) => {
324
+ notImpl("riderService.updateDeliveryZones");
325
+ },
326
+ toggleDeliveryZone: async (_zoneId, _enabled) => {
327
+ notImpl("riderService.toggleDeliveryZone");
328
+ },
329
+ fetchNavigationSettings: async () => {
330
+ notImpl("riderService.fetchNavigationSettings");
331
+ },
332
+ updateNavigationSettings: async (_settings) => {
333
+ notImpl("riderService.updateNavigationSettings");
334
+ },
335
+ fetchNavigationApps: async () => {
336
+ notImpl("riderService.fetchNavigationApps");
337
+ },
338
+ fetchVehicleTypes: async () => {
339
+ notImpl("riderService.fetchVehicleTypes");
340
+ },
341
+ };
102
342
  // region pricing + location stubs for currency utils
103
343
  export function getRegionConfig(_countryCode) {
104
344
  return { currencyCode: "GHS" };
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Session lifecycle — single source for access-expired → refresh →
3
+ * refresh-expired → logout across all host apps.
4
+ *
5
+ * Backend contract (verified against TeincFoodBackend):
6
+ * - expired ACCESS token → 401 + error.code "TOKEN_EXPIRED" (recoverable:
7
+ * refresh once, retry the call)
8
+ * - expired/invalid REFRESH token → 401 + error.code "REFRESH_FAILED"
9
+ * ("Token has expired") or sibling session codes (not recoverable:
10
+ * clear auth → host routes to login)
11
+ *
12
+ * Host HTTP layers (axios interceptors, fetch wrappers) must NOT implement
13
+ * their own refresh: they attach tokens + normalize errors, then delegate
14
+ * classification to isAccessExpired/isSessionDead and recovery to
15
+ * refreshSession()/clearAuth() here. refreshSession is single-flight because
16
+ * the backend rotates refresh tokens — two concurrent refreshes with the
17
+ * same token revoke each other and 401 the session.
18
+ */
19
+ /** Access token expired — recoverable via one refresh + retry. */
20
+ export declare const ACCESS_EXPIRED_CODE = "TOKEN_EXPIRED";
21
+ /**
22
+ * Refresh/session dead — not recoverable, the user must sign in again.
23
+ * Canonical set from the backend auth contract; keep in sync with
24
+ * auth_controller.ex (REFRESH_FAILED) and plugs/auth.ex.
25
+ */
26
+ export declare const SESSION_DEAD_CODES: readonly ["REFRESH_FAILED", "TOKEN_INVALID", "SESSION_EXPIRED", "UNAUTHORIZED", "ACCOUNT_DISABLED"];
27
+ export interface AuthFailure {
28
+ status?: number;
29
+ code?: string;
30
+ message?: string;
31
+ }
32
+ /**
33
+ * Normalize any error shape into { status, code, message }.
34
+ * Handles: core ApiError ({status, code}), raw axios errors
35
+ * ({response.status, response.data.error.code}), and host ApiError
36
+ * wrappers ({status, data.error.code}). Transport codes (ERR_NETWORK,
37
+ * ECONNABORTED, OFFLINE, …) are never backend codes and are dropped —
38
+ * only UPPER_SNAKE body codes classify.
39
+ */
40
+ export declare function extractAuthFailure(e: unknown): AuthFailure;
41
+ /** 401 + TOKEN_EXPIRED: access token dead, refresh may recover. */
42
+ export declare function isAccessExpired(f: AuthFailure): boolean;
43
+ /**
44
+ * 401/403 + session-dead code: refresh token dead, logout required.
45
+ * A 401 WITHOUT a code (proxy/gateway/HTML) is never session-dead — the
46
+ * app keeps working offline instead of logging the user out mid-shift.
47
+ */
48
+ export declare function isSessionDead(f: AuthFailure): boolean;
49
+ export interface SessionTokens {
50
+ access_token: string;
51
+ refresh_token: string;
52
+ }
53
+ /**
54
+ * Marker key for the refresh call's request config. Host interceptors MUST
55
+ * honor it (see hasAuthRetrySkip): the refresh request's own 401 must
56
+ * reject immediately, never recurse into session handling.
57
+ */
58
+ export declare const SKIP_AUTH_RETRY = "x-teinc-skip-auth-retry";
59
+ export declare function hasAuthRetrySkip(config: unknown): boolean;
60
+ export declare function refreshSession(): Promise<SessionTokens | null>;
61
+ /**
62
+ * Run fn; on access-expired, refresh once (single-flight) and retry once.
63
+ * On unrecoverable session death, clearAuth has already run inside
64
+ * refreshSession — the original error is rethrown for the caller to surface.
65
+ */
66
+ export declare function withAuthRetry<T>(fn: () => Promise<T>): Promise<T>;
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Session lifecycle — single source for access-expired → refresh →
3
+ * refresh-expired → logout across all host apps.
4
+ *
5
+ * Backend contract (verified against TeincFoodBackend):
6
+ * - expired ACCESS token → 401 + error.code "TOKEN_EXPIRED" (recoverable:
7
+ * refresh once, retry the call)
8
+ * - expired/invalid REFRESH token → 401 + error.code "REFRESH_FAILED"
9
+ * ("Token has expired") or sibling session codes (not recoverable:
10
+ * clear auth → host routes to login)
11
+ *
12
+ * Host HTTP layers (axios interceptors, fetch wrappers) must NOT implement
13
+ * their own refresh: they attach tokens + normalize errors, then delegate
14
+ * classification to isAccessExpired/isSessionDead and recovery to
15
+ * refreshSession()/clearAuth() here. refreshSession is single-flight because
16
+ * the backend rotates refresh tokens — two concurrent refreshes with the
17
+ * same token revoke each other and 401 the session.
18
+ */
19
+ import { clearAuth, getRefreshToken, setAuthTokens, } from "../adapters/auth";
20
+ import { getHttpClient } from "../adapters/http";
21
+ import { syncLogger } from "../utils/sync-logger";
22
+ /** Access token expired — recoverable via one refresh + retry. */
23
+ export const ACCESS_EXPIRED_CODE = "TOKEN_EXPIRED";
24
+ /**
25
+ * Refresh/session dead — not recoverable, the user must sign in again.
26
+ * Canonical set from the backend auth contract; keep in sync with
27
+ * auth_controller.ex (REFRESH_FAILED) and plugs/auth.ex.
28
+ */
29
+ export const SESSION_DEAD_CODES = [
30
+ "REFRESH_FAILED",
31
+ "TOKEN_INVALID",
32
+ "SESSION_EXPIRED",
33
+ "UNAUTHORIZED",
34
+ "ACCOUNT_DISABLED",
35
+ ];
36
+ /**
37
+ * Transport-layer codes (axios / network stack) are never backend session
38
+ * codes. NOTE: a plain UPPER_SNAKE check is NOT enough — ERR_NETWORK etc.
39
+ * match it. Explicit denylist.
40
+ */
41
+ const TRANSPORT_CODE_RE = /^(ERR_|ECONN|ETIMEDOUT|ENOTFOUND|EAI_|EHOST|ENET|EPIPE|OFFLINE)/;
42
+ function asString(v) {
43
+ return typeof v === "string" && v.length > 0 ? v : undefined;
44
+ }
45
+ /**
46
+ * Normalize any error shape into { status, code, message }.
47
+ * Handles: core ApiError ({status, code}), raw axios errors
48
+ * ({response.status, response.data.error.code}), and host ApiError
49
+ * wrappers ({status, data.error.code}). Transport codes (ERR_NETWORK,
50
+ * ECONNABORTED, OFFLINE, …) are never backend codes and are dropped —
51
+ * only UPPER_SNAKE body codes classify.
52
+ */
53
+ export function extractAuthFailure(e) {
54
+ const err = (e ?? {});
55
+ const status = typeof err.status === "number"
56
+ ? err.status
57
+ : err.response?.status;
58
+ const bodyCode = asString(err.response?.data?.error?.code) ??
59
+ asString(err.data?.error?.code);
60
+ const rawCode = asString(err.code);
61
+ const code = bodyCode ??
62
+ (rawCode && !TRANSPORT_CODE_RE.test(rawCode) ? rawCode : undefined);
63
+ const message = asString(err.response?.data?.error?.message) ??
64
+ asString(err.response?.data?.message) ??
65
+ asString(err.data?.error?.message) ??
66
+ asString(err.message);
67
+ return { status, code, message };
68
+ }
69
+ /** 401 + TOKEN_EXPIRED: access token dead, refresh may recover. */
70
+ export function isAccessExpired(f) {
71
+ return f.status === 401 && f.code === ACCESS_EXPIRED_CODE;
72
+ }
73
+ /**
74
+ * 401/403 + session-dead code: refresh token dead, logout required.
75
+ * A 401 WITHOUT a code (proxy/gateway/HTML) is never session-dead — the
76
+ * app keeps working offline instead of logging the user out mid-shift.
77
+ */
78
+ export function isSessionDead(f) {
79
+ if (f.status !== 401 && f.status !== 403)
80
+ return false;
81
+ if (!f.code)
82
+ return false;
83
+ return SESSION_DEAD_CODES.includes(f.code);
84
+ }
85
+ /**
86
+ * Marker key for the refresh call's request config. Host interceptors MUST
87
+ * honor it (see hasAuthRetrySkip): the refresh request's own 401 must
88
+ * reject immediately, never recurse into session handling.
89
+ */
90
+ export const SKIP_AUTH_RETRY = "x-teinc-skip-auth-retry";
91
+ export function hasAuthRetrySkip(config) {
92
+ try {
93
+ return (!!config &&
94
+ typeof config === "object" &&
95
+ config[SKIP_AUTH_RETRY] === true);
96
+ }
97
+ catch {
98
+ return false;
99
+ }
100
+ }
101
+ let inflight = null;
102
+ /**
103
+ * Single-flight refresh. Returns fresh tokens (stored via provider) or
104
+ * null. Calls clearAuth() (→ host logout) when the backend reports the
105
+ * refresh token itself expired, or when no refresh token exists.
106
+ * Never throws — callers treat null as "unrecoverable, surface original".
107
+ */
108
+ async function doRefresh() {
109
+ try {
110
+ const rt = getRefreshToken();
111
+ if (!rt) {
112
+ clearAuth();
113
+ return null;
114
+ }
115
+ const http = getHttpClient();
116
+ const res = await http.post("/auth/refresh", { refresh_token: rt }, { [SKIP_AUTH_RETRY]: true });
117
+ const body = (res?.data ?? {});
118
+ const at = body.access_token ?? body.data?.access_token;
119
+ const nrt = body.refresh_token ?? body.data?.refresh_token;
120
+ if (!at || !nrt) {
121
+ // 200 without tokens: ambiguous (proxy? shape drift?) — do NOT
122
+ // logout on ambiguity; surface as unrecoverable for this call.
123
+ syncLogger.warn("Session", "Refresh returned no tokens", {});
124
+ return null;
125
+ }
126
+ setAuthTokens(at, nrt);
127
+ return { access_token: at, refresh_token: nrt };
128
+ }
129
+ catch (e) {
130
+ const failure = extractAuthFailure(e);
131
+ if (isSessionDead(failure)) {
132
+ syncLogger.warn("Session", "Refresh token expired — logging out", {
133
+ code: failure.code,
134
+ });
135
+ try {
136
+ clearAuth();
137
+ }
138
+ catch { }
139
+ }
140
+ return null;
141
+ }
142
+ }
143
+ export async function refreshSession() {
144
+ // NOTE: assign via .finally() — assigning the raw IIFE promise leaves a
145
+ // stale settled value in `inflight` whenever the body takes a fully
146
+ // synchronous path (e.g. missing refresh token), pinning every later
147
+ // caller to that stale outcome forever.
148
+ if (!inflight) {
149
+ inflight = doRefresh().finally(() => {
150
+ inflight = null;
151
+ });
152
+ }
153
+ return inflight;
154
+ }
155
+ /**
156
+ * Run fn; on access-expired, refresh once (single-flight) and retry once.
157
+ * On unrecoverable session death, clearAuth has already run inside
158
+ * refreshSession — the original error is rethrown for the caller to surface.
159
+ */
160
+ export async function withAuthRetry(fn) {
161
+ try {
162
+ return await fn();
163
+ }
164
+ catch (e) {
165
+ if (!isAccessExpired(extractAuthFailure(e)))
166
+ throw e;
167
+ const tokens = await refreshSession().catch(() => null);
168
+ if (!tokens)
169
+ throw e;
170
+ return fn();
171
+ }
172
+ }
@@ -23,3 +23,13 @@ export declare function useUpdateMenuItemMutation(): import("@tanstack/react-que
23
23
  payload: Record<string, unknown>;
24
24
  }, unknown>;
25
25
  export declare function useDeleteMenuItemMutation(): import("@tanstack/react-query").UseMutationResult<unknown, Error, string, unknown>;
26
+ export declare function useToggleItemAvailabilityMutation(): import("@tanstack/react-query").UseMutationResult<unknown, Error, {
27
+ id: string;
28
+ available: boolean;
29
+ }, unknown>;
30
+ export declare function useCreateMenuCategoryMutation(): import("@tanstack/react-query").UseMutationResult<unknown, Error, Record<string, unknown>, unknown>;
31
+ export declare function useUpdateMenuCategoryMutation(): import("@tanstack/react-query").UseMutationResult<unknown, Error, {
32
+ id: string;
33
+ payload: Record<string, unknown>;
34
+ }, unknown>;
35
+ export declare function useDeleteMenuCategoryMutation(): import("@tanstack/react-query").UseMutationResult<unknown, Error, string, unknown>;