@stamprally/react 0.4.0 → 0.5.1
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.cjs +95 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +96 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -10,6 +10,9 @@ function getServerSnapshot() {
|
|
|
10
10
|
function toError(error) {
|
|
11
11
|
return error instanceof Error ? error : new Error(String(error));
|
|
12
12
|
}
|
|
13
|
+
function isObject(value) {
|
|
14
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
15
|
+
}
|
|
13
16
|
function applyOptimisticAcquire(currentState, action) {
|
|
14
17
|
if (currentState === null || currentState.records.some((record) => record.stampId === action.stampId)) {
|
|
15
18
|
return currentState;
|
|
@@ -386,6 +389,95 @@ function useStampRally(client, options = {}) {
|
|
|
386
389
|
},
|
|
387
390
|
[client]
|
|
388
391
|
);
|
|
392
|
+
const exportRecoveryToken = react.useCallback(
|
|
393
|
+
async (secretKey, tokenOptions = {}) => {
|
|
394
|
+
const state = client.getState();
|
|
395
|
+
if (state === null)
|
|
396
|
+
throw new Error("Cannot export recovery token before the rally is initialized.");
|
|
397
|
+
return core.createSecureToken(
|
|
398
|
+
{
|
|
399
|
+
type: "recovery",
|
|
400
|
+
rallyId: state.rallyId,
|
|
401
|
+
state,
|
|
402
|
+
exportedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
403
|
+
},
|
|
404
|
+
secretKey,
|
|
405
|
+
{ encrypt: tokenOptions.encrypt ?? true, ...tokenOptions }
|
|
406
|
+
);
|
|
407
|
+
},
|
|
408
|
+
[client]
|
|
409
|
+
);
|
|
410
|
+
const importRecoveryToken = react.useCallback(
|
|
411
|
+
async (token, secretKey) => {
|
|
412
|
+
setClientError(null);
|
|
413
|
+
setIsOperationPending(true);
|
|
414
|
+
try {
|
|
415
|
+
const verified = await core.verifySecureToken(token, secretKey);
|
|
416
|
+
if (!verified.ok || verified.payload.type !== "recovery" || verified.payload.rallyId !== client.getConfig().id) {
|
|
417
|
+
setIsOperationPending(false);
|
|
418
|
+
return false;
|
|
419
|
+
}
|
|
420
|
+
const candidate = verified.payload.state;
|
|
421
|
+
if (!core.isStampRallyState(candidate)) {
|
|
422
|
+
setIsOperationPending(false);
|
|
423
|
+
return false;
|
|
424
|
+
}
|
|
425
|
+
await client.restore(candidate);
|
|
426
|
+
setIsOperationPending(false);
|
|
427
|
+
return true;
|
|
428
|
+
} catch (error2) {
|
|
429
|
+
setClientError({ client, value: toError(error2) });
|
|
430
|
+
setIsOperationPending(false);
|
|
431
|
+
return false;
|
|
432
|
+
}
|
|
433
|
+
},
|
|
434
|
+
[client]
|
|
435
|
+
);
|
|
436
|
+
const syncWithServer = react.useCallback(
|
|
437
|
+
async (serverEndpoint, authHeader) => {
|
|
438
|
+
if (typeof fetch !== "function")
|
|
439
|
+
throw new Error("Fetch API is unavailable in this environment.");
|
|
440
|
+
setClientError(null);
|
|
441
|
+
setIsOperationPending(true);
|
|
442
|
+
try {
|
|
443
|
+
const endpoint = serverEndpoint.replace(/\/$/u, "").endsWith("/sync") ? serverEndpoint : `${serverEndpoint.replace(/\/$/u, "")}/api/sync`;
|
|
444
|
+
const response = await fetch(endpoint, {
|
|
445
|
+
method: "POST",
|
|
446
|
+
headers: {
|
|
447
|
+
"content-type": "application/json",
|
|
448
|
+
...authHeader === void 0 ? {} : { authorization: authHeader }
|
|
449
|
+
},
|
|
450
|
+
body: JSON.stringify({
|
|
451
|
+
userId: options.serverUserId ?? "anonymous",
|
|
452
|
+
queue: offlineQueue.map((request) => ({
|
|
453
|
+
...request,
|
|
454
|
+
userId: options.serverUserId ?? "anonymous",
|
|
455
|
+
spotId: request.stampId,
|
|
456
|
+
claimMethod: request.context.type,
|
|
457
|
+
proofData: request.context.type === "token" ? { token: request.context.token } : request.context.type === "geo" ? {
|
|
458
|
+
latitude: request.context.currentLatitude,
|
|
459
|
+
longitude: request.context.currentLongitude
|
|
460
|
+
} : void 0
|
|
461
|
+
}))
|
|
462
|
+
})
|
|
463
|
+
});
|
|
464
|
+
const payload = await response.json();
|
|
465
|
+
if (!response.ok || !isObject(payload) || payload.ok !== true || !core.isStampRallyState(payload.state)) {
|
|
466
|
+
throw new Error("Server synchronization was rejected.");
|
|
467
|
+
}
|
|
468
|
+
await client.restore(payload.state);
|
|
469
|
+
queuedMetadata.current.clear();
|
|
470
|
+
setOfflineQueue([]);
|
|
471
|
+
client.notifySyncCompleted(payload.state);
|
|
472
|
+
} catch (error2) {
|
|
473
|
+
setClientError({ client, value: toError(error2) });
|
|
474
|
+
throw error2;
|
|
475
|
+
} finally {
|
|
476
|
+
setIsOperationPending(false);
|
|
477
|
+
}
|
|
478
|
+
},
|
|
479
|
+
[client, offlineQueue, options.serverUserId]
|
|
480
|
+
);
|
|
389
481
|
const isLoading = rawState === null && (clientStatus.client !== client || clientStatus.isInitializing);
|
|
390
482
|
const error = clientError?.client === client ? clientError.value : null;
|
|
391
483
|
const flushQueue = react.useCallback(async () => {
|
|
@@ -481,6 +573,9 @@ function useStampRally(client, options = {}) {
|
|
|
481
573
|
redeem,
|
|
482
574
|
exportRecoveryCode,
|
|
483
575
|
importRecoveryCode,
|
|
576
|
+
exportRecoveryToken,
|
|
577
|
+
importRecoveryToken,
|
|
578
|
+
syncWithServer,
|
|
484
579
|
offlineQueue,
|
|
485
580
|
queuedCount: offlineQueue.length,
|
|
486
581
|
flushQueue
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/useStampRally.ts"],"names":["useState","useRef","useCallback","useSyncExternalStore","useTransition","useEffect","result","consumeReward","exportProgressToken","importProgressToken"],"mappings":"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","file":"index.cjs","sourcesContent":["import type {\n ConsumeResult,\n ProcessStampValue,\n Result,\n RewardConsumeError,\n RewardState,\n StampError,\n StampRallyClient,\n StampRallyState,\n VerificationContext,\n} from \"@stamprally/core\";\nimport { consumeReward, exportProgressToken, importProgressToken } from \"@stamprally/core\";\nimport {\n useCallback,\n useEffect,\n useRef,\n useState,\n useSyncExternalStore,\n useTransition,\n} from \"react\";\n\ninterface OptimisticAcquire {\n readonly stampId: string;\n readonly acquiredAt: string;\n}\n\ninterface ClientStatus {\n readonly client: StampRallyClient;\n readonly isInitializing: boolean;\n}\n\ninterface ClientError {\n readonly client: StampRallyClient;\n readonly value: StampError | RewardConsumeError | Error;\n}\n\ninterface QueuedRequestMetadata {\n readonly previousState: StampRallyState | null;\n readonly result: ProcessStampValue;\n}\n\nexport interface CheckInRequest {\n readonly stampId: string;\n readonly context: VerificationContext;\n readonly now: string;\n readonly idempotencyKey: string;\n}\n\nexport interface SyncAdapter {\n readonly isOnline?: boolean | (() => boolean);\n readonly onBeforeCheckIn?: (request: CheckInRequest) => unknown;\n readonly onServerVerify?: (request: CheckInRequest) => unknown;\n readonly onStateChange?: (state: StampRallyState) => void;\n}\n\nexport interface StampRallyEventHandlers {\n readonly onStampClaimed?: (record: StampRallyState[\"records\"][number]) => void;\n readonly onRewardUnlocked?: (rewardId: string) => void;\n readonly onRewardConsumed?: (rewardId: string) => void;\n}\n\nexport interface UseStampRallyOptions {\n readonly syncAdapter?: SyncAdapter;\n readonly events?: StampRallyEventHandlers;\n readonly onStampClaimed?: StampRallyEventHandlers[\"onStampClaimed\"];\n readonly onRewardUnlocked?: StampRallyEventHandlers[\"onRewardUnlocked\"];\n readonly onRewardConsumed?: StampRallyEventHandlers[\"onRewardConsumed\"];\n}\n\nexport interface RedeemOptions {\n readonly passcode?: string;\n readonly staffId?: string;\n}\n\nexport interface UseStampRallyReturn {\n readonly state: StampRallyState | null;\n readonly isLoading: boolean;\n readonly isPending: boolean;\n readonly error: StampError | RewardConsumeError | Error | null;\n readonly rewardsState: ReadonlyArray<RewardState>;\n readonly acquire: (\n stampId: string,\n context: VerificationContext,\n now?: string,\n idempotencyKey?: string,\n ) => Promise<Result<ProcessStampValue, StampError>>;\n readonly reset: (now?: string) => Promise<StampRallyState>;\n readonly redeem: (rewardId: string, options?: RedeemOptions) => Promise<ConsumeResult>;\n readonly exportRecoveryCode: () => string;\n readonly importRecoveryCode: (token: string) => Promise<boolean>;\n readonly offlineQueue: ReadonlyArray<CheckInRequest>;\n readonly queuedCount: number;\n readonly flushQueue: () => Promise<void>;\n}\n\n/** @deprecated Use UseStampRallyReturn instead. */\nexport type UseStampRallyValue = UseStampRallyReturn;\n\nfunction getServerSnapshot(): null {\n return null;\n}\n\nfunction toError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n}\n\nfunction applyOptimisticAcquire(\n currentState: StampRallyState | null,\n action: OptimisticAcquire,\n): StampRallyState | null {\n if (\n currentState === null ||\n currentState.records.some((record) => record.stampId === action.stampId)\n ) {\n return currentState;\n }\n\n return {\n ...currentState,\n records: [...currentState.records, { stampId: action.stampId, acquiredAt: action.acquiredAt }],\n updatedAt: action.acquiredAt,\n };\n}\n\nfunction createIdempotencyKey(): string {\n const cryptoApi = globalThis.crypto;\n if (cryptoApi !== undefined && typeof cryptoApi.randomUUID === \"function\") {\n return cryptoApi.randomUUID();\n }\n return `stamp-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n}\n\nfunction isOnline(adapter: SyncAdapter | undefined): boolean {\n if (adapter?.isOnline === undefined) {\n return typeof navigator === \"undefined\" || navigator.onLine !== false;\n }\n return typeof adapter.isOnline === \"function\" ? adapter.isOnline() : adapter.isOnline;\n}\n\nfunction isRejectedVerification(value: unknown): boolean {\n return (\n value === false ||\n (typeof value === \"object\" && value !== null && \"ok\" in value && value.ok === false)\n );\n}\n\nfunction isNetworkFailure(error: unknown): boolean {\n if (typeof navigator !== \"undefined\" && navigator.onLine === false) return true;\n if (!(error instanceof Error)) return true;\n return /aborted|connection|fetch|network|offline|timeout/i.test(`${error.name} ${error.message}`);\n}\n\nfunction notifyAcquisitionEvents(\n result: ProcessStampValue,\n events: StampRallyEventHandlers | undefined,\n onStampClaimed: StampRallyEventHandlers[\"onStampClaimed\"] | undefined,\n onRewardUnlocked: StampRallyEventHandlers[\"onRewardUnlocked\"] | undefined,\n): void {\n for (const event of result.events) {\n if (event.type === \"stampAcquired\") {\n (events?.onStampClaimed ?? onStampClaimed)?.(event.record);\n }\n if (event.type === \"rewardUnlocked\") {\n (events?.onRewardUnlocked ?? onRewardUnlocked)?.(event.rewardId);\n }\n }\n}\n\nexport function useStampRally(\n client: StampRallyClient,\n options: UseStampRallyOptions = {},\n): UseStampRallyReturn {\n const syncAdapter = options.syncAdapter;\n const events = options.events;\n const [offlineQueue, setOfflineQueue] = useState<ReadonlyArray<CheckInRequest>>([]);\n const queuedMetadata = useRef(new Map<string, QueuedRequestMetadata>());\n const isFlushingQueue = useRef(false);\n const activeClient = useRef(client);\n const subscribe = useCallback(\n (onStoreChange: () => void) => client.subscribe(() => onStoreChange()),\n [client],\n );\n const getSnapshot = useCallback(() => client.getState(), [client]);\n const rawState = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n const [clientStatus, setClientStatus] = useState<ClientStatus>(() => ({\n client,\n isInitializing: client.getState() === null,\n }));\n const [clientError, setClientError] = useState<ClientError | null>(null);\n const [isPending] = useTransition();\n const [isOperationPending, setIsOperationPending] = useState(false);\n const [optimisticState, setOptimisticState] = useState<StampRallyState | null>(rawState);\n\n useEffect(() => {\n setOptimisticState(rawState);\n }, [rawState]);\n\n useEffect(() => {\n if (activeClient.current === client) return;\n activeClient.current = client;\n setOfflineQueue([]);\n queuedMetadata.current.clear();\n }, [client]);\n\n useEffect(() => {\n if (syncAdapter?.onStateChange === undefined) return;\n return client.subscribe(syncAdapter.onStateChange);\n }, [client, syncAdapter]);\n\n useEffect(() => {\n let active = true;\n setClientError(null);\n if (rawState !== null) {\n setClientStatus({ client, isInitializing: false });\n return () => {\n active = false;\n };\n }\n\n setClientStatus({ client, isInitializing: true });\n\n void client\n .init()\n .catch((initializationError: unknown) => {\n if (active) {\n setClientError({ client, value: toError(initializationError) });\n }\n })\n .finally(() => {\n if (active) {\n setClientStatus({ client, isInitializing: false });\n }\n });\n\n return () => {\n active = false;\n };\n }, [client, rawState]);\n\n const queueRequest = useCallback(\n (request: CheckInRequest, metadata?: QueuedRequestMetadata): void => {\n if (metadata !== undefined) queuedMetadata.current.set(request.idempotencyKey, metadata);\n setOfflineQueue((current) =>\n current.some((item) => item.idempotencyKey === request.idempotencyKey)\n ? current\n : [...current, request],\n );\n },\n [],\n );\n\n const acquire = useCallback(\n (\n stampId: string,\n context: VerificationContext,\n now?: string,\n idempotencyKey?: string,\n ): Promise<Result<ProcessStampValue, StampError>> => {\n const acquiredAt = now ?? new Date().toISOString();\n const request: CheckInRequest = {\n stampId,\n context,\n now: acquiredAt,\n idempotencyKey: idempotencyKey ?? createIdempotencyKey(),\n };\n setClientError(null);\n\n if (!isOnline(syncAdapter)) {\n queueRequest(request);\n const queued: Result<ProcessStampValue, StampError> = {\n ok: false,\n error: { code: \"OFFLINE_QUEUED\", stampId, idempotencyKey: request.idempotencyKey },\n };\n setClientError({ client, value: queued.error });\n return Promise.resolve(queued);\n }\n\n setIsOperationPending(true);\n setOptimisticState((current) => applyOptimisticAcquire(current, { stampId, acquiredAt }));\n return new Promise((resolve, reject) => {\n void (async () => {\n try {\n const before = await syncAdapter?.onBeforeCheckIn?.(request);\n if (before === false) {\n const rejected: Result<ProcessStampValue, StampError> = {\n ok: false,\n error: { code: \"INVALID_PROOF\", stampId },\n };\n setClientError({ client, value: rejected.error });\n setIsOperationPending(false);\n resolve(rejected);\n return;\n }\n const previousState = client.getState();\n const result = await client.acquire(stampId, context, acquiredAt);\n if (result.ok) {\n let verified: unknown;\n try {\n verified = await syncAdapter?.onServerVerify?.(request);\n } catch (verificationError) {\n if (!isNetworkFailure(verificationError)) throw verificationError;\n queueRequest(request, { previousState, result: result.value });\n const queued: Result<ProcessStampValue, StampError> = {\n ok: false,\n error: {\n code: \"OFFLINE_QUEUED\",\n stampId,\n idempotencyKey: request.idempotencyKey,\n },\n };\n setClientError({ client, value: toError(verificationError) });\n setOptimisticState(client.getState());\n setIsOperationPending(false);\n resolve(queued);\n return;\n }\n if (isRejectedVerification(verified)) {\n await client.restore(rawState ?? client.getState() ?? result.value.nextState);\n const rejected: Result<ProcessStampValue, StampError> = {\n ok: false,\n error: { code: \"INVALID_PROOF\", stampId },\n };\n setClientError({ client, value: rejected.error });\n setOptimisticState(client.getState());\n setIsOperationPending(false);\n resolve(rejected);\n return;\n }\n notifyAcquisitionEvents(\n result.value,\n events,\n options.onStampClaimed,\n options.onRewardUnlocked,\n );\n }\n if (!result.ok) {\n setClientError({ client, value: result.error });\n setOptimisticState(client.getState());\n }\n setIsOperationPending(false);\n resolve(result);\n } catch (acquireError) {\n if (isNetworkFailure(acquireError)) {\n queueRequest(request);\n const queued: Result<ProcessStampValue, StampError> = {\n ok: false,\n error: { code: \"OFFLINE_QUEUED\", stampId, idempotencyKey: request.idempotencyKey },\n };\n setClientError({ client, value: queued.error });\n setOptimisticState(client.getState());\n setIsOperationPending(false);\n resolve(queued);\n return;\n }\n const normalizedError = toError(acquireError);\n setClientError({ client, value: normalizedError });\n setOptimisticState(client.getState());\n setIsOperationPending(false);\n reject(normalizedError);\n }\n })();\n });\n },\n [\n client,\n events,\n options.onRewardUnlocked,\n options.onStampClaimed,\n queueRequest,\n rawState,\n syncAdapter,\n ],\n );\n\n const reset = useCallback(\n (now?: string): Promise<StampRallyState> => {\n setClientError(null);\n setIsOperationPending(true);\n\n return new Promise((resolve, reject) => {\n void (async () => {\n try {\n const nextState = now === undefined ? await client.reset() : await client.reset(now);\n setIsOperationPending(false);\n resolve(nextState);\n } catch (resetError) {\n const normalizedError = toError(resetError);\n setClientError({ client, value: normalizedError });\n setIsOperationPending(false);\n reject(normalizedError);\n }\n })();\n });\n },\n [client],\n );\n\n const redeem = useCallback(\n (rewardId: string, redeemOptions: RedeemOptions = {}): Promise<ConsumeResult> => {\n setClientError(null);\n setIsOperationPending(true);\n\n return new Promise((resolve, reject) => {\n void (async () => {\n const reward = client.getConfig().rewards?.find((item) => item.id === rewardId);\n if (reward === undefined) {\n const result: ConsumeResult = {\n ok: false,\n error: { code: \"REWARD_NOT_FOUND\", rewardId },\n };\n setClientError({ client, value: result.error });\n setIsOperationPending(false);\n resolve(result);\n return;\n }\n\n const currentState = client.getState();\n const currentRewardState = currentState?.rewards?.find(\n (state) => state.rewardId === rewardId,\n );\n if (currentState === null || currentRewardState === undefined) {\n const result: ConsumeResult = {\n ok: false,\n error: { code: \"NOT_AVAILABLE\", rewardId },\n };\n setClientError({ client, value: result.error });\n setIsOperationPending(false);\n resolve(result);\n return;\n }\n\n const result = consumeReward({\n reward,\n currentState: currentRewardState,\n now: new Date().toISOString(),\n ...(redeemOptions.passcode === undefined\n ? {}\n : { inputPasscode: redeemOptions.passcode }),\n ...(redeemOptions.staffId === undefined ? {} : { staffId: redeemOptions.staffId }),\n });\n if (!result.ok) {\n setClientError({ client, value: result.error });\n setIsOperationPending(false);\n resolve(result);\n return;\n }\n\n if (result.value === currentRewardState) {\n setIsOperationPending(false);\n resolve(result);\n return;\n }\n\n const nextState: StampRallyState = {\n ...currentState,\n rewards: (currentState.rewards ?? []).map((state) =>\n state.rewardId === rewardId ? result.value : state,\n ),\n updatedAt: result.value.consumedAt ?? currentState.updatedAt,\n };\n\n try {\n await client.restore(nextState);\n (events?.onRewardConsumed ?? options.onRewardConsumed)?.(rewardId);\n setIsOperationPending(false);\n resolve(result);\n } catch (redeemError) {\n const normalizedError = toError(redeemError);\n setClientError({ client, value: normalizedError });\n setIsOperationPending(false);\n reject(normalizedError);\n }\n })();\n });\n },\n [client, events, options.onRewardConsumed],\n );\n\n const exportRecoveryCode = useCallback((): string => {\n const state = client.getState();\n if (state === null) {\n throw new Error(\"Cannot export recovery code before the rally is initialized.\");\n }\n\n return exportProgressToken({\n version: 1,\n rallyId: state.rallyId,\n stamps: state.records,\n rewards: state.rewards ?? [],\n exportedAt: new Date().toISOString(),\n });\n }, [client]);\n\n const importRecoveryCode = useCallback(\n (token: string): Promise<boolean> => {\n setClientError(null);\n setIsOperationPending(true);\n\n return new Promise((resolve, reject) => {\n void (async () => {\n const config = client.getConfig();\n const snapshot = importProgressToken(token, config.id);\n if (snapshot === null) {\n setIsOperationPending(false);\n resolve(false);\n return;\n }\n\n const stampIds = new Set(config.stamps.map((stamp) => stamp.id));\n const rewardIds = new Set((config.rewards ?? []).map((reward) => reward.id));\n const importedStampIds = new Set<string>();\n const importedRewardIds = new Set<string>();\n const stamps = snapshot.stamps.filter((record) => {\n if (!stampIds.has(record.stampId) || importedStampIds.has(record.stampId)) return false;\n importedStampIds.add(record.stampId);\n return true;\n });\n const rewards = snapshot.rewards.filter((state) => {\n if (!rewardIds.has(state.rewardId) || importedRewardIds.has(state.rewardId))\n return false;\n importedRewardIds.add(state.rewardId);\n return true;\n });\n\n try {\n await client.restore({\n rallyId: config.id,\n records: stamps,\n ...(config.rewards === undefined && rewards.length === 0 ? {} : { rewards }),\n updatedAt: snapshot.exportedAt,\n });\n setIsOperationPending(false);\n resolve(true);\n } catch (importError) {\n const normalizedError = toError(importError);\n setClientError({ client, value: normalizedError });\n setIsOperationPending(false);\n reject(normalizedError);\n }\n })();\n });\n },\n [client],\n );\n\n const isLoading =\n rawState === null && (clientStatus.client !== client || clientStatus.isInitializing);\n const error = clientError?.client === client ? clientError.value : null;\n\n const flushQueue = useCallback(async (): Promise<void> => {\n if (!isOnline(syncAdapter) || isFlushingQueue.current) return;\n isFlushingQueue.current = true;\n setIsOperationPending(true);\n try {\n for (const request of offlineQueue) {\n if (!isOnline(syncAdapter)) break;\n const metadata = queuedMetadata.current.get(request.idempotencyKey);\n const previousState = metadata?.previousState ?? client.getState();\n try {\n const before = await syncAdapter?.onBeforeCheckIn?.(request);\n if (before === false) {\n if (metadata !== undefined && previousState !== null)\n await client.restore(previousState);\n setClientError({ client, value: { code: \"INVALID_PROOF\", stampId: request.stampId } });\n queuedMetadata.current.delete(request.idempotencyKey);\n setOfflineQueue((current) =>\n current.filter((item) => item.idempotencyKey !== request.idempotencyKey),\n );\n continue;\n }\n\n let result = metadata?.result;\n if (result === undefined) {\n const localResult = await client.acquire(request.stampId, request.context, request.now);\n if (!localResult.ok) {\n setClientError({ client, value: localResult.error });\n queuedMetadata.current.delete(request.idempotencyKey);\n setOfflineQueue((current) =>\n current.filter((item) => item.idempotencyKey !== request.idempotencyKey),\n );\n continue;\n }\n result = localResult.value;\n queuedMetadata.current.set(request.idempotencyKey, {\n previousState,\n result,\n });\n }\n\n const verified = await syncAdapter?.onServerVerify?.(request);\n if (isRejectedVerification(verified)) {\n if (previousState !== null) await client.restore(previousState);\n setClientError({\n client,\n value: { code: \"INVALID_PROOF\", stampId: request.stampId },\n });\n } else {\n notifyAcquisitionEvents(\n result,\n events,\n options.onStampClaimed,\n options.onRewardUnlocked,\n );\n }\n queuedMetadata.current.delete(request.idempotencyKey);\n setOfflineQueue((current) =>\n current.filter((item) => item.idempotencyKey !== request.idempotencyKey),\n );\n } catch (flushError) {\n if (isNetworkFailure(flushError)) {\n setClientError({ client, value: toError(flushError) });\n break;\n }\n setClientError({ client, value: toError(flushError) });\n queuedMetadata.current.delete(request.idempotencyKey);\n setOfflineQueue((current) =>\n current.filter((item) => item.idempotencyKey !== request.idempotencyKey),\n );\n }\n }\n } finally {\n isFlushingQueue.current = false;\n setIsOperationPending(false);\n }\n }, [client, events, offlineQueue, options.onRewardUnlocked, options.onStampClaimed, syncAdapter]);\n\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n const handleOnline = (): void => {\n void flushQueue();\n };\n window.addEventListener(\"online\", handleOnline);\n return () => window.removeEventListener(\"online\", handleOnline);\n }, [flushQueue]);\n\n return {\n state: optimisticState,\n isLoading,\n isPending: isPending || isOperationPending,\n error,\n rewardsState: optimisticState?.rewards ?? [],\n acquire,\n reset,\n redeem,\n exportRecoveryCode,\n importRecoveryCode,\n offlineQueue,\n queuedCount: offlineQueue.length,\n flushQueue,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/useStampRally.ts"],"names":["useState","useRef","useCallback","useSyncExternalStore","useTransition","useEffect","result","consumeReward","exportProgressToken","importProgressToken","createSecureToken","verifySecureToken","isStampRallyState","error"],"mappings":"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","file":"index.cjs","sourcesContent":["import type {\n ConsumeResult,\n ProcessStampValue,\n Result,\n RewardConsumeError,\n RewardState,\n SecureTokenOptions,\n StampError,\n StampRallyClient,\n StampRallyState,\n VerificationContext,\n} from \"@stamprally/core\";\nimport {\n consumeReward,\n createSecureToken,\n exportProgressToken,\n importProgressToken,\n isStampRallyState,\n verifySecureToken,\n} from \"@stamprally/core\";\nimport {\n useCallback,\n useEffect,\n useRef,\n useState,\n useSyncExternalStore,\n useTransition,\n} from \"react\";\n\ninterface OptimisticAcquire {\n readonly stampId: string;\n readonly acquiredAt: string;\n}\n\ninterface ClientStatus {\n readonly client: StampRallyClient;\n readonly isInitializing: boolean;\n}\n\ninterface ClientError {\n readonly client: StampRallyClient;\n readonly value: StampError | RewardConsumeError | Error;\n}\n\ninterface QueuedRequestMetadata {\n readonly previousState: StampRallyState | null;\n readonly result: ProcessStampValue;\n}\n\nexport interface CheckInRequest {\n readonly stampId: string;\n readonly context: VerificationContext;\n readonly now: string;\n readonly idempotencyKey: string;\n}\n\nexport interface SyncAdapter {\n readonly isOnline?: boolean | (() => boolean);\n readonly onBeforeCheckIn?: (request: CheckInRequest) => unknown;\n readonly onServerVerify?: (request: CheckInRequest) => unknown;\n readonly onStateChange?: (state: StampRallyState) => void;\n}\n\nexport interface StampRallyEventHandlers {\n readonly onStampClaimed?: (record: StampRallyState[\"records\"][number]) => void;\n readonly onRewardUnlocked?: (rewardId: string) => void;\n readonly onRewardConsumed?: (rewardId: string) => void;\n}\n\nexport interface UseStampRallyOptions {\n readonly syncAdapter?: SyncAdapter;\n readonly events?: StampRallyEventHandlers;\n readonly onStampClaimed?: StampRallyEventHandlers[\"onStampClaimed\"];\n readonly onRewardUnlocked?: StampRallyEventHandlers[\"onRewardUnlocked\"];\n readonly onRewardConsumed?: StampRallyEventHandlers[\"onRewardConsumed\"];\n readonly serverUserId?: string;\n}\n\nexport interface RedeemOptions {\n readonly passcode?: string;\n readonly staffId?: string;\n}\n\nexport interface UseStampRallyReturn {\n readonly state: StampRallyState | null;\n readonly isLoading: boolean;\n readonly isPending: boolean;\n readonly error: StampError | RewardConsumeError | Error | null;\n readonly rewardsState: ReadonlyArray<RewardState>;\n readonly acquire: (\n stampId: string,\n context: VerificationContext,\n now?: string,\n idempotencyKey?: string,\n ) => Promise<Result<ProcessStampValue, StampError>>;\n readonly reset: (now?: string) => Promise<StampRallyState>;\n readonly redeem: (rewardId: string, options?: RedeemOptions) => Promise<ConsumeResult>;\n readonly exportRecoveryCode: () => string;\n readonly importRecoveryCode: (token: string) => Promise<boolean>;\n readonly exportRecoveryToken: (\n secretKey: string,\n options?: SecureTokenOptions,\n ) => Promise<string>;\n readonly importRecoveryToken: (token: string, secretKey: string) => Promise<boolean>;\n readonly syncWithServer: (serverEndpoint: string, authHeader?: string) => Promise<void>;\n readonly offlineQueue: ReadonlyArray<CheckInRequest>;\n readonly queuedCount: number;\n readonly flushQueue: () => Promise<void>;\n}\n\n/** @deprecated Use UseStampRallyReturn instead. */\nexport type UseStampRallyValue = UseStampRallyReturn;\n\nfunction getServerSnapshot(): null {\n return null;\n}\n\nfunction toError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction applyOptimisticAcquire(\n currentState: StampRallyState | null,\n action: OptimisticAcquire,\n): StampRallyState | null {\n if (\n currentState === null ||\n currentState.records.some((record) => record.stampId === action.stampId)\n ) {\n return currentState;\n }\n\n return {\n ...currentState,\n records: [...currentState.records, { stampId: action.stampId, acquiredAt: action.acquiredAt }],\n updatedAt: action.acquiredAt,\n };\n}\n\nfunction createIdempotencyKey(): string {\n const cryptoApi = globalThis.crypto;\n if (cryptoApi !== undefined && typeof cryptoApi.randomUUID === \"function\") {\n return cryptoApi.randomUUID();\n }\n return `stamp-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n}\n\nfunction isOnline(adapter: SyncAdapter | undefined): boolean {\n if (adapter?.isOnline === undefined) {\n return typeof navigator === \"undefined\" || navigator.onLine !== false;\n }\n return typeof adapter.isOnline === \"function\" ? adapter.isOnline() : adapter.isOnline;\n}\n\nfunction isRejectedVerification(value: unknown): boolean {\n return (\n value === false ||\n (typeof value === \"object\" && value !== null && \"ok\" in value && value.ok === false)\n );\n}\n\nfunction isNetworkFailure(error: unknown): boolean {\n if (typeof navigator !== \"undefined\" && navigator.onLine === false) return true;\n if (!(error instanceof Error)) return true;\n return /aborted|connection|fetch|network|offline|timeout/i.test(`${error.name} ${error.message}`);\n}\n\nfunction notifyAcquisitionEvents(\n result: ProcessStampValue,\n events: StampRallyEventHandlers | undefined,\n onStampClaimed: StampRallyEventHandlers[\"onStampClaimed\"] | undefined,\n onRewardUnlocked: StampRallyEventHandlers[\"onRewardUnlocked\"] | undefined,\n): void {\n for (const event of result.events) {\n if (event.type === \"stampAcquired\") {\n (events?.onStampClaimed ?? onStampClaimed)?.(event.record);\n }\n if (event.type === \"rewardUnlocked\") {\n (events?.onRewardUnlocked ?? onRewardUnlocked)?.(event.rewardId);\n }\n }\n}\n\nexport function useStampRally(\n client: StampRallyClient,\n options: UseStampRallyOptions = {},\n): UseStampRallyReturn {\n const syncAdapter = options.syncAdapter;\n const events = options.events;\n const [offlineQueue, setOfflineQueue] = useState<ReadonlyArray<CheckInRequest>>([]);\n const queuedMetadata = useRef(new Map<string, QueuedRequestMetadata>());\n const isFlushingQueue = useRef(false);\n const activeClient = useRef(client);\n const subscribe = useCallback(\n (onStoreChange: () => void) => client.subscribe(() => onStoreChange()),\n [client],\n );\n const getSnapshot = useCallback(() => client.getState(), [client]);\n const rawState = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n const [clientStatus, setClientStatus] = useState<ClientStatus>(() => ({\n client,\n isInitializing: client.getState() === null,\n }));\n const [clientError, setClientError] = useState<ClientError | null>(null);\n const [isPending] = useTransition();\n const [isOperationPending, setIsOperationPending] = useState(false);\n const [optimisticState, setOptimisticState] = useState<StampRallyState | null>(rawState);\n\n useEffect(() => {\n setOptimisticState(rawState);\n }, [rawState]);\n\n useEffect(() => {\n if (activeClient.current === client) return;\n activeClient.current = client;\n setOfflineQueue([]);\n queuedMetadata.current.clear();\n }, [client]);\n\n useEffect(() => {\n if (syncAdapter?.onStateChange === undefined) return;\n return client.subscribe(syncAdapter.onStateChange);\n }, [client, syncAdapter]);\n\n useEffect(() => {\n let active = true;\n setClientError(null);\n if (rawState !== null) {\n setClientStatus({ client, isInitializing: false });\n return () => {\n active = false;\n };\n }\n\n setClientStatus({ client, isInitializing: true });\n\n void client\n .init()\n .catch((initializationError: unknown) => {\n if (active) {\n setClientError({ client, value: toError(initializationError) });\n }\n })\n .finally(() => {\n if (active) {\n setClientStatus({ client, isInitializing: false });\n }\n });\n\n return () => {\n active = false;\n };\n }, [client, rawState]);\n\n const queueRequest = useCallback(\n (request: CheckInRequest, metadata?: QueuedRequestMetadata): void => {\n if (metadata !== undefined) queuedMetadata.current.set(request.idempotencyKey, metadata);\n setOfflineQueue((current) =>\n current.some((item) => item.idempotencyKey === request.idempotencyKey)\n ? current\n : [...current, request],\n );\n },\n [],\n );\n\n const acquire = useCallback(\n (\n stampId: string,\n context: VerificationContext,\n now?: string,\n idempotencyKey?: string,\n ): Promise<Result<ProcessStampValue, StampError>> => {\n const acquiredAt = now ?? new Date().toISOString();\n const request: CheckInRequest = {\n stampId,\n context,\n now: acquiredAt,\n idempotencyKey: idempotencyKey ?? createIdempotencyKey(),\n };\n setClientError(null);\n\n if (!isOnline(syncAdapter)) {\n queueRequest(request);\n const queued: Result<ProcessStampValue, StampError> = {\n ok: false,\n error: { code: \"OFFLINE_QUEUED\", stampId, idempotencyKey: request.idempotencyKey },\n };\n setClientError({ client, value: queued.error });\n return Promise.resolve(queued);\n }\n\n setIsOperationPending(true);\n setOptimisticState((current) => applyOptimisticAcquire(current, { stampId, acquiredAt }));\n return new Promise((resolve, reject) => {\n void (async () => {\n try {\n const before = await syncAdapter?.onBeforeCheckIn?.(request);\n if (before === false) {\n const rejected: Result<ProcessStampValue, StampError> = {\n ok: false,\n error: { code: \"INVALID_PROOF\", stampId },\n };\n setClientError({ client, value: rejected.error });\n setIsOperationPending(false);\n resolve(rejected);\n return;\n }\n const previousState = client.getState();\n const result = await client.acquire(stampId, context, acquiredAt);\n if (result.ok) {\n let verified: unknown;\n try {\n verified = await syncAdapter?.onServerVerify?.(request);\n } catch (verificationError) {\n if (!isNetworkFailure(verificationError)) throw verificationError;\n queueRequest(request, { previousState, result: result.value });\n const queued: Result<ProcessStampValue, StampError> = {\n ok: false,\n error: {\n code: \"OFFLINE_QUEUED\",\n stampId,\n idempotencyKey: request.idempotencyKey,\n },\n };\n setClientError({ client, value: toError(verificationError) });\n setOptimisticState(client.getState());\n setIsOperationPending(false);\n resolve(queued);\n return;\n }\n if (isRejectedVerification(verified)) {\n await client.restore(rawState ?? client.getState() ?? result.value.nextState);\n const rejected: Result<ProcessStampValue, StampError> = {\n ok: false,\n error: { code: \"INVALID_PROOF\", stampId },\n };\n setClientError({ client, value: rejected.error });\n setOptimisticState(client.getState());\n setIsOperationPending(false);\n resolve(rejected);\n return;\n }\n notifyAcquisitionEvents(\n result.value,\n events,\n options.onStampClaimed,\n options.onRewardUnlocked,\n );\n }\n if (!result.ok) {\n setClientError({ client, value: result.error });\n setOptimisticState(client.getState());\n }\n setIsOperationPending(false);\n resolve(result);\n } catch (acquireError) {\n if (isNetworkFailure(acquireError)) {\n queueRequest(request);\n const queued: Result<ProcessStampValue, StampError> = {\n ok: false,\n error: { code: \"OFFLINE_QUEUED\", stampId, idempotencyKey: request.idempotencyKey },\n };\n setClientError({ client, value: queued.error });\n setOptimisticState(client.getState());\n setIsOperationPending(false);\n resolve(queued);\n return;\n }\n const normalizedError = toError(acquireError);\n setClientError({ client, value: normalizedError });\n setOptimisticState(client.getState());\n setIsOperationPending(false);\n reject(normalizedError);\n }\n })();\n });\n },\n [\n client,\n events,\n options.onRewardUnlocked,\n options.onStampClaimed,\n queueRequest,\n rawState,\n syncAdapter,\n ],\n );\n\n const reset = useCallback(\n (now?: string): Promise<StampRallyState> => {\n setClientError(null);\n setIsOperationPending(true);\n\n return new Promise((resolve, reject) => {\n void (async () => {\n try {\n const nextState = now === undefined ? await client.reset() : await client.reset(now);\n setIsOperationPending(false);\n resolve(nextState);\n } catch (resetError) {\n const normalizedError = toError(resetError);\n setClientError({ client, value: normalizedError });\n setIsOperationPending(false);\n reject(normalizedError);\n }\n })();\n });\n },\n [client],\n );\n\n const redeem = useCallback(\n (rewardId: string, redeemOptions: RedeemOptions = {}): Promise<ConsumeResult> => {\n setClientError(null);\n setIsOperationPending(true);\n\n return new Promise((resolve, reject) => {\n void (async () => {\n const reward = client.getConfig().rewards?.find((item) => item.id === rewardId);\n if (reward === undefined) {\n const result: ConsumeResult = {\n ok: false,\n error: { code: \"REWARD_NOT_FOUND\", rewardId },\n };\n setClientError({ client, value: result.error });\n setIsOperationPending(false);\n resolve(result);\n return;\n }\n\n const currentState = client.getState();\n const currentRewardState = currentState?.rewards?.find(\n (state) => state.rewardId === rewardId,\n );\n if (currentState === null || currentRewardState === undefined) {\n const result: ConsumeResult = {\n ok: false,\n error: { code: \"NOT_AVAILABLE\", rewardId },\n };\n setClientError({ client, value: result.error });\n setIsOperationPending(false);\n resolve(result);\n return;\n }\n\n const result = consumeReward({\n reward,\n currentState: currentRewardState,\n now: new Date().toISOString(),\n ...(redeemOptions.passcode === undefined\n ? {}\n : { inputPasscode: redeemOptions.passcode }),\n ...(redeemOptions.staffId === undefined ? {} : { staffId: redeemOptions.staffId }),\n });\n if (!result.ok) {\n setClientError({ client, value: result.error });\n setIsOperationPending(false);\n resolve(result);\n return;\n }\n\n if (result.value === currentRewardState) {\n setIsOperationPending(false);\n resolve(result);\n return;\n }\n\n const nextState: StampRallyState = {\n ...currentState,\n rewards: (currentState.rewards ?? []).map((state) =>\n state.rewardId === rewardId ? result.value : state,\n ),\n updatedAt: result.value.consumedAt ?? currentState.updatedAt,\n };\n\n try {\n await client.restore(nextState);\n (events?.onRewardConsumed ?? options.onRewardConsumed)?.(rewardId);\n setIsOperationPending(false);\n resolve(result);\n } catch (redeemError) {\n const normalizedError = toError(redeemError);\n setClientError({ client, value: normalizedError });\n setIsOperationPending(false);\n reject(normalizedError);\n }\n })();\n });\n },\n [client, events, options.onRewardConsumed],\n );\n\n const exportRecoveryCode = useCallback((): string => {\n const state = client.getState();\n if (state === null) {\n throw new Error(\"Cannot export recovery code before the rally is initialized.\");\n }\n\n return exportProgressToken({\n version: 1,\n rallyId: state.rallyId,\n stamps: state.records,\n rewards: state.rewards ?? [],\n exportedAt: new Date().toISOString(),\n });\n }, [client]);\n\n const importRecoveryCode = useCallback(\n (token: string): Promise<boolean> => {\n setClientError(null);\n setIsOperationPending(true);\n\n return new Promise((resolve, reject) => {\n void (async () => {\n const config = client.getConfig();\n const snapshot = importProgressToken(token, config.id);\n if (snapshot === null) {\n setIsOperationPending(false);\n resolve(false);\n return;\n }\n\n const stampIds = new Set(config.stamps.map((stamp) => stamp.id));\n const rewardIds = new Set((config.rewards ?? []).map((reward) => reward.id));\n const importedStampIds = new Set<string>();\n const importedRewardIds = new Set<string>();\n const stamps = snapshot.stamps.filter((record) => {\n if (!stampIds.has(record.stampId) || importedStampIds.has(record.stampId)) return false;\n importedStampIds.add(record.stampId);\n return true;\n });\n const rewards = snapshot.rewards.filter((state) => {\n if (!rewardIds.has(state.rewardId) || importedRewardIds.has(state.rewardId))\n return false;\n importedRewardIds.add(state.rewardId);\n return true;\n });\n\n try {\n await client.restore({\n rallyId: config.id,\n records: stamps,\n ...(config.rewards === undefined && rewards.length === 0 ? {} : { rewards }),\n updatedAt: snapshot.exportedAt,\n });\n setIsOperationPending(false);\n resolve(true);\n } catch (importError) {\n const normalizedError = toError(importError);\n setClientError({ client, value: normalizedError });\n setIsOperationPending(false);\n reject(normalizedError);\n }\n })();\n });\n },\n [client],\n );\n\n const exportRecoveryToken = useCallback(\n async (secretKey: string, tokenOptions: SecureTokenOptions = {}): Promise<string> => {\n const state = client.getState();\n if (state === null)\n throw new Error(\"Cannot export recovery token before the rally is initialized.\");\n return createSecureToken(\n {\n type: \"recovery\",\n rallyId: state.rallyId,\n state,\n exportedAt: new Date().toISOString(),\n },\n secretKey,\n { encrypt: tokenOptions.encrypt ?? true, ...tokenOptions },\n );\n },\n [client],\n );\n\n const importRecoveryToken = useCallback(\n async (token: string, secretKey: string): Promise<boolean> => {\n setClientError(null);\n setIsOperationPending(true);\n try {\n const verified = await verifySecureToken(token, secretKey);\n if (\n !verified.ok ||\n verified.payload.type !== \"recovery\" ||\n verified.payload.rallyId !== client.getConfig().id\n ) {\n setIsOperationPending(false);\n return false;\n }\n const candidate = verified.payload.state;\n if (!isStampRallyState(candidate)) {\n setIsOperationPending(false);\n return false;\n }\n await client.restore(candidate);\n setIsOperationPending(false);\n return true;\n } catch (error) {\n setClientError({ client, value: toError(error) });\n setIsOperationPending(false);\n return false;\n }\n },\n [client],\n );\n\n const syncWithServer = useCallback(\n async (serverEndpoint: string, authHeader?: string): Promise<void> => {\n if (typeof fetch !== \"function\")\n throw new Error(\"Fetch API is unavailable in this environment.\");\n setClientError(null);\n setIsOperationPending(true);\n try {\n const endpoint = serverEndpoint.replace(/\\/$/u, \"\").endsWith(\"/sync\")\n ? serverEndpoint\n : `${serverEndpoint.replace(/\\/$/u, \"\")}/api/sync`;\n const response = await fetch(endpoint, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n ...(authHeader === undefined ? {} : { authorization: authHeader }),\n },\n body: JSON.stringify({\n userId: options.serverUserId ?? \"anonymous\",\n queue: offlineQueue.map((request) => ({\n ...request,\n userId: options.serverUserId ?? \"anonymous\",\n spotId: request.stampId,\n claimMethod: request.context.type,\n proofData:\n request.context.type === \"token\"\n ? { token: request.context.token }\n : request.context.type === \"geo\"\n ? {\n latitude: request.context.currentLatitude,\n longitude: request.context.currentLongitude,\n }\n : undefined,\n })),\n }),\n });\n const payload: unknown = await response.json();\n if (\n !response.ok ||\n !isObject(payload) ||\n payload.ok !== true ||\n !isStampRallyState(payload.state)\n ) {\n throw new Error(\"Server synchronization was rejected.\");\n }\n await client.restore(payload.state);\n queuedMetadata.current.clear();\n setOfflineQueue([]);\n client.notifySyncCompleted(payload.state);\n } catch (error) {\n setClientError({ client, value: toError(error) });\n throw error;\n } finally {\n setIsOperationPending(false);\n }\n },\n [client, offlineQueue, options.serverUserId],\n );\n\n const isLoading =\n rawState === null && (clientStatus.client !== client || clientStatus.isInitializing);\n const error = clientError?.client === client ? clientError.value : null;\n\n const flushQueue = useCallback(async (): Promise<void> => {\n if (!isOnline(syncAdapter) || isFlushingQueue.current) return;\n isFlushingQueue.current = true;\n setIsOperationPending(true);\n try {\n for (const request of offlineQueue) {\n if (!isOnline(syncAdapter)) break;\n const metadata = queuedMetadata.current.get(request.idempotencyKey);\n const previousState = metadata?.previousState ?? client.getState();\n try {\n const before = await syncAdapter?.onBeforeCheckIn?.(request);\n if (before === false) {\n if (metadata !== undefined && previousState !== null)\n await client.restore(previousState);\n setClientError({ client, value: { code: \"INVALID_PROOF\", stampId: request.stampId } });\n queuedMetadata.current.delete(request.idempotencyKey);\n setOfflineQueue((current) =>\n current.filter((item) => item.idempotencyKey !== request.idempotencyKey),\n );\n continue;\n }\n\n let result = metadata?.result;\n if (result === undefined) {\n const localResult = await client.acquire(request.stampId, request.context, request.now);\n if (!localResult.ok) {\n setClientError({ client, value: localResult.error });\n queuedMetadata.current.delete(request.idempotencyKey);\n setOfflineQueue((current) =>\n current.filter((item) => item.idempotencyKey !== request.idempotencyKey),\n );\n continue;\n }\n result = localResult.value;\n queuedMetadata.current.set(request.idempotencyKey, {\n previousState,\n result,\n });\n }\n\n const verified = await syncAdapter?.onServerVerify?.(request);\n if (isRejectedVerification(verified)) {\n if (previousState !== null) await client.restore(previousState);\n setClientError({\n client,\n value: { code: \"INVALID_PROOF\", stampId: request.stampId },\n });\n } else {\n notifyAcquisitionEvents(\n result,\n events,\n options.onStampClaimed,\n options.onRewardUnlocked,\n );\n }\n queuedMetadata.current.delete(request.idempotencyKey);\n setOfflineQueue((current) =>\n current.filter((item) => item.idempotencyKey !== request.idempotencyKey),\n );\n } catch (flushError) {\n if (isNetworkFailure(flushError)) {\n setClientError({ client, value: toError(flushError) });\n break;\n }\n setClientError({ client, value: toError(flushError) });\n queuedMetadata.current.delete(request.idempotencyKey);\n setOfflineQueue((current) =>\n current.filter((item) => item.idempotencyKey !== request.idempotencyKey),\n );\n }\n }\n } finally {\n isFlushingQueue.current = false;\n setIsOperationPending(false);\n }\n }, [client, events, offlineQueue, options.onRewardUnlocked, options.onStampClaimed, syncAdapter]);\n\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n const handleOnline = (): void => {\n void flushQueue();\n };\n window.addEventListener(\"online\", handleOnline);\n return () => window.removeEventListener(\"online\", handleOnline);\n }, [flushQueue]);\n\n return {\n state: optimisticState,\n isLoading,\n isPending: isPending || isOperationPending,\n error,\n rewardsState: optimisticState?.rewards ?? [],\n acquire,\n reset,\n redeem,\n exportRecoveryCode,\n importRecoveryCode,\n exportRecoveryToken,\n importRecoveryToken,\n syncWithServer,\n offlineQueue,\n queuedCount: offlineQueue.length,\n flushQueue,\n };\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { VerificationContext, StampRallyState, StampError, RewardConsumeError, RewardState, Result, ProcessStampValue, ConsumeResult, StampRallyClient } from '@stamprally/core';
|
|
1
|
+
import { VerificationContext, StampRallyState, StampError, RewardConsumeError, RewardState, Result, ProcessStampValue, ConsumeResult, SecureTokenOptions, StampRallyClient } from '@stamprally/core';
|
|
2
2
|
|
|
3
3
|
interface CheckInRequest {
|
|
4
4
|
readonly stampId: string;
|
|
@@ -23,6 +23,7 @@ interface UseStampRallyOptions {
|
|
|
23
23
|
readonly onStampClaimed?: StampRallyEventHandlers["onStampClaimed"];
|
|
24
24
|
readonly onRewardUnlocked?: StampRallyEventHandlers["onRewardUnlocked"];
|
|
25
25
|
readonly onRewardConsumed?: StampRallyEventHandlers["onRewardConsumed"];
|
|
26
|
+
readonly serverUserId?: string;
|
|
26
27
|
}
|
|
27
28
|
interface RedeemOptions {
|
|
28
29
|
readonly passcode?: string;
|
|
@@ -39,6 +40,9 @@ interface UseStampRallyReturn {
|
|
|
39
40
|
readonly redeem: (rewardId: string, options?: RedeemOptions) => Promise<ConsumeResult>;
|
|
40
41
|
readonly exportRecoveryCode: () => string;
|
|
41
42
|
readonly importRecoveryCode: (token: string) => Promise<boolean>;
|
|
43
|
+
readonly exportRecoveryToken: (secretKey: string, options?: SecureTokenOptions) => Promise<string>;
|
|
44
|
+
readonly importRecoveryToken: (token: string, secretKey: string) => Promise<boolean>;
|
|
45
|
+
readonly syncWithServer: (serverEndpoint: string, authHeader?: string) => Promise<void>;
|
|
42
46
|
readonly offlineQueue: ReadonlyArray<CheckInRequest>;
|
|
43
47
|
readonly queuedCount: number;
|
|
44
48
|
readonly flushQueue: () => Promise<void>;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { VerificationContext, StampRallyState, StampError, RewardConsumeError, RewardState, Result, ProcessStampValue, ConsumeResult, StampRallyClient } from '@stamprally/core';
|
|
1
|
+
import { VerificationContext, StampRallyState, StampError, RewardConsumeError, RewardState, Result, ProcessStampValue, ConsumeResult, SecureTokenOptions, StampRallyClient } from '@stamprally/core';
|
|
2
2
|
|
|
3
3
|
interface CheckInRequest {
|
|
4
4
|
readonly stampId: string;
|
|
@@ -23,6 +23,7 @@ interface UseStampRallyOptions {
|
|
|
23
23
|
readonly onStampClaimed?: StampRallyEventHandlers["onStampClaimed"];
|
|
24
24
|
readonly onRewardUnlocked?: StampRallyEventHandlers["onRewardUnlocked"];
|
|
25
25
|
readonly onRewardConsumed?: StampRallyEventHandlers["onRewardConsumed"];
|
|
26
|
+
readonly serverUserId?: string;
|
|
26
27
|
}
|
|
27
28
|
interface RedeemOptions {
|
|
28
29
|
readonly passcode?: string;
|
|
@@ -39,6 +40,9 @@ interface UseStampRallyReturn {
|
|
|
39
40
|
readonly redeem: (rewardId: string, options?: RedeemOptions) => Promise<ConsumeResult>;
|
|
40
41
|
readonly exportRecoveryCode: () => string;
|
|
41
42
|
readonly importRecoveryCode: (token: string) => Promise<boolean>;
|
|
43
|
+
readonly exportRecoveryToken: (secretKey: string, options?: SecureTokenOptions) => Promise<string>;
|
|
44
|
+
readonly importRecoveryToken: (token: string, secretKey: string) => Promise<boolean>;
|
|
45
|
+
readonly syncWithServer: (serverEndpoint: string, authHeader?: string) => Promise<void>;
|
|
42
46
|
readonly offlineQueue: ReadonlyArray<CheckInRequest>;
|
|
43
47
|
readonly queuedCount: number;
|
|
44
48
|
readonly flushQueue: () => Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { consumeReward, exportProgressToken, importProgressToken } from '@stamprally/core';
|
|
1
|
+
import { consumeReward, exportProgressToken, importProgressToken, createSecureToken, verifySecureToken, isStampRallyState } from '@stamprally/core';
|
|
2
2
|
import { useState, useRef, useCallback, useSyncExternalStore, useTransition, useEffect } from 'react';
|
|
3
3
|
|
|
4
4
|
// src/useStampRally.ts
|
|
@@ -8,6 +8,9 @@ function getServerSnapshot() {
|
|
|
8
8
|
function toError(error) {
|
|
9
9
|
return error instanceof Error ? error : new Error(String(error));
|
|
10
10
|
}
|
|
11
|
+
function isObject(value) {
|
|
12
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13
|
+
}
|
|
11
14
|
function applyOptimisticAcquire(currentState, action) {
|
|
12
15
|
if (currentState === null || currentState.records.some((record) => record.stampId === action.stampId)) {
|
|
13
16
|
return currentState;
|
|
@@ -384,6 +387,95 @@ function useStampRally(client, options = {}) {
|
|
|
384
387
|
},
|
|
385
388
|
[client]
|
|
386
389
|
);
|
|
390
|
+
const exportRecoveryToken = useCallback(
|
|
391
|
+
async (secretKey, tokenOptions = {}) => {
|
|
392
|
+
const state = client.getState();
|
|
393
|
+
if (state === null)
|
|
394
|
+
throw new Error("Cannot export recovery token before the rally is initialized.");
|
|
395
|
+
return createSecureToken(
|
|
396
|
+
{
|
|
397
|
+
type: "recovery",
|
|
398
|
+
rallyId: state.rallyId,
|
|
399
|
+
state,
|
|
400
|
+
exportedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
401
|
+
},
|
|
402
|
+
secretKey,
|
|
403
|
+
{ encrypt: tokenOptions.encrypt ?? true, ...tokenOptions }
|
|
404
|
+
);
|
|
405
|
+
},
|
|
406
|
+
[client]
|
|
407
|
+
);
|
|
408
|
+
const importRecoveryToken = useCallback(
|
|
409
|
+
async (token, secretKey) => {
|
|
410
|
+
setClientError(null);
|
|
411
|
+
setIsOperationPending(true);
|
|
412
|
+
try {
|
|
413
|
+
const verified = await verifySecureToken(token, secretKey);
|
|
414
|
+
if (!verified.ok || verified.payload.type !== "recovery" || verified.payload.rallyId !== client.getConfig().id) {
|
|
415
|
+
setIsOperationPending(false);
|
|
416
|
+
return false;
|
|
417
|
+
}
|
|
418
|
+
const candidate = verified.payload.state;
|
|
419
|
+
if (!isStampRallyState(candidate)) {
|
|
420
|
+
setIsOperationPending(false);
|
|
421
|
+
return false;
|
|
422
|
+
}
|
|
423
|
+
await client.restore(candidate);
|
|
424
|
+
setIsOperationPending(false);
|
|
425
|
+
return true;
|
|
426
|
+
} catch (error2) {
|
|
427
|
+
setClientError({ client, value: toError(error2) });
|
|
428
|
+
setIsOperationPending(false);
|
|
429
|
+
return false;
|
|
430
|
+
}
|
|
431
|
+
},
|
|
432
|
+
[client]
|
|
433
|
+
);
|
|
434
|
+
const syncWithServer = useCallback(
|
|
435
|
+
async (serverEndpoint, authHeader) => {
|
|
436
|
+
if (typeof fetch !== "function")
|
|
437
|
+
throw new Error("Fetch API is unavailable in this environment.");
|
|
438
|
+
setClientError(null);
|
|
439
|
+
setIsOperationPending(true);
|
|
440
|
+
try {
|
|
441
|
+
const endpoint = serverEndpoint.replace(/\/$/u, "").endsWith("/sync") ? serverEndpoint : `${serverEndpoint.replace(/\/$/u, "")}/api/sync`;
|
|
442
|
+
const response = await fetch(endpoint, {
|
|
443
|
+
method: "POST",
|
|
444
|
+
headers: {
|
|
445
|
+
"content-type": "application/json",
|
|
446
|
+
...authHeader === void 0 ? {} : { authorization: authHeader }
|
|
447
|
+
},
|
|
448
|
+
body: JSON.stringify({
|
|
449
|
+
userId: options.serverUserId ?? "anonymous",
|
|
450
|
+
queue: offlineQueue.map((request) => ({
|
|
451
|
+
...request,
|
|
452
|
+
userId: options.serverUserId ?? "anonymous",
|
|
453
|
+
spotId: request.stampId,
|
|
454
|
+
claimMethod: request.context.type,
|
|
455
|
+
proofData: request.context.type === "token" ? { token: request.context.token } : request.context.type === "geo" ? {
|
|
456
|
+
latitude: request.context.currentLatitude,
|
|
457
|
+
longitude: request.context.currentLongitude
|
|
458
|
+
} : void 0
|
|
459
|
+
}))
|
|
460
|
+
})
|
|
461
|
+
});
|
|
462
|
+
const payload = await response.json();
|
|
463
|
+
if (!response.ok || !isObject(payload) || payload.ok !== true || !isStampRallyState(payload.state)) {
|
|
464
|
+
throw new Error("Server synchronization was rejected.");
|
|
465
|
+
}
|
|
466
|
+
await client.restore(payload.state);
|
|
467
|
+
queuedMetadata.current.clear();
|
|
468
|
+
setOfflineQueue([]);
|
|
469
|
+
client.notifySyncCompleted(payload.state);
|
|
470
|
+
} catch (error2) {
|
|
471
|
+
setClientError({ client, value: toError(error2) });
|
|
472
|
+
throw error2;
|
|
473
|
+
} finally {
|
|
474
|
+
setIsOperationPending(false);
|
|
475
|
+
}
|
|
476
|
+
},
|
|
477
|
+
[client, offlineQueue, options.serverUserId]
|
|
478
|
+
);
|
|
387
479
|
const isLoading = rawState === null && (clientStatus.client !== client || clientStatus.isInitializing);
|
|
388
480
|
const error = clientError?.client === client ? clientError.value : null;
|
|
389
481
|
const flushQueue = useCallback(async () => {
|
|
@@ -479,6 +571,9 @@ function useStampRally(client, options = {}) {
|
|
|
479
571
|
redeem,
|
|
480
572
|
exportRecoveryCode,
|
|
481
573
|
importRecoveryCode,
|
|
574
|
+
exportRecoveryToken,
|
|
575
|
+
importRecoveryToken,
|
|
576
|
+
syncWithServer,
|
|
482
577
|
offlineQueue,
|
|
483
578
|
queuedCount: offlineQueue.length,
|
|
484
579
|
flushQueue
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/useStampRally.ts"],"names":["result"],"mappings":"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","file":"index.js","sourcesContent":["import type {\n ConsumeResult,\n ProcessStampValue,\n Result,\n RewardConsumeError,\n RewardState,\n StampError,\n StampRallyClient,\n StampRallyState,\n VerificationContext,\n} from \"@stamprally/core\";\nimport { consumeReward, exportProgressToken, importProgressToken } from \"@stamprally/core\";\nimport {\n useCallback,\n useEffect,\n useRef,\n useState,\n useSyncExternalStore,\n useTransition,\n} from \"react\";\n\ninterface OptimisticAcquire {\n readonly stampId: string;\n readonly acquiredAt: string;\n}\n\ninterface ClientStatus {\n readonly client: StampRallyClient;\n readonly isInitializing: boolean;\n}\n\ninterface ClientError {\n readonly client: StampRallyClient;\n readonly value: StampError | RewardConsumeError | Error;\n}\n\ninterface QueuedRequestMetadata {\n readonly previousState: StampRallyState | null;\n readonly result: ProcessStampValue;\n}\n\nexport interface CheckInRequest {\n readonly stampId: string;\n readonly context: VerificationContext;\n readonly now: string;\n readonly idempotencyKey: string;\n}\n\nexport interface SyncAdapter {\n readonly isOnline?: boolean | (() => boolean);\n readonly onBeforeCheckIn?: (request: CheckInRequest) => unknown;\n readonly onServerVerify?: (request: CheckInRequest) => unknown;\n readonly onStateChange?: (state: StampRallyState) => void;\n}\n\nexport interface StampRallyEventHandlers {\n readonly onStampClaimed?: (record: StampRallyState[\"records\"][number]) => void;\n readonly onRewardUnlocked?: (rewardId: string) => void;\n readonly onRewardConsumed?: (rewardId: string) => void;\n}\n\nexport interface UseStampRallyOptions {\n readonly syncAdapter?: SyncAdapter;\n readonly events?: StampRallyEventHandlers;\n readonly onStampClaimed?: StampRallyEventHandlers[\"onStampClaimed\"];\n readonly onRewardUnlocked?: StampRallyEventHandlers[\"onRewardUnlocked\"];\n readonly onRewardConsumed?: StampRallyEventHandlers[\"onRewardConsumed\"];\n}\n\nexport interface RedeemOptions {\n readonly passcode?: string;\n readonly staffId?: string;\n}\n\nexport interface UseStampRallyReturn {\n readonly state: StampRallyState | null;\n readonly isLoading: boolean;\n readonly isPending: boolean;\n readonly error: StampError | RewardConsumeError | Error | null;\n readonly rewardsState: ReadonlyArray<RewardState>;\n readonly acquire: (\n stampId: string,\n context: VerificationContext,\n now?: string,\n idempotencyKey?: string,\n ) => Promise<Result<ProcessStampValue, StampError>>;\n readonly reset: (now?: string) => Promise<StampRallyState>;\n readonly redeem: (rewardId: string, options?: RedeemOptions) => Promise<ConsumeResult>;\n readonly exportRecoveryCode: () => string;\n readonly importRecoveryCode: (token: string) => Promise<boolean>;\n readonly offlineQueue: ReadonlyArray<CheckInRequest>;\n readonly queuedCount: number;\n readonly flushQueue: () => Promise<void>;\n}\n\n/** @deprecated Use UseStampRallyReturn instead. */\nexport type UseStampRallyValue = UseStampRallyReturn;\n\nfunction getServerSnapshot(): null {\n return null;\n}\n\nfunction toError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n}\n\nfunction applyOptimisticAcquire(\n currentState: StampRallyState | null,\n action: OptimisticAcquire,\n): StampRallyState | null {\n if (\n currentState === null ||\n currentState.records.some((record) => record.stampId === action.stampId)\n ) {\n return currentState;\n }\n\n return {\n ...currentState,\n records: [...currentState.records, { stampId: action.stampId, acquiredAt: action.acquiredAt }],\n updatedAt: action.acquiredAt,\n };\n}\n\nfunction createIdempotencyKey(): string {\n const cryptoApi = globalThis.crypto;\n if (cryptoApi !== undefined && typeof cryptoApi.randomUUID === \"function\") {\n return cryptoApi.randomUUID();\n }\n return `stamp-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n}\n\nfunction isOnline(adapter: SyncAdapter | undefined): boolean {\n if (adapter?.isOnline === undefined) {\n return typeof navigator === \"undefined\" || navigator.onLine !== false;\n }\n return typeof adapter.isOnline === \"function\" ? adapter.isOnline() : adapter.isOnline;\n}\n\nfunction isRejectedVerification(value: unknown): boolean {\n return (\n value === false ||\n (typeof value === \"object\" && value !== null && \"ok\" in value && value.ok === false)\n );\n}\n\nfunction isNetworkFailure(error: unknown): boolean {\n if (typeof navigator !== \"undefined\" && navigator.onLine === false) return true;\n if (!(error instanceof Error)) return true;\n return /aborted|connection|fetch|network|offline|timeout/i.test(`${error.name} ${error.message}`);\n}\n\nfunction notifyAcquisitionEvents(\n result: ProcessStampValue,\n events: StampRallyEventHandlers | undefined,\n onStampClaimed: StampRallyEventHandlers[\"onStampClaimed\"] | undefined,\n onRewardUnlocked: StampRallyEventHandlers[\"onRewardUnlocked\"] | undefined,\n): void {\n for (const event of result.events) {\n if (event.type === \"stampAcquired\") {\n (events?.onStampClaimed ?? onStampClaimed)?.(event.record);\n }\n if (event.type === \"rewardUnlocked\") {\n (events?.onRewardUnlocked ?? onRewardUnlocked)?.(event.rewardId);\n }\n }\n}\n\nexport function useStampRally(\n client: StampRallyClient,\n options: UseStampRallyOptions = {},\n): UseStampRallyReturn {\n const syncAdapter = options.syncAdapter;\n const events = options.events;\n const [offlineQueue, setOfflineQueue] = useState<ReadonlyArray<CheckInRequest>>([]);\n const queuedMetadata = useRef(new Map<string, QueuedRequestMetadata>());\n const isFlushingQueue = useRef(false);\n const activeClient = useRef(client);\n const subscribe = useCallback(\n (onStoreChange: () => void) => client.subscribe(() => onStoreChange()),\n [client],\n );\n const getSnapshot = useCallback(() => client.getState(), [client]);\n const rawState = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n const [clientStatus, setClientStatus] = useState<ClientStatus>(() => ({\n client,\n isInitializing: client.getState() === null,\n }));\n const [clientError, setClientError] = useState<ClientError | null>(null);\n const [isPending] = useTransition();\n const [isOperationPending, setIsOperationPending] = useState(false);\n const [optimisticState, setOptimisticState] = useState<StampRallyState | null>(rawState);\n\n useEffect(() => {\n setOptimisticState(rawState);\n }, [rawState]);\n\n useEffect(() => {\n if (activeClient.current === client) return;\n activeClient.current = client;\n setOfflineQueue([]);\n queuedMetadata.current.clear();\n }, [client]);\n\n useEffect(() => {\n if (syncAdapter?.onStateChange === undefined) return;\n return client.subscribe(syncAdapter.onStateChange);\n }, [client, syncAdapter]);\n\n useEffect(() => {\n let active = true;\n setClientError(null);\n if (rawState !== null) {\n setClientStatus({ client, isInitializing: false });\n return () => {\n active = false;\n };\n }\n\n setClientStatus({ client, isInitializing: true });\n\n void client\n .init()\n .catch((initializationError: unknown) => {\n if (active) {\n setClientError({ client, value: toError(initializationError) });\n }\n })\n .finally(() => {\n if (active) {\n setClientStatus({ client, isInitializing: false });\n }\n });\n\n return () => {\n active = false;\n };\n }, [client, rawState]);\n\n const queueRequest = useCallback(\n (request: CheckInRequest, metadata?: QueuedRequestMetadata): void => {\n if (metadata !== undefined) queuedMetadata.current.set(request.idempotencyKey, metadata);\n setOfflineQueue((current) =>\n current.some((item) => item.idempotencyKey === request.idempotencyKey)\n ? current\n : [...current, request],\n );\n },\n [],\n );\n\n const acquire = useCallback(\n (\n stampId: string,\n context: VerificationContext,\n now?: string,\n idempotencyKey?: string,\n ): Promise<Result<ProcessStampValue, StampError>> => {\n const acquiredAt = now ?? new Date().toISOString();\n const request: CheckInRequest = {\n stampId,\n context,\n now: acquiredAt,\n idempotencyKey: idempotencyKey ?? createIdempotencyKey(),\n };\n setClientError(null);\n\n if (!isOnline(syncAdapter)) {\n queueRequest(request);\n const queued: Result<ProcessStampValue, StampError> = {\n ok: false,\n error: { code: \"OFFLINE_QUEUED\", stampId, idempotencyKey: request.idempotencyKey },\n };\n setClientError({ client, value: queued.error });\n return Promise.resolve(queued);\n }\n\n setIsOperationPending(true);\n setOptimisticState((current) => applyOptimisticAcquire(current, { stampId, acquiredAt }));\n return new Promise((resolve, reject) => {\n void (async () => {\n try {\n const before = await syncAdapter?.onBeforeCheckIn?.(request);\n if (before === false) {\n const rejected: Result<ProcessStampValue, StampError> = {\n ok: false,\n error: { code: \"INVALID_PROOF\", stampId },\n };\n setClientError({ client, value: rejected.error });\n setIsOperationPending(false);\n resolve(rejected);\n return;\n }\n const previousState = client.getState();\n const result = await client.acquire(stampId, context, acquiredAt);\n if (result.ok) {\n let verified: unknown;\n try {\n verified = await syncAdapter?.onServerVerify?.(request);\n } catch (verificationError) {\n if (!isNetworkFailure(verificationError)) throw verificationError;\n queueRequest(request, { previousState, result: result.value });\n const queued: Result<ProcessStampValue, StampError> = {\n ok: false,\n error: {\n code: \"OFFLINE_QUEUED\",\n stampId,\n idempotencyKey: request.idempotencyKey,\n },\n };\n setClientError({ client, value: toError(verificationError) });\n setOptimisticState(client.getState());\n setIsOperationPending(false);\n resolve(queued);\n return;\n }\n if (isRejectedVerification(verified)) {\n await client.restore(rawState ?? client.getState() ?? result.value.nextState);\n const rejected: Result<ProcessStampValue, StampError> = {\n ok: false,\n error: { code: \"INVALID_PROOF\", stampId },\n };\n setClientError({ client, value: rejected.error });\n setOptimisticState(client.getState());\n setIsOperationPending(false);\n resolve(rejected);\n return;\n }\n notifyAcquisitionEvents(\n result.value,\n events,\n options.onStampClaimed,\n options.onRewardUnlocked,\n );\n }\n if (!result.ok) {\n setClientError({ client, value: result.error });\n setOptimisticState(client.getState());\n }\n setIsOperationPending(false);\n resolve(result);\n } catch (acquireError) {\n if (isNetworkFailure(acquireError)) {\n queueRequest(request);\n const queued: Result<ProcessStampValue, StampError> = {\n ok: false,\n error: { code: \"OFFLINE_QUEUED\", stampId, idempotencyKey: request.idempotencyKey },\n };\n setClientError({ client, value: queued.error });\n setOptimisticState(client.getState());\n setIsOperationPending(false);\n resolve(queued);\n return;\n }\n const normalizedError = toError(acquireError);\n setClientError({ client, value: normalizedError });\n setOptimisticState(client.getState());\n setIsOperationPending(false);\n reject(normalizedError);\n }\n })();\n });\n },\n [\n client,\n events,\n options.onRewardUnlocked,\n options.onStampClaimed,\n queueRequest,\n rawState,\n syncAdapter,\n ],\n );\n\n const reset = useCallback(\n (now?: string): Promise<StampRallyState> => {\n setClientError(null);\n setIsOperationPending(true);\n\n return new Promise((resolve, reject) => {\n void (async () => {\n try {\n const nextState = now === undefined ? await client.reset() : await client.reset(now);\n setIsOperationPending(false);\n resolve(nextState);\n } catch (resetError) {\n const normalizedError = toError(resetError);\n setClientError({ client, value: normalizedError });\n setIsOperationPending(false);\n reject(normalizedError);\n }\n })();\n });\n },\n [client],\n );\n\n const redeem = useCallback(\n (rewardId: string, redeemOptions: RedeemOptions = {}): Promise<ConsumeResult> => {\n setClientError(null);\n setIsOperationPending(true);\n\n return new Promise((resolve, reject) => {\n void (async () => {\n const reward = client.getConfig().rewards?.find((item) => item.id === rewardId);\n if (reward === undefined) {\n const result: ConsumeResult = {\n ok: false,\n error: { code: \"REWARD_NOT_FOUND\", rewardId },\n };\n setClientError({ client, value: result.error });\n setIsOperationPending(false);\n resolve(result);\n return;\n }\n\n const currentState = client.getState();\n const currentRewardState = currentState?.rewards?.find(\n (state) => state.rewardId === rewardId,\n );\n if (currentState === null || currentRewardState === undefined) {\n const result: ConsumeResult = {\n ok: false,\n error: { code: \"NOT_AVAILABLE\", rewardId },\n };\n setClientError({ client, value: result.error });\n setIsOperationPending(false);\n resolve(result);\n return;\n }\n\n const result = consumeReward({\n reward,\n currentState: currentRewardState,\n now: new Date().toISOString(),\n ...(redeemOptions.passcode === undefined\n ? {}\n : { inputPasscode: redeemOptions.passcode }),\n ...(redeemOptions.staffId === undefined ? {} : { staffId: redeemOptions.staffId }),\n });\n if (!result.ok) {\n setClientError({ client, value: result.error });\n setIsOperationPending(false);\n resolve(result);\n return;\n }\n\n if (result.value === currentRewardState) {\n setIsOperationPending(false);\n resolve(result);\n return;\n }\n\n const nextState: StampRallyState = {\n ...currentState,\n rewards: (currentState.rewards ?? []).map((state) =>\n state.rewardId === rewardId ? result.value : state,\n ),\n updatedAt: result.value.consumedAt ?? currentState.updatedAt,\n };\n\n try {\n await client.restore(nextState);\n (events?.onRewardConsumed ?? options.onRewardConsumed)?.(rewardId);\n setIsOperationPending(false);\n resolve(result);\n } catch (redeemError) {\n const normalizedError = toError(redeemError);\n setClientError({ client, value: normalizedError });\n setIsOperationPending(false);\n reject(normalizedError);\n }\n })();\n });\n },\n [client, events, options.onRewardConsumed],\n );\n\n const exportRecoveryCode = useCallback((): string => {\n const state = client.getState();\n if (state === null) {\n throw new Error(\"Cannot export recovery code before the rally is initialized.\");\n }\n\n return exportProgressToken({\n version: 1,\n rallyId: state.rallyId,\n stamps: state.records,\n rewards: state.rewards ?? [],\n exportedAt: new Date().toISOString(),\n });\n }, [client]);\n\n const importRecoveryCode = useCallback(\n (token: string): Promise<boolean> => {\n setClientError(null);\n setIsOperationPending(true);\n\n return new Promise((resolve, reject) => {\n void (async () => {\n const config = client.getConfig();\n const snapshot = importProgressToken(token, config.id);\n if (snapshot === null) {\n setIsOperationPending(false);\n resolve(false);\n return;\n }\n\n const stampIds = new Set(config.stamps.map((stamp) => stamp.id));\n const rewardIds = new Set((config.rewards ?? []).map((reward) => reward.id));\n const importedStampIds = new Set<string>();\n const importedRewardIds = new Set<string>();\n const stamps = snapshot.stamps.filter((record) => {\n if (!stampIds.has(record.stampId) || importedStampIds.has(record.stampId)) return false;\n importedStampIds.add(record.stampId);\n return true;\n });\n const rewards = snapshot.rewards.filter((state) => {\n if (!rewardIds.has(state.rewardId) || importedRewardIds.has(state.rewardId))\n return false;\n importedRewardIds.add(state.rewardId);\n return true;\n });\n\n try {\n await client.restore({\n rallyId: config.id,\n records: stamps,\n ...(config.rewards === undefined && rewards.length === 0 ? {} : { rewards }),\n updatedAt: snapshot.exportedAt,\n });\n setIsOperationPending(false);\n resolve(true);\n } catch (importError) {\n const normalizedError = toError(importError);\n setClientError({ client, value: normalizedError });\n setIsOperationPending(false);\n reject(normalizedError);\n }\n })();\n });\n },\n [client],\n );\n\n const isLoading =\n rawState === null && (clientStatus.client !== client || clientStatus.isInitializing);\n const error = clientError?.client === client ? clientError.value : null;\n\n const flushQueue = useCallback(async (): Promise<void> => {\n if (!isOnline(syncAdapter) || isFlushingQueue.current) return;\n isFlushingQueue.current = true;\n setIsOperationPending(true);\n try {\n for (const request of offlineQueue) {\n if (!isOnline(syncAdapter)) break;\n const metadata = queuedMetadata.current.get(request.idempotencyKey);\n const previousState = metadata?.previousState ?? client.getState();\n try {\n const before = await syncAdapter?.onBeforeCheckIn?.(request);\n if (before === false) {\n if (metadata !== undefined && previousState !== null)\n await client.restore(previousState);\n setClientError({ client, value: { code: \"INVALID_PROOF\", stampId: request.stampId } });\n queuedMetadata.current.delete(request.idempotencyKey);\n setOfflineQueue((current) =>\n current.filter((item) => item.idempotencyKey !== request.idempotencyKey),\n );\n continue;\n }\n\n let result = metadata?.result;\n if (result === undefined) {\n const localResult = await client.acquire(request.stampId, request.context, request.now);\n if (!localResult.ok) {\n setClientError({ client, value: localResult.error });\n queuedMetadata.current.delete(request.idempotencyKey);\n setOfflineQueue((current) =>\n current.filter((item) => item.idempotencyKey !== request.idempotencyKey),\n );\n continue;\n }\n result = localResult.value;\n queuedMetadata.current.set(request.idempotencyKey, {\n previousState,\n result,\n });\n }\n\n const verified = await syncAdapter?.onServerVerify?.(request);\n if (isRejectedVerification(verified)) {\n if (previousState !== null) await client.restore(previousState);\n setClientError({\n client,\n value: { code: \"INVALID_PROOF\", stampId: request.stampId },\n });\n } else {\n notifyAcquisitionEvents(\n result,\n events,\n options.onStampClaimed,\n options.onRewardUnlocked,\n );\n }\n queuedMetadata.current.delete(request.idempotencyKey);\n setOfflineQueue((current) =>\n current.filter((item) => item.idempotencyKey !== request.idempotencyKey),\n );\n } catch (flushError) {\n if (isNetworkFailure(flushError)) {\n setClientError({ client, value: toError(flushError) });\n break;\n }\n setClientError({ client, value: toError(flushError) });\n queuedMetadata.current.delete(request.idempotencyKey);\n setOfflineQueue((current) =>\n current.filter((item) => item.idempotencyKey !== request.idempotencyKey),\n );\n }\n }\n } finally {\n isFlushingQueue.current = false;\n setIsOperationPending(false);\n }\n }, [client, events, offlineQueue, options.onRewardUnlocked, options.onStampClaimed, syncAdapter]);\n\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n const handleOnline = (): void => {\n void flushQueue();\n };\n window.addEventListener(\"online\", handleOnline);\n return () => window.removeEventListener(\"online\", handleOnline);\n }, [flushQueue]);\n\n return {\n state: optimisticState,\n isLoading,\n isPending: isPending || isOperationPending,\n error,\n rewardsState: optimisticState?.rewards ?? [],\n acquire,\n reset,\n redeem,\n exportRecoveryCode,\n importRecoveryCode,\n offlineQueue,\n queuedCount: offlineQueue.length,\n flushQueue,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/useStampRally.ts"],"names":["result","error"],"mappings":"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","file":"index.js","sourcesContent":["import type {\n ConsumeResult,\n ProcessStampValue,\n Result,\n RewardConsumeError,\n RewardState,\n SecureTokenOptions,\n StampError,\n StampRallyClient,\n StampRallyState,\n VerificationContext,\n} from \"@stamprally/core\";\nimport {\n consumeReward,\n createSecureToken,\n exportProgressToken,\n importProgressToken,\n isStampRallyState,\n verifySecureToken,\n} from \"@stamprally/core\";\nimport {\n useCallback,\n useEffect,\n useRef,\n useState,\n useSyncExternalStore,\n useTransition,\n} from \"react\";\n\ninterface OptimisticAcquire {\n readonly stampId: string;\n readonly acquiredAt: string;\n}\n\ninterface ClientStatus {\n readonly client: StampRallyClient;\n readonly isInitializing: boolean;\n}\n\ninterface ClientError {\n readonly client: StampRallyClient;\n readonly value: StampError | RewardConsumeError | Error;\n}\n\ninterface QueuedRequestMetadata {\n readonly previousState: StampRallyState | null;\n readonly result: ProcessStampValue;\n}\n\nexport interface CheckInRequest {\n readonly stampId: string;\n readonly context: VerificationContext;\n readonly now: string;\n readonly idempotencyKey: string;\n}\n\nexport interface SyncAdapter {\n readonly isOnline?: boolean | (() => boolean);\n readonly onBeforeCheckIn?: (request: CheckInRequest) => unknown;\n readonly onServerVerify?: (request: CheckInRequest) => unknown;\n readonly onStateChange?: (state: StampRallyState) => void;\n}\n\nexport interface StampRallyEventHandlers {\n readonly onStampClaimed?: (record: StampRallyState[\"records\"][number]) => void;\n readonly onRewardUnlocked?: (rewardId: string) => void;\n readonly onRewardConsumed?: (rewardId: string) => void;\n}\n\nexport interface UseStampRallyOptions {\n readonly syncAdapter?: SyncAdapter;\n readonly events?: StampRallyEventHandlers;\n readonly onStampClaimed?: StampRallyEventHandlers[\"onStampClaimed\"];\n readonly onRewardUnlocked?: StampRallyEventHandlers[\"onRewardUnlocked\"];\n readonly onRewardConsumed?: StampRallyEventHandlers[\"onRewardConsumed\"];\n readonly serverUserId?: string;\n}\n\nexport interface RedeemOptions {\n readonly passcode?: string;\n readonly staffId?: string;\n}\n\nexport interface UseStampRallyReturn {\n readonly state: StampRallyState | null;\n readonly isLoading: boolean;\n readonly isPending: boolean;\n readonly error: StampError | RewardConsumeError | Error | null;\n readonly rewardsState: ReadonlyArray<RewardState>;\n readonly acquire: (\n stampId: string,\n context: VerificationContext,\n now?: string,\n idempotencyKey?: string,\n ) => Promise<Result<ProcessStampValue, StampError>>;\n readonly reset: (now?: string) => Promise<StampRallyState>;\n readonly redeem: (rewardId: string, options?: RedeemOptions) => Promise<ConsumeResult>;\n readonly exportRecoveryCode: () => string;\n readonly importRecoveryCode: (token: string) => Promise<boolean>;\n readonly exportRecoveryToken: (\n secretKey: string,\n options?: SecureTokenOptions,\n ) => Promise<string>;\n readonly importRecoveryToken: (token: string, secretKey: string) => Promise<boolean>;\n readonly syncWithServer: (serverEndpoint: string, authHeader?: string) => Promise<void>;\n readonly offlineQueue: ReadonlyArray<CheckInRequest>;\n readonly queuedCount: number;\n readonly flushQueue: () => Promise<void>;\n}\n\n/** @deprecated Use UseStampRallyReturn instead. */\nexport type UseStampRallyValue = UseStampRallyReturn;\n\nfunction getServerSnapshot(): null {\n return null;\n}\n\nfunction toError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction applyOptimisticAcquire(\n currentState: StampRallyState | null,\n action: OptimisticAcquire,\n): StampRallyState | null {\n if (\n currentState === null ||\n currentState.records.some((record) => record.stampId === action.stampId)\n ) {\n return currentState;\n }\n\n return {\n ...currentState,\n records: [...currentState.records, { stampId: action.stampId, acquiredAt: action.acquiredAt }],\n updatedAt: action.acquiredAt,\n };\n}\n\nfunction createIdempotencyKey(): string {\n const cryptoApi = globalThis.crypto;\n if (cryptoApi !== undefined && typeof cryptoApi.randomUUID === \"function\") {\n return cryptoApi.randomUUID();\n }\n return `stamp-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n}\n\nfunction isOnline(adapter: SyncAdapter | undefined): boolean {\n if (adapter?.isOnline === undefined) {\n return typeof navigator === \"undefined\" || navigator.onLine !== false;\n }\n return typeof adapter.isOnline === \"function\" ? adapter.isOnline() : adapter.isOnline;\n}\n\nfunction isRejectedVerification(value: unknown): boolean {\n return (\n value === false ||\n (typeof value === \"object\" && value !== null && \"ok\" in value && value.ok === false)\n );\n}\n\nfunction isNetworkFailure(error: unknown): boolean {\n if (typeof navigator !== \"undefined\" && navigator.onLine === false) return true;\n if (!(error instanceof Error)) return true;\n return /aborted|connection|fetch|network|offline|timeout/i.test(`${error.name} ${error.message}`);\n}\n\nfunction notifyAcquisitionEvents(\n result: ProcessStampValue,\n events: StampRallyEventHandlers | undefined,\n onStampClaimed: StampRallyEventHandlers[\"onStampClaimed\"] | undefined,\n onRewardUnlocked: StampRallyEventHandlers[\"onRewardUnlocked\"] | undefined,\n): void {\n for (const event of result.events) {\n if (event.type === \"stampAcquired\") {\n (events?.onStampClaimed ?? onStampClaimed)?.(event.record);\n }\n if (event.type === \"rewardUnlocked\") {\n (events?.onRewardUnlocked ?? onRewardUnlocked)?.(event.rewardId);\n }\n }\n}\n\nexport function useStampRally(\n client: StampRallyClient,\n options: UseStampRallyOptions = {},\n): UseStampRallyReturn {\n const syncAdapter = options.syncAdapter;\n const events = options.events;\n const [offlineQueue, setOfflineQueue] = useState<ReadonlyArray<CheckInRequest>>([]);\n const queuedMetadata = useRef(new Map<string, QueuedRequestMetadata>());\n const isFlushingQueue = useRef(false);\n const activeClient = useRef(client);\n const subscribe = useCallback(\n (onStoreChange: () => void) => client.subscribe(() => onStoreChange()),\n [client],\n );\n const getSnapshot = useCallback(() => client.getState(), [client]);\n const rawState = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n const [clientStatus, setClientStatus] = useState<ClientStatus>(() => ({\n client,\n isInitializing: client.getState() === null,\n }));\n const [clientError, setClientError] = useState<ClientError | null>(null);\n const [isPending] = useTransition();\n const [isOperationPending, setIsOperationPending] = useState(false);\n const [optimisticState, setOptimisticState] = useState<StampRallyState | null>(rawState);\n\n useEffect(() => {\n setOptimisticState(rawState);\n }, [rawState]);\n\n useEffect(() => {\n if (activeClient.current === client) return;\n activeClient.current = client;\n setOfflineQueue([]);\n queuedMetadata.current.clear();\n }, [client]);\n\n useEffect(() => {\n if (syncAdapter?.onStateChange === undefined) return;\n return client.subscribe(syncAdapter.onStateChange);\n }, [client, syncAdapter]);\n\n useEffect(() => {\n let active = true;\n setClientError(null);\n if (rawState !== null) {\n setClientStatus({ client, isInitializing: false });\n return () => {\n active = false;\n };\n }\n\n setClientStatus({ client, isInitializing: true });\n\n void client\n .init()\n .catch((initializationError: unknown) => {\n if (active) {\n setClientError({ client, value: toError(initializationError) });\n }\n })\n .finally(() => {\n if (active) {\n setClientStatus({ client, isInitializing: false });\n }\n });\n\n return () => {\n active = false;\n };\n }, [client, rawState]);\n\n const queueRequest = useCallback(\n (request: CheckInRequest, metadata?: QueuedRequestMetadata): void => {\n if (metadata !== undefined) queuedMetadata.current.set(request.idempotencyKey, metadata);\n setOfflineQueue((current) =>\n current.some((item) => item.idempotencyKey === request.idempotencyKey)\n ? current\n : [...current, request],\n );\n },\n [],\n );\n\n const acquire = useCallback(\n (\n stampId: string,\n context: VerificationContext,\n now?: string,\n idempotencyKey?: string,\n ): Promise<Result<ProcessStampValue, StampError>> => {\n const acquiredAt = now ?? new Date().toISOString();\n const request: CheckInRequest = {\n stampId,\n context,\n now: acquiredAt,\n idempotencyKey: idempotencyKey ?? createIdempotencyKey(),\n };\n setClientError(null);\n\n if (!isOnline(syncAdapter)) {\n queueRequest(request);\n const queued: Result<ProcessStampValue, StampError> = {\n ok: false,\n error: { code: \"OFFLINE_QUEUED\", stampId, idempotencyKey: request.idempotencyKey },\n };\n setClientError({ client, value: queued.error });\n return Promise.resolve(queued);\n }\n\n setIsOperationPending(true);\n setOptimisticState((current) => applyOptimisticAcquire(current, { stampId, acquiredAt }));\n return new Promise((resolve, reject) => {\n void (async () => {\n try {\n const before = await syncAdapter?.onBeforeCheckIn?.(request);\n if (before === false) {\n const rejected: Result<ProcessStampValue, StampError> = {\n ok: false,\n error: { code: \"INVALID_PROOF\", stampId },\n };\n setClientError({ client, value: rejected.error });\n setIsOperationPending(false);\n resolve(rejected);\n return;\n }\n const previousState = client.getState();\n const result = await client.acquire(stampId, context, acquiredAt);\n if (result.ok) {\n let verified: unknown;\n try {\n verified = await syncAdapter?.onServerVerify?.(request);\n } catch (verificationError) {\n if (!isNetworkFailure(verificationError)) throw verificationError;\n queueRequest(request, { previousState, result: result.value });\n const queued: Result<ProcessStampValue, StampError> = {\n ok: false,\n error: {\n code: \"OFFLINE_QUEUED\",\n stampId,\n idempotencyKey: request.idempotencyKey,\n },\n };\n setClientError({ client, value: toError(verificationError) });\n setOptimisticState(client.getState());\n setIsOperationPending(false);\n resolve(queued);\n return;\n }\n if (isRejectedVerification(verified)) {\n await client.restore(rawState ?? client.getState() ?? result.value.nextState);\n const rejected: Result<ProcessStampValue, StampError> = {\n ok: false,\n error: { code: \"INVALID_PROOF\", stampId },\n };\n setClientError({ client, value: rejected.error });\n setOptimisticState(client.getState());\n setIsOperationPending(false);\n resolve(rejected);\n return;\n }\n notifyAcquisitionEvents(\n result.value,\n events,\n options.onStampClaimed,\n options.onRewardUnlocked,\n );\n }\n if (!result.ok) {\n setClientError({ client, value: result.error });\n setOptimisticState(client.getState());\n }\n setIsOperationPending(false);\n resolve(result);\n } catch (acquireError) {\n if (isNetworkFailure(acquireError)) {\n queueRequest(request);\n const queued: Result<ProcessStampValue, StampError> = {\n ok: false,\n error: { code: \"OFFLINE_QUEUED\", stampId, idempotencyKey: request.idempotencyKey },\n };\n setClientError({ client, value: queued.error });\n setOptimisticState(client.getState());\n setIsOperationPending(false);\n resolve(queued);\n return;\n }\n const normalizedError = toError(acquireError);\n setClientError({ client, value: normalizedError });\n setOptimisticState(client.getState());\n setIsOperationPending(false);\n reject(normalizedError);\n }\n })();\n });\n },\n [\n client,\n events,\n options.onRewardUnlocked,\n options.onStampClaimed,\n queueRequest,\n rawState,\n syncAdapter,\n ],\n );\n\n const reset = useCallback(\n (now?: string): Promise<StampRallyState> => {\n setClientError(null);\n setIsOperationPending(true);\n\n return new Promise((resolve, reject) => {\n void (async () => {\n try {\n const nextState = now === undefined ? await client.reset() : await client.reset(now);\n setIsOperationPending(false);\n resolve(nextState);\n } catch (resetError) {\n const normalizedError = toError(resetError);\n setClientError({ client, value: normalizedError });\n setIsOperationPending(false);\n reject(normalizedError);\n }\n })();\n });\n },\n [client],\n );\n\n const redeem = useCallback(\n (rewardId: string, redeemOptions: RedeemOptions = {}): Promise<ConsumeResult> => {\n setClientError(null);\n setIsOperationPending(true);\n\n return new Promise((resolve, reject) => {\n void (async () => {\n const reward = client.getConfig().rewards?.find((item) => item.id === rewardId);\n if (reward === undefined) {\n const result: ConsumeResult = {\n ok: false,\n error: { code: \"REWARD_NOT_FOUND\", rewardId },\n };\n setClientError({ client, value: result.error });\n setIsOperationPending(false);\n resolve(result);\n return;\n }\n\n const currentState = client.getState();\n const currentRewardState = currentState?.rewards?.find(\n (state) => state.rewardId === rewardId,\n );\n if (currentState === null || currentRewardState === undefined) {\n const result: ConsumeResult = {\n ok: false,\n error: { code: \"NOT_AVAILABLE\", rewardId },\n };\n setClientError({ client, value: result.error });\n setIsOperationPending(false);\n resolve(result);\n return;\n }\n\n const result = consumeReward({\n reward,\n currentState: currentRewardState,\n now: new Date().toISOString(),\n ...(redeemOptions.passcode === undefined\n ? {}\n : { inputPasscode: redeemOptions.passcode }),\n ...(redeemOptions.staffId === undefined ? {} : { staffId: redeemOptions.staffId }),\n });\n if (!result.ok) {\n setClientError({ client, value: result.error });\n setIsOperationPending(false);\n resolve(result);\n return;\n }\n\n if (result.value === currentRewardState) {\n setIsOperationPending(false);\n resolve(result);\n return;\n }\n\n const nextState: StampRallyState = {\n ...currentState,\n rewards: (currentState.rewards ?? []).map((state) =>\n state.rewardId === rewardId ? result.value : state,\n ),\n updatedAt: result.value.consumedAt ?? currentState.updatedAt,\n };\n\n try {\n await client.restore(nextState);\n (events?.onRewardConsumed ?? options.onRewardConsumed)?.(rewardId);\n setIsOperationPending(false);\n resolve(result);\n } catch (redeemError) {\n const normalizedError = toError(redeemError);\n setClientError({ client, value: normalizedError });\n setIsOperationPending(false);\n reject(normalizedError);\n }\n })();\n });\n },\n [client, events, options.onRewardConsumed],\n );\n\n const exportRecoveryCode = useCallback((): string => {\n const state = client.getState();\n if (state === null) {\n throw new Error(\"Cannot export recovery code before the rally is initialized.\");\n }\n\n return exportProgressToken({\n version: 1,\n rallyId: state.rallyId,\n stamps: state.records,\n rewards: state.rewards ?? [],\n exportedAt: new Date().toISOString(),\n });\n }, [client]);\n\n const importRecoveryCode = useCallback(\n (token: string): Promise<boolean> => {\n setClientError(null);\n setIsOperationPending(true);\n\n return new Promise((resolve, reject) => {\n void (async () => {\n const config = client.getConfig();\n const snapshot = importProgressToken(token, config.id);\n if (snapshot === null) {\n setIsOperationPending(false);\n resolve(false);\n return;\n }\n\n const stampIds = new Set(config.stamps.map((stamp) => stamp.id));\n const rewardIds = new Set((config.rewards ?? []).map((reward) => reward.id));\n const importedStampIds = new Set<string>();\n const importedRewardIds = new Set<string>();\n const stamps = snapshot.stamps.filter((record) => {\n if (!stampIds.has(record.stampId) || importedStampIds.has(record.stampId)) return false;\n importedStampIds.add(record.stampId);\n return true;\n });\n const rewards = snapshot.rewards.filter((state) => {\n if (!rewardIds.has(state.rewardId) || importedRewardIds.has(state.rewardId))\n return false;\n importedRewardIds.add(state.rewardId);\n return true;\n });\n\n try {\n await client.restore({\n rallyId: config.id,\n records: stamps,\n ...(config.rewards === undefined && rewards.length === 0 ? {} : { rewards }),\n updatedAt: snapshot.exportedAt,\n });\n setIsOperationPending(false);\n resolve(true);\n } catch (importError) {\n const normalizedError = toError(importError);\n setClientError({ client, value: normalizedError });\n setIsOperationPending(false);\n reject(normalizedError);\n }\n })();\n });\n },\n [client],\n );\n\n const exportRecoveryToken = useCallback(\n async (secretKey: string, tokenOptions: SecureTokenOptions = {}): Promise<string> => {\n const state = client.getState();\n if (state === null)\n throw new Error(\"Cannot export recovery token before the rally is initialized.\");\n return createSecureToken(\n {\n type: \"recovery\",\n rallyId: state.rallyId,\n state,\n exportedAt: new Date().toISOString(),\n },\n secretKey,\n { encrypt: tokenOptions.encrypt ?? true, ...tokenOptions },\n );\n },\n [client],\n );\n\n const importRecoveryToken = useCallback(\n async (token: string, secretKey: string): Promise<boolean> => {\n setClientError(null);\n setIsOperationPending(true);\n try {\n const verified = await verifySecureToken(token, secretKey);\n if (\n !verified.ok ||\n verified.payload.type !== \"recovery\" ||\n verified.payload.rallyId !== client.getConfig().id\n ) {\n setIsOperationPending(false);\n return false;\n }\n const candidate = verified.payload.state;\n if (!isStampRallyState(candidate)) {\n setIsOperationPending(false);\n return false;\n }\n await client.restore(candidate);\n setIsOperationPending(false);\n return true;\n } catch (error) {\n setClientError({ client, value: toError(error) });\n setIsOperationPending(false);\n return false;\n }\n },\n [client],\n );\n\n const syncWithServer = useCallback(\n async (serverEndpoint: string, authHeader?: string): Promise<void> => {\n if (typeof fetch !== \"function\")\n throw new Error(\"Fetch API is unavailable in this environment.\");\n setClientError(null);\n setIsOperationPending(true);\n try {\n const endpoint = serverEndpoint.replace(/\\/$/u, \"\").endsWith(\"/sync\")\n ? serverEndpoint\n : `${serverEndpoint.replace(/\\/$/u, \"\")}/api/sync`;\n const response = await fetch(endpoint, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n ...(authHeader === undefined ? {} : { authorization: authHeader }),\n },\n body: JSON.stringify({\n userId: options.serverUserId ?? \"anonymous\",\n queue: offlineQueue.map((request) => ({\n ...request,\n userId: options.serverUserId ?? \"anonymous\",\n spotId: request.stampId,\n claimMethod: request.context.type,\n proofData:\n request.context.type === \"token\"\n ? { token: request.context.token }\n : request.context.type === \"geo\"\n ? {\n latitude: request.context.currentLatitude,\n longitude: request.context.currentLongitude,\n }\n : undefined,\n })),\n }),\n });\n const payload: unknown = await response.json();\n if (\n !response.ok ||\n !isObject(payload) ||\n payload.ok !== true ||\n !isStampRallyState(payload.state)\n ) {\n throw new Error(\"Server synchronization was rejected.\");\n }\n await client.restore(payload.state);\n queuedMetadata.current.clear();\n setOfflineQueue([]);\n client.notifySyncCompleted(payload.state);\n } catch (error) {\n setClientError({ client, value: toError(error) });\n throw error;\n } finally {\n setIsOperationPending(false);\n }\n },\n [client, offlineQueue, options.serverUserId],\n );\n\n const isLoading =\n rawState === null && (clientStatus.client !== client || clientStatus.isInitializing);\n const error = clientError?.client === client ? clientError.value : null;\n\n const flushQueue = useCallback(async (): Promise<void> => {\n if (!isOnline(syncAdapter) || isFlushingQueue.current) return;\n isFlushingQueue.current = true;\n setIsOperationPending(true);\n try {\n for (const request of offlineQueue) {\n if (!isOnline(syncAdapter)) break;\n const metadata = queuedMetadata.current.get(request.idempotencyKey);\n const previousState = metadata?.previousState ?? client.getState();\n try {\n const before = await syncAdapter?.onBeforeCheckIn?.(request);\n if (before === false) {\n if (metadata !== undefined && previousState !== null)\n await client.restore(previousState);\n setClientError({ client, value: { code: \"INVALID_PROOF\", stampId: request.stampId } });\n queuedMetadata.current.delete(request.idempotencyKey);\n setOfflineQueue((current) =>\n current.filter((item) => item.idempotencyKey !== request.idempotencyKey),\n );\n continue;\n }\n\n let result = metadata?.result;\n if (result === undefined) {\n const localResult = await client.acquire(request.stampId, request.context, request.now);\n if (!localResult.ok) {\n setClientError({ client, value: localResult.error });\n queuedMetadata.current.delete(request.idempotencyKey);\n setOfflineQueue((current) =>\n current.filter((item) => item.idempotencyKey !== request.idempotencyKey),\n );\n continue;\n }\n result = localResult.value;\n queuedMetadata.current.set(request.idempotencyKey, {\n previousState,\n result,\n });\n }\n\n const verified = await syncAdapter?.onServerVerify?.(request);\n if (isRejectedVerification(verified)) {\n if (previousState !== null) await client.restore(previousState);\n setClientError({\n client,\n value: { code: \"INVALID_PROOF\", stampId: request.stampId },\n });\n } else {\n notifyAcquisitionEvents(\n result,\n events,\n options.onStampClaimed,\n options.onRewardUnlocked,\n );\n }\n queuedMetadata.current.delete(request.idempotencyKey);\n setOfflineQueue((current) =>\n current.filter((item) => item.idempotencyKey !== request.idempotencyKey),\n );\n } catch (flushError) {\n if (isNetworkFailure(flushError)) {\n setClientError({ client, value: toError(flushError) });\n break;\n }\n setClientError({ client, value: toError(flushError) });\n queuedMetadata.current.delete(request.idempotencyKey);\n setOfflineQueue((current) =>\n current.filter((item) => item.idempotencyKey !== request.idempotencyKey),\n );\n }\n }\n } finally {\n isFlushingQueue.current = false;\n setIsOperationPending(false);\n }\n }, [client, events, offlineQueue, options.onRewardUnlocked, options.onStampClaimed, syncAdapter]);\n\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n const handleOnline = (): void => {\n void flushQueue();\n };\n window.addEventListener(\"online\", handleOnline);\n return () => window.removeEventListener(\"online\", handleOnline);\n }, [flushQueue]);\n\n return {\n state: optimisticState,\n isLoading,\n isPending: isPending || isOperationPending,\n error,\n rewardsState: optimisticState?.rewards ?? [],\n acquire,\n reset,\n redeem,\n exportRecoveryCode,\n importRecoveryCode,\n exportRecoveryToken,\n importRecoveryToken,\n syncWithServer,\n offlineQueue,\n queuedCount: offlineQueue.length,\n flushQueue,\n };\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stamprally/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "React hooks for @stamprally/core.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
}
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@stamprally/core": "0.
|
|
34
|
+
"@stamprally/core": "0.5.1"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
37
|
"react": "^18.0.0 || ^19.0.0",
|