@stamprally/core 0.16.0 → 0.18.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
@@ -117,6 +117,10 @@ interface Reward<TLocale extends string = string> {
117
117
  readonly validUntil?: string;
118
118
  readonly stockLimit?: number;
119
119
  readonly userClaimLimit?: number;
120
+ /** Explicit primary inventory key. Defaults to the reward id. */
121
+ readonly stockKey?: string;
122
+ /** Optional second inventory bucket that must be decremented atomically. */
123
+ readonly secondaryStockKey?: string;
120
124
  }
121
125
  type PublicReward<TLocale extends string = string> = Omit<Reward<TLocale>, "digitalContentUrl" | "staffPasscode">;
122
126
  interface AdminRallyConfig<TLocale extends string = string, TMeta extends Record<string, unknown> = Record<string, unknown>> {
@@ -332,11 +336,9 @@ declare function createClaimTicketNumber(rewardId: string, options?: ClaimTicket
332
336
  declare function createUniqueClaimTicketNumber(rewardId: string, issuedAt: string): string;
333
337
  declare function issueClaimTicketNumber(reward: Reward, currentState: RewardState, options?: ClaimTicketOptions): RewardState;
334
338
 
335
- interface MergeConflictOptions {
336
- readonly policy: "server_wins" | "merge";
337
- }
338
- /** Resolves a server/local state conflict without mutating either input state. */
339
- declare function resolveRallyStateConflict(serverState: UserRallyState, localState: UserRallyState, options?: MergeConflictOptions): UserRallyState;
339
+ type ConflictResolutionPolicy = "authoritative_replay";
340
+ /** Uses the server snapshot as the immutable replay baseline. */
341
+ declare function resolveRallyStateConflict(serverState: UserRallyState, _localState: UserRallyState): UserRallyState;
340
342
 
341
343
  type RewardConsumeError = {
342
344
  readonly code: "NOT_AVAILABLE" | "ALREADY_CONSUMED" | "OUT_OF_STOCK" | "REWARD_NOT_FOUND";
@@ -374,20 +376,23 @@ declare function processStamp(state: StampRallyState, config: AdminRallyConfig,
374
376
 
375
377
  type QueueOperationStatus = "PENDING" | "IN_FLIGHT" | "ACCEPTED" | "REJECTED_PERMANENT" | "FAILED_RETRYABLE";
376
378
  type OfflineQueueCapability = "indexeddb" | "localstorage" | "memory" | "custom";
379
+ type OfflineStorageCapability = OfflineQueueCapability | "volatile_single_tab";
377
380
  type OfflineOperation = {
378
381
  readonly kind: "checkIn";
379
382
  readonly request: CheckInRequest;
383
+ readonly optimisticState?: UserRallyState;
380
384
  readonly status?: QueueOperationStatus;
381
385
  readonly attempts?: number;
382
386
  } | {
383
387
  readonly kind: "claimReward";
384
388
  readonly request: ClaimRequest;
389
+ readonly optimisticState?: UserRallyState;
385
390
  readonly status?: QueueOperationStatus;
386
391
  readonly attempts?: number;
387
392
  };
388
393
  type OfflineResult = CheckInResult | ClaimResult | UserRallyState;
389
394
  type SyncState = "idle" | "syncing" | "error";
390
- type SyncConflictPolicy = "server_wins" | "merge";
395
+ type SyncConflictPolicy = "authoritative_replay";
391
396
  interface OfflineConflictResult {
392
397
  readonly conflict: true;
393
398
  readonly localState: UserRallyState;
@@ -399,6 +404,12 @@ interface OfflineQueueStorage {
399
404
  loadRejectedHistory?(key: string): Promise<ReadonlyArray<RejectedOperationHistoryEntry>>;
400
405
  saveRejectedHistory?(key: string, history: ReadonlyArray<RejectedOperationHistoryEntry>): Promise<void>;
401
406
  }
407
+ interface OfflineQueueCapabilityWarning {
408
+ readonly type: "STORAGE_CAPABILITY_WARNING";
409
+ readonly storageCapability: "volatile_single_tab" | "memory";
410
+ readonly isStoragePersistent: boolean;
411
+ readonly message: string;
412
+ }
402
413
  interface OfflineQueueOptions {
403
414
  readonly storage?: OfflineQueueStorage;
404
415
  readonly storageLike?: {
@@ -412,8 +423,6 @@ interface OfflineQueueOptions {
412
423
  /** User scope used by the default durable key. */
413
424
  readonly userId?: string | null;
414
425
  readonly databaseName?: string;
415
- readonly conflictPolicy?: SyncConflictPolicy;
416
- readonly onSyncConflict?: SyncConflictPolicy | ((context: OfflineConflict) => SyncConflictPolicy | Promise<SyncConflictPolicy>);
417
426
  /** Enables storage-event/BroadcastChannel synchronization when browser APIs exist. */
418
427
  readonly synchronizeInstances?: boolean;
419
428
  readonly retryOptions?: SyncRetryOptions;
@@ -426,11 +435,6 @@ interface SyncRetryOptions {
426
435
  readonly initialIntervalMs?: number;
427
436
  readonly backoffMultiplier?: number;
428
437
  }
429
- interface OfflineConflict {
430
- readonly operation: OfflineOperation;
431
- readonly localState: UserRallyState;
432
- readonly serverState: UserRallyState;
433
- }
434
438
  type OfflineSender = (operation: OfflineOperation) => Promise<OfflineResult | OfflineConflictResult | OfflineOperationResponse>;
435
439
  type OfflineOperationStatus = QueueOperationStatus | "REJECTED" | "RETRYABLE_ERROR";
436
440
  type OfflineOperationLifecycleStatus = QueueOperationStatus;
@@ -469,6 +473,9 @@ interface OfflineSyncResultEvent {
469
473
  readonly state?: UserRallyState;
470
474
  }
471
475
  type OfflineSyncResultListener = (event: OfflineSyncResultEvent) => void | Promise<void>;
476
+ type OfflineQueueChangeListener = () => void;
477
+ /** Removes only an operation's optimistic changes without mutating its inputs. */
478
+ declare function rollbackOptimisticOperation(state: UserRallyState, operation: OfflineOperation): UserRallyState;
472
479
  declare class MemoryQueueStorage implements OfflineQueueStorage {
473
480
  #private;
474
481
  load(key: string): Promise<ReadonlyArray<OfflineOperation>>;
@@ -496,14 +503,18 @@ declare class OfflineQueue {
496
503
  get syncState(): SyncState;
497
504
  get pendingCount(): number;
498
505
  get queueCapability(): OfflineQueueCapability;
506
+ get storageCapability(): OfflineStorageCapability;
507
+ get isStoragePersistent(): boolean;
499
508
  get rejectedHistory(): ReadonlyArray<RejectedOperationHistoryEntry>;
500
509
  get error(): Error | null;
501
510
  get operations(): ReadonlyArray<OfflineOperation>;
502
- get conflictPolicy(): SyncConflictPolicy;
503
511
  get storageKey(): string;
504
512
  get rallyId(): string | undefined;
505
513
  get userId(): string | null;
506
514
  setSyncResultListener(listener: OfflineSyncResultListener | undefined): void;
515
+ setChangeListener(listener: OfflineQueueChangeListener | undefined): void;
516
+ setCapabilityWarningListener(listener: ((event: OfflineQueueCapabilityWarning) => void) | undefined): void;
517
+ setReplayConfig(config: AdminRallyConfig | PublicRallyConfig): void;
507
518
  initialize(): Promise<void>;
508
519
  /** Releases browser listeners when the queue is no longer used. */
509
520
  dispose(): void;
@@ -512,8 +523,8 @@ declare class OfflineQueue {
512
523
  switchUser(newUserId: string | null): Promise<void>;
513
524
  setSender(sender: OfflineSender): void;
514
525
  enqueue(operation: OfflineOperation): Promise<void>;
515
- enqueueCheckIn(request: CheckInRequest): Promise<void>;
516
- enqueueClaimReward(request: ClaimRequest): Promise<void>;
526
+ enqueueCheckIn(request: CheckInRequest, optimisticState?: UserRallyState): Promise<void>;
527
+ enqueueClaimReward(request: ClaimRequest, optimisticState?: UserRallyState): Promise<void>;
517
528
  clear(): Promise<void>;
518
529
  discardRejected(operationId: string): Promise<boolean>;
519
530
  retryRejected(operationId: string): Promise<boolean>;
@@ -522,7 +533,6 @@ declare class OfflineQueue {
522
533
  clearRejectedHistory(): Promise<void>;
523
534
  sync(sender?: OfflineSender | undefined): Promise<void>;
524
535
  retrySync(sender?: OfflineSender | undefined): Promise<void>;
525
- resolveConflict(operation: OfflineOperation, localState: UserRallyState, serverState: UserRallyState): Promise<UserRallyState>;
526
536
  }
527
537
 
528
538
  interface StampStorage {
@@ -640,12 +650,37 @@ type ClientEvent = {
640
650
  } | {
641
651
  readonly type: "sync";
642
652
  readonly state: UserRallyState;
653
+ } | {
654
+ readonly type: "syncLifecycle";
655
+ readonly event: SyncLifecycleEvent;
643
656
  } | {
644
657
  readonly type: "error";
645
658
  readonly error: ClientError | OfflineOperationError;
646
659
  };
647
660
  type ClientListener = (state: UserRallyState) => void;
648
661
  type ClientEventListener = (event: ClientEvent) => void;
662
+ type SyncLifecycleEvent = {
663
+ readonly type: "SYNC_STARTED";
664
+ } | {
665
+ readonly type: "OPERATION_ACCEPTED";
666
+ readonly operationId: string;
667
+ readonly resourceId: string;
668
+ } | {
669
+ readonly type: "OPERATION_ROLLED_BACK";
670
+ readonly operationId: string;
671
+ readonly resourceId: string;
672
+ readonly reason: string;
673
+ readonly errorCode: string;
674
+ } | {
675
+ readonly type: "OPERATION_RETRYABLE_ERROR";
676
+ readonly operationId: string;
677
+ readonly error: string;
678
+ } | {
679
+ readonly type: "SYNC_COMPLETED";
680
+ readonly totalProcessed: number;
681
+ readonly failedCount: number;
682
+ };
683
+ type SyncEventListener = (event: SyncLifecycleEvent) => void;
649
684
  interface CheckInRequest {
650
685
  readonly rallyId: string;
651
686
  readonly userId: string | null;
@@ -697,11 +732,18 @@ declare class StampRallyClient {
697
732
  get syncState(): SyncState;
698
733
  get pendingCount(): number;
699
734
  get rejectedHistory(): ReadonlyArray<RejectedOperationHistoryEntry>;
735
+ getSyncRevision(): number;
700
736
  get queueCapability(): OfflineQueueCapability;
737
+ get storageCapability(): OfflineStorageCapability;
738
+ get isStoragePersistent(): boolean;
701
739
  discardRejected(operationId: string): Promise<boolean>;
702
740
  retryRejected(operationId: string): Promise<boolean>;
741
+ dismissRejectedOperation(operationId: string): Promise<boolean>;
742
+ retryOperation(operationId: string): Promise<boolean>;
703
743
  subscribe(listener: ClientListener): () => void;
704
744
  subscribeEvents(listener: ClientEventListener): () => void;
745
+ subscribeSyncEvents(listener: SyncEventListener): () => void;
746
+ subscribeSyncState(listener: () => void): () => void;
705
747
  init(): Promise<UserRallyState>;
706
748
  initialize(): Promise<UserRallyState>;
707
749
  switchUser(newUserId: string | null): Promise<UserRallyState>;
@@ -715,6 +757,20 @@ declare class StampRallyClient {
715
757
  restore(state: UserRallyState): Promise<UserRallyState>;
716
758
  }
717
759
 
760
+ interface RebuildUserStateOptions {
761
+ readonly baseline: UserRallyState;
762
+ readonly operations: ReadonlyArray<OfflineOperation>;
763
+ readonly config?: AdminRallyConfig | PublicRallyConfig;
764
+ }
765
+ interface RebuildUserStateResult {
766
+ readonly state: UserRallyState;
767
+ readonly rejectedOperationIds: ReadonlyArray<string>;
768
+ }
769
+ /** Replays the durable operation log on top of a server-confirmed baseline. */
770
+ declare function rebuildUserStateFromLog(baseline: UserRallyState, operations: ReadonlyArray<OfflineOperation>, config?: AdminRallyConfig | PublicRallyConfig): UserRallyState;
771
+ declare function rebuildUserStateFromLog(options: RebuildUserStateOptions): UserRallyState;
772
+ declare function rebuildUserStateLog(baseline: UserRallyState, operations: ReadonlyArray<OfflineOperation>, config?: AdminRallyConfig | PublicRallyConfig): RebuildUserStateResult;
773
+
718
774
  type SecureTokenSecretKey = string | Uint8Array;
719
775
  interface SecureTokenOptions {
720
776
  readonly encrypt?: boolean;
@@ -820,4 +876,4 @@ type SnapshotTokenVerification<T extends SnapshotTokenPayload = SnapshotTokenPay
820
876
  declare function createSignedSnapshotToken<T extends SnapshotTokenPayload>(payload: T, secretKey: SnapshotSecretKey): Promise<string>;
821
877
  declare function verifySnapshotToken<T extends SnapshotTokenPayload = SnapshotTokenPayload>(token: string, secretKey: SnapshotSecretKey, now?: number): Promise<SnapshotTokenVerification<T>>;
822
878
 
823
- 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 OfflineQueueCapability, 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 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, safeParseAdminConfig, safeParsePublicConfig, sanitizeAdminConfig, storageKey, toLocalizedString, toPublicConfig, updateLocalizedField, validatePublicConfigSafety, validateRallyConfigRelations, verifyPasscode, verifySecureToken, verifySnapshotToken };
879
+ 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 NfcDetectorOptions, type NfcVerificationContext, type OfflineConflictResult, type OfflineOperation, type OfflineOperationError, type OfflineOperationLifecycleStatus, type OfflineOperationResponse, type OfflineOperationStatus, OfflineQueue, type OfflineQueueCapability, type OfflineQueueCapabilityWarning, type OfflineQueueChangeListener, type OfflineQueueOptions, type OfflineQueueStorage, type OfflineResult, type OfflineSender, type OfflineStorageCapability, 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 RebuildUserStateOptions, type RebuildUserStateResult, 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, rebuildUserStateFromLog, rebuildUserStateLog, reconcileRewardStates, resolveLocalizedText, resolveRallyStateConflict, rollbackOptimisticOperation, safeParseAdminConfig, safeParsePublicConfig, sanitizeAdminConfig, storageKey, toLocalizedString, toPublicConfig, updateLocalizedField, validatePublicConfigSafety, validateRallyConfigRelations, verifyPasscode, verifySecureToken, verifySnapshotToken };
package/dist/index.d.ts CHANGED
@@ -117,6 +117,10 @@ interface Reward<TLocale extends string = string> {
117
117
  readonly validUntil?: string;
118
118
  readonly stockLimit?: number;
119
119
  readonly userClaimLimit?: number;
120
+ /** Explicit primary inventory key. Defaults to the reward id. */
121
+ readonly stockKey?: string;
122
+ /** Optional second inventory bucket that must be decremented atomically. */
123
+ readonly secondaryStockKey?: string;
120
124
  }
121
125
  type PublicReward<TLocale extends string = string> = Omit<Reward<TLocale>, "digitalContentUrl" | "staffPasscode">;
122
126
  interface AdminRallyConfig<TLocale extends string = string, TMeta extends Record<string, unknown> = Record<string, unknown>> {
@@ -332,11 +336,9 @@ declare function createClaimTicketNumber(rewardId: string, options?: ClaimTicket
332
336
  declare function createUniqueClaimTicketNumber(rewardId: string, issuedAt: string): string;
333
337
  declare function issueClaimTicketNumber(reward: Reward, currentState: RewardState, options?: ClaimTicketOptions): RewardState;
334
338
 
335
- interface MergeConflictOptions {
336
- readonly policy: "server_wins" | "merge";
337
- }
338
- /** Resolves a server/local state conflict without mutating either input state. */
339
- declare function resolveRallyStateConflict(serverState: UserRallyState, localState: UserRallyState, options?: MergeConflictOptions): UserRallyState;
339
+ type ConflictResolutionPolicy = "authoritative_replay";
340
+ /** Uses the server snapshot as the immutable replay baseline. */
341
+ declare function resolveRallyStateConflict(serverState: UserRallyState, _localState: UserRallyState): UserRallyState;
340
342
 
341
343
  type RewardConsumeError = {
342
344
  readonly code: "NOT_AVAILABLE" | "ALREADY_CONSUMED" | "OUT_OF_STOCK" | "REWARD_NOT_FOUND";
@@ -374,20 +376,23 @@ declare function processStamp(state: StampRallyState, config: AdminRallyConfig,
374
376
 
375
377
  type QueueOperationStatus = "PENDING" | "IN_FLIGHT" | "ACCEPTED" | "REJECTED_PERMANENT" | "FAILED_RETRYABLE";
376
378
  type OfflineQueueCapability = "indexeddb" | "localstorage" | "memory" | "custom";
379
+ type OfflineStorageCapability = OfflineQueueCapability | "volatile_single_tab";
377
380
  type OfflineOperation = {
378
381
  readonly kind: "checkIn";
379
382
  readonly request: CheckInRequest;
383
+ readonly optimisticState?: UserRallyState;
380
384
  readonly status?: QueueOperationStatus;
381
385
  readonly attempts?: number;
382
386
  } | {
383
387
  readonly kind: "claimReward";
384
388
  readonly request: ClaimRequest;
389
+ readonly optimisticState?: UserRallyState;
385
390
  readonly status?: QueueOperationStatus;
386
391
  readonly attempts?: number;
387
392
  };
388
393
  type OfflineResult = CheckInResult | ClaimResult | UserRallyState;
389
394
  type SyncState = "idle" | "syncing" | "error";
390
- type SyncConflictPolicy = "server_wins" | "merge";
395
+ type SyncConflictPolicy = "authoritative_replay";
391
396
  interface OfflineConflictResult {
392
397
  readonly conflict: true;
393
398
  readonly localState: UserRallyState;
@@ -399,6 +404,12 @@ interface OfflineQueueStorage {
399
404
  loadRejectedHistory?(key: string): Promise<ReadonlyArray<RejectedOperationHistoryEntry>>;
400
405
  saveRejectedHistory?(key: string, history: ReadonlyArray<RejectedOperationHistoryEntry>): Promise<void>;
401
406
  }
407
+ interface OfflineQueueCapabilityWarning {
408
+ readonly type: "STORAGE_CAPABILITY_WARNING";
409
+ readonly storageCapability: "volatile_single_tab" | "memory";
410
+ readonly isStoragePersistent: boolean;
411
+ readonly message: string;
412
+ }
402
413
  interface OfflineQueueOptions {
403
414
  readonly storage?: OfflineQueueStorage;
404
415
  readonly storageLike?: {
@@ -412,8 +423,6 @@ interface OfflineQueueOptions {
412
423
  /** User scope used by the default durable key. */
413
424
  readonly userId?: string | null;
414
425
  readonly databaseName?: string;
415
- readonly conflictPolicy?: SyncConflictPolicy;
416
- readonly onSyncConflict?: SyncConflictPolicy | ((context: OfflineConflict) => SyncConflictPolicy | Promise<SyncConflictPolicy>);
417
426
  /** Enables storage-event/BroadcastChannel synchronization when browser APIs exist. */
418
427
  readonly synchronizeInstances?: boolean;
419
428
  readonly retryOptions?: SyncRetryOptions;
@@ -426,11 +435,6 @@ interface SyncRetryOptions {
426
435
  readonly initialIntervalMs?: number;
427
436
  readonly backoffMultiplier?: number;
428
437
  }
429
- interface OfflineConflict {
430
- readonly operation: OfflineOperation;
431
- readonly localState: UserRallyState;
432
- readonly serverState: UserRallyState;
433
- }
434
438
  type OfflineSender = (operation: OfflineOperation) => Promise<OfflineResult | OfflineConflictResult | OfflineOperationResponse>;
435
439
  type OfflineOperationStatus = QueueOperationStatus | "REJECTED" | "RETRYABLE_ERROR";
436
440
  type OfflineOperationLifecycleStatus = QueueOperationStatus;
@@ -469,6 +473,9 @@ interface OfflineSyncResultEvent {
469
473
  readonly state?: UserRallyState;
470
474
  }
471
475
  type OfflineSyncResultListener = (event: OfflineSyncResultEvent) => void | Promise<void>;
476
+ type OfflineQueueChangeListener = () => void;
477
+ /** Removes only an operation's optimistic changes without mutating its inputs. */
478
+ declare function rollbackOptimisticOperation(state: UserRallyState, operation: OfflineOperation): UserRallyState;
472
479
  declare class MemoryQueueStorage implements OfflineQueueStorage {
473
480
  #private;
474
481
  load(key: string): Promise<ReadonlyArray<OfflineOperation>>;
@@ -496,14 +503,18 @@ declare class OfflineQueue {
496
503
  get syncState(): SyncState;
497
504
  get pendingCount(): number;
498
505
  get queueCapability(): OfflineQueueCapability;
506
+ get storageCapability(): OfflineStorageCapability;
507
+ get isStoragePersistent(): boolean;
499
508
  get rejectedHistory(): ReadonlyArray<RejectedOperationHistoryEntry>;
500
509
  get error(): Error | null;
501
510
  get operations(): ReadonlyArray<OfflineOperation>;
502
- get conflictPolicy(): SyncConflictPolicy;
503
511
  get storageKey(): string;
504
512
  get rallyId(): string | undefined;
505
513
  get userId(): string | null;
506
514
  setSyncResultListener(listener: OfflineSyncResultListener | undefined): void;
515
+ setChangeListener(listener: OfflineQueueChangeListener | undefined): void;
516
+ setCapabilityWarningListener(listener: ((event: OfflineQueueCapabilityWarning) => void) | undefined): void;
517
+ setReplayConfig(config: AdminRallyConfig | PublicRallyConfig): void;
507
518
  initialize(): Promise<void>;
508
519
  /** Releases browser listeners when the queue is no longer used. */
509
520
  dispose(): void;
@@ -512,8 +523,8 @@ declare class OfflineQueue {
512
523
  switchUser(newUserId: string | null): Promise<void>;
513
524
  setSender(sender: OfflineSender): void;
514
525
  enqueue(operation: OfflineOperation): Promise<void>;
515
- enqueueCheckIn(request: CheckInRequest): Promise<void>;
516
- enqueueClaimReward(request: ClaimRequest): Promise<void>;
526
+ enqueueCheckIn(request: CheckInRequest, optimisticState?: UserRallyState): Promise<void>;
527
+ enqueueClaimReward(request: ClaimRequest, optimisticState?: UserRallyState): Promise<void>;
517
528
  clear(): Promise<void>;
518
529
  discardRejected(operationId: string): Promise<boolean>;
519
530
  retryRejected(operationId: string): Promise<boolean>;
@@ -522,7 +533,6 @@ declare class OfflineQueue {
522
533
  clearRejectedHistory(): Promise<void>;
523
534
  sync(sender?: OfflineSender | undefined): Promise<void>;
524
535
  retrySync(sender?: OfflineSender | undefined): Promise<void>;
525
- resolveConflict(operation: OfflineOperation, localState: UserRallyState, serverState: UserRallyState): Promise<UserRallyState>;
526
536
  }
527
537
 
528
538
  interface StampStorage {
@@ -640,12 +650,37 @@ type ClientEvent = {
640
650
  } | {
641
651
  readonly type: "sync";
642
652
  readonly state: UserRallyState;
653
+ } | {
654
+ readonly type: "syncLifecycle";
655
+ readonly event: SyncLifecycleEvent;
643
656
  } | {
644
657
  readonly type: "error";
645
658
  readonly error: ClientError | OfflineOperationError;
646
659
  };
647
660
  type ClientListener = (state: UserRallyState) => void;
648
661
  type ClientEventListener = (event: ClientEvent) => void;
662
+ type SyncLifecycleEvent = {
663
+ readonly type: "SYNC_STARTED";
664
+ } | {
665
+ readonly type: "OPERATION_ACCEPTED";
666
+ readonly operationId: string;
667
+ readonly resourceId: string;
668
+ } | {
669
+ readonly type: "OPERATION_ROLLED_BACK";
670
+ readonly operationId: string;
671
+ readonly resourceId: string;
672
+ readonly reason: string;
673
+ readonly errorCode: string;
674
+ } | {
675
+ readonly type: "OPERATION_RETRYABLE_ERROR";
676
+ readonly operationId: string;
677
+ readonly error: string;
678
+ } | {
679
+ readonly type: "SYNC_COMPLETED";
680
+ readonly totalProcessed: number;
681
+ readonly failedCount: number;
682
+ };
683
+ type SyncEventListener = (event: SyncLifecycleEvent) => void;
649
684
  interface CheckInRequest {
650
685
  readonly rallyId: string;
651
686
  readonly userId: string | null;
@@ -697,11 +732,18 @@ declare class StampRallyClient {
697
732
  get syncState(): SyncState;
698
733
  get pendingCount(): number;
699
734
  get rejectedHistory(): ReadonlyArray<RejectedOperationHistoryEntry>;
735
+ getSyncRevision(): number;
700
736
  get queueCapability(): OfflineQueueCapability;
737
+ get storageCapability(): OfflineStorageCapability;
738
+ get isStoragePersistent(): boolean;
701
739
  discardRejected(operationId: string): Promise<boolean>;
702
740
  retryRejected(operationId: string): Promise<boolean>;
741
+ dismissRejectedOperation(operationId: string): Promise<boolean>;
742
+ retryOperation(operationId: string): Promise<boolean>;
703
743
  subscribe(listener: ClientListener): () => void;
704
744
  subscribeEvents(listener: ClientEventListener): () => void;
745
+ subscribeSyncEvents(listener: SyncEventListener): () => void;
746
+ subscribeSyncState(listener: () => void): () => void;
705
747
  init(): Promise<UserRallyState>;
706
748
  initialize(): Promise<UserRallyState>;
707
749
  switchUser(newUserId: string | null): Promise<UserRallyState>;
@@ -715,6 +757,20 @@ declare class StampRallyClient {
715
757
  restore(state: UserRallyState): Promise<UserRallyState>;
716
758
  }
717
759
 
760
+ interface RebuildUserStateOptions {
761
+ readonly baseline: UserRallyState;
762
+ readonly operations: ReadonlyArray<OfflineOperation>;
763
+ readonly config?: AdminRallyConfig | PublicRallyConfig;
764
+ }
765
+ interface RebuildUserStateResult {
766
+ readonly state: UserRallyState;
767
+ readonly rejectedOperationIds: ReadonlyArray<string>;
768
+ }
769
+ /** Replays the durable operation log on top of a server-confirmed baseline. */
770
+ declare function rebuildUserStateFromLog(baseline: UserRallyState, operations: ReadonlyArray<OfflineOperation>, config?: AdminRallyConfig | PublicRallyConfig): UserRallyState;
771
+ declare function rebuildUserStateFromLog(options: RebuildUserStateOptions): UserRallyState;
772
+ declare function rebuildUserStateLog(baseline: UserRallyState, operations: ReadonlyArray<OfflineOperation>, config?: AdminRallyConfig | PublicRallyConfig): RebuildUserStateResult;
773
+
718
774
  type SecureTokenSecretKey = string | Uint8Array;
719
775
  interface SecureTokenOptions {
720
776
  readonly encrypt?: boolean;
@@ -820,4 +876,4 @@ type SnapshotTokenVerification<T extends SnapshotTokenPayload = SnapshotTokenPay
820
876
  declare function createSignedSnapshotToken<T extends SnapshotTokenPayload>(payload: T, secretKey: SnapshotSecretKey): Promise<string>;
821
877
  declare function verifySnapshotToken<T extends SnapshotTokenPayload = SnapshotTokenPayload>(token: string, secretKey: SnapshotSecretKey, now?: number): Promise<SnapshotTokenVerification<T>>;
822
878
 
823
- 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 OfflineQueueCapability, 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 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, safeParseAdminConfig, safeParsePublicConfig, sanitizeAdminConfig, storageKey, toLocalizedString, toPublicConfig, updateLocalizedField, validatePublicConfigSafety, validateRallyConfigRelations, verifyPasscode, verifySecureToken, verifySnapshotToken };
879
+ 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 NfcDetectorOptions, type NfcVerificationContext, type OfflineConflictResult, type OfflineOperation, type OfflineOperationError, type OfflineOperationLifecycleStatus, type OfflineOperationResponse, type OfflineOperationStatus, OfflineQueue, type OfflineQueueCapability, type OfflineQueueCapabilityWarning, type OfflineQueueChangeListener, type OfflineQueueOptions, type OfflineQueueStorage, type OfflineResult, type OfflineSender, type OfflineStorageCapability, 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 RebuildUserStateOptions, type RebuildUserStateResult, 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, rebuildUserStateFromLog, rebuildUserStateLog, reconcileRewardStates, resolveLocalizedText, resolveRallyStateConflict, rollbackOptimisticOperation, safeParseAdminConfig, safeParsePublicConfig, sanitizeAdminConfig, storageKey, toLocalizedString, toPublicConfig, updateLocalizedField, validatePublicConfigSafety, validateRallyConfigRelations, verifyPasscode, verifySecureToken, verifySnapshotToken };