@stamprally/core 0.11.0 → 0.13.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
@@ -147,6 +147,7 @@ interface StampRecord {
147
147
  readonly acquiredAt: string;
148
148
  readonly metadata?: Readonly<Record<string, unknown>>;
149
149
  }
150
+ type SpotStatus = "UNCLAIMED" | "CLAIMED" | "LOCKED" | "VERIFYING";
150
151
  type RewardStatus = "LOCKED" | "AVAILABLE" | "CONSUMED" | "EXPIRED";
151
152
  interface RewardState {
152
153
  readonly rewardId: string;
@@ -292,6 +293,11 @@ declare function parsePublicConfig(input: unknown): PublicRallyConfig;
292
293
  declare function calculateDistanceMeters(aLat: number, aLon: number, bLat: number, bLon: number): number;
293
294
  declare function evaluateConditionDetailed(condition: CheckInCondition, context: VerificationContext): Result<ConditionMatch, ConditionMismatch>;
294
295
  declare function evaluateCondition(condition: CheckInCondition, context: VerificationContext): boolean;
296
+ /** Derives a viewer-safe spot status without changing the supplied state. */
297
+ declare function evaluateSpotStatus(spot: Pick<SpotItem, "id" | "prerequisites">, state: Pick<UserRallyState, "records">, options?: {
298
+ readonly verifying?: boolean;
299
+ }): SpotStatus;
300
+ declare const getSpotStatus: typeof evaluateSpotStatus;
295
301
 
296
302
  declare function getOrderedSpots<TLocale extends string, TMeta extends Record<string, unknown>>(spots: ReadonlyArray<SpotItem<TLocale, TMeta>>): ReadonlyArray<SpotItem<TLocale, TMeta>>;
297
303
 
@@ -313,6 +319,12 @@ declare function createClaimTicketNumber(rewardId: string, options?: ClaimTicket
313
319
  declare function createUniqueClaimTicketNumber(rewardId: string, issuedAt: string): string;
314
320
  declare function issueClaimTicketNumber(reward: Reward, currentState: RewardState, options?: ClaimTicketOptions): RewardState;
315
321
 
322
+ interface MergeConflictOptions {
323
+ readonly policy: "server_wins" | "merge";
324
+ }
325
+ /** Resolves a server/local state conflict without mutating either input state. */
326
+ declare function resolveRallyStateConflict(serverState: UserRallyState, localState: UserRallyState, options?: MergeConflictOptions): UserRallyState;
327
+
316
328
  type RewardConsumeError = {
317
329
  readonly code: "NOT_AVAILABLE" | "ALREADY_CONSUMED" | "OUT_OF_STOCK" | "REWARD_NOT_FOUND";
318
330
  readonly rewardId: string;
@@ -374,6 +386,10 @@ interface OfflineQueueOptions {
374
386
  removeItem?(key: string): void;
375
387
  } | null;
376
388
  readonly key?: string;
389
+ /** Rally scope used by the default durable key. */
390
+ readonly rallyId?: string;
391
+ /** User scope used by the default durable key. */
392
+ readonly userId?: string | null;
377
393
  readonly databaseName?: string;
378
394
  readonly conflictPolicy?: SyncConflictPolicy;
379
395
  readonly onSyncConflict?: SyncConflictPolicy | ((context: OfflineConflict) => SyncConflictPolicy | Promise<SyncConflictPolicy>);
@@ -383,7 +399,35 @@ interface OfflineConflict {
383
399
  readonly localState: UserRallyState;
384
400
  readonly serverState: UserRallyState;
385
401
  }
386
- type OfflineSender = (operation: OfflineOperation) => Promise<OfflineResult | OfflineConflictResult>;
402
+ type OfflineSender = (operation: OfflineOperation) => Promise<OfflineResult | OfflineConflictResult | OfflineOperationResponse>;
403
+ type OfflineOperationStatus = "ACCEPTED" | "REJECTED_PERMANENT" | "RETRYABLE_ERROR";
404
+ interface OfflineOperationError {
405
+ readonly code: string;
406
+ readonly message: string;
407
+ readonly [key: string]: unknown;
408
+ }
409
+ type OfflineOperationResponse = {
410
+ readonly status: "ACCEPTED";
411
+ readonly result?: OfflineResult | OfflineConflictResult;
412
+ readonly state?: UserRallyState;
413
+ } | {
414
+ readonly status: "REJECTED_PERMANENT";
415
+ readonly error?: ClientError | OfflineOperationError;
416
+ readonly reason?: ClientError | OfflineOperationError;
417
+ readonly state?: UserRallyState;
418
+ } | {
419
+ readonly status: "RETRYABLE_ERROR";
420
+ readonly error?: ClientError | OfflineOperationError;
421
+ readonly reason?: ClientError | OfflineOperationError;
422
+ };
423
+ interface OfflineSyncResultEvent {
424
+ readonly operation: OfflineOperation;
425
+ readonly result?: OfflineResult | OfflineConflictResult;
426
+ readonly status?: OfflineOperationStatus;
427
+ readonly error?: ClientError | OfflineOperationError;
428
+ readonly state?: UserRallyState;
429
+ }
430
+ type OfflineSyncResultListener = (event: OfflineSyncResultEvent) => void | Promise<void>;
387
431
  declare class MemoryQueueStorage implements OfflineQueueStorage {
388
432
  #private;
389
433
  load(key: string): Promise<ReadonlyArray<OfflineOperation>>;
@@ -407,7 +451,15 @@ declare class OfflineQueue {
407
451
  get pendingCount(): number;
408
452
  get error(): Error | null;
409
453
  get operations(): ReadonlyArray<OfflineOperation>;
454
+ get conflictPolicy(): SyncConflictPolicy;
455
+ get storageKey(): string;
456
+ get rallyId(): string | undefined;
457
+ get userId(): string | null;
458
+ setSyncResultListener(listener: OfflineSyncResultListener | undefined): void;
410
459
  initialize(): Promise<void>;
460
+ /** Selects a rally/user queue scope and loads its pending operations. */
461
+ setScope(rallyId: string, userId: string | null): Promise<void>;
462
+ switchUser(newUserId: string | null): Promise<void>;
411
463
  setSender(sender: OfflineSender): void;
412
464
  enqueue(operation: OfflineOperation): Promise<void>;
413
465
  enqueueCheckIn(request: CheckInRequest): Promise<void>;
@@ -415,7 +467,7 @@ declare class OfflineQueue {
415
467
  clear(): Promise<void>;
416
468
  sync(sender?: OfflineSender | undefined): Promise<void>;
417
469
  retrySync(sender?: OfflineSender | undefined): Promise<void>;
418
- resolveConflict(operation: OfflineOperation, localState: UserRallyState, serverState: UserRallyState): Promise<void>;
470
+ resolveConflict(operation: OfflineOperation, localState: UserRallyState, serverState: UserRallyState): Promise<UserRallyState>;
419
471
  }
420
472
 
421
473
  interface StampStorage {
@@ -534,7 +586,7 @@ type ClientEvent = {
534
586
  readonly state: UserRallyState;
535
587
  } | {
536
588
  readonly type: "error";
537
- readonly error: ClientError;
589
+ readonly error: ClientError | OfflineOperationError;
538
590
  };
539
591
  type ClientListener = (state: UserRallyState) => void;
540
592
  type ClientEventListener = (event: ClientEvent) => void;
@@ -703,4 +755,4 @@ type SnapshotTokenVerification<T extends SnapshotTokenPayload = SnapshotTokenPay
703
755
  declare function createSignedSnapshotToken<T extends SnapshotTokenPayload>(payload: T, secretKey: SnapshotSecretKey): Promise<string>;
704
756
  declare function verifySnapshotToken<T extends SnapshotTokenPayload = SnapshotTokenPayload>(token: string, secretKey: SnapshotSecretKey, now?: number): Promise<SnapshotTokenVerification<T>>;
705
757
 
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 };
758
+ 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, type OfflineOperationError, 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 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 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, 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, verifyPasscode, verifySecureToken, verifySnapshotToken };
package/dist/index.d.ts CHANGED
@@ -147,6 +147,7 @@ interface StampRecord {
147
147
  readonly acquiredAt: string;
148
148
  readonly metadata?: Readonly<Record<string, unknown>>;
149
149
  }
150
+ type SpotStatus = "UNCLAIMED" | "CLAIMED" | "LOCKED" | "VERIFYING";
150
151
  type RewardStatus = "LOCKED" | "AVAILABLE" | "CONSUMED" | "EXPIRED";
151
152
  interface RewardState {
152
153
  readonly rewardId: string;
@@ -292,6 +293,11 @@ declare function parsePublicConfig(input: unknown): PublicRallyConfig;
292
293
  declare function calculateDistanceMeters(aLat: number, aLon: number, bLat: number, bLon: number): number;
293
294
  declare function evaluateConditionDetailed(condition: CheckInCondition, context: VerificationContext): Result<ConditionMatch, ConditionMismatch>;
294
295
  declare function evaluateCondition(condition: CheckInCondition, context: VerificationContext): boolean;
296
+ /** Derives a viewer-safe spot status without changing the supplied state. */
297
+ declare function evaluateSpotStatus(spot: Pick<SpotItem, "id" | "prerequisites">, state: Pick<UserRallyState, "records">, options?: {
298
+ readonly verifying?: boolean;
299
+ }): SpotStatus;
300
+ declare const getSpotStatus: typeof evaluateSpotStatus;
295
301
 
296
302
  declare function getOrderedSpots<TLocale extends string, TMeta extends Record<string, unknown>>(spots: ReadonlyArray<SpotItem<TLocale, TMeta>>): ReadonlyArray<SpotItem<TLocale, TMeta>>;
297
303
 
@@ -313,6 +319,12 @@ declare function createClaimTicketNumber(rewardId: string, options?: ClaimTicket
313
319
  declare function createUniqueClaimTicketNumber(rewardId: string, issuedAt: string): string;
314
320
  declare function issueClaimTicketNumber(reward: Reward, currentState: RewardState, options?: ClaimTicketOptions): RewardState;
315
321
 
322
+ interface MergeConflictOptions {
323
+ readonly policy: "server_wins" | "merge";
324
+ }
325
+ /** Resolves a server/local state conflict without mutating either input state. */
326
+ declare function resolveRallyStateConflict(serverState: UserRallyState, localState: UserRallyState, options?: MergeConflictOptions): UserRallyState;
327
+
316
328
  type RewardConsumeError = {
317
329
  readonly code: "NOT_AVAILABLE" | "ALREADY_CONSUMED" | "OUT_OF_STOCK" | "REWARD_NOT_FOUND";
318
330
  readonly rewardId: string;
@@ -374,6 +386,10 @@ interface OfflineQueueOptions {
374
386
  removeItem?(key: string): void;
375
387
  } | null;
376
388
  readonly key?: string;
389
+ /** Rally scope used by the default durable key. */
390
+ readonly rallyId?: string;
391
+ /** User scope used by the default durable key. */
392
+ readonly userId?: string | null;
377
393
  readonly databaseName?: string;
378
394
  readonly conflictPolicy?: SyncConflictPolicy;
379
395
  readonly onSyncConflict?: SyncConflictPolicy | ((context: OfflineConflict) => SyncConflictPolicy | Promise<SyncConflictPolicy>);
@@ -383,7 +399,35 @@ interface OfflineConflict {
383
399
  readonly localState: UserRallyState;
384
400
  readonly serverState: UserRallyState;
385
401
  }
386
- type OfflineSender = (operation: OfflineOperation) => Promise<OfflineResult | OfflineConflictResult>;
402
+ type OfflineSender = (operation: OfflineOperation) => Promise<OfflineResult | OfflineConflictResult | OfflineOperationResponse>;
403
+ type OfflineOperationStatus = "ACCEPTED" | "REJECTED_PERMANENT" | "RETRYABLE_ERROR";
404
+ interface OfflineOperationError {
405
+ readonly code: string;
406
+ readonly message: string;
407
+ readonly [key: string]: unknown;
408
+ }
409
+ type OfflineOperationResponse = {
410
+ readonly status: "ACCEPTED";
411
+ readonly result?: OfflineResult | OfflineConflictResult;
412
+ readonly state?: UserRallyState;
413
+ } | {
414
+ readonly status: "REJECTED_PERMANENT";
415
+ readonly error?: ClientError | OfflineOperationError;
416
+ readonly reason?: ClientError | OfflineOperationError;
417
+ readonly state?: UserRallyState;
418
+ } | {
419
+ readonly status: "RETRYABLE_ERROR";
420
+ readonly error?: ClientError | OfflineOperationError;
421
+ readonly reason?: ClientError | OfflineOperationError;
422
+ };
423
+ interface OfflineSyncResultEvent {
424
+ readonly operation: OfflineOperation;
425
+ readonly result?: OfflineResult | OfflineConflictResult;
426
+ readonly status?: OfflineOperationStatus;
427
+ readonly error?: ClientError | OfflineOperationError;
428
+ readonly state?: UserRallyState;
429
+ }
430
+ type OfflineSyncResultListener = (event: OfflineSyncResultEvent) => void | Promise<void>;
387
431
  declare class MemoryQueueStorage implements OfflineQueueStorage {
388
432
  #private;
389
433
  load(key: string): Promise<ReadonlyArray<OfflineOperation>>;
@@ -407,7 +451,15 @@ declare class OfflineQueue {
407
451
  get pendingCount(): number;
408
452
  get error(): Error | null;
409
453
  get operations(): ReadonlyArray<OfflineOperation>;
454
+ get conflictPolicy(): SyncConflictPolicy;
455
+ get storageKey(): string;
456
+ get rallyId(): string | undefined;
457
+ get userId(): string | null;
458
+ setSyncResultListener(listener: OfflineSyncResultListener | undefined): void;
410
459
  initialize(): Promise<void>;
460
+ /** Selects a rally/user queue scope and loads its pending operations. */
461
+ setScope(rallyId: string, userId: string | null): Promise<void>;
462
+ switchUser(newUserId: string | null): Promise<void>;
411
463
  setSender(sender: OfflineSender): void;
412
464
  enqueue(operation: OfflineOperation): Promise<void>;
413
465
  enqueueCheckIn(request: CheckInRequest): Promise<void>;
@@ -415,7 +467,7 @@ declare class OfflineQueue {
415
467
  clear(): Promise<void>;
416
468
  sync(sender?: OfflineSender | undefined): Promise<void>;
417
469
  retrySync(sender?: OfflineSender | undefined): Promise<void>;
418
- resolveConflict(operation: OfflineOperation, localState: UserRallyState, serverState: UserRallyState): Promise<void>;
470
+ resolveConflict(operation: OfflineOperation, localState: UserRallyState, serverState: UserRallyState): Promise<UserRallyState>;
419
471
  }
420
472
 
421
473
  interface StampStorage {
@@ -534,7 +586,7 @@ type ClientEvent = {
534
586
  readonly state: UserRallyState;
535
587
  } | {
536
588
  readonly type: "error";
537
- readonly error: ClientError;
589
+ readonly error: ClientError | OfflineOperationError;
538
590
  };
539
591
  type ClientListener = (state: UserRallyState) => void;
540
592
  type ClientEventListener = (event: ClientEvent) => void;
@@ -703,4 +755,4 @@ type SnapshotTokenVerification<T extends SnapshotTokenPayload = SnapshotTokenPay
703
755
  declare function createSignedSnapshotToken<T extends SnapshotTokenPayload>(payload: T, secretKey: SnapshotSecretKey): Promise<string>;
704
756
  declare function verifySnapshotToken<T extends SnapshotTokenPayload = SnapshotTokenPayload>(token: string, secretKey: SnapshotSecretKey, now?: number): Promise<SnapshotTokenVerification<T>>;
705
757
 
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 };
758
+ 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, type OfflineOperationError, 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 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 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, 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, verifyPasscode, verifySecureToken, verifySnapshotToken };
package/dist/index.js CHANGED
@@ -39,6 +39,14 @@ function evaluateConditionDetailed(condition2, context) {
39
39
  function evaluateCondition(condition2, context) {
40
40
  return evaluateConditionDetailed(condition2, context).ok;
41
41
  }
42
+ function evaluateSpotStatus(spot2, state, options = {}) {
43
+ if (options.verifying === true) return "VERIFYING";
44
+ if (state.records.some((record) => record.stampId === spot2.id)) return "CLAIMED";
45
+ const acquired = new Set(state.records.map((record) => record.stampId));
46
+ if (spot2.prerequisites?.some((prerequisite) => !acquired.has(prerequisite))) return "LOCKED";
47
+ return "UNCLAIMED";
48
+ }
49
+ var getSpotStatus = evaluateSpotStatus;
42
50
 
43
51
  // src/engine/order.ts
44
52
  function getOrderedSpots(spots) {
@@ -97,6 +105,76 @@ function issueClaimTicketNumber(reward2, currentState, options = {}) {
97
105
  return { ...currentState, claimTicketNumber };
98
106
  }
99
107
 
108
+ // src/engine/sync.ts
109
+ function latestTimestamp(serverTimestamp, localTimestamp) {
110
+ const serverTime = Date.parse(serverTimestamp);
111
+ const localTime = Date.parse(localTimestamp);
112
+ if (!Number.isNaN(serverTime) && !Number.isNaN(localTime))
113
+ return serverTime >= localTime ? serverTimestamp : localTimestamp;
114
+ if (!Number.isNaN(serverTime)) return serverTimestamp;
115
+ if (!Number.isNaN(localTime)) return localTimestamp;
116
+ return serverTimestamp >= localTimestamp ? serverTimestamp : localTimestamp;
117
+ }
118
+ function mergeRewardStates(serverRewards, localRewards) {
119
+ const merged = new Map(serverRewards.map((reward2) => [reward2.rewardId, reward2]));
120
+ for (const localReward of localRewards) {
121
+ const serverReward = merged.get(localReward.rewardId);
122
+ if (serverReward === void 0) {
123
+ merged.set(localReward.rewardId, localReward);
124
+ continue;
125
+ }
126
+ const winner = localReward.status === "CONSUMED" && serverReward.status !== "CONSUMED" ? localReward : serverReward;
127
+ merged.set(localReward.rewardId, {
128
+ ...winner,
129
+ ...serverReward.unlockedAt === void 0 && localReward.unlockedAt === void 0 ? {} : {
130
+ unlockedAt: serverReward.unlockedAt === void 0 ? localReward.unlockedAt : localReward.unlockedAt === void 0 ? serverReward.unlockedAt : latestTimestamp(serverReward.unlockedAt, localReward.unlockedAt)
131
+ },
132
+ ...serverReward.consumedAt === void 0 && localReward.consumedAt === void 0 ? {} : {
133
+ consumedAt: serverReward.consumedAt === void 0 ? localReward.consumedAt : localReward.consumedAt === void 0 ? serverReward.consumedAt : latestTimestamp(serverReward.consumedAt, localReward.consumedAt)
134
+ },
135
+ ...serverReward.redeemedCount === void 0 && localReward.redeemedCount === void 0 ? {} : {
136
+ redeemedCount: Math.max(
137
+ serverReward.redeemedCount ?? 0,
138
+ localReward.redeemedCount ?? 0
139
+ )
140
+ },
141
+ ...serverReward.userRedemptionCount === void 0 && localReward.userRedemptionCount === void 0 ? {} : {
142
+ userRedemptionCount: Math.max(
143
+ serverReward.userRedemptionCount ?? 0,
144
+ localReward.userRedemptionCount ?? 0
145
+ )
146
+ }
147
+ });
148
+ }
149
+ return [...merged.values()];
150
+ }
151
+ function mergeStampRecords(serverRecords, localRecords) {
152
+ const merged = /* @__PURE__ */ new Map();
153
+ for (const record of [...serverRecords, ...localRecords]) {
154
+ const current = merged.get(record.stampId);
155
+ if (current === void 0) {
156
+ merged.set(record.stampId, record);
157
+ continue;
158
+ }
159
+ const acquiredAt = latestTimestamp(current.acquiredAt, record.acquiredAt);
160
+ merged.set(record.stampId, {
161
+ ...current,
162
+ ...acquiredAt === current.acquiredAt ? {} : { acquiredAt },
163
+ ...current.metadata === void 0 && record.metadata !== void 0 ? { metadata: record.metadata } : {}
164
+ });
165
+ }
166
+ return [...merged.values()];
167
+ }
168
+ function resolveRallyStateConflict(serverState, localState, options = { policy: "merge" }) {
169
+ if (options.policy === "server_wins") return serverState;
170
+ return {
171
+ ...serverState,
172
+ records: mergeStampRecords(serverState.records, localState.records),
173
+ rewards: mergeRewardStates(serverState.rewards, localState.rewards),
174
+ updatedAt: latestTimestamp(serverState.updatedAt, localState.updatedAt)
175
+ };
176
+ }
177
+
100
178
  // src/detectors/types.ts
101
179
  function createDetectorError(detector, code, message, cause) {
102
180
  return cause === void 0 ? { detector, code, message } : { detector, code, message, cause };
@@ -998,6 +1076,7 @@ var StampRallyClient = class {
998
1076
  this.#storage = this.#options.storage ?? new InMemoryStorage();
999
1077
  this.#userId = this.#options.userId ?? null;
1000
1078
  this.#offlineQueue = this.#options.offlineQueue;
1079
+ this.#offlineQueue?.setSyncResultListener((event) => this.#handleOfflineSyncResult(event));
1001
1080
  }
1002
1081
  getConfig() {
1003
1082
  return this.#config;
@@ -1028,7 +1107,10 @@ var StampRallyClient = class {
1028
1107
  initialize() {
1029
1108
  if (this.#state !== null) return Promise.resolve(this.#state);
1030
1109
  if (this.#initialization === null) {
1031
- this.#initialization = this.#storage.load(this.#config.id, this.#userId).then((state) => {
1110
+ this.#initialization = (async () => {
1111
+ await this.#offlineQueue?.setScope(this.#config.id, this.#userId);
1112
+ return this.#storage.load(this.#config.id, this.#userId);
1113
+ })().then((state) => {
1032
1114
  const next = state === null ? emptyState(this.#config, this.#userId, this.#now()) : this.#reconcile(state);
1033
1115
  this.#state = next;
1034
1116
  this.#emit(next);
@@ -1046,6 +1128,7 @@ var StampRallyClient = class {
1046
1128
  this.#userId = newUserId;
1047
1129
  this.#state = null;
1048
1130
  this.#initialization = null;
1131
+ await this.#offlineQueue?.switchUser(newUserId);
1049
1132
  return this.initialize();
1050
1133
  });
1051
1134
  }
@@ -1215,12 +1298,19 @@ var StampRallyClient = class {
1215
1298
  });
1216
1299
  }
1217
1300
  if (adapter?.sync === void 0) {
1218
- this.#emitEvent({ type: "sync", state: current });
1301
+ this.#emitEvent({ type: "sync", state: this.#state ?? current });
1219
1302
  return;
1220
1303
  }
1221
- const next = this.#reconcile(
1222
- await adapter.sync({ rallyId: this.#config.id, userId: this.#userId, state: current })
1223
- );
1304
+ const serverState = await adapter.sync({
1305
+ rallyId: this.#config.id,
1306
+ userId: this.#userId,
1307
+ state: this.#state ?? current
1308
+ });
1309
+ const localState = this.#state ?? current;
1310
+ const merged = this.#offlineQueue === void 0 ? serverState : resolveRallyStateConflict(serverState, localState, {
1311
+ policy: this.#offlineQueue.conflictPolicy
1312
+ });
1313
+ const next = this.#reconcile(merged);
1224
1314
  await this.#storage.save(next);
1225
1315
  this.#state = next;
1226
1316
  this.#emit(next);
@@ -1280,6 +1370,17 @@ var StampRallyClient = class {
1280
1370
  )
1281
1371
  };
1282
1372
  }
1373
+ async #handleOfflineSyncResult(event) {
1374
+ if (event.state !== void 0) {
1375
+ const next = this.#reconcile(event.state);
1376
+ await this.#storage.save(next);
1377
+ this.#state = next;
1378
+ this.#emit(next);
1379
+ }
1380
+ if (event.error !== void 0) this.#emitEvent({ type: "error", error: event.error });
1381
+ else if (event.result !== void 0 && "ok" in event.result && !event.result.ok)
1382
+ this.#emitEvent({ type: "error", error: event.result.error });
1383
+ }
1283
1384
  #now() {
1284
1385
  return this.#options.clock?.() ?? (/* @__PURE__ */ new Date()).toISOString();
1285
1386
  }
@@ -1405,9 +1506,27 @@ function defaultStorage(databaseName) {
1405
1506
  function operationId(operation) {
1406
1507
  return operation.kind === "checkIn" ? `checkIn:${operation.request.rallyId}:${operation.request.userId ?? "anonymous"}:${operation.request.idempotencyKey}` : `claimReward:${operation.request.rallyId}:${operation.request.userId ?? "anonymous"}:${operation.request.idempotencyKey}`;
1407
1508
  }
1509
+ function requestScope(operation) {
1510
+ return {
1511
+ rallyId: operation.request.rallyId,
1512
+ userId: operation.request.userId
1513
+ };
1514
+ }
1515
+ function errorValue(value, fallbackCode) {
1516
+ if (typeof value === "object" && value !== null) {
1517
+ const candidate = value;
1518
+ if (typeof candidate.code === "string" && typeof candidate.message === "string")
1519
+ return { ...candidate, code: candidate.code, message: candidate.message };
1520
+ }
1521
+ if (value instanceof Error) return { code: fallbackCode, message: value.message };
1522
+ if (typeof value === "string") return { code: fallbackCode, message: value };
1523
+ return { code: fallbackCode, message: "Offline operation was rejected." };
1524
+ }
1408
1525
  var OfflineQueue = class {
1409
1526
  #storage;
1410
- #key;
1527
+ #configuredKey;
1528
+ #rallyId;
1529
+ #userId;
1411
1530
  #conflictPolicy;
1412
1531
  #onSyncConflict;
1413
1532
  #operations = [];
@@ -1416,12 +1535,15 @@ var OfflineQueue = class {
1416
1535
  #error = null;
1417
1536
  #sender;
1418
1537
  #syncPromise = null;
1538
+ #syncResultListener;
1419
1539
  constructor(options = {}) {
1420
1540
  if (options.storage !== void 0) this.#storage = options.storage;
1421
1541
  else if (options.storageLike !== void 0 && options.storageLike !== null)
1422
1542
  this.#storage = new LocalStorageQueueStorage(options.storageLike);
1423
1543
  else this.#storage = defaultStorage(options.databaseName);
1424
- this.#key = options.key ?? "stamprally:offline-queue";
1544
+ this.#configuredKey = options.key;
1545
+ this.#rallyId = options.rallyId;
1546
+ this.#userId = options.userId ?? null;
1425
1547
  this.#conflictPolicy = options.conflictPolicy ?? "server_wins";
1426
1548
  this.#onSyncConflict = options.onSyncConflict;
1427
1549
  }
@@ -1437,20 +1559,60 @@ var OfflineQueue = class {
1437
1559
  get operations() {
1438
1560
  return this.#operations;
1439
1561
  }
1562
+ get conflictPolicy() {
1563
+ return this.#conflictPolicy;
1564
+ }
1565
+ get storageKey() {
1566
+ return this.#storageKey();
1567
+ }
1568
+ get rallyId() {
1569
+ return this.#rallyId;
1570
+ }
1571
+ get userId() {
1572
+ return this.#userId;
1573
+ }
1574
+ setSyncResultListener(listener) {
1575
+ this.#syncResultListener = listener;
1576
+ }
1440
1577
  async initialize() {
1441
1578
  if (this.#loaded) return;
1442
- this.#operations = [...await this.#storage.load(this.#key)];
1579
+ this.#operations = [...await this.#storage.load(this.#storageKey())];
1443
1580
  this.#loaded = true;
1444
1581
  }
1582
+ /** Selects a rally/user queue scope and loads its pending operations. */
1583
+ async setScope(rallyId, userId) {
1584
+ if (this.#configuredKey !== void 0) {
1585
+ this.#rallyId = rallyId;
1586
+ this.#userId = userId;
1587
+ return this.initialize();
1588
+ }
1589
+ if (this.#rallyId === rallyId && this.#userId === userId && this.#loaded) return;
1590
+ this.#rallyId = rallyId;
1591
+ this.#userId = userId;
1592
+ this.#operations = [];
1593
+ this.#loaded = false;
1594
+ await this.initialize();
1595
+ }
1596
+ async switchUser(newUserId) {
1597
+ if (this.#rallyId === void 0)
1598
+ throw new Error("OfflineQueue.switchUser requires a rally scope.");
1599
+ await this.setScope(this.#rallyId, newUserId);
1600
+ }
1445
1601
  setSender(sender) {
1446
1602
  this.#sender = sender;
1447
1603
  }
1448
1604
  async enqueue(operation) {
1605
+ if (this.#configuredKey === void 0) {
1606
+ const scope = requestScope(operation);
1607
+ if (this.#rallyId === void 0) await this.setScope(scope.rallyId, scope.userId);
1608
+ if (this.#rallyId !== scope.rallyId || this.#userId !== scope.userId)
1609
+ throw new Error("Offline operation belongs to another rally or user queue.");
1610
+ }
1449
1611
  await this.initialize();
1450
1612
  const id2 = operationId(operation);
1451
1613
  if (this.#operations.some((item) => operationId(item) === id2)) return;
1452
1614
  this.#operations = [...this.#operations, operation];
1453
- await this.#storage.save(this.#key, this.#operations);
1615
+ await this.#storage.save(this.#storageKey(), this.#operations);
1454
1616
  }
1455
1617
  async enqueueCheckIn(request) {
1456
1618
  return this.enqueue({ kind: "checkIn", request });
@@ -1461,7 +1623,7 @@ var OfflineQueue = class {
1461
1623
  async clear() {
1462
1624
  await this.initialize();
1463
1625
  this.#operations = [];
1464
- await this.#storage.save(this.#key, this.#operations);
1626
+ await this.#storage.save(this.#storageKey(), this.#operations);
1465
1627
  }
1466
1628
  async sync(sender = this.#sender) {
1467
1629
  await this.initialize();
@@ -1483,16 +1645,30 @@ var OfflineQueue = class {
1483
1645
  while (this.#operations.length > 0) {
1484
1646
  const operation = this.#operations[0];
1485
1647
  if (operation === void 0) break;
1486
- let result;
1648
+ let rawResult;
1487
1649
  try {
1488
- result = await sender(operation);
1650
+ rawResult = await sender(operation);
1489
1651
  } catch (cause) {
1490
- throw cause instanceof Error ? cause : new Error(String(cause));
1652
+ throw new Error(errorValue(cause, "RETRYABLE_ERROR").message);
1491
1653
  }
1492
- if ("conflict" in result && result.conflict === true)
1493
- await this.resolveConflict(operation, result.localState, result.serverState);
1654
+ const response = this.#normalizeResponse(rawResult);
1655
+ if (response.status === "RETRYABLE_ERROR") {
1656
+ const error2 = errorValue(response.error ?? response.reason, "RETRYABLE_ERROR");
1657
+ await this.#syncResultListener?.({ operation, status: response.status, error: error2 });
1658
+ throw new Error(error2.message);
1659
+ }
1660
+ const result = response.result;
1661
+ const state = response.state ?? (result !== void 0 && "conflict" in result && result.conflict === true ? await this.resolveConflict(operation, result.localState, result.serverState) : result !== void 0 && "ok" in result && result.ok ? result.value.state : void 0);
1662
+ const error = response.status === "REJECTED_PERMANENT" ? errorValue(response.error ?? response.reason, "REJECTED_PERMANENT") : void 0;
1494
1663
  this.#operations = this.#operations.slice(1);
1495
- await this.#storage.save(this.#key, this.#operations);
1664
+ await this.#storage.save(this.#storageKey(), this.#operations);
1665
+ await this.#syncResultListener?.({
1666
+ operation,
1667
+ ...result === void 0 ? {} : { result },
1668
+ status: response.status,
1669
+ ...error === void 0 ? {} : { error },
1670
+ ...state === void 0 ? {} : { state }
1671
+ });
1496
1672
  }
1497
1673
  this.#state = "idle";
1498
1674
  } catch (cause) {
@@ -1501,12 +1677,29 @@ var OfflineQueue = class {
1501
1677
  throw this.#error;
1502
1678
  }
1503
1679
  }
1680
+ #storageKey() {
1681
+ if (this.#configuredKey !== void 0) return this.#configuredKey;
1682
+ return `stamprally:queue:${this.#rallyId ?? "unscoped"}:${this.#userId ?? "anonymous"}`;
1683
+ }
1684
+ #normalizeResponse(value) {
1685
+ if ("ok" in value) {
1686
+ if (value.ok === false) {
1687
+ if ("status" in value && value.status === "RETRYABLE_ERROR")
1688
+ return { status: "RETRYABLE_ERROR", error: errorValue(value.error, "RETRYABLE_ERROR") };
1689
+ return { status: "REJECTED_PERMANENT", result: value };
1690
+ }
1691
+ return { status: "ACCEPTED", result: value };
1692
+ }
1693
+ if ("status" in value) {
1694
+ if (value.status === "ACCEPTED") return value;
1695
+ return value;
1696
+ }
1697
+ return { status: "ACCEPTED", result: value };
1698
+ }
1504
1699
  async resolveConflict(operation, localState, serverState) {
1505
1700
  const configured = this.#onSyncConflict;
1506
1701
  const policy = (typeof configured === "function" ? await configured({ operation, localState, serverState }) : configured) ?? this.#conflictPolicy;
1507
- if (policy === "merge") {
1508
- return;
1509
- }
1702
+ return resolveRallyStateConflict(serverState, localState, { policy });
1510
1703
  }
1511
1704
  };
1512
1705
 
@@ -2395,6 +2588,6 @@ async function verifySnapshotToken(token, secretKey, now = Date.now()) {
2395
2588
  }
2396
2589
  }
2397
2590
 
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 };
2591
+ 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, 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, verifyPasscode, verifySecureToken, verifySnapshotToken };
2399
2592
  //# sourceMappingURL=index.js.map
2400
2593
  //# sourceMappingURL=index.js.map