@stamprally/core 0.14.0 → 0.15.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/README.md +7 -3
- package/dist/index.cjs +141 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +31 -3
- package/dist/index.d.ts +31 -3
- package/dist/index.js +141 -28
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
package/dist/index.d.cts
CHANGED
|
@@ -97,6 +97,13 @@ type RewardUnlockCondition = {
|
|
|
97
97
|
type RewardType = "digital" | "in_person";
|
|
98
98
|
type RedemptionMethod = "manual_slide" | "staff_passcode" | "view_only" | "server_claim";
|
|
99
99
|
type InventoryAggregationMode = "shared" | "per_reward";
|
|
100
|
+
type RallyInventory = Readonly<Record<string, number>> & {
|
|
101
|
+
readonly sharedStock?: number;
|
|
102
|
+
};
|
|
103
|
+
interface RallyInventoryState {
|
|
104
|
+
readonly sharedRemaining?: number;
|
|
105
|
+
readonly rewardRemaining?: Readonly<Record<string, number>>;
|
|
106
|
+
}
|
|
100
107
|
interface Reward<TLocale extends string = string> {
|
|
101
108
|
readonly id: string;
|
|
102
109
|
readonly title: LocalizedText<TLocale>;
|
|
@@ -121,7 +128,7 @@ interface AdminRallyConfig<TLocale extends string = string, TMeta extends Record
|
|
|
121
128
|
readonly spots: ReadonlyArray<SpotItem<TLocale, TMeta>>;
|
|
122
129
|
readonly rewards: ReadonlyArray<Reward<TLocale>>;
|
|
123
130
|
readonly staffPasscode?: string;
|
|
124
|
-
readonly inventory?:
|
|
131
|
+
readonly inventory?: RallyInventory;
|
|
125
132
|
/** How the optional top-level inventory limit is aggregated. */
|
|
126
133
|
readonly inventoryMode?: InventoryAggregationMode;
|
|
127
134
|
readonly serverMetadata?: Readonly<Record<string, unknown>>;
|
|
@@ -167,6 +174,7 @@ interface StampRallyState {
|
|
|
167
174
|
readonly userId: string | null;
|
|
168
175
|
readonly records: ReadonlyArray<StampRecord>;
|
|
169
176
|
readonly rewards: ReadonlyArray<RewardState>;
|
|
177
|
+
readonly inventory?: RallyInventoryState;
|
|
170
178
|
readonly updatedAt: string;
|
|
171
179
|
}
|
|
172
180
|
type UserRallyState = StampRallyState;
|
|
@@ -367,9 +375,13 @@ declare function processStamp(state: StampRallyState, config: AdminRallyConfig,
|
|
|
367
375
|
type OfflineOperation = {
|
|
368
376
|
readonly kind: "checkIn";
|
|
369
377
|
readonly request: CheckInRequest;
|
|
378
|
+
readonly status?: OfflineOperationLifecycleStatus;
|
|
379
|
+
readonly attempts?: number;
|
|
370
380
|
} | {
|
|
371
381
|
readonly kind: "claimReward";
|
|
372
382
|
readonly request: ClaimRequest;
|
|
383
|
+
readonly status?: OfflineOperationLifecycleStatus;
|
|
384
|
+
readonly attempts?: number;
|
|
373
385
|
};
|
|
374
386
|
type OfflineResult = CheckInResult | ClaimResult | UserRallyState;
|
|
375
387
|
type SyncState = "idle" | "syncing" | "error";
|
|
@@ -400,6 +412,15 @@ interface OfflineQueueOptions {
|
|
|
400
412
|
readonly onSyncConflict?: SyncConflictPolicy | ((context: OfflineConflict) => SyncConflictPolicy | Promise<SyncConflictPolicy>);
|
|
401
413
|
/** Enables storage-event/BroadcastChannel synchronization when browser APIs exist. */
|
|
402
414
|
readonly synchronizeInstances?: boolean;
|
|
415
|
+
readonly retryOptions?: SyncRetryOptions;
|
|
416
|
+
readonly retry?: SyncRetryOptions;
|
|
417
|
+
/** Alias accepted for callers that configure retry behavior at sync time. */
|
|
418
|
+
readonly syncRetryOptions?: SyncRetryOptions;
|
|
419
|
+
}
|
|
420
|
+
interface SyncRetryOptions {
|
|
421
|
+
readonly maxRetries?: number;
|
|
422
|
+
readonly initialIntervalMs?: number;
|
|
423
|
+
readonly backoffMultiplier?: number;
|
|
403
424
|
}
|
|
404
425
|
interface OfflineConflict {
|
|
405
426
|
readonly operation: OfflineOperation;
|
|
@@ -407,7 +428,8 @@ interface OfflineConflict {
|
|
|
407
428
|
readonly serverState: UserRallyState;
|
|
408
429
|
}
|
|
409
430
|
type OfflineSender = (operation: OfflineOperation) => Promise<OfflineResult | OfflineConflictResult | OfflineOperationResponse>;
|
|
410
|
-
type OfflineOperationStatus = "ACCEPTED" | "REJECTED_PERMANENT" | "RETRYABLE_ERROR";
|
|
431
|
+
type OfflineOperationStatus = "PENDING" | "IN_FLIGHT" | "ACCEPTED" | "REJECTED" | "REJECTED_PERMANENT" | "RETRYABLE_ERROR";
|
|
432
|
+
type OfflineOperationLifecycleStatus = "PENDING" | "IN_FLIGHT" | "ACCEPTED" | "REJECTED";
|
|
411
433
|
interface OfflineOperationError {
|
|
412
434
|
readonly code: string;
|
|
413
435
|
readonly message: string;
|
|
@@ -519,6 +541,7 @@ declare class InMemoryStorage implements StampStorage {
|
|
|
519
541
|
remove(rallyId: string, userId: string | null): Promise<void>;
|
|
520
542
|
}
|
|
521
543
|
declare function storageKey(rallyId: string, userId: string | null): string;
|
|
544
|
+
declare function createAnonymousSessionId(storage?: StorageLike | null): string;
|
|
522
545
|
interface LocalStorageAdapterOptions {
|
|
523
546
|
readonly storage?: StorageLike | null;
|
|
524
547
|
readonly keyPrefix?: string;
|
|
@@ -607,6 +630,7 @@ interface CheckInRequest {
|
|
|
607
630
|
readonly idempotencyKey: string;
|
|
608
631
|
readonly now: string;
|
|
609
632
|
readonly state: UserRallyState;
|
|
633
|
+
readonly anonymousSessionId?: string;
|
|
610
634
|
}
|
|
611
635
|
interface ClaimRequest {
|
|
612
636
|
readonly rallyId: string;
|
|
@@ -616,6 +640,7 @@ interface ClaimRequest {
|
|
|
616
640
|
readonly now: string;
|
|
617
641
|
readonly options: ClaimOptions;
|
|
618
642
|
readonly state: UserRallyState;
|
|
643
|
+
readonly anonymousSessionId?: string;
|
|
619
644
|
}
|
|
620
645
|
interface SyncAdapter {
|
|
621
646
|
readonly checkIn?: (request: CheckInRequest) => Promise<CheckInResult>;
|
|
@@ -624,6 +649,7 @@ interface SyncAdapter {
|
|
|
624
649
|
readonly rallyId: string;
|
|
625
650
|
readonly userId: string | null;
|
|
626
651
|
readonly state: UserRallyState;
|
|
652
|
+
readonly anonymousSessionId?: string;
|
|
627
653
|
}) => Promise<UserRallyState>;
|
|
628
654
|
}
|
|
629
655
|
interface ClientOptions {
|
|
@@ -633,6 +659,7 @@ interface ClientOptions {
|
|
|
633
659
|
readonly customValidators?: Readonly<Record<string, Validator>>;
|
|
634
660
|
readonly clock?: () => string;
|
|
635
661
|
readonly userId?: string | null;
|
|
662
|
+
readonly anonymousSessionId?: string;
|
|
636
663
|
readonly offlineQueue?: OfflineQueue;
|
|
637
664
|
}
|
|
638
665
|
type StorageOrOptions = StampStorage | ClientOptions;
|
|
@@ -642,6 +669,7 @@ declare class StampRallyClient {
|
|
|
642
669
|
getConfig(): RallyConfig;
|
|
643
670
|
getState(): UserRallyState | null;
|
|
644
671
|
getUserId(): string | null;
|
|
672
|
+
getAnonymousSessionId(): string;
|
|
645
673
|
get syncState(): SyncState;
|
|
646
674
|
get pendingCount(): number;
|
|
647
675
|
subscribe(listener: ClientListener): () => void;
|
|
@@ -764,4 +792,4 @@ type SnapshotTokenVerification<T extends SnapshotTokenPayload = SnapshotTokenPay
|
|
|
764
792
|
declare function createSignedSnapshotToken<T extends SnapshotTokenPayload>(payload: T, secretKey: SnapshotSecretKey): Promise<string>;
|
|
765
793
|
declare function verifySnapshotToken<T extends SnapshotTokenPayload = SnapshotTokenPayload>(token: string, secretKey: SnapshotSecretKey, now?: number): Promise<SnapshotTokenVerification<T>>;
|
|
766
794
|
|
|
767
|
-
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 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, validateRallyConfigRelations, verifyPasscode, verifySecureToken, verifySnapshotToken };
|
|
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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -97,6 +97,13 @@ type RewardUnlockCondition = {
|
|
|
97
97
|
type RewardType = "digital" | "in_person";
|
|
98
98
|
type RedemptionMethod = "manual_slide" | "staff_passcode" | "view_only" | "server_claim";
|
|
99
99
|
type InventoryAggregationMode = "shared" | "per_reward";
|
|
100
|
+
type RallyInventory = Readonly<Record<string, number>> & {
|
|
101
|
+
readonly sharedStock?: number;
|
|
102
|
+
};
|
|
103
|
+
interface RallyInventoryState {
|
|
104
|
+
readonly sharedRemaining?: number;
|
|
105
|
+
readonly rewardRemaining?: Readonly<Record<string, number>>;
|
|
106
|
+
}
|
|
100
107
|
interface Reward<TLocale extends string = string> {
|
|
101
108
|
readonly id: string;
|
|
102
109
|
readonly title: LocalizedText<TLocale>;
|
|
@@ -121,7 +128,7 @@ interface AdminRallyConfig<TLocale extends string = string, TMeta extends Record
|
|
|
121
128
|
readonly spots: ReadonlyArray<SpotItem<TLocale, TMeta>>;
|
|
122
129
|
readonly rewards: ReadonlyArray<Reward<TLocale>>;
|
|
123
130
|
readonly staffPasscode?: string;
|
|
124
|
-
readonly inventory?:
|
|
131
|
+
readonly inventory?: RallyInventory;
|
|
125
132
|
/** How the optional top-level inventory limit is aggregated. */
|
|
126
133
|
readonly inventoryMode?: InventoryAggregationMode;
|
|
127
134
|
readonly serverMetadata?: Readonly<Record<string, unknown>>;
|
|
@@ -167,6 +174,7 @@ interface StampRallyState {
|
|
|
167
174
|
readonly userId: string | null;
|
|
168
175
|
readonly records: ReadonlyArray<StampRecord>;
|
|
169
176
|
readonly rewards: ReadonlyArray<RewardState>;
|
|
177
|
+
readonly inventory?: RallyInventoryState;
|
|
170
178
|
readonly updatedAt: string;
|
|
171
179
|
}
|
|
172
180
|
type UserRallyState = StampRallyState;
|
|
@@ -367,9 +375,13 @@ declare function processStamp(state: StampRallyState, config: AdminRallyConfig,
|
|
|
367
375
|
type OfflineOperation = {
|
|
368
376
|
readonly kind: "checkIn";
|
|
369
377
|
readonly request: CheckInRequest;
|
|
378
|
+
readonly status?: OfflineOperationLifecycleStatus;
|
|
379
|
+
readonly attempts?: number;
|
|
370
380
|
} | {
|
|
371
381
|
readonly kind: "claimReward";
|
|
372
382
|
readonly request: ClaimRequest;
|
|
383
|
+
readonly status?: OfflineOperationLifecycleStatus;
|
|
384
|
+
readonly attempts?: number;
|
|
373
385
|
};
|
|
374
386
|
type OfflineResult = CheckInResult | ClaimResult | UserRallyState;
|
|
375
387
|
type SyncState = "idle" | "syncing" | "error";
|
|
@@ -400,6 +412,15 @@ interface OfflineQueueOptions {
|
|
|
400
412
|
readonly onSyncConflict?: SyncConflictPolicy | ((context: OfflineConflict) => SyncConflictPolicy | Promise<SyncConflictPolicy>);
|
|
401
413
|
/** Enables storage-event/BroadcastChannel synchronization when browser APIs exist. */
|
|
402
414
|
readonly synchronizeInstances?: boolean;
|
|
415
|
+
readonly retryOptions?: SyncRetryOptions;
|
|
416
|
+
readonly retry?: SyncRetryOptions;
|
|
417
|
+
/** Alias accepted for callers that configure retry behavior at sync time. */
|
|
418
|
+
readonly syncRetryOptions?: SyncRetryOptions;
|
|
419
|
+
}
|
|
420
|
+
interface SyncRetryOptions {
|
|
421
|
+
readonly maxRetries?: number;
|
|
422
|
+
readonly initialIntervalMs?: number;
|
|
423
|
+
readonly backoffMultiplier?: number;
|
|
403
424
|
}
|
|
404
425
|
interface OfflineConflict {
|
|
405
426
|
readonly operation: OfflineOperation;
|
|
@@ -407,7 +428,8 @@ interface OfflineConflict {
|
|
|
407
428
|
readonly serverState: UserRallyState;
|
|
408
429
|
}
|
|
409
430
|
type OfflineSender = (operation: OfflineOperation) => Promise<OfflineResult | OfflineConflictResult | OfflineOperationResponse>;
|
|
410
|
-
type OfflineOperationStatus = "ACCEPTED" | "REJECTED_PERMANENT" | "RETRYABLE_ERROR";
|
|
431
|
+
type OfflineOperationStatus = "PENDING" | "IN_FLIGHT" | "ACCEPTED" | "REJECTED" | "REJECTED_PERMANENT" | "RETRYABLE_ERROR";
|
|
432
|
+
type OfflineOperationLifecycleStatus = "PENDING" | "IN_FLIGHT" | "ACCEPTED" | "REJECTED";
|
|
411
433
|
interface OfflineOperationError {
|
|
412
434
|
readonly code: string;
|
|
413
435
|
readonly message: string;
|
|
@@ -519,6 +541,7 @@ declare class InMemoryStorage implements StampStorage {
|
|
|
519
541
|
remove(rallyId: string, userId: string | null): Promise<void>;
|
|
520
542
|
}
|
|
521
543
|
declare function storageKey(rallyId: string, userId: string | null): string;
|
|
544
|
+
declare function createAnonymousSessionId(storage?: StorageLike | null): string;
|
|
522
545
|
interface LocalStorageAdapterOptions {
|
|
523
546
|
readonly storage?: StorageLike | null;
|
|
524
547
|
readonly keyPrefix?: string;
|
|
@@ -607,6 +630,7 @@ interface CheckInRequest {
|
|
|
607
630
|
readonly idempotencyKey: string;
|
|
608
631
|
readonly now: string;
|
|
609
632
|
readonly state: UserRallyState;
|
|
633
|
+
readonly anonymousSessionId?: string;
|
|
610
634
|
}
|
|
611
635
|
interface ClaimRequest {
|
|
612
636
|
readonly rallyId: string;
|
|
@@ -616,6 +640,7 @@ interface ClaimRequest {
|
|
|
616
640
|
readonly now: string;
|
|
617
641
|
readonly options: ClaimOptions;
|
|
618
642
|
readonly state: UserRallyState;
|
|
643
|
+
readonly anonymousSessionId?: string;
|
|
619
644
|
}
|
|
620
645
|
interface SyncAdapter {
|
|
621
646
|
readonly checkIn?: (request: CheckInRequest) => Promise<CheckInResult>;
|
|
@@ -624,6 +649,7 @@ interface SyncAdapter {
|
|
|
624
649
|
readonly rallyId: string;
|
|
625
650
|
readonly userId: string | null;
|
|
626
651
|
readonly state: UserRallyState;
|
|
652
|
+
readonly anonymousSessionId?: string;
|
|
627
653
|
}) => Promise<UserRallyState>;
|
|
628
654
|
}
|
|
629
655
|
interface ClientOptions {
|
|
@@ -633,6 +659,7 @@ interface ClientOptions {
|
|
|
633
659
|
readonly customValidators?: Readonly<Record<string, Validator>>;
|
|
634
660
|
readonly clock?: () => string;
|
|
635
661
|
readonly userId?: string | null;
|
|
662
|
+
readonly anonymousSessionId?: string;
|
|
636
663
|
readonly offlineQueue?: OfflineQueue;
|
|
637
664
|
}
|
|
638
665
|
type StorageOrOptions = StampStorage | ClientOptions;
|
|
@@ -642,6 +669,7 @@ declare class StampRallyClient {
|
|
|
642
669
|
getConfig(): RallyConfig;
|
|
643
670
|
getState(): UserRallyState | null;
|
|
644
671
|
getUserId(): string | null;
|
|
672
|
+
getAnonymousSessionId(): string;
|
|
645
673
|
get syncState(): SyncState;
|
|
646
674
|
get pendingCount(): number;
|
|
647
675
|
subscribe(listener: ClientListener): () => void;
|
|
@@ -764,4 +792,4 @@ type SnapshotTokenVerification<T extends SnapshotTokenPayload = SnapshotTokenPay
|
|
|
764
792
|
declare function createSignedSnapshotToken<T extends SnapshotTokenPayload>(payload: T, secretKey: SnapshotSecretKey): Promise<string>;
|
|
765
793
|
declare function verifySnapshotToken<T extends SnapshotTokenPayload = SnapshotTokenPayload>(token: string, secretKey: SnapshotSecretKey, now?: number): Promise<SnapshotTokenVerification<T>>;
|
|
766
794
|
|
|
767
|
-
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 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, validateRallyConfigRelations, verifyPasscode, verifySecureToken, verifySnapshotToken };
|
|
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 };
|
package/dist/index.js
CHANGED
|
@@ -615,7 +615,13 @@ function cloneState(state) {
|
|
|
615
615
|
return {
|
|
616
616
|
...state,
|
|
617
617
|
records: state.records.map(cloneRecord),
|
|
618
|
-
...state.rewards === void 0 ? {} : { rewards: state.rewards.map(cloneRewardState) }
|
|
618
|
+
...state.rewards === void 0 ? {} : { rewards: state.rewards.map(cloneRewardState) },
|
|
619
|
+
...state.inventory === void 0 ? {} : {
|
|
620
|
+
inventory: {
|
|
621
|
+
...state.inventory,
|
|
622
|
+
...state.inventory.rewardRemaining === void 0 ? {} : { rewardRemaining: { ...state.inventory.rewardRemaining } }
|
|
623
|
+
}
|
|
624
|
+
}
|
|
619
625
|
};
|
|
620
626
|
}
|
|
621
627
|
function isRecord(value) {
|
|
@@ -635,7 +641,7 @@ function isRewardState(value) {
|
|
|
635
641
|
function isStampRallyState(value) {
|
|
636
642
|
if (typeof value !== "object" || value === null) return false;
|
|
637
643
|
const state = value;
|
|
638
|
-
return typeof state.rallyId === "string" && (typeof state.userId === "string" || state.userId === null) && typeof state.updatedAt === "string" && Array.isArray(state.records) && state.records.every(isRecord) && (state.rewards === void 0 || Array.isArray(state.rewards) && state.rewards.every(isRewardState));
|
|
644
|
+
return typeof state.rallyId === "string" && (typeof state.userId === "string" || state.userId === null) && typeof state.updatedAt === "string" && Array.isArray(state.records) && state.records.every(isRecord) && (state.rewards === void 0 || Array.isArray(state.rewards) && state.rewards.every(isRewardState)) && (state.inventory === void 0 || typeof state.inventory === "object" && state.inventory !== null && !Array.isArray(state.inventory) && (state.inventory.sharedRemaining === void 0 || typeof state.inventory.sharedRemaining === "number" && Number.isInteger(state.inventory.sharedRemaining) && state.inventory.sharedRemaining >= 0) && (state.inventory.rewardRemaining === void 0 || typeof state.inventory.rewardRemaining === "object" && state.inventory.rewardRemaining !== null && !Array.isArray(state.inventory.rewardRemaining)));
|
|
639
645
|
}
|
|
640
646
|
function isValidDate(value) {
|
|
641
647
|
return typeof value === "string" && !Number.isNaN(Date.parse(value));
|
|
@@ -680,6 +686,35 @@ var InMemoryStorage = class {
|
|
|
680
686
|
function storageKey(rallyId, userId) {
|
|
681
687
|
return `stamprally:${rallyId}:${userId ?? "anonymous"}`;
|
|
682
688
|
}
|
|
689
|
+
function createAnonymousSessionId(storage) {
|
|
690
|
+
const key = "stamprally:anonymous-session-id";
|
|
691
|
+
try {
|
|
692
|
+
const browserStorage = typeof window === "undefined" ? null : window.localStorage;
|
|
693
|
+
const value = storage?.getItem(key) ?? browserStorage?.getItem(key);
|
|
694
|
+
if (value !== null && value !== void 0 && isUuidV4(value)) return value;
|
|
695
|
+
const generated = randomUuidV4();
|
|
696
|
+
(storage ?? browserStorage)?.setItem(key, generated);
|
|
697
|
+
return generated;
|
|
698
|
+
} catch {
|
|
699
|
+
return randomUuidV4();
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
function isUuidV4(value) {
|
|
703
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
|
|
704
|
+
}
|
|
705
|
+
function randomUuidV4() {
|
|
706
|
+
const cryptoApi3 = globalThis.crypto;
|
|
707
|
+
if (cryptoApi3?.randomUUID !== void 0) return cryptoApi3.randomUUID();
|
|
708
|
+
if (cryptoApi3?.getRandomValues !== void 0) {
|
|
709
|
+
const bytes2 = cryptoApi3.getRandomValues(new Uint8Array(16));
|
|
710
|
+
bytes2[6] = (bytes2[6] ?? 0) & 15 | 64;
|
|
711
|
+
bytes2[8] = (bytes2[8] ?? 0) & 63 | 128;
|
|
712
|
+
const hex = Array.from(bytes2, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
713
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
714
|
+
}
|
|
715
|
+
const random = `${Date.now().toString(16)}${Math.random().toString(16).slice(2)}`.padEnd(32, "0").slice(0, 32);
|
|
716
|
+
return `${random.slice(0, 8)}-${random.slice(8, 12)}-4${random.slice(13, 16)}-8${random.slice(17, 20)}-${random.slice(20)}`;
|
|
717
|
+
}
|
|
683
718
|
var defaultStorageWarningHandler = (error) => {
|
|
684
719
|
console.warn(`[@stamprally/core] ${error.message}`, error);
|
|
685
720
|
};
|
|
@@ -1069,6 +1104,7 @@ var StampRallyClient = class {
|
|
|
1069
1104
|
#config;
|
|
1070
1105
|
#offlineQueue;
|
|
1071
1106
|
#userId;
|
|
1107
|
+
#anonymousSessionId;
|
|
1072
1108
|
#state = null;
|
|
1073
1109
|
#initialization = null;
|
|
1074
1110
|
#queue = Promise.resolve();
|
|
@@ -1076,7 +1112,8 @@ var StampRallyClient = class {
|
|
|
1076
1112
|
this.#config = config;
|
|
1077
1113
|
this.#options = isStorage(storageOrOptions) ? { storage: storageOrOptions } : storageOrOptions;
|
|
1078
1114
|
this.#storage = this.#options.storage ?? new InMemoryStorage();
|
|
1079
|
-
this.#
|
|
1115
|
+
this.#anonymousSessionId = this.#options.anonymousSessionId ?? createAnonymousSessionId();
|
|
1116
|
+
this.#userId = this.#options.userId ?? this.#anonymousSessionId;
|
|
1080
1117
|
this.#offlineQueue = this.#options.offlineQueue;
|
|
1081
1118
|
this.#offlineQueue?.setSyncResultListener((event) => this.#handleOfflineSyncResult(event));
|
|
1082
1119
|
}
|
|
@@ -1089,6 +1126,9 @@ var StampRallyClient = class {
|
|
|
1089
1126
|
getUserId() {
|
|
1090
1127
|
return this.#userId;
|
|
1091
1128
|
}
|
|
1129
|
+
getAnonymousSessionId() {
|
|
1130
|
+
return this.#anonymousSessionId;
|
|
1131
|
+
}
|
|
1092
1132
|
get syncState() {
|
|
1093
1133
|
return this.#offlineQueue?.syncState ?? "idle";
|
|
1094
1134
|
}
|
|
@@ -1126,11 +1166,12 @@ var StampRallyClient = class {
|
|
|
1126
1166
|
}
|
|
1127
1167
|
switchUser(newUserId) {
|
|
1128
1168
|
return this.#enqueue(async () => {
|
|
1129
|
-
|
|
1130
|
-
this.#userId
|
|
1169
|
+
const nextUserId = newUserId ?? this.#anonymousSessionId;
|
|
1170
|
+
if (this.#userId === nextUserId && this.#state !== null) return this.#state;
|
|
1171
|
+
this.#userId = nextUserId;
|
|
1131
1172
|
this.#state = null;
|
|
1132
1173
|
this.#initialization = null;
|
|
1133
|
-
await this.#offlineQueue?.switchUser(
|
|
1174
|
+
await this.#offlineQueue?.switchUser(nextUserId);
|
|
1134
1175
|
return this.initialize();
|
|
1135
1176
|
});
|
|
1136
1177
|
}
|
|
@@ -1198,7 +1239,8 @@ var StampRallyClient = class {
|
|
|
1198
1239
|
proofData,
|
|
1199
1240
|
idempotencyKey: options.idempotencyKey ?? id("check-in"),
|
|
1200
1241
|
now,
|
|
1201
|
-
state: current
|
|
1242
|
+
state: current,
|
|
1243
|
+
...this.#userId === this.#anonymousSessionId ? { anonymousSessionId: this.#anonymousSessionId } : {}
|
|
1202
1244
|
};
|
|
1203
1245
|
const remote = this.#options.syncAdapter?.checkIn;
|
|
1204
1246
|
if (options.sync !== false && remote !== void 0) {
|
|
@@ -1254,7 +1296,8 @@ var StampRallyClient = class {
|
|
|
1254
1296
|
idempotencyKey: options.idempotencyKey ?? id("claim"),
|
|
1255
1297
|
now,
|
|
1256
1298
|
options,
|
|
1257
|
-
state: current
|
|
1299
|
+
state: current,
|
|
1300
|
+
...this.#userId === this.#anonymousSessionId ? { anonymousSessionId: this.#anonymousSessionId } : {}
|
|
1258
1301
|
};
|
|
1259
1302
|
const remote = this.#options.syncAdapter?.claimReward;
|
|
1260
1303
|
if (options.sync !== false && remote !== void 0) {
|
|
@@ -1306,13 +1349,10 @@ var StampRallyClient = class {
|
|
|
1306
1349
|
const serverState = await adapter.sync({
|
|
1307
1350
|
rallyId: this.#config.id,
|
|
1308
1351
|
userId: this.#userId,
|
|
1309
|
-
state: this.#state ?? current
|
|
1310
|
-
|
|
1311
|
-
const localState = this.#state ?? current;
|
|
1312
|
-
const merged = this.#offlineQueue === void 0 ? serverState : resolveRallyStateConflict(serverState, localState, {
|
|
1313
|
-
policy: this.#offlineQueue.conflictPolicy
|
|
1352
|
+
state: this.#state ?? current,
|
|
1353
|
+
...this.#userId === this.#anonymousSessionId ? { anonymousSessionId: this.#anonymousSessionId } : {}
|
|
1314
1354
|
});
|
|
1315
|
-
const next = this.#reconcile(
|
|
1355
|
+
const next = this.#reconcile(serverState);
|
|
1316
1356
|
await this.#storage.save(next);
|
|
1317
1357
|
this.#state = next;
|
|
1318
1358
|
this.#emit(next);
|
|
@@ -1526,6 +1566,11 @@ function errorValue(value, fallbackCode) {
|
|
|
1526
1566
|
}
|
|
1527
1567
|
var syncLocks = /* @__PURE__ */ new Map();
|
|
1528
1568
|
var SYNC_LOCK_TTL_MS = 3e4;
|
|
1569
|
+
var DEFAULT_RETRY_OPTIONS = {
|
|
1570
|
+
maxRetries: 0,
|
|
1571
|
+
initialIntervalMs: 250,
|
|
1572
|
+
backoffMultiplier: 2
|
|
1573
|
+
};
|
|
1529
1574
|
function randomId() {
|
|
1530
1575
|
return globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
1531
1576
|
}
|
|
@@ -1544,6 +1589,7 @@ var OfflineQueue = class {
|
|
|
1544
1589
|
#syncPromise = null;
|
|
1545
1590
|
#syncResultListener;
|
|
1546
1591
|
#synchronizeInstances;
|
|
1592
|
+
#retryOptions;
|
|
1547
1593
|
#instanceId = randomId();
|
|
1548
1594
|
#lockStorage;
|
|
1549
1595
|
#storageListener;
|
|
@@ -1559,6 +1605,15 @@ var OfflineQueue = class {
|
|
|
1559
1605
|
this.#conflictPolicy = options.conflictPolicy ?? "server_wins";
|
|
1560
1606
|
this.#onSyncConflict = options.onSyncConflict;
|
|
1561
1607
|
this.#synchronizeInstances = options.synchronizeInstances ?? true;
|
|
1608
|
+
const retryOptions = {
|
|
1609
|
+
...DEFAULT_RETRY_OPTIONS,
|
|
1610
|
+
...options.retryOptions ?? options.syncRetryOptions ?? options.retry ?? {}
|
|
1611
|
+
};
|
|
1612
|
+
this.#retryOptions = {
|
|
1613
|
+
maxRetries: Math.max(0, Math.floor(retryOptions.maxRetries)),
|
|
1614
|
+
initialIntervalMs: Math.max(0, retryOptions.initialIntervalMs),
|
|
1615
|
+
backoffMultiplier: Math.max(1, retryOptions.backoffMultiplier)
|
|
1616
|
+
};
|
|
1562
1617
|
this.#lockStorage = options.storageLike ?? globalThis.localStorage ?? null;
|
|
1563
1618
|
if (this.#synchronizeInstances) this.#subscribeToExternalChanges();
|
|
1564
1619
|
}
|
|
@@ -1591,7 +1646,7 @@ var OfflineQueue = class {
|
|
|
1591
1646
|
}
|
|
1592
1647
|
async initialize() {
|
|
1593
1648
|
if (this.#loaded) return;
|
|
1594
|
-
this.#operations =
|
|
1649
|
+
this.#operations = (await this.#storage.load(this.#storageKey())).map(normalizeOperation);
|
|
1595
1650
|
this.#loaded = true;
|
|
1596
1651
|
}
|
|
1597
1652
|
/** Releases browser listeners when the queue is no longer used. */
|
|
@@ -1636,7 +1691,7 @@ var OfflineQueue = class {
|
|
|
1636
1691
|
await this.initialize();
|
|
1637
1692
|
const id2 = operationId(operation);
|
|
1638
1693
|
if (this.#operations.some((item) => operationId(item) === id2)) return;
|
|
1639
|
-
this.#operations = [...this.#operations, operation];
|
|
1694
|
+
this.#operations = [...this.#operations, { ...operation, status: "PENDING", attempts: 0 }];
|
|
1640
1695
|
await this.#storage.save(this.#storageKey(), this.#operations);
|
|
1641
1696
|
this.#announceChange();
|
|
1642
1697
|
}
|
|
@@ -1666,6 +1721,33 @@ var OfflineQueue = class {
|
|
|
1666
1721
|
return this.sync(sender);
|
|
1667
1722
|
}
|
|
1668
1723
|
async #run(sender) {
|
|
1724
|
+
const locks = globalThis.navigator?.locks;
|
|
1725
|
+
if (locks !== void 0) {
|
|
1726
|
+
let callbackStarted = false;
|
|
1727
|
+
try {
|
|
1728
|
+
const acquired = await locks.request(
|
|
1729
|
+
`stamprally:${this.#storageKey()}:sync`,
|
|
1730
|
+
{ ifAvailable: true },
|
|
1731
|
+
async (lock) => {
|
|
1732
|
+
if (lock === null) {
|
|
1733
|
+
await this.#reloadFromStorage();
|
|
1734
|
+
this.#state = "idle";
|
|
1735
|
+
return false;
|
|
1736
|
+
}
|
|
1737
|
+
callbackStarted = true;
|
|
1738
|
+
await this.#runWithStorageLock(sender);
|
|
1739
|
+
return true;
|
|
1740
|
+
}
|
|
1741
|
+
);
|
|
1742
|
+
if (!acquired) return;
|
|
1743
|
+
return;
|
|
1744
|
+
} catch (error) {
|
|
1745
|
+
if (callbackStarted) throw error;
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
await this.#runWithStorageLock(sender);
|
|
1749
|
+
}
|
|
1750
|
+
async #runWithStorageLock(sender) {
|
|
1669
1751
|
this.#state = "syncing";
|
|
1670
1752
|
this.#error = null;
|
|
1671
1753
|
if (!this.#acquireSyncLock()) {
|
|
@@ -1677,21 +1759,36 @@ var OfflineQueue = class {
|
|
|
1677
1759
|
while (this.#operations.length > 0) {
|
|
1678
1760
|
const operation = this.#operations[0];
|
|
1679
1761
|
if (operation === void 0) break;
|
|
1680
|
-
let
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1762
|
+
let attempt = 0;
|
|
1763
|
+
let response;
|
|
1764
|
+
while (true) {
|
|
1765
|
+
await this.#updateOperationStatus("IN_FLIGHT", attempt);
|
|
1766
|
+
try {
|
|
1767
|
+
response = this.#normalizeResponse(await sender(operation));
|
|
1768
|
+
} catch (cause) {
|
|
1769
|
+
response = {
|
|
1770
|
+
status: "RETRYABLE_ERROR",
|
|
1771
|
+
error: errorValue(cause, "RETRYABLE_ERROR")
|
|
1772
|
+
};
|
|
1773
|
+
}
|
|
1774
|
+
if (response.status !== "RETRYABLE_ERROR") break;
|
|
1688
1775
|
const error2 = errorValue(response.error ?? response.reason, "RETRYABLE_ERROR");
|
|
1776
|
+
await this.#updateOperationStatus("PENDING", attempt + 1);
|
|
1689
1777
|
await this.#syncResultListener?.({ operation, status: response.status, error: error2 });
|
|
1690
|
-
throw new Error(error2.message);
|
|
1778
|
+
if (attempt >= this.#retryOptions.maxRetries) throw new Error(error2.message);
|
|
1779
|
+
const interval = this.#retryOptions.initialIntervalMs * this.#retryOptions.backoffMultiplier ** attempt;
|
|
1780
|
+
await new Promise((resolve) => setTimeout(resolve, Math.max(0, interval)));
|
|
1781
|
+
attempt += 1;
|
|
1691
1782
|
}
|
|
1692
1783
|
const result = response.result;
|
|
1693
1784
|
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);
|
|
1694
1785
|
const error = response.status === "REJECTED_PERMANENT" ? errorValue(response.error ?? response.reason, "REJECTED_PERMANENT") : void 0;
|
|
1786
|
+
await this.#updateOperationStatus(
|
|
1787
|
+
response.status === "ACCEPTED" ? "ACCEPTED" : "REJECTED",
|
|
1788
|
+
attempt + 1
|
|
1789
|
+
);
|
|
1790
|
+
const fallbackState = response.status === "REJECTED_PERMANENT" ? operation.request.state : void 0;
|
|
1791
|
+
const eventState = state ?? fallbackState;
|
|
1695
1792
|
this.#operations = this.#operations.slice(1);
|
|
1696
1793
|
await this.#storage.save(this.#storageKey(), this.#operations);
|
|
1697
1794
|
this.#announceChange();
|
|
@@ -1700,7 +1797,7 @@ var OfflineQueue = class {
|
|
|
1700
1797
|
...result === void 0 ? {} : { result },
|
|
1701
1798
|
status: response.status,
|
|
1702
1799
|
...error === void 0 ? {} : { error },
|
|
1703
|
-
...
|
|
1800
|
+
...eventState === void 0 ? {} : { state: eventState }
|
|
1704
1801
|
});
|
|
1705
1802
|
}
|
|
1706
1803
|
this.#state = "idle";
|
|
@@ -1712,6 +1809,13 @@ var OfflineQueue = class {
|
|
|
1712
1809
|
this.#releaseSyncLock();
|
|
1713
1810
|
}
|
|
1714
1811
|
}
|
|
1812
|
+
async #updateOperationStatus(status, attempts) {
|
|
1813
|
+
const operation = this.#operations[0];
|
|
1814
|
+
if (operation === void 0) return;
|
|
1815
|
+
this.#operations = [{ ...operation, status, attempts }, ...this.#operations.slice(1)];
|
|
1816
|
+
await this.#storage.save(this.#storageKey(), this.#operations);
|
|
1817
|
+
this.#announceChange();
|
|
1818
|
+
}
|
|
1715
1819
|
#subscribeToExternalChanges() {
|
|
1716
1820
|
const windowLike = globalThis.window;
|
|
1717
1821
|
if (windowLike !== void 0) {
|
|
@@ -1739,7 +1843,7 @@ var OfflineQueue = class {
|
|
|
1739
1843
|
async #reloadFromStorage() {
|
|
1740
1844
|
if (this.#state === "syncing") return;
|
|
1741
1845
|
try {
|
|
1742
|
-
this.#operations =
|
|
1846
|
+
this.#operations = (await this.#storage.load(this.#storageKey())).map(normalizeOperation);
|
|
1743
1847
|
this.#loaded = true;
|
|
1744
1848
|
} catch {
|
|
1745
1849
|
}
|
|
@@ -1809,6 +1913,13 @@ var OfflineQueue = class {
|
|
|
1809
1913
|
return resolveRallyStateConflict(serverState, localState, { policy });
|
|
1810
1914
|
}
|
|
1811
1915
|
};
|
|
1916
|
+
function normalizeOperation(operation) {
|
|
1917
|
+
return {
|
|
1918
|
+
...operation,
|
|
1919
|
+
status: operation.status === "IN_FLIGHT" ? "PENDING" : operation.status ?? "PENDING",
|
|
1920
|
+
attempts: operation.attempts ?? 0
|
|
1921
|
+
};
|
|
1922
|
+
}
|
|
1812
1923
|
|
|
1813
1924
|
// src/crypto/token.ts
|
|
1814
1925
|
var encoder = new TextEncoder();
|
|
@@ -2377,6 +2488,8 @@ function condition(value, path, errors, isPublic) {
|
|
|
2377
2488
|
finiteNumber(value, "latitude", path, errors);
|
|
2378
2489
|
finiteNumber(value, "longitude", path, errors);
|
|
2379
2490
|
finiteNumber(value, "radiusMeters", path, errors, 0);
|
|
2491
|
+
if (typeof value.radiusMeters === "number" && value.radiusMeters <= 0)
|
|
2492
|
+
add(errors, `${path}.radiusMeters`, "Expected a radius greater than 0.", "out_of_range");
|
|
2380
2493
|
if (typeof value.latitude === "number" && (value.latitude < -90 || value.latitude > 90))
|
|
2381
2494
|
add(errors, `${path}.latitude`, "Expected a latitude between -90 and 90.", "out_of_range");
|
|
2382
2495
|
if (typeof value.longitude === "number" && (value.longitude < -180 || value.longitude > 180))
|
|
@@ -2787,6 +2900,6 @@ async function verifySnapshotToken(token, secretKey, now = Date.now()) {
|
|
|
2787
2900
|
}
|
|
2788
2901
|
}
|
|
2789
2902
|
|
|
2790
|
-
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, validateRallyConfigRelations, verifyPasscode, verifySecureToken, verifySnapshotToken };
|
|
2903
|
+
export { ConfigValidationError, DEFAULT_SHEET_THEME, MemoryQueueStorage as InMemoryOfflineQueueStorage, InMemoryStorage, IndexedDBAdapter, IndexedDBOfflineQueueStorage, LocalStorageAdapter, OfflineQueue, StampRallyClient, StorageAdapterError, THEME_PRESETS, 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 };
|
|
2791
2904
|
//# sourceMappingURL=index.js.map
|
|
2792
2905
|
//# sourceMappingURL=index.js.map
|