@stamprally/core 0.15.0 → 0.17.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
@@ -99,6 +99,7 @@ type RedemptionMethod = "manual_slide" | "staff_passcode" | "view_only" | "serve
99
99
  type InventoryAggregationMode = "shared" | "per_reward";
100
100
  type RallyInventory = Readonly<Record<string, number>> & {
101
101
  readonly sharedStock?: number;
102
+ readonly global?: number;
102
103
  };
103
104
  interface RallyInventoryState {
104
105
  readonly sharedRemaining?: number;
@@ -332,8 +333,10 @@ declare function createClaimTicketNumber(rewardId: string, options?: ClaimTicket
332
333
  declare function createUniqueClaimTicketNumber(rewardId: string, issuedAt: string): string;
333
334
  declare function issueClaimTicketNumber(reward: Reward, currentState: RewardState, options?: ClaimTicketOptions): RewardState;
334
335
 
336
+ type ConflictResolutionPolicy = "authoritative_replay" | "server_wins" | "merge";
335
337
  interface MergeConflictOptions {
336
- readonly policy: "server_wins" | "merge";
338
+ /** `merge` is retained for compatibility; synchronization paths use `authoritative_replay`. */
339
+ readonly policy: ConflictResolutionPolicy;
337
340
  }
338
341
  /** Resolves a server/local state conflict without mutating either input state. */
339
342
  declare function resolveRallyStateConflict(serverState: UserRallyState, localState: UserRallyState, options?: MergeConflictOptions): UserRallyState;
@@ -372,20 +375,22 @@ interface ProcessStampValue {
372
375
  }
373
376
  declare function processStamp(state: StampRallyState, config: AdminRallyConfig, spotId: string, context: VerificationContext, now: string): Result<ProcessStampValue, StampError>;
374
377
 
378
+ type QueueOperationStatus = "PENDING" | "IN_FLIGHT" | "ACCEPTED" | "REJECTED_PERMANENT" | "FAILED_RETRYABLE";
379
+ type OfflineQueueCapability = "indexeddb" | "localstorage" | "memory" | "custom";
375
380
  type OfflineOperation = {
376
381
  readonly kind: "checkIn";
377
382
  readonly request: CheckInRequest;
378
- readonly status?: OfflineOperationLifecycleStatus;
383
+ readonly status?: QueueOperationStatus;
379
384
  readonly attempts?: number;
380
385
  } | {
381
386
  readonly kind: "claimReward";
382
387
  readonly request: ClaimRequest;
383
- readonly status?: OfflineOperationLifecycleStatus;
388
+ readonly status?: QueueOperationStatus;
384
389
  readonly attempts?: number;
385
390
  };
386
391
  type OfflineResult = CheckInResult | ClaimResult | UserRallyState;
387
392
  type SyncState = "idle" | "syncing" | "error";
388
- type SyncConflictPolicy = "server_wins" | "merge";
393
+ type SyncConflictPolicy = ConflictResolutionPolicy;
389
394
  interface OfflineConflictResult {
390
395
  readonly conflict: true;
391
396
  readonly localState: UserRallyState;
@@ -394,6 +399,8 @@ interface OfflineConflictResult {
394
399
  interface OfflineQueueStorage {
395
400
  load(key: string): Promise<ReadonlyArray<OfflineOperation>>;
396
401
  save(key: string, operations: ReadonlyArray<OfflineOperation>): Promise<void>;
402
+ loadRejectedHistory?(key: string): Promise<ReadonlyArray<RejectedOperationHistoryEntry>>;
403
+ saveRejectedHistory?(key: string, history: ReadonlyArray<RejectedOperationHistoryEntry>): Promise<void>;
397
404
  }
398
405
  interface OfflineQueueOptions {
399
406
  readonly storage?: OfflineQueueStorage;
@@ -428,13 +435,21 @@ interface OfflineConflict {
428
435
  readonly serverState: UserRallyState;
429
436
  }
430
437
  type OfflineSender = (operation: OfflineOperation) => Promise<OfflineResult | OfflineConflictResult | OfflineOperationResponse>;
431
- type OfflineOperationStatus = "PENDING" | "IN_FLIGHT" | "ACCEPTED" | "REJECTED" | "REJECTED_PERMANENT" | "RETRYABLE_ERROR";
432
- type OfflineOperationLifecycleStatus = "PENDING" | "IN_FLIGHT" | "ACCEPTED" | "REJECTED";
438
+ type OfflineOperationStatus = QueueOperationStatus | "REJECTED" | "RETRYABLE_ERROR";
439
+ type OfflineOperationLifecycleStatus = QueueOperationStatus;
433
440
  interface OfflineOperationError {
434
441
  readonly code: string;
435
442
  readonly message: string;
436
443
  readonly [key: string]: unknown;
437
444
  }
445
+ interface RejectedOperationHistoryEntry {
446
+ readonly operation: OfflineOperation;
447
+ readonly reason: OfflineOperationError;
448
+ readonly errorCode: string;
449
+ readonly rejectedAt: string;
450
+ readonly attempts: number;
451
+ }
452
+ type RejectedOperation = RejectedOperationHistoryEntry;
438
453
  type OfflineOperationResponse = {
439
454
  readonly status: "ACCEPTED";
440
455
  readonly result?: OfflineResult | OfflineConflictResult;
@@ -457,10 +472,15 @@ interface OfflineSyncResultEvent {
457
472
  readonly state?: UserRallyState;
458
473
  }
459
474
  type OfflineSyncResultListener = (event: OfflineSyncResultEvent) => void | Promise<void>;
475
+ type OfflineQueueChangeListener = () => void;
476
+ /** Removes only an operation's optimistic changes without mutating its inputs. */
477
+ declare function rollbackOptimisticOperation(state: UserRallyState, operation: OfflineOperation): UserRallyState;
460
478
  declare class MemoryQueueStorage implements OfflineQueueStorage {
461
479
  #private;
462
480
  load(key: string): Promise<ReadonlyArray<OfflineOperation>>;
463
481
  save(key: string, operations: ReadonlyArray<OfflineOperation>): Promise<void>;
482
+ loadRejectedHistory(key: string): Promise<ReadonlyArray<RejectedOperationHistoryEntry>>;
483
+ saveRejectedHistory(key: string, history: ReadonlyArray<RejectedOperationHistoryEntry>): Promise<void>;
464
484
  }
465
485
  interface IndexedDBOfflineQueueOptions {
466
486
  readonly indexedDB?: IDBFactory | null;
@@ -471,13 +491,18 @@ declare class IndexedDBOfflineQueueStorage implements OfflineQueueStorage {
471
491
  constructor(options?: IndexedDBOfflineQueueOptions);
472
492
  load(key: string): Promise<ReadonlyArray<OfflineOperation>>;
473
493
  save(key: string, operations: ReadonlyArray<OfflineOperation>): Promise<void>;
494
+ loadRejectedHistory(key: string): Promise<ReadonlyArray<RejectedOperationHistoryEntry>>;
495
+ saveRejectedHistory(key: string, history: ReadonlyArray<RejectedOperationHistoryEntry>): Promise<void>;
474
496
  }
497
+ declare function offlineOperationId(operation: OfflineOperation): string;
475
498
  /** Durable, sequential retry queue for operations created while disconnected. */
476
499
  declare class OfflineQueue {
477
500
  #private;
478
501
  constructor(options?: OfflineQueueOptions);
479
502
  get syncState(): SyncState;
480
503
  get pendingCount(): number;
504
+ get queueCapability(): OfflineQueueCapability;
505
+ get rejectedHistory(): ReadonlyArray<RejectedOperationHistoryEntry>;
481
506
  get error(): Error | null;
482
507
  get operations(): ReadonlyArray<OfflineOperation>;
483
508
  get conflictPolicy(): SyncConflictPolicy;
@@ -485,6 +510,7 @@ declare class OfflineQueue {
485
510
  get rallyId(): string | undefined;
486
511
  get userId(): string | null;
487
512
  setSyncResultListener(listener: OfflineSyncResultListener | undefined): void;
513
+ setChangeListener(listener: OfflineQueueChangeListener | undefined): void;
488
514
  initialize(): Promise<void>;
489
515
  /** Releases browser listeners when the queue is no longer used. */
490
516
  dispose(): void;
@@ -496,6 +522,11 @@ declare class OfflineQueue {
496
522
  enqueueCheckIn(request: CheckInRequest): Promise<void>;
497
523
  enqueueClaimReward(request: ClaimRequest): Promise<void>;
498
524
  clear(): Promise<void>;
525
+ discardRejected(operationId: string): Promise<boolean>;
526
+ retryRejected(operationId: string): Promise<boolean>;
527
+ discardRejectedOperation(operationId: string): Promise<boolean>;
528
+ retryRejectedOperation(operationId: string): Promise<boolean>;
529
+ clearRejectedHistory(): Promise<void>;
499
530
  sync(sender?: OfflineSender | undefined): Promise<void>;
500
531
  retrySync(sender?: OfflineSender | undefined): Promise<void>;
501
532
  resolveConflict(operation: OfflineOperation, localState: UserRallyState, serverState: UserRallyState): Promise<UserRallyState>;
@@ -616,12 +647,37 @@ type ClientEvent = {
616
647
  } | {
617
648
  readonly type: "sync";
618
649
  readonly state: UserRallyState;
650
+ } | {
651
+ readonly type: "syncLifecycle";
652
+ readonly event: SyncLifecycleEvent;
619
653
  } | {
620
654
  readonly type: "error";
621
655
  readonly error: ClientError | OfflineOperationError;
622
656
  };
623
657
  type ClientListener = (state: UserRallyState) => void;
624
658
  type ClientEventListener = (event: ClientEvent) => void;
659
+ type SyncLifecycleEvent = {
660
+ readonly type: "SYNC_STARTED";
661
+ } | {
662
+ readonly type: "OPERATION_ACCEPTED";
663
+ readonly operationId: string;
664
+ readonly resourceId: string;
665
+ } | {
666
+ readonly type: "OPERATION_ROLLED_BACK";
667
+ readonly operationId: string;
668
+ readonly resourceId: string;
669
+ readonly reason: string;
670
+ readonly errorCode: string;
671
+ } | {
672
+ readonly type: "OPERATION_RETRYABLE_ERROR";
673
+ readonly operationId: string;
674
+ readonly error: string;
675
+ } | {
676
+ readonly type: "SYNC_COMPLETED";
677
+ readonly totalProcessed: number;
678
+ readonly failedCount: number;
679
+ };
680
+ type SyncEventListener = (event: SyncLifecycleEvent) => void;
625
681
  interface CheckInRequest {
626
682
  readonly rallyId: string;
627
683
  readonly userId: string | null;
@@ -661,6 +717,9 @@ interface ClientOptions {
661
717
  readonly userId?: string | null;
662
718
  readonly anonymousSessionId?: string;
663
719
  readonly offlineQueue?: OfflineQueue;
720
+ readonly conflictResolutionPolicy?: ConflictResolutionPolicy;
721
+ /** Alias for conflictResolutionPolicy. */
722
+ readonly conflictPolicy?: ConflictResolutionPolicy;
664
723
  }
665
724
  type StorageOrOptions = StampStorage | ClientOptions;
666
725
  declare class StampRallyClient {
@@ -672,8 +731,17 @@ declare class StampRallyClient {
672
731
  getAnonymousSessionId(): string;
673
732
  get syncState(): SyncState;
674
733
  get pendingCount(): number;
734
+ get rejectedHistory(): ReadonlyArray<RejectedOperationHistoryEntry>;
735
+ getSyncRevision(): number;
736
+ get queueCapability(): OfflineQueueCapability;
737
+ discardRejected(operationId: string): Promise<boolean>;
738
+ retryRejected(operationId: string): Promise<boolean>;
739
+ dismissRejectedOperation(operationId: string): Promise<boolean>;
740
+ retryOperation(operationId: string): Promise<boolean>;
675
741
  subscribe(listener: ClientListener): () => void;
676
742
  subscribeEvents(listener: ClientEventListener): () => void;
743
+ subscribeSyncEvents(listener: SyncEventListener): () => void;
744
+ subscribeSyncState(listener: () => void): () => void;
677
745
  init(): Promise<UserRallyState>;
678
746
  initialize(): Promise<UserRallyState>;
679
747
  switchUser(newUserId: string | null): Promise<UserRallyState>;
@@ -792,4 +860,4 @@ type SnapshotTokenVerification<T extends SnapshotTokenPayload = SnapshotTokenPay
792
860
  declare function createSignedSnapshotToken<T extends SnapshotTokenPayload>(payload: T, secretKey: SnapshotSecretKey): Promise<string>;
793
861
  declare function verifySnapshotToken<T extends SnapshotTokenPayload = SnapshotTokenPayload>(token: string, secretKey: SnapshotSecretKey, now?: number): Promise<SnapshotTokenVerification<T>>;
794
862
 
795
- 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, type InventoryAggregationMode, LocalStorageAdapter, type LocalStorageAdapterOptions, type LocalStorageFailureMode, type LocaleDictionary, type LocalizedString, type LocalizedText, type MergeConflictOptions, type NfcDetectorOptions, type NfcVerificationContext, type OfflineConflict, type OfflineConflictResult, type OfflineOperation, type OfflineOperationError, type OfflineOperationLifecycleStatus, type OfflineOperationResponse, type OfflineOperationStatus, 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 RallyInventory, type RallyInventoryState, 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 SpotStatus, 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 SyncRetryOptions, type SyncState, THEME_PRESETS, type ThemePreset, type ThemePresetId, type UserRallyState, type ValidationError, type Validator, type VerificationContext, assertPublicConfig, calculateDistanceMeters, calculateProgress, consumeReward, createAnonymousSessionId, createClaimTicketNumber, createSecureToken, createSignedSnapshotToken, createUniqueClaimTicketNumber, evaluateCondition, evaluateConditionDetailed, evaluateSpotStatus, exportProgressToken, getCurrentGeoContext, getOrderedSpots, getSpotStatus, 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, validateRallyConfigRelations, verifyPasscode, verifySecureToken, verifySnapshotToken };
863
+ 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 ConflictResolutionPolicy, 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, type InventoryAggregationMode, LocalStorageAdapter, type LocalStorageAdapterOptions, type LocalStorageFailureMode, type LocaleDictionary, type LocalizedString, type LocalizedText, type MergeConflictOptions, type NfcDetectorOptions, type NfcVerificationContext, type OfflineConflict, type OfflineConflictResult, type OfflineOperation, type OfflineOperationError, type OfflineOperationLifecycleStatus, type OfflineOperationResponse, type OfflineOperationStatus, OfflineQueue, type OfflineQueueCapability, type OfflineQueueChangeListener, 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 QueueOperationStatus, type RallyConfig, type RallyInventory, type RallyInventoryState, type RallySnapshot, type RedemptionMethod, type RejectedOperation, type RejectedOperationHistoryEntry, 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 SpotStatus, 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 SyncEventListener, type SyncLifecycleEvent, type SyncRetryOptions, type SyncState, THEME_PRESETS, type ThemePreset, type ThemePresetId, type UserRallyState, type ValidationError, type Validator, type VerificationContext, assertPublicConfig, calculateDistanceMeters, calculateProgress, consumeReward, createAnonymousSessionId, createClaimTicketNumber, createSecureToken, createSignedSnapshotToken, createUniqueClaimTicketNumber, evaluateCondition, evaluateConditionDetailed, evaluateSpotStatus, exportProgressToken, getCurrentGeoContext, getOrderedSpots, getSpotStatus, importProgressToken, isGeolocationSupported, isNfcSupported, isPublicConfig, isQrSupported, isRewardState, isStampRallyState, issueClaimTicketNumber, normalizePasscode, offlineOperationId, parseAdminConfig, parsePublicConfig, processStamp, readNfcContext, readQrContext, reconcileRewardStates, resolveLocalizedText, resolveRallyStateConflict, rollbackOptimisticOperation, safeParseAdminConfig, safeParsePublicConfig, sanitizeAdminConfig, storageKey, toLocalizedString, toPublicConfig, updateLocalizedField, validatePublicConfigSafety, validateRallyConfigRelations, verifyPasscode, verifySecureToken, verifySnapshotToken };
package/dist/index.d.ts CHANGED
@@ -99,6 +99,7 @@ type RedemptionMethod = "manual_slide" | "staff_passcode" | "view_only" | "serve
99
99
  type InventoryAggregationMode = "shared" | "per_reward";
100
100
  type RallyInventory = Readonly<Record<string, number>> & {
101
101
  readonly sharedStock?: number;
102
+ readonly global?: number;
102
103
  };
103
104
  interface RallyInventoryState {
104
105
  readonly sharedRemaining?: number;
@@ -332,8 +333,10 @@ declare function createClaimTicketNumber(rewardId: string, options?: ClaimTicket
332
333
  declare function createUniqueClaimTicketNumber(rewardId: string, issuedAt: string): string;
333
334
  declare function issueClaimTicketNumber(reward: Reward, currentState: RewardState, options?: ClaimTicketOptions): RewardState;
334
335
 
336
+ type ConflictResolutionPolicy = "authoritative_replay" | "server_wins" | "merge";
335
337
  interface MergeConflictOptions {
336
- readonly policy: "server_wins" | "merge";
338
+ /** `merge` is retained for compatibility; synchronization paths use `authoritative_replay`. */
339
+ readonly policy: ConflictResolutionPolicy;
337
340
  }
338
341
  /** Resolves a server/local state conflict without mutating either input state. */
339
342
  declare function resolveRallyStateConflict(serverState: UserRallyState, localState: UserRallyState, options?: MergeConflictOptions): UserRallyState;
@@ -372,20 +375,22 @@ interface ProcessStampValue {
372
375
  }
373
376
  declare function processStamp(state: StampRallyState, config: AdminRallyConfig, spotId: string, context: VerificationContext, now: string): Result<ProcessStampValue, StampError>;
374
377
 
378
+ type QueueOperationStatus = "PENDING" | "IN_FLIGHT" | "ACCEPTED" | "REJECTED_PERMANENT" | "FAILED_RETRYABLE";
379
+ type OfflineQueueCapability = "indexeddb" | "localstorage" | "memory" | "custom";
375
380
  type OfflineOperation = {
376
381
  readonly kind: "checkIn";
377
382
  readonly request: CheckInRequest;
378
- readonly status?: OfflineOperationLifecycleStatus;
383
+ readonly status?: QueueOperationStatus;
379
384
  readonly attempts?: number;
380
385
  } | {
381
386
  readonly kind: "claimReward";
382
387
  readonly request: ClaimRequest;
383
- readonly status?: OfflineOperationLifecycleStatus;
388
+ readonly status?: QueueOperationStatus;
384
389
  readonly attempts?: number;
385
390
  };
386
391
  type OfflineResult = CheckInResult | ClaimResult | UserRallyState;
387
392
  type SyncState = "idle" | "syncing" | "error";
388
- type SyncConflictPolicy = "server_wins" | "merge";
393
+ type SyncConflictPolicy = ConflictResolutionPolicy;
389
394
  interface OfflineConflictResult {
390
395
  readonly conflict: true;
391
396
  readonly localState: UserRallyState;
@@ -394,6 +399,8 @@ interface OfflineConflictResult {
394
399
  interface OfflineQueueStorage {
395
400
  load(key: string): Promise<ReadonlyArray<OfflineOperation>>;
396
401
  save(key: string, operations: ReadonlyArray<OfflineOperation>): Promise<void>;
402
+ loadRejectedHistory?(key: string): Promise<ReadonlyArray<RejectedOperationHistoryEntry>>;
403
+ saveRejectedHistory?(key: string, history: ReadonlyArray<RejectedOperationHistoryEntry>): Promise<void>;
397
404
  }
398
405
  interface OfflineQueueOptions {
399
406
  readonly storage?: OfflineQueueStorage;
@@ -428,13 +435,21 @@ interface OfflineConflict {
428
435
  readonly serverState: UserRallyState;
429
436
  }
430
437
  type OfflineSender = (operation: OfflineOperation) => Promise<OfflineResult | OfflineConflictResult | OfflineOperationResponse>;
431
- type OfflineOperationStatus = "PENDING" | "IN_FLIGHT" | "ACCEPTED" | "REJECTED" | "REJECTED_PERMANENT" | "RETRYABLE_ERROR";
432
- type OfflineOperationLifecycleStatus = "PENDING" | "IN_FLIGHT" | "ACCEPTED" | "REJECTED";
438
+ type OfflineOperationStatus = QueueOperationStatus | "REJECTED" | "RETRYABLE_ERROR";
439
+ type OfflineOperationLifecycleStatus = QueueOperationStatus;
433
440
  interface OfflineOperationError {
434
441
  readonly code: string;
435
442
  readonly message: string;
436
443
  readonly [key: string]: unknown;
437
444
  }
445
+ interface RejectedOperationHistoryEntry {
446
+ readonly operation: OfflineOperation;
447
+ readonly reason: OfflineOperationError;
448
+ readonly errorCode: string;
449
+ readonly rejectedAt: string;
450
+ readonly attempts: number;
451
+ }
452
+ type RejectedOperation = RejectedOperationHistoryEntry;
438
453
  type OfflineOperationResponse = {
439
454
  readonly status: "ACCEPTED";
440
455
  readonly result?: OfflineResult | OfflineConflictResult;
@@ -457,10 +472,15 @@ interface OfflineSyncResultEvent {
457
472
  readonly state?: UserRallyState;
458
473
  }
459
474
  type OfflineSyncResultListener = (event: OfflineSyncResultEvent) => void | Promise<void>;
475
+ type OfflineQueueChangeListener = () => void;
476
+ /** Removes only an operation's optimistic changes without mutating its inputs. */
477
+ declare function rollbackOptimisticOperation(state: UserRallyState, operation: OfflineOperation): UserRallyState;
460
478
  declare class MemoryQueueStorage implements OfflineQueueStorage {
461
479
  #private;
462
480
  load(key: string): Promise<ReadonlyArray<OfflineOperation>>;
463
481
  save(key: string, operations: ReadonlyArray<OfflineOperation>): Promise<void>;
482
+ loadRejectedHistory(key: string): Promise<ReadonlyArray<RejectedOperationHistoryEntry>>;
483
+ saveRejectedHistory(key: string, history: ReadonlyArray<RejectedOperationHistoryEntry>): Promise<void>;
464
484
  }
465
485
  interface IndexedDBOfflineQueueOptions {
466
486
  readonly indexedDB?: IDBFactory | null;
@@ -471,13 +491,18 @@ declare class IndexedDBOfflineQueueStorage implements OfflineQueueStorage {
471
491
  constructor(options?: IndexedDBOfflineQueueOptions);
472
492
  load(key: string): Promise<ReadonlyArray<OfflineOperation>>;
473
493
  save(key: string, operations: ReadonlyArray<OfflineOperation>): Promise<void>;
494
+ loadRejectedHistory(key: string): Promise<ReadonlyArray<RejectedOperationHistoryEntry>>;
495
+ saveRejectedHistory(key: string, history: ReadonlyArray<RejectedOperationHistoryEntry>): Promise<void>;
474
496
  }
497
+ declare function offlineOperationId(operation: OfflineOperation): string;
475
498
  /** Durable, sequential retry queue for operations created while disconnected. */
476
499
  declare class OfflineQueue {
477
500
  #private;
478
501
  constructor(options?: OfflineQueueOptions);
479
502
  get syncState(): SyncState;
480
503
  get pendingCount(): number;
504
+ get queueCapability(): OfflineQueueCapability;
505
+ get rejectedHistory(): ReadonlyArray<RejectedOperationHistoryEntry>;
481
506
  get error(): Error | null;
482
507
  get operations(): ReadonlyArray<OfflineOperation>;
483
508
  get conflictPolicy(): SyncConflictPolicy;
@@ -485,6 +510,7 @@ declare class OfflineQueue {
485
510
  get rallyId(): string | undefined;
486
511
  get userId(): string | null;
487
512
  setSyncResultListener(listener: OfflineSyncResultListener | undefined): void;
513
+ setChangeListener(listener: OfflineQueueChangeListener | undefined): void;
488
514
  initialize(): Promise<void>;
489
515
  /** Releases browser listeners when the queue is no longer used. */
490
516
  dispose(): void;
@@ -496,6 +522,11 @@ declare class OfflineQueue {
496
522
  enqueueCheckIn(request: CheckInRequest): Promise<void>;
497
523
  enqueueClaimReward(request: ClaimRequest): Promise<void>;
498
524
  clear(): Promise<void>;
525
+ discardRejected(operationId: string): Promise<boolean>;
526
+ retryRejected(operationId: string): Promise<boolean>;
527
+ discardRejectedOperation(operationId: string): Promise<boolean>;
528
+ retryRejectedOperation(operationId: string): Promise<boolean>;
529
+ clearRejectedHistory(): Promise<void>;
499
530
  sync(sender?: OfflineSender | undefined): Promise<void>;
500
531
  retrySync(sender?: OfflineSender | undefined): Promise<void>;
501
532
  resolveConflict(operation: OfflineOperation, localState: UserRallyState, serverState: UserRallyState): Promise<UserRallyState>;
@@ -616,12 +647,37 @@ type ClientEvent = {
616
647
  } | {
617
648
  readonly type: "sync";
618
649
  readonly state: UserRallyState;
650
+ } | {
651
+ readonly type: "syncLifecycle";
652
+ readonly event: SyncLifecycleEvent;
619
653
  } | {
620
654
  readonly type: "error";
621
655
  readonly error: ClientError | OfflineOperationError;
622
656
  };
623
657
  type ClientListener = (state: UserRallyState) => void;
624
658
  type ClientEventListener = (event: ClientEvent) => void;
659
+ type SyncLifecycleEvent = {
660
+ readonly type: "SYNC_STARTED";
661
+ } | {
662
+ readonly type: "OPERATION_ACCEPTED";
663
+ readonly operationId: string;
664
+ readonly resourceId: string;
665
+ } | {
666
+ readonly type: "OPERATION_ROLLED_BACK";
667
+ readonly operationId: string;
668
+ readonly resourceId: string;
669
+ readonly reason: string;
670
+ readonly errorCode: string;
671
+ } | {
672
+ readonly type: "OPERATION_RETRYABLE_ERROR";
673
+ readonly operationId: string;
674
+ readonly error: string;
675
+ } | {
676
+ readonly type: "SYNC_COMPLETED";
677
+ readonly totalProcessed: number;
678
+ readonly failedCount: number;
679
+ };
680
+ type SyncEventListener = (event: SyncLifecycleEvent) => void;
625
681
  interface CheckInRequest {
626
682
  readonly rallyId: string;
627
683
  readonly userId: string | null;
@@ -661,6 +717,9 @@ interface ClientOptions {
661
717
  readonly userId?: string | null;
662
718
  readonly anonymousSessionId?: string;
663
719
  readonly offlineQueue?: OfflineQueue;
720
+ readonly conflictResolutionPolicy?: ConflictResolutionPolicy;
721
+ /** Alias for conflictResolutionPolicy. */
722
+ readonly conflictPolicy?: ConflictResolutionPolicy;
664
723
  }
665
724
  type StorageOrOptions = StampStorage | ClientOptions;
666
725
  declare class StampRallyClient {
@@ -672,8 +731,17 @@ declare class StampRallyClient {
672
731
  getAnonymousSessionId(): string;
673
732
  get syncState(): SyncState;
674
733
  get pendingCount(): number;
734
+ get rejectedHistory(): ReadonlyArray<RejectedOperationHistoryEntry>;
735
+ getSyncRevision(): number;
736
+ get queueCapability(): OfflineQueueCapability;
737
+ discardRejected(operationId: string): Promise<boolean>;
738
+ retryRejected(operationId: string): Promise<boolean>;
739
+ dismissRejectedOperation(operationId: string): Promise<boolean>;
740
+ retryOperation(operationId: string): Promise<boolean>;
675
741
  subscribe(listener: ClientListener): () => void;
676
742
  subscribeEvents(listener: ClientEventListener): () => void;
743
+ subscribeSyncEvents(listener: SyncEventListener): () => void;
744
+ subscribeSyncState(listener: () => void): () => void;
677
745
  init(): Promise<UserRallyState>;
678
746
  initialize(): Promise<UserRallyState>;
679
747
  switchUser(newUserId: string | null): Promise<UserRallyState>;
@@ -792,4 +860,4 @@ type SnapshotTokenVerification<T extends SnapshotTokenPayload = SnapshotTokenPay
792
860
  declare function createSignedSnapshotToken<T extends SnapshotTokenPayload>(payload: T, secretKey: SnapshotSecretKey): Promise<string>;
793
861
  declare function verifySnapshotToken<T extends SnapshotTokenPayload = SnapshotTokenPayload>(token: string, secretKey: SnapshotSecretKey, now?: number): Promise<SnapshotTokenVerification<T>>;
794
862
 
795
- 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, type InventoryAggregationMode, LocalStorageAdapter, type LocalStorageAdapterOptions, type LocalStorageFailureMode, type LocaleDictionary, type LocalizedString, type LocalizedText, type MergeConflictOptions, type NfcDetectorOptions, type NfcVerificationContext, type OfflineConflict, type OfflineConflictResult, type OfflineOperation, type OfflineOperationError, type OfflineOperationLifecycleStatus, type OfflineOperationResponse, type OfflineOperationStatus, 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 RallyInventory, type RallyInventoryState, 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 SpotStatus, 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 SyncRetryOptions, type SyncState, THEME_PRESETS, type ThemePreset, type ThemePresetId, type UserRallyState, type ValidationError, type Validator, type VerificationContext, assertPublicConfig, calculateDistanceMeters, calculateProgress, consumeReward, createAnonymousSessionId, createClaimTicketNumber, createSecureToken, createSignedSnapshotToken, createUniqueClaimTicketNumber, evaluateCondition, evaluateConditionDetailed, evaluateSpotStatus, exportProgressToken, getCurrentGeoContext, getOrderedSpots, getSpotStatus, 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, validateRallyConfigRelations, verifyPasscode, verifySecureToken, verifySnapshotToken };
863
+ 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 ConflictResolutionPolicy, 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, type InventoryAggregationMode, LocalStorageAdapter, type LocalStorageAdapterOptions, type LocalStorageFailureMode, type LocaleDictionary, type LocalizedString, type LocalizedText, type MergeConflictOptions, type NfcDetectorOptions, type NfcVerificationContext, type OfflineConflict, type OfflineConflictResult, type OfflineOperation, type OfflineOperationError, type OfflineOperationLifecycleStatus, type OfflineOperationResponse, type OfflineOperationStatus, OfflineQueue, type OfflineQueueCapability, type OfflineQueueChangeListener, 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 QueueOperationStatus, type RallyConfig, type RallyInventory, type RallyInventoryState, type RallySnapshot, type RedemptionMethod, type RejectedOperation, type RejectedOperationHistoryEntry, 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 SpotStatus, 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 SyncEventListener, type SyncLifecycleEvent, type SyncRetryOptions, type SyncState, THEME_PRESETS, type ThemePreset, type ThemePresetId, type UserRallyState, type ValidationError, type Validator, type VerificationContext, assertPublicConfig, calculateDistanceMeters, calculateProgress, consumeReward, createAnonymousSessionId, createClaimTicketNumber, createSecureToken, createSignedSnapshotToken, createUniqueClaimTicketNumber, evaluateCondition, evaluateConditionDetailed, evaluateSpotStatus, exportProgressToken, getCurrentGeoContext, getOrderedSpots, getSpotStatus, importProgressToken, isGeolocationSupported, isNfcSupported, isPublicConfig, isQrSupported, isRewardState, isStampRallyState, issueClaimTicketNumber, normalizePasscode, offlineOperationId, parseAdminConfig, parsePublicConfig, processStamp, readNfcContext, readQrContext, reconcileRewardStates, resolveLocalizedText, resolveRallyStateConflict, rollbackOptimisticOperation, safeParseAdminConfig, safeParsePublicConfig, sanitizeAdminConfig, storageKey, toLocalizedString, toPublicConfig, updateLocalizedField, validatePublicConfigSafety, validateRallyConfigRelations, verifyPasscode, verifySecureToken, verifySnapshotToken };