@stamprally/core 0.11.0 → 0.12.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/dist/index.d.cts CHANGED
@@ -313,6 +313,12 @@ declare function createClaimTicketNumber(rewardId: string, options?: ClaimTicket
313
313
  declare function createUniqueClaimTicketNumber(rewardId: string, issuedAt: string): string;
314
314
  declare function issueClaimTicketNumber(reward: Reward, currentState: RewardState, options?: ClaimTicketOptions): RewardState;
315
315
 
316
+ interface MergeConflictOptions {
317
+ readonly policy: "server_wins" | "merge";
318
+ }
319
+ /** Resolves a server/local state conflict without mutating either input state. */
320
+ declare function resolveRallyStateConflict(serverState: UserRallyState, localState: UserRallyState, options?: MergeConflictOptions): UserRallyState;
321
+
316
322
  type RewardConsumeError = {
317
323
  readonly code: "NOT_AVAILABLE" | "ALREADY_CONSUMED" | "OUT_OF_STOCK" | "REWARD_NOT_FOUND";
318
324
  readonly rewardId: string;
@@ -384,6 +390,12 @@ interface OfflineConflict {
384
390
  readonly serverState: UserRallyState;
385
391
  }
386
392
  type OfflineSender = (operation: OfflineOperation) => Promise<OfflineResult | OfflineConflictResult>;
393
+ interface OfflineSyncResultEvent {
394
+ readonly operation: OfflineOperation;
395
+ readonly result: OfflineResult | OfflineConflictResult;
396
+ readonly state?: UserRallyState;
397
+ }
398
+ type OfflineSyncResultListener = (event: OfflineSyncResultEvent) => void | Promise<void>;
387
399
  declare class MemoryQueueStorage implements OfflineQueueStorage {
388
400
  #private;
389
401
  load(key: string): Promise<ReadonlyArray<OfflineOperation>>;
@@ -407,6 +419,8 @@ declare class OfflineQueue {
407
419
  get pendingCount(): number;
408
420
  get error(): Error | null;
409
421
  get operations(): ReadonlyArray<OfflineOperation>;
422
+ get conflictPolicy(): SyncConflictPolicy;
423
+ setSyncResultListener(listener: OfflineSyncResultListener | undefined): void;
410
424
  initialize(): Promise<void>;
411
425
  setSender(sender: OfflineSender): void;
412
426
  enqueue(operation: OfflineOperation): Promise<void>;
@@ -415,7 +429,7 @@ declare class OfflineQueue {
415
429
  clear(): Promise<void>;
416
430
  sync(sender?: OfflineSender | undefined): Promise<void>;
417
431
  retrySync(sender?: OfflineSender | undefined): Promise<void>;
418
- resolveConflict(operation: OfflineOperation, localState: UserRallyState, serverState: UserRallyState): Promise<void>;
432
+ resolveConflict(operation: OfflineOperation, localState: UserRallyState, serverState: UserRallyState): Promise<UserRallyState>;
419
433
  }
420
434
 
421
435
  interface StampStorage {
@@ -703,4 +717,4 @@ type SnapshotTokenVerification<T extends SnapshotTokenPayload = SnapshotTokenPay
703
717
  declare function createSignedSnapshotToken<T extends SnapshotTokenPayload>(payload: T, secretKey: SnapshotSecretKey): Promise<string>;
704
718
  declare function verifySnapshotToken<T extends SnapshotTokenPayload = SnapshotTokenPayload>(token: string, secretKey: SnapshotSecretKey, now?: number): Promise<SnapshotTokenVerification<T>>;
705
719
 
706
- export { type AdminRallyConfig, type CheckInCondition, type CheckInOptions, type CheckInRequest, type CheckInResult, type CheckInSuccess, type ClaimOptions, type ClaimRequest, type ClaimResult, type ClaimSuccess, type ClaimTicketOptions, type ClientError, type ClientEvent, type ClientEventListener, type ClientListener, type ClientOptions, type CompositeConditionFailure, type ConditionMatch, type ConditionMismatch, ConfigValidationError, type ConsumeResult, type ConsumeRewardParams, type CustomValidationContext, type CustomValidator, DEFAULT_SHEET_THEME, type DetectorError, type DetectorErrorCode, type DetectorKind, type DetectorResult, type ExternalReference, type FontFamily, type GeoDetectorOptions, type GeoVerificationContext, MemoryQueueStorage as InMemoryOfflineQueueStorage, InMemoryStorage, IndexedDBAdapter, type IndexedDBAdapterOptions, type IndexedDBOfflineQueueOptions, IndexedDBOfflineQueueStorage, LocalStorageAdapter, type LocalStorageAdapterOptions, type LocalStorageFailureMode, type LocaleDictionary, type LocalizedString, type LocalizedText, type NfcDetectorOptions, type NfcVerificationContext, type OfflineConflict, type OfflineConflictResult, type OfflineOperation, OfflineQueue, type OfflineQueueOptions, type OfflineQueueStorage, type OfflineResult, type OfflineSender, type ParseResult, type PasscodeCondition$1 as PasscodeCondition, type ProcessStampValue, type PublicCheckInCondition, type PublicConfigSafety, type PublicRallyConfig, type PublicReward, type PublicSpotItem, type QrDetectorOptions, type QrVerificationContext, type RallyConfig, type RallySnapshot, type RedemptionMethod, type Result, type Reward, type RewardConsumeError, type RewardState, type RewardStatus, type RewardType, type RewardUnlockCondition, type SecureTokenError, type SecureTokenErrorCode, type SecureTokenOptions, type SecureTokenPayload, type SecureTokenSecretKey, type SecureTokenVerification, type SheetTheme, type SlotShape, type SnapshotSecretKey, type SnapshotTokenError, type SnapshotTokenErrorCode, type SnapshotTokenPayload, type SnapshotTokenVerification, type SpotItem, type StampError, StampRallyClient, type StampRallyProgress, type StampRallyState, type StampRecord, type StampStorage, StorageAdapterError, type StorageAdapterErrorCode, type StorageLike, type StorageOperation, type StorageWarningHandler, type SupportedLocale, type SyncAdapter, type SyncConflictPolicy, type SyncState, THEME_PRESETS, type ThemePreset, type ThemePresetId, type UserRallyState, type ValidationError, type Validator, type VerificationContext, assertPublicConfig, calculateDistanceMeters, calculateProgress, consumeReward, createClaimTicketNumber, createSecureToken, createSignedSnapshotToken, createUniqueClaimTicketNumber, evaluateCondition, evaluateConditionDetailed, exportProgressToken, getCurrentGeoContext, getOrderedSpots, importProgressToken, isGeolocationSupported, isNfcSupported, isPublicConfig, isQrSupported, isRewardState, isStampRallyState, issueClaimTicketNumber, normalizePasscode, parseAdminConfig, parsePublicConfig, processStamp, readNfcContext, readQrContext, reconcileRewardStates, resolveLocalizedText, safeParseAdminConfig, safeParsePublicConfig, sanitizeAdminConfig, storageKey, toLocalizedString, toPublicConfig, updateLocalizedField, validatePublicConfigSafety, verifyPasscode, verifySecureToken, verifySnapshotToken };
720
+ export { type AdminRallyConfig, type CheckInCondition, type CheckInOptions, type CheckInRequest, type CheckInResult, type CheckInSuccess, type ClaimOptions, type ClaimRequest, type ClaimResult, type ClaimSuccess, type ClaimTicketOptions, type ClientError, type ClientEvent, type ClientEventListener, type ClientListener, type ClientOptions, type CompositeConditionFailure, type ConditionMatch, type ConditionMismatch, ConfigValidationError, type ConsumeResult, type ConsumeRewardParams, type CustomValidationContext, type CustomValidator, DEFAULT_SHEET_THEME, type DetectorError, type DetectorErrorCode, type DetectorKind, type DetectorResult, type ExternalReference, type FontFamily, type GeoDetectorOptions, type GeoVerificationContext, MemoryQueueStorage as InMemoryOfflineQueueStorage, InMemoryStorage, IndexedDBAdapter, type IndexedDBAdapterOptions, type IndexedDBOfflineQueueOptions, IndexedDBOfflineQueueStorage, LocalStorageAdapter, type LocalStorageAdapterOptions, type LocalStorageFailureMode, type LocaleDictionary, type LocalizedString, type LocalizedText, type MergeConflictOptions, type NfcDetectorOptions, type NfcVerificationContext, type OfflineConflict, type OfflineConflictResult, type OfflineOperation, OfflineQueue, type OfflineQueueOptions, type OfflineQueueStorage, type OfflineResult, type OfflineSender, type OfflineSyncResultEvent, type OfflineSyncResultListener, type ParseResult, type PasscodeCondition$1 as PasscodeCondition, type ProcessStampValue, type PublicCheckInCondition, type PublicConfigSafety, type PublicRallyConfig, type PublicReward, type PublicSpotItem, type QrDetectorOptions, type QrVerificationContext, type RallyConfig, type RallySnapshot, type RedemptionMethod, type Result, type Reward, type RewardConsumeError, type RewardState, type RewardStatus, type RewardType, type RewardUnlockCondition, type SecureTokenError, type SecureTokenErrorCode, type SecureTokenOptions, type SecureTokenPayload, type SecureTokenSecretKey, type SecureTokenVerification, type SheetTheme, type SlotShape, type SnapshotSecretKey, type SnapshotTokenError, type SnapshotTokenErrorCode, type SnapshotTokenPayload, type SnapshotTokenVerification, type SpotItem, type StampError, StampRallyClient, type StampRallyProgress, type StampRallyState, type StampRecord, type StampStorage, StorageAdapterError, type StorageAdapterErrorCode, type StorageLike, type StorageOperation, type StorageWarningHandler, type SupportedLocale, type SyncAdapter, type SyncConflictPolicy, type SyncState, THEME_PRESETS, type ThemePreset, type ThemePresetId, type UserRallyState, type ValidationError, type Validator, type VerificationContext, assertPublicConfig, calculateDistanceMeters, calculateProgress, consumeReward, createClaimTicketNumber, createSecureToken, createSignedSnapshotToken, createUniqueClaimTicketNumber, evaluateCondition, evaluateConditionDetailed, exportProgressToken, getCurrentGeoContext, getOrderedSpots, importProgressToken, isGeolocationSupported, isNfcSupported, isPublicConfig, isQrSupported, isRewardState, isStampRallyState, issueClaimTicketNumber, normalizePasscode, parseAdminConfig, parsePublicConfig, processStamp, readNfcContext, readQrContext, reconcileRewardStates, resolveLocalizedText, resolveRallyStateConflict, safeParseAdminConfig, safeParsePublicConfig, sanitizeAdminConfig, storageKey, toLocalizedString, toPublicConfig, updateLocalizedField, validatePublicConfigSafety, verifyPasscode, verifySecureToken, verifySnapshotToken };
package/dist/index.d.ts CHANGED
@@ -313,6 +313,12 @@ declare function createClaimTicketNumber(rewardId: string, options?: ClaimTicket
313
313
  declare function createUniqueClaimTicketNumber(rewardId: string, issuedAt: string): string;
314
314
  declare function issueClaimTicketNumber(reward: Reward, currentState: RewardState, options?: ClaimTicketOptions): RewardState;
315
315
 
316
+ interface MergeConflictOptions {
317
+ readonly policy: "server_wins" | "merge";
318
+ }
319
+ /** Resolves a server/local state conflict without mutating either input state. */
320
+ declare function resolveRallyStateConflict(serverState: UserRallyState, localState: UserRallyState, options?: MergeConflictOptions): UserRallyState;
321
+
316
322
  type RewardConsumeError = {
317
323
  readonly code: "NOT_AVAILABLE" | "ALREADY_CONSUMED" | "OUT_OF_STOCK" | "REWARD_NOT_FOUND";
318
324
  readonly rewardId: string;
@@ -384,6 +390,12 @@ interface OfflineConflict {
384
390
  readonly serverState: UserRallyState;
385
391
  }
386
392
  type OfflineSender = (operation: OfflineOperation) => Promise<OfflineResult | OfflineConflictResult>;
393
+ interface OfflineSyncResultEvent {
394
+ readonly operation: OfflineOperation;
395
+ readonly result: OfflineResult | OfflineConflictResult;
396
+ readonly state?: UserRallyState;
397
+ }
398
+ type OfflineSyncResultListener = (event: OfflineSyncResultEvent) => void | Promise<void>;
387
399
  declare class MemoryQueueStorage implements OfflineQueueStorage {
388
400
  #private;
389
401
  load(key: string): Promise<ReadonlyArray<OfflineOperation>>;
@@ -407,6 +419,8 @@ declare class OfflineQueue {
407
419
  get pendingCount(): number;
408
420
  get error(): Error | null;
409
421
  get operations(): ReadonlyArray<OfflineOperation>;
422
+ get conflictPolicy(): SyncConflictPolicy;
423
+ setSyncResultListener(listener: OfflineSyncResultListener | undefined): void;
410
424
  initialize(): Promise<void>;
411
425
  setSender(sender: OfflineSender): void;
412
426
  enqueue(operation: OfflineOperation): Promise<void>;
@@ -415,7 +429,7 @@ declare class OfflineQueue {
415
429
  clear(): Promise<void>;
416
430
  sync(sender?: OfflineSender | undefined): Promise<void>;
417
431
  retrySync(sender?: OfflineSender | undefined): Promise<void>;
418
- resolveConflict(operation: OfflineOperation, localState: UserRallyState, serverState: UserRallyState): Promise<void>;
432
+ resolveConflict(operation: OfflineOperation, localState: UserRallyState, serverState: UserRallyState): Promise<UserRallyState>;
419
433
  }
420
434
 
421
435
  interface StampStorage {
@@ -703,4 +717,4 @@ type SnapshotTokenVerification<T extends SnapshotTokenPayload = SnapshotTokenPay
703
717
  declare function createSignedSnapshotToken<T extends SnapshotTokenPayload>(payload: T, secretKey: SnapshotSecretKey): Promise<string>;
704
718
  declare function verifySnapshotToken<T extends SnapshotTokenPayload = SnapshotTokenPayload>(token: string, secretKey: SnapshotSecretKey, now?: number): Promise<SnapshotTokenVerification<T>>;
705
719
 
706
- export { type AdminRallyConfig, type CheckInCondition, type CheckInOptions, type CheckInRequest, type CheckInResult, type CheckInSuccess, type ClaimOptions, type ClaimRequest, type ClaimResult, type ClaimSuccess, type ClaimTicketOptions, type ClientError, type ClientEvent, type ClientEventListener, type ClientListener, type ClientOptions, type CompositeConditionFailure, type ConditionMatch, type ConditionMismatch, ConfigValidationError, type ConsumeResult, type ConsumeRewardParams, type CustomValidationContext, type CustomValidator, DEFAULT_SHEET_THEME, type DetectorError, type DetectorErrorCode, type DetectorKind, type DetectorResult, type ExternalReference, type FontFamily, type GeoDetectorOptions, type GeoVerificationContext, MemoryQueueStorage as InMemoryOfflineQueueStorage, InMemoryStorage, IndexedDBAdapter, type IndexedDBAdapterOptions, type IndexedDBOfflineQueueOptions, IndexedDBOfflineQueueStorage, LocalStorageAdapter, type LocalStorageAdapterOptions, type LocalStorageFailureMode, type LocaleDictionary, type LocalizedString, type LocalizedText, type NfcDetectorOptions, type NfcVerificationContext, type OfflineConflict, type OfflineConflictResult, type OfflineOperation, OfflineQueue, type OfflineQueueOptions, type OfflineQueueStorage, type OfflineResult, type OfflineSender, type ParseResult, type PasscodeCondition$1 as PasscodeCondition, type ProcessStampValue, type PublicCheckInCondition, type PublicConfigSafety, type PublicRallyConfig, type PublicReward, type PublicSpotItem, type QrDetectorOptions, type QrVerificationContext, type RallyConfig, type RallySnapshot, type RedemptionMethod, type Result, type Reward, type RewardConsumeError, type RewardState, type RewardStatus, type RewardType, type RewardUnlockCondition, type SecureTokenError, type SecureTokenErrorCode, type SecureTokenOptions, type SecureTokenPayload, type SecureTokenSecretKey, type SecureTokenVerification, type SheetTheme, type SlotShape, type SnapshotSecretKey, type SnapshotTokenError, type SnapshotTokenErrorCode, type SnapshotTokenPayload, type SnapshotTokenVerification, type SpotItem, type StampError, StampRallyClient, type StampRallyProgress, type StampRallyState, type StampRecord, type StampStorage, StorageAdapterError, type StorageAdapterErrorCode, type StorageLike, type StorageOperation, type StorageWarningHandler, type SupportedLocale, type SyncAdapter, type SyncConflictPolicy, type SyncState, THEME_PRESETS, type ThemePreset, type ThemePresetId, type UserRallyState, type ValidationError, type Validator, type VerificationContext, assertPublicConfig, calculateDistanceMeters, calculateProgress, consumeReward, createClaimTicketNumber, createSecureToken, createSignedSnapshotToken, createUniqueClaimTicketNumber, evaluateCondition, evaluateConditionDetailed, exportProgressToken, getCurrentGeoContext, getOrderedSpots, importProgressToken, isGeolocationSupported, isNfcSupported, isPublicConfig, isQrSupported, isRewardState, isStampRallyState, issueClaimTicketNumber, normalizePasscode, parseAdminConfig, parsePublicConfig, processStamp, readNfcContext, readQrContext, reconcileRewardStates, resolveLocalizedText, safeParseAdminConfig, safeParsePublicConfig, sanitizeAdminConfig, storageKey, toLocalizedString, toPublicConfig, updateLocalizedField, validatePublicConfigSafety, verifyPasscode, verifySecureToken, verifySnapshotToken };
720
+ export { type AdminRallyConfig, type CheckInCondition, type CheckInOptions, type CheckInRequest, type CheckInResult, type CheckInSuccess, type ClaimOptions, type ClaimRequest, type ClaimResult, type ClaimSuccess, type ClaimTicketOptions, type ClientError, type ClientEvent, type ClientEventListener, type ClientListener, type ClientOptions, type CompositeConditionFailure, type ConditionMatch, type ConditionMismatch, ConfigValidationError, type ConsumeResult, type ConsumeRewardParams, type CustomValidationContext, type CustomValidator, DEFAULT_SHEET_THEME, type DetectorError, type DetectorErrorCode, type DetectorKind, type DetectorResult, type ExternalReference, type FontFamily, type GeoDetectorOptions, type GeoVerificationContext, MemoryQueueStorage as InMemoryOfflineQueueStorage, InMemoryStorage, IndexedDBAdapter, type IndexedDBAdapterOptions, type IndexedDBOfflineQueueOptions, IndexedDBOfflineQueueStorage, LocalStorageAdapter, type LocalStorageAdapterOptions, type LocalStorageFailureMode, type LocaleDictionary, type LocalizedString, type LocalizedText, type MergeConflictOptions, type NfcDetectorOptions, type NfcVerificationContext, type OfflineConflict, type OfflineConflictResult, type OfflineOperation, OfflineQueue, type OfflineQueueOptions, type OfflineQueueStorage, type OfflineResult, type OfflineSender, type OfflineSyncResultEvent, type OfflineSyncResultListener, type ParseResult, type PasscodeCondition$1 as PasscodeCondition, type ProcessStampValue, type PublicCheckInCondition, type PublicConfigSafety, type PublicRallyConfig, type PublicReward, type PublicSpotItem, type QrDetectorOptions, type QrVerificationContext, type RallyConfig, type RallySnapshot, type RedemptionMethod, type Result, type Reward, type RewardConsumeError, type RewardState, type RewardStatus, type RewardType, type RewardUnlockCondition, type SecureTokenError, type SecureTokenErrorCode, type SecureTokenOptions, type SecureTokenPayload, type SecureTokenSecretKey, type SecureTokenVerification, type SheetTheme, type SlotShape, type SnapshotSecretKey, type SnapshotTokenError, type SnapshotTokenErrorCode, type SnapshotTokenPayload, type SnapshotTokenVerification, type SpotItem, type StampError, StampRallyClient, type StampRallyProgress, type StampRallyState, type StampRecord, type StampStorage, StorageAdapterError, type StorageAdapterErrorCode, type StorageLike, type StorageOperation, type StorageWarningHandler, type SupportedLocale, type SyncAdapter, type SyncConflictPolicy, type SyncState, THEME_PRESETS, type ThemePreset, type ThemePresetId, type UserRallyState, type ValidationError, type Validator, type VerificationContext, assertPublicConfig, calculateDistanceMeters, calculateProgress, consumeReward, createClaimTicketNumber, createSecureToken, createSignedSnapshotToken, createUniqueClaimTicketNumber, evaluateCondition, evaluateConditionDetailed, exportProgressToken, getCurrentGeoContext, getOrderedSpots, importProgressToken, isGeolocationSupported, isNfcSupported, isPublicConfig, isQrSupported, isRewardState, isStampRallyState, issueClaimTicketNumber, normalizePasscode, parseAdminConfig, parsePublicConfig, processStamp, readNfcContext, readQrContext, reconcileRewardStates, resolveLocalizedText, resolveRallyStateConflict, safeParseAdminConfig, safeParsePublicConfig, sanitizeAdminConfig, storageKey, toLocalizedString, toPublicConfig, updateLocalizedField, validatePublicConfigSafety, verifyPasscode, verifySecureToken, verifySnapshotToken };
package/dist/index.js CHANGED
@@ -97,6 +97,47 @@ function issueClaimTicketNumber(reward2, currentState, options = {}) {
97
97
  return { ...currentState, claimTicketNumber };
98
98
  }
99
99
 
100
+ // src/engine/sync.ts
101
+ function latestTimestamp(serverTimestamp, localTimestamp) {
102
+ const serverTime = Date.parse(serverTimestamp);
103
+ const localTime = Date.parse(localTimestamp);
104
+ if (!Number.isNaN(serverTime) && !Number.isNaN(localTime))
105
+ return serverTime >= localTime ? serverTimestamp : localTimestamp;
106
+ if (!Number.isNaN(serverTime)) return serverTimestamp;
107
+ if (!Number.isNaN(localTime)) return localTimestamp;
108
+ return serverTimestamp >= localTimestamp ? serverTimestamp : localTimestamp;
109
+ }
110
+ function mergeRewardStates(serverRewards, localRewards) {
111
+ const merged = new Map(serverRewards.map((reward2) => [reward2.rewardId, reward2]));
112
+ for (const localReward of localRewards) {
113
+ const serverReward = merged.get(localReward.rewardId);
114
+ if (serverReward === void 0) {
115
+ merged.set(localReward.rewardId, localReward);
116
+ continue;
117
+ }
118
+ if (localReward.status === "CONSUMED" && serverReward.status !== "CONSUMED")
119
+ merged.set(localReward.rewardId, localReward);
120
+ }
121
+ return [...merged.values()];
122
+ }
123
+ function resolveRallyStateConflict(serverState, localState, options = { policy: "merge" }) {
124
+ if (options.policy === "server_wins") return serverState;
125
+ const records = [...serverState.records];
126
+ const knownStamps = new Set(records.map((record) => record.stampId));
127
+ for (const record of localState.records) {
128
+ if (!knownStamps.has(record.stampId)) {
129
+ records.push(record);
130
+ knownStamps.add(record.stampId);
131
+ }
132
+ }
133
+ return {
134
+ ...serverState,
135
+ records,
136
+ rewards: mergeRewardStates(serverState.rewards, localState.rewards),
137
+ updatedAt: latestTimestamp(serverState.updatedAt, localState.updatedAt)
138
+ };
139
+ }
140
+
100
141
  // src/detectors/types.ts
101
142
  function createDetectorError(detector, code, message, cause) {
102
143
  return cause === void 0 ? { detector, code, message } : { detector, code, message, cause };
@@ -998,6 +1039,7 @@ var StampRallyClient = class {
998
1039
  this.#storage = this.#options.storage ?? new InMemoryStorage();
999
1040
  this.#userId = this.#options.userId ?? null;
1000
1041
  this.#offlineQueue = this.#options.offlineQueue;
1042
+ this.#offlineQueue?.setSyncResultListener((event) => this.#handleOfflineSyncResult(event));
1001
1043
  }
1002
1044
  getConfig() {
1003
1045
  return this.#config;
@@ -1215,12 +1257,19 @@ var StampRallyClient = class {
1215
1257
  });
1216
1258
  }
1217
1259
  if (adapter?.sync === void 0) {
1218
- this.#emitEvent({ type: "sync", state: current });
1260
+ this.#emitEvent({ type: "sync", state: this.#state ?? current });
1219
1261
  return;
1220
1262
  }
1221
- const next = this.#reconcile(
1222
- await adapter.sync({ rallyId: this.#config.id, userId: this.#userId, state: current })
1223
- );
1263
+ const serverState = await adapter.sync({
1264
+ rallyId: this.#config.id,
1265
+ userId: this.#userId,
1266
+ state: this.#state ?? current
1267
+ });
1268
+ const localState = this.#state ?? current;
1269
+ const merged = this.#offlineQueue === void 0 ? serverState : resolveRallyStateConflict(serverState, localState, {
1270
+ policy: this.#offlineQueue.conflictPolicy
1271
+ });
1272
+ const next = this.#reconcile(merged);
1224
1273
  await this.#storage.save(next);
1225
1274
  this.#state = next;
1226
1275
  this.#emit(next);
@@ -1280,6 +1329,16 @@ var StampRallyClient = class {
1280
1329
  )
1281
1330
  };
1282
1331
  }
1332
+ async #handleOfflineSyncResult(event) {
1333
+ if (event.state !== void 0) {
1334
+ const next = this.#reconcile(event.state);
1335
+ await this.#storage.save(next);
1336
+ this.#state = next;
1337
+ this.#emit(next);
1338
+ }
1339
+ if ("ok" in event.result && !event.result.ok)
1340
+ this.#emitEvent({ type: "error", error: event.result.error });
1341
+ }
1283
1342
  #now() {
1284
1343
  return this.#options.clock?.() ?? (/* @__PURE__ */ new Date()).toISOString();
1285
1344
  }
@@ -1416,6 +1475,7 @@ var OfflineQueue = class {
1416
1475
  #error = null;
1417
1476
  #sender;
1418
1477
  #syncPromise = null;
1478
+ #syncResultListener;
1419
1479
  constructor(options = {}) {
1420
1480
  if (options.storage !== void 0) this.#storage = options.storage;
1421
1481
  else if (options.storageLike !== void 0 && options.storageLike !== null)
@@ -1437,6 +1497,12 @@ var OfflineQueue = class {
1437
1497
  get operations() {
1438
1498
  return this.#operations;
1439
1499
  }
1500
+ get conflictPolicy() {
1501
+ return this.#conflictPolicy;
1502
+ }
1503
+ setSyncResultListener(listener) {
1504
+ this.#syncResultListener = listener;
1505
+ }
1440
1506
  async initialize() {
1441
1507
  if (this.#loaded) return;
1442
1508
  this.#operations = [...await this.#storage.load(this.#key)];
@@ -1489,8 +1555,12 @@ var OfflineQueue = class {
1489
1555
  } catch (cause) {
1490
1556
  throw cause instanceof Error ? cause : new Error(String(cause));
1491
1557
  }
1492
- if ("conflict" in result && result.conflict === true)
1493
- await this.resolveConflict(operation, result.localState, result.serverState);
1558
+ const state = "conflict" in result && result.conflict === true ? await this.resolveConflict(operation, result.localState, result.serverState) : "ok" in result && result.ok ? result.value.state : void 0;
1559
+ await this.#syncResultListener?.({
1560
+ operation,
1561
+ result,
1562
+ ...state === void 0 ? {} : { state }
1563
+ });
1494
1564
  this.#operations = this.#operations.slice(1);
1495
1565
  await this.#storage.save(this.#key, this.#operations);
1496
1566
  }
@@ -1504,9 +1574,7 @@ var OfflineQueue = class {
1504
1574
  async resolveConflict(operation, localState, serverState) {
1505
1575
  const configured = this.#onSyncConflict;
1506
1576
  const policy = (typeof configured === "function" ? await configured({ operation, localState, serverState }) : configured) ?? this.#conflictPolicy;
1507
- if (policy === "merge") {
1508
- return;
1509
- }
1577
+ return resolveRallyStateConflict(serverState, localState, { policy });
1510
1578
  }
1511
1579
  };
1512
1580
 
@@ -2395,6 +2463,6 @@ async function verifySnapshotToken(token, secretKey, now = Date.now()) {
2395
2463
  }
2396
2464
  }
2397
2465
 
2398
- export { ConfigValidationError, DEFAULT_SHEET_THEME, MemoryQueueStorage as InMemoryOfflineQueueStorage, InMemoryStorage, IndexedDBAdapter, IndexedDBOfflineQueueStorage, LocalStorageAdapter, OfflineQueue, StampRallyClient, StorageAdapterError, THEME_PRESETS, assertPublicConfig, calculateDistanceMeters, calculateProgress, consumeReward, createClaimTicketNumber, createSecureToken, createSignedSnapshotToken, createUniqueClaimTicketNumber, evaluateCondition, evaluateConditionDetailed, exportProgressToken, getCurrentGeoContext, getOrderedSpots, importProgressToken, isGeolocationSupported, isNfcSupported, isPublicConfig, isQrSupported, isRewardState, isStampRallyState, issueClaimTicketNumber, normalizePasscode, parseAdminConfig, parsePublicConfig, processStamp, readNfcContext, readQrContext, reconcileRewardStates, resolveLocalizedText, safeParseAdminConfig, safeParsePublicConfig, sanitizeAdminConfig, storageKey, toLocalizedString, toPublicConfig, updateLocalizedField, validatePublicConfigSafety, verifyPasscode, verifySecureToken, verifySnapshotToken };
2466
+ export { ConfigValidationError, DEFAULT_SHEET_THEME, MemoryQueueStorage as InMemoryOfflineQueueStorage, InMemoryStorage, IndexedDBAdapter, IndexedDBOfflineQueueStorage, LocalStorageAdapter, OfflineQueue, StampRallyClient, StorageAdapterError, THEME_PRESETS, assertPublicConfig, calculateDistanceMeters, calculateProgress, consumeReward, createClaimTicketNumber, createSecureToken, createSignedSnapshotToken, createUniqueClaimTicketNumber, evaluateCondition, evaluateConditionDetailed, exportProgressToken, getCurrentGeoContext, getOrderedSpots, importProgressToken, isGeolocationSupported, isNfcSupported, isPublicConfig, isQrSupported, isRewardState, isStampRallyState, issueClaimTicketNumber, normalizePasscode, parseAdminConfig, parsePublicConfig, processStamp, readNfcContext, readQrContext, reconcileRewardStates, resolveLocalizedText, resolveRallyStateConflict, safeParseAdminConfig, safeParsePublicConfig, sanitizeAdminConfig, storageKey, toLocalizedString, toPublicConfig, updateLocalizedField, validatePublicConfigSafety, verifyPasscode, verifySecureToken, verifySnapshotToken };
2399
2467
  //# sourceMappingURL=index.js.map
2400
2468
  //# sourceMappingURL=index.js.map