@metamask/ramps-controller 21.0.0 → 22.0.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 (40) hide show
  1. package/CHANGELOG.md +26 -1
  2. package/dist/RampsController-method-action-types.d.ts +16 -1
  3. package/dist/RampsController-method-action-types.d.ts.map +1 -1
  4. package/dist/RampsController-method-action-types.js.map +1 -1
  5. package/dist/RampsController.d.ts +69 -6
  6. package/dist/RampsController.d.ts.map +1 -1
  7. package/dist/RampsController.js +251 -21
  8. package/dist/RampsController.js.map +1 -1
  9. package/dist/RampsService.d.ts +6 -0
  10. package/dist/RampsService.d.ts.map +1 -1
  11. package/dist/RampsService.js.map +1 -1
  12. package/dist/index.d.ts +3 -1
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +1 -0
  15. package/dist/index.js.map +1 -1
  16. package/dist/order-syncing/constants.d.ts +23 -0
  17. package/dist/order-syncing/constants.d.ts.map +1 -0
  18. package/dist/order-syncing/constants.js +23 -0
  19. package/dist/order-syncing/constants.js.map +1 -0
  20. package/dist/order-syncing/controller-integration.d.ts +91 -0
  21. package/dist/order-syncing/controller-integration.d.ts.map +1 -0
  22. package/dist/order-syncing/controller-integration.js +364 -0
  23. package/dist/order-syncing/controller-integration.js.map +1 -0
  24. package/dist/order-syncing/index.d.ts +7 -0
  25. package/dist/order-syncing/index.d.ts.map +1 -0
  26. package/dist/order-syncing/index.js +5 -0
  27. package/dist/order-syncing/index.js.map +1 -0
  28. package/dist/order-syncing/sync-utils.d.ts +9 -0
  29. package/dist/order-syncing/sync-utils.d.ts.map +1 -0
  30. package/dist/order-syncing/sync-utils.js +27 -0
  31. package/dist/order-syncing/sync-utils.js.map +1 -0
  32. package/dist/order-syncing/types.d.ts +66 -0
  33. package/dist/order-syncing/types.d.ts.map +1 -0
  34. package/dist/order-syncing/types.js +2 -0
  35. package/dist/order-syncing/types.js.map +1 -0
  36. package/dist/order-syncing/utils.d.ts +83 -0
  37. package/dist/order-syncing/utils.d.ts.map +1 -0
  38. package/dist/order-syncing/utils.js +144 -0
  39. package/dist/order-syncing/utils.js.map +1 -0
  40. package/package.json +4 -3
@@ -0,0 +1,91 @@
1
+ import type { RampsOrder } from '../RampsService.js';
2
+ import type { OrderSyncingOptions, SyncRampsOrder } from './types.js';
3
+ export type SyncOrdersWithUserStorageConfig = {
4
+ onOrderSyncErroneousSituation?: (errorMessage: string, sentryContext?: Record<string, unknown>) => void;
5
+ };
6
+ type MergePlan = {
7
+ ordersToAddOrUpdateLocally: SyncRampsOrder[];
8
+ ordersToDeleteLocally: SyncRampsOrder[];
9
+ ordersToUpdateRemotely: RampsOrder[];
10
+ };
11
+ /**
12
+ * Returns the timestamp used for ramps order conflict resolution.
13
+ *
14
+ * Content conflicts use `local >= remote` (local wins ties). Tombstones use
15
+ * `local > deletedAt` (tie deletes locally) so equal-time deletes still apply.
16
+ *
17
+ * @param order - A ramps order that may include sync metadata.
18
+ * @returns The best available last-updated timestamp.
19
+ */
20
+ declare function getOrderTimestamp(order: RampsOrder | SyncRampsOrder): number;
21
+ /**
22
+ * Builds the local/remote merge plan from the current local snapshot and remote
23
+ * entries.
24
+ *
25
+ * @param localOrders - Syncable orders currently in {@link RampsController} state.
26
+ * @param validRemoteOrders - Syncable orders fetched from User Storage.
27
+ * @returns Lists of local mutations and remote uploads to apply.
28
+ */
29
+ declare function computeMergePlan(localOrders: RampsOrder[], validRemoteOrders: SyncRampsOrder[]): MergePlan & {
30
+ remoteOrdersMap: Map<string, SyncRampsOrder>;
31
+ };
32
+ /**
33
+ * Re-reads live local orders after merge mutations so orders added or updated
34
+ * while `isOrderSyncingInProgress` is true are still uploaded remotely.
35
+ *
36
+ * @param plannedRemoteUploads - Orders already queued for remote upload.
37
+ * @param remoteOrdersMap - Remote orders keyed by storage key.
38
+ * @param currentLocalOrders - Latest syncable local orders.
39
+ * @returns Remote uploads using the freshest local payloads.
40
+ */
41
+ declare function reconcileOrdersForRemoteUpload(plannedRemoteUploads: RampsOrder[], remoteOrdersMap: Map<string, SyncRampsOrder>, currentLocalOrders: RampsOrder[]): RampsOrder[];
42
+ /**
43
+ * Syncs V2 ramps orders between local {@link RampsController} state and User Storage.
44
+ *
45
+ * Handles first sync, new-device sync, merges, timestamp-based conflict resolution,
46
+ * and remote soft-deletes.
47
+ *
48
+ * @param config - Optional callbacks for sync errors.
49
+ * @param options - Parameters used for syncing operations.
50
+ */
51
+ export declare function syncOrdersWithUserStorage(config: SyncOrdersWithUserStorageConfig, options: OrderSyncingOptions): Promise<void>;
52
+ /**
53
+ * Saves orders to User Storage in a batch.
54
+ *
55
+ * @param orders - The orders to save.
56
+ * @param options - Parameters used for saving orders.
57
+ * @param config - Optional sync callbacks for error reporting.
58
+ * @returns Resolves when the batch write completes.
59
+ */
60
+ declare function saveOrdersToUserStorage(orders: SyncRampsOrder[], options: OrderSyncingOptions, config?: SyncOrdersWithUserStorageConfig): Promise<void>;
61
+ /**
62
+ * Updates a single order in User Storage without a full sync.
63
+ *
64
+ * This helper always stamps `lastUpdatedAt` and writes. Callers own change
65
+ * detection — skip this when the syncable payload is unchanged so polling
66
+ * cannot amplify User Storage writes. `RampsController.addOrder` performs
67
+ * that check via {@link areOrdersEqual}.
68
+ *
69
+ * @param order - The order that was updated locally.
70
+ * @param options - Parameters used for syncing operations.
71
+ * @param config - Optional sync callbacks for error reporting.
72
+ * @returns Resolves when the User Storage update completes or no-ops.
73
+ */
74
+ export declare function updateOrderInUserStorage(order: RampsOrder, options: OrderSyncingOptions, config?: SyncOrdersWithUserStorageConfig): Promise<void>;
75
+ /**
76
+ * Marks a single order as deleted in remote storage (soft delete).
77
+ *
78
+ * @param order - The order that was deleted locally (needs id / providerOrderId).
79
+ * @param options - Parameters used for syncing operations.
80
+ * @param config - Optional sync callbacks for error reporting.
81
+ * @returns Resolves when the remote soft-delete completes or no-ops.
82
+ */
83
+ export declare function deleteOrderInUserStorage(order: RampsOrder, options: OrderSyncingOptions, config?: SyncOrdersWithUserStorageConfig): Promise<void>;
84
+ export declare const orderSyncingTestExports: {
85
+ computeMergePlan: typeof computeMergePlan;
86
+ reconcileOrdersForRemoteUpload: typeof reconcileOrdersForRemoteUpload;
87
+ getOrderTimestamp: typeof getOrderTimestamp;
88
+ saveOrdersToUserStorage: typeof saveOrdersToUserStorage;
89
+ };
90
+ export {};
91
+ //# sourceMappingURL=controller-integration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller-integration.d.ts","sourceRoot":"","sources":["../../src/order-syncing/controller-integration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAQrD,OAAO,KAAK,EACV,mBAAmB,EACnB,cAAc,EAEf,MAAM,YAAY,CAAC;AAYpB,MAAM,MAAM,+BAA+B,GAAG;IAC5C,6BAA6B,CAAC,EAAE,CAC9B,YAAY,EAAE,MAAM,EACpB,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KACpC,IAAI,CAAC;CACX,CAAC;AAEF,KAAK,SAAS,GAAG;IACf,0BAA0B,EAAE,cAAc,EAAE,CAAC;IAC7C,qBAAqB,EAAE,cAAc,EAAE,CAAC;IACxC,sBAAsB,EAAE,UAAU,EAAE,CAAC;CACtC,CAAC;AAEF;;;;;;;;GAQG;AACH,iBAAS,iBAAiB,CAAC,KAAK,EAAE,UAAU,GAAG,cAAc,GAAG,MAAM,CAIrE;AAED;;;;;;;GAOG;AACH,iBAAS,gBAAgB,CACvB,WAAW,EAAE,UAAU,EAAE,EACzB,iBAAiB,EAAE,cAAc,EAAE,GAClC,SAAS,GAAG;IAAE,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAA;CAAE,CA0D9D;AAED;;;;;;;;GAQG;AACH,iBAAS,8BAA8B,CACrC,oBAAoB,EAAE,UAAU,EAAE,EAClC,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,EAC5C,kBAAkB,EAAE,UAAU,EAAE,GAC/B,UAAU,EAAE,CAuBd;AAED;;;;;;;;GAQG;AACH,wBAAsB,yBAAyB,CAC7C,MAAM,EAAE,+BAA+B,EACvC,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,IAAI,CAAC,CA6Hf;AA6ED;;;;;;;GAOG;AACH,iBAAe,uBAAuB,CACpC,MAAM,EAAE,cAAc,EAAE,EACxB,OAAO,EAAE,mBAAmB,EAC5B,MAAM,GAAE,+BAAoC,GAC3C,OAAO,CAAC,IAAI,CAAC,CA8Cf;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,wBAAwB,CAC5C,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE,mBAAmB,EAC5B,MAAM,GAAE,+BAAoC,GAC3C,OAAO,CAAC,IAAI,CAAC,CA8Bf;AAED;;;;;;;GAOG;AACH,wBAAsB,wBAAwB,CAC5C,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE,mBAAmB,EAC5B,MAAM,GAAE,+BAAoC,GAC3C,OAAO,CAAC,IAAI,CAAC,CAqBf;AAED,eAAO,MAAM,uBAAuB;;;;;CAKnC,CAAC"}
@@ -0,0 +1,364 @@
1
+ import { TraceName, USER_STORAGE_RAMPS_ORDERS_FEATURE, USER_STORAGE_VERSION, USER_STORAGE_VERSION_KEY, } from './constants.js';
2
+ import { canPerformOrderSyncing } from './sync-utils.js';
3
+ import { areOrdersEqual, createOrderStorageKey, isSyncableOrder, mapRampsOrderToUserStorageEntry, mapUserStorageEntryToRampsOrder, normalizeCreatedAt, stripDeletedAt, stripSyncMetadata, } from './utils.js';
4
+ /**
5
+ * Returns the timestamp used for ramps order conflict resolution.
6
+ *
7
+ * Content conflicts use `local >= remote` (local wins ties). Tombstones use
8
+ * `local > deletedAt` (tie deletes locally) so equal-time deletes still apply.
9
+ *
10
+ * @param order - A ramps order that may include sync metadata.
11
+ * @returns The best available last-updated timestamp.
12
+ */
13
+ function getOrderTimestamp(order) {
14
+ return normalizeCreatedAt(order.lastUpdatedAt ?? order.createdAt);
15
+ }
16
+ /**
17
+ * Builds the local/remote merge plan from the current local snapshot and remote
18
+ * entries.
19
+ *
20
+ * @param localOrders - Syncable orders currently in {@link RampsController} state.
21
+ * @param validRemoteOrders - Syncable orders fetched from User Storage.
22
+ * @returns Lists of local mutations and remote uploads to apply.
23
+ */
24
+ function computeMergePlan(localOrders, validRemoteOrders) {
25
+ const localOrdersMap = new Map();
26
+ const remoteOrdersMap = new Map();
27
+ localOrders.forEach((order) => {
28
+ localOrdersMap.set(createOrderStorageKey(order), order);
29
+ });
30
+ validRemoteOrders.forEach((order) => {
31
+ remoteOrdersMap.set(createOrderStorageKey(order), order);
32
+ });
33
+ const ordersToAddOrUpdateLocally = [];
34
+ const ordersToDeleteLocally = [];
35
+ const ordersToUpdateRemotely = [];
36
+ for (const remoteOrder of validRemoteOrders) {
37
+ const key = createOrderStorageKey(remoteOrder);
38
+ const localOrder = localOrdersMap.get(key);
39
+ if (remoteOrder.deletedAt) {
40
+ if (localOrder) {
41
+ const localTimestamp = getOrderTimestamp(localOrder);
42
+ const remoteTimestamp = remoteOrder.deletedAt;
43
+ if (localTimestamp > remoteTimestamp) {
44
+ ordersToUpdateRemotely.push(localOrder);
45
+ }
46
+ else {
47
+ ordersToDeleteLocally.push(remoteOrder);
48
+ }
49
+ }
50
+ }
51
+ else if (!localOrder) {
52
+ ordersToAddOrUpdateLocally.push(remoteOrder);
53
+ }
54
+ else if (!areOrdersEqual(localOrder, remoteOrder)) {
55
+ const localTimestamp = getOrderTimestamp(localOrder);
56
+ const remoteTimestamp = getOrderTimestamp(remoteOrder);
57
+ if (localTimestamp >= remoteTimestamp) {
58
+ ordersToUpdateRemotely.push(localOrder);
59
+ }
60
+ else {
61
+ ordersToAddOrUpdateLocally.push(remoteOrder);
62
+ }
63
+ }
64
+ }
65
+ for (const localOrder of localOrders) {
66
+ const key = createOrderStorageKey(localOrder);
67
+ if (!remoteOrdersMap.has(key)) {
68
+ ordersToUpdateRemotely.push(localOrder);
69
+ }
70
+ }
71
+ return {
72
+ remoteOrdersMap,
73
+ ordersToAddOrUpdateLocally,
74
+ ordersToDeleteLocally,
75
+ ordersToUpdateRemotely,
76
+ };
77
+ }
78
+ /**
79
+ * Re-reads live local orders after merge mutations so orders added or updated
80
+ * while `isOrderSyncingInProgress` is true are still uploaded remotely.
81
+ *
82
+ * @param plannedRemoteUploads - Orders already queued for remote upload.
83
+ * @param remoteOrdersMap - Remote orders keyed by storage key.
84
+ * @param currentLocalOrders - Latest syncable local orders.
85
+ * @returns Remote uploads using the freshest local payloads.
86
+ */
87
+ function reconcileOrdersForRemoteUpload(plannedRemoteUploads, remoteOrdersMap, currentLocalOrders) {
88
+ const keyedUploads = new Map(plannedRemoteUploads.map((order) => [createOrderStorageKey(order), order]));
89
+ for (const localOrder of currentLocalOrders) {
90
+ const key = createOrderStorageKey(localOrder);
91
+ const existingUpload = keyedUploads.get(key);
92
+ const remoteOrder = remoteOrdersMap.get(key);
93
+ if (existingUpload) {
94
+ if (!areOrdersEqual(existingUpload, localOrder)) {
95
+ keyedUploads.set(key, localOrder);
96
+ }
97
+ continue;
98
+ }
99
+ if (!remoteOrder || !areOrdersEqual(localOrder, remoteOrder)) {
100
+ keyedUploads.set(key, localOrder);
101
+ }
102
+ }
103
+ return [...keyedUploads.values()];
104
+ }
105
+ /**
106
+ * Syncs V2 ramps orders between local {@link RampsController} state and User Storage.
107
+ *
108
+ * Handles first sync, new-device sync, merges, timestamp-based conflict resolution,
109
+ * and remote soft-deletes.
110
+ *
111
+ * @param config - Optional callbacks for sync errors.
112
+ * @param options - Parameters used for syncing operations.
113
+ */
114
+ export async function syncOrdersWithUserStorage(config, options) {
115
+ const { getRampsControllerInstance, trace } = options;
116
+ const { onOrderSyncErroneousSituation } = config;
117
+ if (!canPerformOrderSyncing(options)) {
118
+ return;
119
+ }
120
+ const controller = getRampsControllerInstance();
121
+ // Set the semaphore before the remote fetch so overlapping sync calls and
122
+ // incremental addOrder/removeOrder pushes cannot race the merge.
123
+ controller.setIsOrderSyncingInProgress(true);
124
+ try {
125
+ const validRemoteOrders = (await getRemoteOrders(options, config)).filter(isSyncableOrder);
126
+ const performSync = async () => {
127
+ const getLocalOrders = () => controller.state.orders.filter(isSyncableOrder);
128
+ const pendingDeleteKeysBeforeApply = new Set(controller
129
+ .getPendingRemoteDeletes()
130
+ .map((order) => createOrderStorageKey(order))
131
+ .filter((key) => key.length > 0));
132
+ const { remoteOrdersMap, ordersToAddOrUpdateLocally, ordersToDeleteLocally, ordersToUpdateRemotely, } = computeMergePlan(getLocalOrders(), validRemoteOrders);
133
+ controller.setIsApplyingOrderSyncChanges(true);
134
+ try {
135
+ for (const order of ordersToDeleteLocally) {
136
+ controller.removeOrder(createOrderStorageKey(order));
137
+ }
138
+ for (const order of ordersToAddOrUpdateLocally) {
139
+ if (!order.deletedAt &&
140
+ !pendingDeleteKeysBeforeApply.has(createOrderStorageKey(order))) {
141
+ controller.addOrder(stripDeletedAt(order));
142
+ }
143
+ }
144
+ }
145
+ finally {
146
+ controller.setIsApplyingOrderSyncChanges(false);
147
+ }
148
+ const ordersToUpload = reconcileOrdersForRemoteUpload(ordersToUpdateRemotely, remoteOrdersMap, getLocalOrders());
149
+ const localKeys = new Set(getLocalOrders().map((order) => createOrderStorageKey(order)));
150
+ const pendingDeletes = controller
151
+ .getPendingRemoteDeletes()
152
+ .filter((order) => {
153
+ const key = createOrderStorageKey(order);
154
+ return key.length > 0 && !localKeys.has(key);
155
+ });
156
+ const pendingDeleteKeys = new Set(pendingDeletes.map((order) => createOrderStorageKey(order)));
157
+ const now = Date.now();
158
+ const syncedUploads = [
159
+ ...ordersToUpload
160
+ .filter((localOrder) => !pendingDeleteKeys.has(createOrderStorageKey(localOrder)))
161
+ .map((localOrder) => ({
162
+ ...stripSyncMetadata(localOrder),
163
+ lastUpdatedAt: localOrder.lastUpdatedAt ?? now,
164
+ })),
165
+ ...pendingDeletes.map((order) => ({
166
+ ...stripSyncMetadata(order),
167
+ deletedAt: now,
168
+ lastUpdatedAt: now,
169
+ })),
170
+ ];
171
+ if (syncedUploads.length > 0) {
172
+ await saveOrdersToUserStorage(syncedUploads, options, config);
173
+ controller.acknowledgePendingRemoteDeletes(pendingDeletes);
174
+ }
175
+ };
176
+ if (trace) {
177
+ const localOrderCount = controller.state.orders.filter(isSyncableOrder).length;
178
+ await trace({
179
+ name: TraceName.RampsOrderSyncFull,
180
+ data: {
181
+ localOrderCount,
182
+ remoteOrderCount: validRemoteOrders.length,
183
+ isFirstSync: validRemoteOrders.length === 0 && localOrderCount > 0,
184
+ isNewDeviceSync: localOrderCount === 0 && validRemoteOrders.length > 0,
185
+ },
186
+ }, performSync);
187
+ return;
188
+ }
189
+ await performSync();
190
+ }
191
+ catch (error) {
192
+ onOrderSyncErroneousSituation?.('Error synchronizing ramps orders', {
193
+ error,
194
+ });
195
+ throw error;
196
+ }
197
+ finally {
198
+ controller.setIsOrderSyncingInProgress(false);
199
+ }
200
+ }
201
+ /**
202
+ * Retrieves remote ramps orders from User Storage.
203
+ *
204
+ * @param options - Parameters used for retrieving remote orders.
205
+ * @param config - Optional sync callbacks for error reporting.
206
+ * @returns Parsed sync-aware orders. Returns an empty array when none exist.
207
+ */
208
+ async function getRemoteOrders(options, config) {
209
+ const { getMessenger } = options;
210
+ const { onOrderSyncErroneousSituation } = config;
211
+ try {
212
+ // Reads the active/primary SRP profile only. Order sync is same-SRP: hosts
213
+ // sync Extension ↔ Mobile for a given SRP, not across secondary wallets.
214
+ const remoteOrdersJsonArray = (await getMessenger().call('UserStorageController:performGetStorageAllFeatureEntries', USER_STORAGE_RAMPS_ORDERS_FEATURE)) ?? [];
215
+ if (remoteOrdersJsonArray.length === 0) {
216
+ return [];
217
+ }
218
+ const remoteOrders = [];
219
+ for (const orderJson of remoteOrdersJsonArray) {
220
+ try {
221
+ const entry = JSON.parse(orderJson);
222
+ if (entry[USER_STORAGE_VERSION_KEY] !== USER_STORAGE_VERSION) {
223
+ onOrderSyncErroneousSituation?.('Unsupported ramps order storage version', {
224
+ version: entry[USER_STORAGE_VERSION_KEY],
225
+ expectedVersion: USER_STORAGE_VERSION,
226
+ });
227
+ continue;
228
+ }
229
+ if (!entry.o || typeof entry.o !== 'object') {
230
+ onOrderSyncErroneousSituation?.('Remote ramps order entry missing order payload', {});
231
+ continue;
232
+ }
233
+ const mapped = mapUserStorageEntryToRampsOrder(entry);
234
+ if (!createOrderStorageKey(mapped)) {
235
+ continue;
236
+ }
237
+ remoteOrders.push(mapped);
238
+ }
239
+ catch (error) {
240
+ // Do not attach raw order JSON — it can include wallet/PII fields.
241
+ onOrderSyncErroneousSituation?.('Failed to parse remote ramps order entry', {
242
+ error,
243
+ entryLength: orderJson.length,
244
+ });
245
+ }
246
+ }
247
+ return remoteOrders;
248
+ }
249
+ catch (error) {
250
+ onOrderSyncErroneousSituation?.('Failed to fetch remote ramps orders', {
251
+ error,
252
+ });
253
+ throw error;
254
+ }
255
+ }
256
+ /**
257
+ * Saves orders to User Storage in a batch.
258
+ *
259
+ * @param orders - The orders to save.
260
+ * @param options - Parameters used for saving orders.
261
+ * @param config - Optional sync callbacks for error reporting.
262
+ * @returns Resolves when the batch write completes.
263
+ */
264
+ async function saveOrdersToUserStorage(orders, options, config = {}) {
265
+ const { getMessenger, trace } = options;
266
+ const { onOrderSyncErroneousSituation } = config;
267
+ const saveOrders = async () => {
268
+ const storageEntries = [];
269
+ for (const order of orders) {
270
+ const key = createOrderStorageKey(order);
271
+ if (!key) {
272
+ onOrderSyncErroneousSituation?.('Skipping ramps order remote write with empty storage key', {
273
+ hasId: Boolean(order.id),
274
+ hasProviderOrderId: Boolean(order.providerOrderId),
275
+ });
276
+ continue;
277
+ }
278
+ const storageEntry = mapRampsOrderToUserStorageEntry(order);
279
+ storageEntries.push([key, JSON.stringify(storageEntry)]);
280
+ }
281
+ if (storageEntries.length === 0) {
282
+ return;
283
+ }
284
+ await getMessenger().call('UserStorageController:performBatchSetStorage', USER_STORAGE_RAMPS_ORDERS_FEATURE, storageEntries);
285
+ };
286
+ return trace
287
+ ? await trace({
288
+ name: TraceName.RampsOrderSyncSaveBatch,
289
+ data: {
290
+ orderCount: orders.length,
291
+ hasBatchOperations: orders.length > 1,
292
+ },
293
+ }, saveOrders)
294
+ : await saveOrders();
295
+ }
296
+ /**
297
+ * Updates a single order in User Storage without a full sync.
298
+ *
299
+ * This helper always stamps `lastUpdatedAt` and writes. Callers own change
300
+ * detection — skip this when the syncable payload is unchanged so polling
301
+ * cannot amplify User Storage writes. `RampsController.addOrder` performs
302
+ * that check via {@link areOrdersEqual}.
303
+ *
304
+ * @param order - The order that was updated locally.
305
+ * @param options - Parameters used for syncing operations.
306
+ * @param config - Optional sync callbacks for error reporting.
307
+ * @returns Resolves when the User Storage update completes or no-ops.
308
+ */
309
+ export async function updateOrderInUserStorage(order, options, config = {}) {
310
+ const { trace } = options;
311
+ const updateOrder = async () => {
312
+ if (!canPerformOrderSyncing(options) || !isSyncableOrder(order)) {
313
+ return;
314
+ }
315
+ const updatedEntry = {
316
+ ...order,
317
+ lastUpdatedAt: Date.now(),
318
+ };
319
+ await saveOrdersToUserStorage([updatedEntry], options, config);
320
+ };
321
+ if (trace) {
322
+ return await trace({
323
+ name: TraceName.RampsOrderSyncUpdateRemote,
324
+ data: {
325
+ orderStatus: order.status,
326
+ hasProviderOrderId: Boolean(order.providerOrderId),
327
+ },
328
+ }, updateOrder);
329
+ }
330
+ return await updateOrder();
331
+ }
332
+ /**
333
+ * Marks a single order as deleted in remote storage (soft delete).
334
+ *
335
+ * @param order - The order that was deleted locally (needs id / providerOrderId).
336
+ * @param options - Parameters used for syncing operations.
337
+ * @param config - Optional sync callbacks for error reporting.
338
+ * @returns Resolves when the remote soft-delete completes or no-ops.
339
+ */
340
+ export async function deleteOrderInUserStorage(order, options, config = {}) {
341
+ const { trace } = options;
342
+ const deleteOrder = async () => {
343
+ if (!canPerformOrderSyncing(options) || !isSyncableOrder(order)) {
344
+ return;
345
+ }
346
+ const now = Date.now();
347
+ const deletedOrder = {
348
+ ...order,
349
+ deletedAt: now,
350
+ lastUpdatedAt: now,
351
+ };
352
+ await saveOrdersToUserStorage([deletedOrder], options, config);
353
+ };
354
+ return trace
355
+ ? await trace({ name: TraceName.RampsOrderSyncDeleteRemote }, deleteOrder)
356
+ : await deleteOrder();
357
+ }
358
+ export const orderSyncingTestExports = {
359
+ computeMergePlan,
360
+ reconcileOrdersForRemoteUpload,
361
+ getOrderTimestamp,
362
+ saveOrdersToUserStorage,
363
+ };
364
+ //# sourceMappingURL=controller-integration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller-integration.js","sourceRoot":"","sources":["../../src/order-syncing/controller-integration.ts"],"names":[],"mappings":"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","sourcesContent":["import type { RampsOrder } from '../RampsService.js';\nimport {\n TraceName,\n USER_STORAGE_RAMPS_ORDERS_FEATURE,\n USER_STORAGE_VERSION,\n USER_STORAGE_VERSION_KEY,\n} from './constants.js';\nimport { canPerformOrderSyncing } from './sync-utils.js';\nimport type {\n OrderSyncingOptions,\n SyncRampsOrder,\n UserStorageRampsOrderEntry,\n} from './types.js';\nimport {\n areOrdersEqual,\n createOrderStorageKey,\n isSyncableOrder,\n mapRampsOrderToUserStorageEntry,\n mapUserStorageEntryToRampsOrder,\n normalizeCreatedAt,\n stripDeletedAt,\n stripSyncMetadata,\n} from './utils.js';\n\nexport type SyncOrdersWithUserStorageConfig = {\n onOrderSyncErroneousSituation?: (\n errorMessage: string,\n sentryContext?: Record<string, unknown>,\n ) => void;\n};\n\ntype MergePlan = {\n ordersToAddOrUpdateLocally: SyncRampsOrder[];\n ordersToDeleteLocally: SyncRampsOrder[];\n ordersToUpdateRemotely: RampsOrder[];\n};\n\n/**\n * Returns the timestamp used for ramps order conflict resolution.\n *\n * Content conflicts use `local >= remote` (local wins ties). Tombstones use\n * `local > deletedAt` (tie deletes locally) so equal-time deletes still apply.\n *\n * @param order - A ramps order that may include sync metadata.\n * @returns The best available last-updated timestamp.\n */\nfunction getOrderTimestamp(order: RampsOrder | SyncRampsOrder): number {\n return normalizeCreatedAt(\n (order as SyncRampsOrder).lastUpdatedAt ?? order.createdAt,\n );\n}\n\n/**\n * Builds the local/remote merge plan from the current local snapshot and remote\n * entries.\n *\n * @param localOrders - Syncable orders currently in {@link RampsController} state.\n * @param validRemoteOrders - Syncable orders fetched from User Storage.\n * @returns Lists of local mutations and remote uploads to apply.\n */\nfunction computeMergePlan(\n localOrders: RampsOrder[],\n validRemoteOrders: SyncRampsOrder[],\n): MergePlan & { remoteOrdersMap: Map<string, SyncRampsOrder> } {\n const localOrdersMap = new Map<string, RampsOrder>();\n const remoteOrdersMap = new Map<string, SyncRampsOrder>();\n\n localOrders.forEach((order) => {\n localOrdersMap.set(createOrderStorageKey(order), order);\n });\n\n validRemoteOrders.forEach((order) => {\n remoteOrdersMap.set(createOrderStorageKey(order), order);\n });\n\n const ordersToAddOrUpdateLocally: SyncRampsOrder[] = [];\n const ordersToDeleteLocally: SyncRampsOrder[] = [];\n const ordersToUpdateRemotely: RampsOrder[] = [];\n\n for (const remoteOrder of validRemoteOrders) {\n const key = createOrderStorageKey(remoteOrder);\n const localOrder = localOrdersMap.get(key);\n\n if (remoteOrder.deletedAt) {\n if (localOrder) {\n const localTimestamp = getOrderTimestamp(localOrder);\n const remoteTimestamp = remoteOrder.deletedAt;\n\n if (localTimestamp > remoteTimestamp) {\n ordersToUpdateRemotely.push(localOrder);\n } else {\n ordersToDeleteLocally.push(remoteOrder);\n }\n }\n } else if (!localOrder) {\n ordersToAddOrUpdateLocally.push(remoteOrder);\n } else if (!areOrdersEqual(localOrder, remoteOrder)) {\n const localTimestamp = getOrderTimestamp(localOrder);\n const remoteTimestamp = getOrderTimestamp(remoteOrder);\n\n if (localTimestamp >= remoteTimestamp) {\n ordersToUpdateRemotely.push(localOrder);\n } else {\n ordersToAddOrUpdateLocally.push(remoteOrder);\n }\n }\n }\n\n for (const localOrder of localOrders) {\n const key = createOrderStorageKey(localOrder);\n if (!remoteOrdersMap.has(key)) {\n ordersToUpdateRemotely.push(localOrder);\n }\n }\n\n return {\n remoteOrdersMap,\n ordersToAddOrUpdateLocally,\n ordersToDeleteLocally,\n ordersToUpdateRemotely,\n };\n}\n\n/**\n * Re-reads live local orders after merge mutations so orders added or updated\n * while `isOrderSyncingInProgress` is true are still uploaded remotely.\n *\n * @param plannedRemoteUploads - Orders already queued for remote upload.\n * @param remoteOrdersMap - Remote orders keyed by storage key.\n * @param currentLocalOrders - Latest syncable local orders.\n * @returns Remote uploads using the freshest local payloads.\n */\nfunction reconcileOrdersForRemoteUpload(\n plannedRemoteUploads: RampsOrder[],\n remoteOrdersMap: Map<string, SyncRampsOrder>,\n currentLocalOrders: RampsOrder[],\n): RampsOrder[] {\n const keyedUploads = new Map(\n plannedRemoteUploads.map((order) => [createOrderStorageKey(order), order]),\n );\n\n for (const localOrder of currentLocalOrders) {\n const key = createOrderStorageKey(localOrder);\n const existingUpload = keyedUploads.get(key);\n const remoteOrder = remoteOrdersMap.get(key);\n\n if (existingUpload) {\n if (!areOrdersEqual(existingUpload, localOrder)) {\n keyedUploads.set(key, localOrder);\n }\n continue;\n }\n\n if (!remoteOrder || !areOrdersEqual(localOrder, remoteOrder)) {\n keyedUploads.set(key, localOrder);\n }\n }\n\n return [...keyedUploads.values()];\n}\n\n/**\n * Syncs V2 ramps orders between local {@link RampsController} state and User Storage.\n *\n * Handles first sync, new-device sync, merges, timestamp-based conflict resolution,\n * and remote soft-deletes.\n *\n * @param config - Optional callbacks for sync errors.\n * @param options - Parameters used for syncing operations.\n */\nexport async function syncOrdersWithUserStorage(\n config: SyncOrdersWithUserStorageConfig,\n options: OrderSyncingOptions,\n): Promise<void> {\n const { getRampsControllerInstance, trace } = options;\n const { onOrderSyncErroneousSituation } = config;\n\n if (!canPerformOrderSyncing(options)) {\n return;\n }\n\n const controller = getRampsControllerInstance();\n // Set the semaphore before the remote fetch so overlapping sync calls and\n // incremental addOrder/removeOrder pushes cannot race the merge.\n controller.setIsOrderSyncingInProgress(true);\n\n try {\n const validRemoteOrders = (await getRemoteOrders(options, config)).filter(\n isSyncableOrder,\n );\n\n const performSync = async (): Promise<void> => {\n const getLocalOrders = (): RampsOrder[] =>\n controller.state.orders.filter(isSyncableOrder);\n const pendingDeleteKeysBeforeApply = new Set(\n controller\n .getPendingRemoteDeletes()\n .map((order) => createOrderStorageKey(order))\n .filter((key) => key.length > 0),\n );\n\n const {\n remoteOrdersMap,\n ordersToAddOrUpdateLocally,\n ordersToDeleteLocally,\n ordersToUpdateRemotely,\n } = computeMergePlan(getLocalOrders(), validRemoteOrders);\n\n controller.setIsApplyingOrderSyncChanges(true);\n try {\n for (const order of ordersToDeleteLocally) {\n controller.removeOrder(createOrderStorageKey(order));\n }\n\n for (const order of ordersToAddOrUpdateLocally) {\n if (\n !order.deletedAt &&\n !pendingDeleteKeysBeforeApply.has(createOrderStorageKey(order))\n ) {\n controller.addOrder(stripDeletedAt(order));\n }\n }\n } finally {\n controller.setIsApplyingOrderSyncChanges(false);\n }\n\n const ordersToUpload = reconcileOrdersForRemoteUpload(\n ordersToUpdateRemotely,\n remoteOrdersMap,\n getLocalOrders(),\n );\n\n const localKeys = new Set(\n getLocalOrders().map((order) => createOrderStorageKey(order)),\n );\n const pendingDeletes = controller\n .getPendingRemoteDeletes()\n .filter((order) => {\n const key = createOrderStorageKey(order);\n return key.length > 0 && !localKeys.has(key);\n });\n const pendingDeleteKeys = new Set(\n pendingDeletes.map((order) => createOrderStorageKey(order)),\n );\n\n const now = Date.now();\n const syncedUploads: SyncRampsOrder[] = [\n ...ordersToUpload\n .filter(\n (localOrder) =>\n !pendingDeleteKeys.has(createOrderStorageKey(localOrder)),\n )\n .map((localOrder) => ({\n ...stripSyncMetadata(localOrder),\n lastUpdatedAt: (localOrder as SyncRampsOrder).lastUpdatedAt ?? now,\n })),\n ...pendingDeletes.map((order) => ({\n ...stripSyncMetadata(order),\n deletedAt: now,\n lastUpdatedAt: now,\n })),\n ];\n\n if (syncedUploads.length > 0) {\n await saveOrdersToUserStorage(syncedUploads, options, config);\n controller.acknowledgePendingRemoteDeletes(pendingDeletes);\n }\n };\n\n if (trace) {\n const localOrderCount =\n controller.state.orders.filter(isSyncableOrder).length;\n\n await trace(\n {\n name: TraceName.RampsOrderSyncFull,\n data: {\n localOrderCount,\n remoteOrderCount: validRemoteOrders.length,\n isFirstSync: validRemoteOrders.length === 0 && localOrderCount > 0,\n isNewDeviceSync:\n localOrderCount === 0 && validRemoteOrders.length > 0,\n },\n },\n performSync,\n );\n return;\n }\n\n await performSync();\n } catch (error) {\n onOrderSyncErroneousSituation?.('Error synchronizing ramps orders', {\n error,\n });\n throw error;\n } finally {\n controller.setIsOrderSyncingInProgress(false);\n }\n}\n\n/**\n * Retrieves remote ramps orders from User Storage.\n *\n * @param options - Parameters used for retrieving remote orders.\n * @param config - Optional sync callbacks for error reporting.\n * @returns Parsed sync-aware orders. Returns an empty array when none exist.\n */\nasync function getRemoteOrders(\n options: OrderSyncingOptions,\n config: SyncOrdersWithUserStorageConfig,\n): Promise<SyncRampsOrder[]> {\n const { getMessenger } = options;\n const { onOrderSyncErroneousSituation } = config;\n\n try {\n // Reads the active/primary SRP profile only. Order sync is same-SRP: hosts\n // sync Extension ↔ Mobile for a given SRP, not across secondary wallets.\n const remoteOrdersJsonArray =\n (await getMessenger().call(\n 'UserStorageController:performGetStorageAllFeatureEntries',\n USER_STORAGE_RAMPS_ORDERS_FEATURE,\n )) ?? [];\n\n if (remoteOrdersJsonArray.length === 0) {\n return [];\n }\n\n const remoteOrders: SyncRampsOrder[] = [];\n\n for (const orderJson of remoteOrdersJsonArray) {\n try {\n const entry = JSON.parse(orderJson) as UserStorageRampsOrderEntry;\n if (entry[USER_STORAGE_VERSION_KEY] !== USER_STORAGE_VERSION) {\n onOrderSyncErroneousSituation?.(\n 'Unsupported ramps order storage version',\n {\n version: entry[USER_STORAGE_VERSION_KEY],\n expectedVersion: USER_STORAGE_VERSION,\n },\n );\n continue;\n }\n if (!entry.o || typeof entry.o !== 'object') {\n onOrderSyncErroneousSituation?.(\n 'Remote ramps order entry missing order payload',\n {},\n );\n continue;\n }\n const mapped = mapUserStorageEntryToRampsOrder(entry);\n if (!createOrderStorageKey(mapped)) {\n continue;\n }\n remoteOrders.push(mapped);\n } catch (error) {\n // Do not attach raw order JSON — it can include wallet/PII fields.\n onOrderSyncErroneousSituation?.(\n 'Failed to parse remote ramps order entry',\n {\n error,\n entryLength: orderJson.length,\n },\n );\n }\n }\n\n return remoteOrders;\n } catch (error) {\n onOrderSyncErroneousSituation?.('Failed to fetch remote ramps orders', {\n error,\n });\n throw error;\n }\n}\n\n/**\n * Saves orders to User Storage in a batch.\n *\n * @param orders - The orders to save.\n * @param options - Parameters used for saving orders.\n * @param config - Optional sync callbacks for error reporting.\n * @returns Resolves when the batch write completes.\n */\nasync function saveOrdersToUserStorage(\n orders: SyncRampsOrder[],\n options: OrderSyncingOptions,\n config: SyncOrdersWithUserStorageConfig = {},\n): Promise<void> {\n const { getMessenger, trace } = options;\n const { onOrderSyncErroneousSituation } = config;\n\n const saveOrders = async (): Promise<void> => {\n const storageEntries: [string, string][] = [];\n\n for (const order of orders) {\n const key = createOrderStorageKey(order);\n if (!key) {\n onOrderSyncErroneousSituation?.(\n 'Skipping ramps order remote write with empty storage key',\n {\n hasId: Boolean(order.id),\n hasProviderOrderId: Boolean(order.providerOrderId),\n },\n );\n continue;\n }\n const storageEntry = mapRampsOrderToUserStorageEntry(order);\n storageEntries.push([key, JSON.stringify(storageEntry)]);\n }\n\n if (storageEntries.length === 0) {\n return;\n }\n\n await getMessenger().call(\n 'UserStorageController:performBatchSetStorage',\n USER_STORAGE_RAMPS_ORDERS_FEATURE,\n storageEntries,\n );\n };\n\n return trace\n ? await trace(\n {\n name: TraceName.RampsOrderSyncSaveBatch,\n data: {\n orderCount: orders.length,\n hasBatchOperations: orders.length > 1,\n },\n },\n saveOrders,\n )\n : await saveOrders();\n}\n\n/**\n * Updates a single order in User Storage without a full sync.\n *\n * This helper always stamps `lastUpdatedAt` and writes. Callers own change\n * detection — skip this when the syncable payload is unchanged so polling\n * cannot amplify User Storage writes. `RampsController.addOrder` performs\n * that check via {@link areOrdersEqual}.\n *\n * @param order - The order that was updated locally.\n * @param options - Parameters used for syncing operations.\n * @param config - Optional sync callbacks for error reporting.\n * @returns Resolves when the User Storage update completes or no-ops.\n */\nexport async function updateOrderInUserStorage(\n order: RampsOrder,\n options: OrderSyncingOptions,\n config: SyncOrdersWithUserStorageConfig = {},\n): Promise<void> {\n const { trace } = options;\n\n const updateOrder = async (): Promise<void> => {\n if (!canPerformOrderSyncing(options) || !isSyncableOrder(order)) {\n return;\n }\n\n const updatedEntry: SyncRampsOrder = {\n ...order,\n lastUpdatedAt: Date.now(),\n };\n\n await saveOrdersToUserStorage([updatedEntry], options, config);\n };\n\n if (trace) {\n return await trace(\n {\n name: TraceName.RampsOrderSyncUpdateRemote,\n data: {\n orderStatus: order.status,\n hasProviderOrderId: Boolean(order.providerOrderId),\n },\n },\n updateOrder,\n );\n }\n\n return await updateOrder();\n}\n\n/**\n * Marks a single order as deleted in remote storage (soft delete).\n *\n * @param order - The order that was deleted locally (needs id / providerOrderId).\n * @param options - Parameters used for syncing operations.\n * @param config - Optional sync callbacks for error reporting.\n * @returns Resolves when the remote soft-delete completes or no-ops.\n */\nexport async function deleteOrderInUserStorage(\n order: RampsOrder,\n options: OrderSyncingOptions,\n config: SyncOrdersWithUserStorageConfig = {},\n): Promise<void> {\n const { trace } = options;\n\n const deleteOrder = async (): Promise<void> => {\n if (!canPerformOrderSyncing(options) || !isSyncableOrder(order)) {\n return;\n }\n\n const now = Date.now();\n const deletedOrder: SyncRampsOrder = {\n ...order,\n deletedAt: now,\n lastUpdatedAt: now,\n };\n\n await saveOrdersToUserStorage([deletedOrder], options, config);\n };\n\n return trace\n ? await trace({ name: TraceName.RampsOrderSyncDeleteRemote }, deleteOrder)\n : await deleteOrder();\n}\n\nexport const orderSyncingTestExports = {\n computeMergePlan,\n reconcileOrdersForRemoteUpload,\n getOrderTimestamp,\n saveOrdersToUserStorage,\n};\n"]}
@@ -0,0 +1,7 @@
1
+ export { USER_STORAGE_RAMPS_ORDERS_FEATURE, USER_STORAGE_VERSION, USER_STORAGE_VERSION_KEY, TraceName, } from './constants.js';
2
+ export type { UserStorageRampsOrderEntry, SyncRampsOrder, OrderSyncingController, OrderSyncingOptions, } from './types.js';
3
+ export { createOrderStorageKey, isSyncableOrder, mapRampsOrderToUserStorageEntry, mapUserStorageEntryToRampsOrder, stripPaymentDetailsForRemoteStorage, stripSyncMetadata, stripDeletedAt, areOrdersEqual, } from './utils.js';
4
+ export { canPerformOrderSyncing } from './sync-utils.js';
5
+ export type { SyncOrdersWithUserStorageConfig } from './controller-integration.js';
6
+ export { syncOrdersWithUserStorage, updateOrderInUserStorage, deleteOrderInUserStorage, } from './controller-integration.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/order-syncing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iCAAiC,EACjC,oBAAoB,EACpB,wBAAwB,EACxB,SAAS,GACV,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,0BAA0B,EAC1B,cAAc,EACd,sBAAsB,EACtB,mBAAmB,GACpB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,qBAAqB,EACrB,eAAe,EACf,+BAA+B,EAC/B,+BAA+B,EAC/B,mCAAmC,EACnC,iBAAiB,EACjB,cAAc,EACd,cAAc,GACf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,YAAY,EAAE,+BAA+B,EAAE,MAAM,6BAA6B,CAAC;AACnF,OAAO,EACL,yBAAyB,EACzB,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,6BAA6B,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { USER_STORAGE_RAMPS_ORDERS_FEATURE, USER_STORAGE_VERSION, USER_STORAGE_VERSION_KEY, TraceName, } from './constants.js';
2
+ export { createOrderStorageKey, isSyncableOrder, mapRampsOrderToUserStorageEntry, mapUserStorageEntryToRampsOrder, stripPaymentDetailsForRemoteStorage, stripSyncMetadata, stripDeletedAt, areOrdersEqual, } from './utils.js';
3
+ export { canPerformOrderSyncing } from './sync-utils.js';
4
+ export { syncOrdersWithUserStorage, updateOrderInUserStorage, deleteOrderInUserStorage, } from './controller-integration.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/order-syncing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iCAAiC,EACjC,oBAAoB,EACpB,wBAAwB,EACxB,SAAS,GACV,MAAM,gBAAgB,CAAC;AAOxB,OAAO,EACL,qBAAqB,EACrB,eAAe,EACf,+BAA+B,EAC/B,+BAA+B,EAC/B,mCAAmC,EACnC,iBAAiB,EACjB,cAAc,EACd,cAAc,GACf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,OAAO,EACL,yBAAyB,EACzB,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,6BAA6B,CAAC","sourcesContent":["export {\n USER_STORAGE_RAMPS_ORDERS_FEATURE,\n USER_STORAGE_VERSION,\n USER_STORAGE_VERSION_KEY,\n TraceName,\n} from './constants.js';\nexport type {\n UserStorageRampsOrderEntry,\n SyncRampsOrder,\n OrderSyncingController,\n OrderSyncingOptions,\n} from './types.js';\nexport {\n createOrderStorageKey,\n isSyncableOrder,\n mapRampsOrderToUserStorageEntry,\n mapUserStorageEntryToRampsOrder,\n stripPaymentDetailsForRemoteStorage,\n stripSyncMetadata,\n stripDeletedAt,\n areOrdersEqual,\n} from './utils.js';\nexport { canPerformOrderSyncing } from './sync-utils.js';\nexport type { SyncOrdersWithUserStorageConfig } from './controller-integration.js';\nexport {\n syncOrdersWithUserStorage,\n updateOrderInUserStorage,\n deleteOrderInUserStorage,\n} from './controller-integration.js';\n"]}
@@ -0,0 +1,9 @@
1
+ import type { OrderSyncingOptions } from './types.js';
2
+ /**
3
+ * Check if we can perform ramps order syncing.
4
+ *
5
+ * @param options - Parameters used for checking if we can perform order syncing.
6
+ * @returns Whether we can perform order syncing.
7
+ */
8
+ export declare function canPerformOrderSyncing(options: OrderSyncingOptions): boolean;
9
+ //# sourceMappingURL=sync-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-utils.d.ts","sourceRoot":"","sources":["../../src/order-syncing/sync-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CA8B5E"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Check if we can perform ramps order syncing.
3
+ *
4
+ * @param options - Parameters used for checking if we can perform order syncing.
5
+ * @returns Whether we can perform order syncing.
6
+ */
7
+ export function canPerformOrderSyncing(options) {
8
+ const { getMessenger, getRampsControllerInstance } = options;
9
+ try {
10
+ const { isBackupAndSyncEnabled, isRampsSyncingEnabled: rawIsRampsSyncingEnabled, } = getMessenger().call('UserStorageController:getState');
11
+ const isRampsSyncingEnabled = rawIsRampsSyncingEnabled ?? true;
12
+ const isAuthEnabled = getMessenger().call('AuthenticationController:isSignedIn');
13
+ const { isOrderSyncingInProgress } = getRampsControllerInstance();
14
+ if (!isBackupAndSyncEnabled ||
15
+ !isRampsSyncingEnabled ||
16
+ isOrderSyncingInProgress ||
17
+ !isAuthEnabled) {
18
+ return false;
19
+ }
20
+ return true;
21
+ }
22
+ catch {
23
+ // Host has not delegated User Storage / auth actions yet
24
+ return false;
25
+ }
26
+ }
27
+ //# sourceMappingURL=sync-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-utils.js","sourceRoot":"","sources":["../../src/order-syncing/sync-utils.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,OAA4B;IACjE,MAAM,EAAE,YAAY,EAAE,0BAA0B,EAAE,GAAG,OAAO,CAAC;IAE7D,IAAI,CAAC;QACH,MAAM,EACJ,sBAAsB,EACtB,qBAAqB,EAAE,wBAAwB,GAChD,GAAG,YAAY,EAAE,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC;QAC1D,MAAM,qBAAqB,GAAG,wBAAwB,IAAI,IAAI,CAAC;QAE/D,MAAM,aAAa,GAAG,YAAY,EAAE,CAAC,IAAI,CACvC,qCAAqC,CACtC,CAAC;QAEF,MAAM,EAAE,wBAAwB,EAAE,GAAG,0BAA0B,EAAE,CAAC;QAElE,IACE,CAAC,sBAAsB;YACvB,CAAC,qBAAqB;YACtB,wBAAwB;YACxB,CAAC,aAAa,EACd,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,yDAAyD;QACzD,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC","sourcesContent":["import type { OrderSyncingOptions } from './types.js';\n\n/**\n * Check if we can perform ramps order syncing.\n *\n * @param options - Parameters used for checking if we can perform order syncing.\n * @returns Whether we can perform order syncing.\n */\nexport function canPerformOrderSyncing(options: OrderSyncingOptions): boolean {\n const { getMessenger, getRampsControllerInstance } = options;\n\n try {\n const {\n isBackupAndSyncEnabled,\n isRampsSyncingEnabled: rawIsRampsSyncingEnabled,\n } = getMessenger().call('UserStorageController:getState');\n const isRampsSyncingEnabled = rawIsRampsSyncingEnabled ?? true;\n\n const isAuthEnabled = getMessenger().call(\n 'AuthenticationController:isSignedIn',\n );\n\n const { isOrderSyncingInProgress } = getRampsControllerInstance();\n\n if (\n !isBackupAndSyncEnabled ||\n !isRampsSyncingEnabled ||\n isOrderSyncingInProgress ||\n !isAuthEnabled\n ) {\n return false;\n }\n\n return true;\n } catch {\n // Host has not delegated User Storage / auth actions yet\n return false;\n }\n}\n"]}
@@ -0,0 +1,66 @@
1
+ import type { TraceCallback } from '@metamask/controller-utils';
2
+ import type { RampsControllerMessenger } from '../RampsController.js';
3
+ import type { RampsOrder } from '../RampsService.js';
4
+ import type { USER_STORAGE_VERSION, USER_STORAGE_VERSION_KEY } from './constants.js';
5
+ /**
6
+ * Compact User Storage entry wrapping a full {@link RampsOrder}.
7
+ */
8
+ export type UserStorageRampsOrderEntry = {
9
+ /**
10
+ * Schema version — allows upgrade/downgrade handling in the future.
11
+ */
12
+ [USER_STORAGE_VERSION_KEY]: typeof USER_STORAGE_VERSION;
13
+ /**
14
+ * Full V2 ramps order payload.
15
+ */
16
+ o: RampsOrder;
17
+ /**
18
+ * Last-updated timestamp used for conflict resolution.
19
+ */
20
+ lu?: number;
21
+ /**
22
+ * Soft-delete tombstone timestamp. When set, the order should be removed locally.
23
+ */
24
+ dt?: number;
25
+ };
26
+ /**
27
+ * {@link RampsOrder} extended with sync metadata used for conflict resolution.
28
+ *
29
+ * `lastUpdatedAt` is persisted on local controller orders so last-write-wins can
30
+ * compare against remote `lu` after local edits. `deletedAt` is remote-only and
31
+ * must be stripped before writing into {@link RampsController} state.
32
+ */
33
+ export type SyncRampsOrder = RampsOrder & {
34
+ lastUpdatedAt?: number;
35
+ deletedAt?: number;
36
+ };
37
+ /**
38
+ * Minimal controller surface required by order syncing.
39
+ */
40
+ export type OrderSyncingController = {
41
+ state: {
42
+ orders: SyncRampsOrder[];
43
+ };
44
+ readonly isOrderSyncingInProgress: boolean;
45
+ setIsOrderSyncingInProgress: (value: boolean) => void;
46
+ setIsApplyingOrderSyncChanges: (value: boolean) => void;
47
+ addOrder: (order: RampsOrder) => void;
48
+ removeOrder: (providerOrderId: string) => void;
49
+ /**
50
+ * Returns orders deleted locally while a full sync held the semaphore.
51
+ */
52
+ getPendingRemoteDeletes: () => RampsOrder[];
53
+ /**
54
+ * Clears pending deletes after their remote tombstones are persisted.
55
+ */
56
+ acknowledgePendingRemoteDeletes: (orders: RampsOrder[]) => void;
57
+ };
58
+ /**
59
+ * Options for ramps order syncing operations.
60
+ */
61
+ export type OrderSyncingOptions = {
62
+ getRampsControllerInstance: () => OrderSyncingController;
63
+ getMessenger: () => RampsControllerMessenger;
64
+ trace?: TraceCallback;
65
+ };
66
+ //# sourceMappingURL=types.d.ts.map