@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.
- package/README.md +26 -21
- package/dist/adapters/auth.d.ts +8 -0
- package/dist/adapters/auth.js +36 -0
- package/dist/adapters/devices.d.ts +3 -1
- package/dist/adapters/devices.js +187 -3
- package/dist/adapters/filesystem.d.ts +4 -0
- package/dist/adapters/kv.d.ts +10 -0
- package/dist/adapters/kv.js +23 -7
- package/dist/adapters/local-node.d.ts +4 -6
- package/dist/adapters/local-node.js +9 -32
- package/dist/adapters/notify.d.ts +12 -0
- package/dist/adapters/notify.js +33 -0
- package/dist/adapters/query-keys.d.ts +4 -0
- package/dist/adapters/query-keys.js +4 -0
- package/dist/adapters/services.d.ts +96 -15
- package/dist/adapters/services.js +241 -4
- package/dist/auth/session.d.ts +66 -0
- package/dist/auth/session.js +172 -0
- package/dist/catalog/hooks.d.ts +10 -0
- package/dist/catalog/hooks.js +82 -11
- package/dist/db/migrations.js +274 -0
- package/dist/db/operations.d.ts +53 -2
- package/dist/db/operations.js +177 -8
- package/dist/domain/business/index.d.ts +5 -0
- package/dist/domain/business/index.js +5 -0
- package/dist/domain/orders/lifecycle.d.ts +6 -0
- package/dist/domain/orders/lifecycle.js +6 -0
- package/dist/domain/orders/service.d.ts +24 -0
- package/dist/domain/orders/service.js +242 -0
- package/dist/domain/payments/index.d.ts +6 -0
- package/dist/domain/payments/index.js +5 -0
- package/dist/domain/reference/index.d.ts +8 -0
- package/dist/domain/reference/index.js +8 -0
- package/dist/e2e/harness.d.ts +1 -0
- package/dist/e2e/harness.js +61 -0
- package/dist/hooks/analytics.hooks.d.ts +25 -0
- package/dist/hooks/analytics.hooks.js +214 -0
- package/dist/hooks/business-hours.hooks.d.ts +8 -0
- package/dist/hooks/business-hours.hooks.js +45 -0
- package/dist/hooks/dashboard.hooks.d.ts +30 -0
- package/dist/hooks/dashboard.hooks.js +132 -0
- package/dist/hooks/delivery.hooks.d.ts +225 -0
- package/dist/hooks/delivery.hooks.js +80 -0
- package/dist/hooks/fleet.hooks.d.ts +37 -0
- package/dist/hooks/fleet.hooks.js +229 -0
- package/dist/hooks/image.hooks.d.ts +20 -0
- package/dist/hooks/image.hooks.js +62 -0
- package/dist/hooks/kiosk.hooks.d.ts +22 -5
- package/dist/hooks/kiosk.hooks.js +74 -5
- package/dist/hooks/members.hooks.d.ts +48 -0
- package/dist/hooks/members.hooks.js +302 -0
- package/dist/hooks/offline-orders.hooks.d.ts +9 -0
- package/dist/hooks/offline-orders.hooks.js +51 -0
- package/dist/hooks/orders.hooks.d.ts +18 -39
- package/dist/hooks/orders.hooks.js +124 -343
- package/dist/hooks/places.hooks.d.ts +21 -0
- package/dist/hooks/places.hooks.js +112 -0
- package/dist/hooks/realtime-stubs.d.ts +28 -0
- package/dist/hooks/realtime-stubs.js +85 -0
- package/dist/hooks/rider.hooks.d.ts +470 -0
- package/dist/hooks/rider.hooks.js +373 -0
- package/dist/hooks/use-infinite-list.d.ts +243 -0
- package/dist/hooks/use-infinite-list.js +58 -0
- package/dist/hooks/wallet.hooks.d.ts +219 -0
- package/dist/hooks/wallet.hooks.js +56 -0
- package/dist/image-cache/service.d.ts +8 -0
- package/dist/image-cache/service.js +15 -3
- package/dist/index.d.ts +56 -6
- package/dist/index.js +73 -6
- package/dist/local-node/adapters.d.ts +87 -0
- package/dist/local-node/adapters.js +4 -0
- package/dist/local-node/index.d.ts +5 -0
- package/dist/local-node/index.js +5 -0
- package/dist/local-node/security.d.ts +25 -0
- package/dist/local-node/security.js +149 -0
- package/dist/local-node/service.d.ts +83 -0
- package/dist/local-node/service.js +706 -0
- package/dist/local-node/store.d.ts +50 -0
- package/dist/local-node/store.js +284 -0
- package/dist/local-node/types.d.ts +92 -0
- package/dist/local-node/types.js +4 -0
- package/dist/printer/receipt.d.ts +4 -0
- package/dist/printer/receipt.js +75 -0
- package/dist/printer/service.js +1 -1
- package/dist/providers/query-client.d.ts +13 -0
- package/dist/providers/query-client.js +58 -0
- package/dist/realtime/phoenix-socket.js +30 -1
- package/dist/realtime/useEventsConnection.js +11 -2
- package/dist/reference/api.d.ts +2 -0
- package/dist/reference/api.js +2 -0
- package/dist/reference/capabilities.d.ts +12 -7
- package/dist/reference/capabilities.js +27 -13
- package/dist/reference/hooks.d.ts +34 -0
- package/dist/reference/hooks.js +124 -0
- package/dist/reference/menu-apply.d.ts +29 -0
- package/dist/reference/menu-apply.js +289 -0
- package/dist/reference/menu-record.d.ts +69 -0
- package/dist/reference/menu-record.js +163 -0
- package/dist/reference/operations.d.ts +3 -0
- package/dist/reference/operations.js +35 -1
- package/dist/reference/service.js +36 -9
- package/dist/reference/types.d.ts +1 -1
- package/dist/repositories/order.repository.d.ts +7 -0
- package/dist/repositories/order.repository.js +15 -2
- package/dist/repositories/pos.repository.d.ts +4 -0
- package/dist/repositories/pos.repository.js +4 -0
- package/dist/sync/backoff.d.ts +21 -0
- package/dist/sync/backoff.js +42 -0
- package/dist/sync/command-builder.js +10 -2
- package/dist/sync/command-queue.service.d.ts +12 -0
- package/dist/sync/command-queue.service.js +81 -16
- package/dist/sync/device-lease.service.d.ts +3 -3
- package/dist/sync/device-lease.service.js +80 -20
- package/dist/sync/engine.d.ts +3 -1
- package/dist/sync/engine.js +44 -4
- package/dist/sync/full-sync.d.ts +6 -0
- package/dist/sync/full-sync.js +72 -0
- package/dist/sync/health-probe.d.ts +24 -0
- package/dist/sync/health-probe.js +138 -0
- package/dist/sync/offline-capable.js +25 -0
- package/dist/sync/online-gates.d.ts +17 -0
- package/dist/sync/online-gates.js +55 -0
- package/dist/sync/order-number.d.ts +4 -4
- package/dist/sync/order-number.js +33 -13
- package/dist/sync/order-snapshots.service.js +18 -3
- package/dist/sync/pending-orders.service.d.ts +18 -0
- package/dist/sync/pending-orders.service.js +67 -1
- package/dist/sync/reconciliation/index.d.ts +5 -0
- package/dist/sync/reconciliation/index.js +5 -0
- package/dist/sync/snapshots/entity.d.ts +6 -0
- package/dist/sync/snapshots/entity.js +6 -0
- package/dist/sync/sync-jobs.service.d.ts +18 -0
- package/dist/sync/sync-jobs.service.js +539 -0
- package/dist/sync/transition-endpoints.d.ts +11 -0
- package/dist/sync/transition-endpoints.js +30 -0
- package/dist/sync/transport-local-node.js +3 -2
- package/dist/sync/transport-rest.d.ts +7 -0
- package/dist/sync/transport-rest.js +84 -26
- package/dist/sync/use-sync-reconciliation.js +15 -1
- package/dist/test-helpers/memory-driver.js +182 -4
- package/dist/test-helpers/seed-hours.d.ts +1 -0
- package/dist/test-helpers/seed-hours.js +23 -0
- package/dist/types/api/business.api.d.ts +42 -0
- package/dist/types/api/menu.api.d.ts +15 -0
- package/dist/types/api/seller.api.d.ts +20 -0
- package/dist/types/places.d.ts +19 -0
- package/dist/types/places.js +2 -0
- package/dist/types/rider.d.ts +67 -0
- package/dist/types/rider.js +2 -0
- package/dist/utils/attempt-stats.d.ts +23 -0
- package/dist/utils/attempt-stats.js +61 -0
- package/dist/utils/business-hours.d.ts +37 -0
- package/dist/utils/business-hours.js +96 -0
- package/dist/utils/debug-log.d.ts +23 -0
- package/dist/utils/debug-log.js +153 -0
- package/dist/utils/order-filter.d.ts +1 -1
- package/dist/utils/order-filter.js +1 -1
- package/dist/utils/order-transitions.d.ts +30 -0
- package/dist/utils/order-transitions.js +185 -0
- package/dist/utils/sync-logger.js +5 -4
- package/dist/validation/offline.d.ts +46 -0
- package/dist/validation/offline.js +79 -0
- package/package.json +6 -1
- package/dist/pricing/engine.test.d.ts +0 -1
- package/dist/pricing/engine.test.js +0 -142
- package/dist/reference/lww.test.d.ts +0 -1
- package/dist/reference/lww.test.js +0 -23
- package/dist/reference/operations.test.d.ts +0 -1
- package/dist/reference/operations.test.js +0 -66
- package/dist/standalone.integration.test.d.ts +0 -1
- package/dist/standalone.integration.test.js +0 -235
- package/dist/sync/device-lease.test.d.ts +0 -1
- package/dist/sync/device-lease.test.js +0 -38
- package/dist/sync/offline-integration.test.d.ts +0 -1
- package/dist/sync/offline-integration.test.js +0 -52
- package/dist/sync/order-number.test.d.ts +0 -1
- package/dist/sync/order-number.test.js +0 -51
- package/dist/sync/perms.test.d.ts +0 -1
- package/dist/sync/perms.test.js +0 -15
- package/dist/utils/currency.test.d.ts +0 -1
- package/dist/utils/currency.test.js +0 -17
- package/dist/utils/hlc.test.d.ts +0 -1
- package/dist/utils/hlc.test.js +0 -29
- package/dist/utils/phone.test.d.ts +0 -1
- package/dist/utils/phone.test.js +0 -19
- package/dist/utils/responsive.d.ts +0 -13
- package/dist/utils/responsive.js +0 -18
- package/dist/utils/uuid.test.d.ts +0 -1
- package/dist/utils/uuid.test.js +0 -13
- package/dist/utils/validation.test.d.ts +0 -1
- 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?:
|
|
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
|
-
|
|
15
|
-
|
|
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
|
}>;
|
|
@@ -54,10 +45,23 @@ export declare const menuService: {
|
|
|
54
45
|
updateMenuItem: (_itemId: string, _payload: Record<string, unknown>) => Promise<unknown>;
|
|
55
46
|
deleteMenuItem: (_itemId: string) => Promise<unknown>;
|
|
56
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>;
|
|
57
50
|
};
|
|
58
51
|
export declare const businessService: {
|
|
59
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>;
|
|
60
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>;
|
|
61
65
|
fetchBusinessById: (_businessId: string) => Promise<unknown>;
|
|
62
66
|
fetchOperatingHours: (_businessId: string) => Promise<unknown>;
|
|
63
67
|
fetchMyBusinessMembership: (_businessId: string) => Promise<unknown>;
|
|
@@ -65,6 +69,83 @@ export declare const businessService: {
|
|
|
65
69
|
export declare const rolesService: {
|
|
66
70
|
fetchBusinessRoles: () => Promise<unknown>;
|
|
67
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
|
+
};
|
|
68
149
|
export declare function getRegionConfig(_countryCode?: string): {
|
|
69
150
|
currencyCode: string;
|
|
70
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
|
},
|
|
@@ -79,14 +78,53 @@ export const menuService = {
|
|
|
79
78
|
createCategory: async (_businessId, _payload) => {
|
|
80
79
|
notImpl("menuService.createCategory");
|
|
81
80
|
},
|
|
81
|
+
updateCategory: async (_categoryId, _payload) => {
|
|
82
|
+
notImpl("menuService.updateCategory");
|
|
83
|
+
},
|
|
84
|
+
deleteCategory: async (_categoryId) => {
|
|
85
|
+
notImpl("menuService.deleteCategory");
|
|
86
|
+
},
|
|
82
87
|
};
|
|
83
88
|
export const businessService = {
|
|
84
89
|
fetchBusinessMembers: async (_businessId) => {
|
|
85
90
|
notImpl("businessService.fetchBusinessMembers");
|
|
86
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
|
+
},
|
|
87
116
|
fetchBusinessTaxRules: async (_businessId) => {
|
|
88
117
|
notImpl("businessService.fetchBusinessTaxRules");
|
|
89
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
|
+
},
|
|
90
128
|
fetchBusinessById: async (_businessId) => {
|
|
91
129
|
notImpl("businessService.fetchBusinessById");
|
|
92
130
|
},
|
|
@@ -102,6 +140,205 @@ export const rolesService = {
|
|
|
102
140
|
notImpl("rolesService.fetchBusinessRoles");
|
|
103
141
|
},
|
|
104
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
|
+
};
|
|
105
342
|
// region pricing + location stubs for currency utils
|
|
106
343
|
export function getRegionConfig(_countryCode) {
|
|
107
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
|
+
}
|
package/dist/catalog/hooks.d.ts
CHANGED
|
@@ -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>;
|