@stamprally/core 0.14.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -3
- package/dist/index.cjs +141 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +31 -3
- package/dist/index.d.ts +31 -3
- package/dist/index.js +141 -28
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @stamprally/core
|
|
1
|
+
# @stamprally/core v0.15.0
|
|
2
2
|
|
|
3
3
|
Dependency-free domain models, immutable state transitions, storage adapters, browser detectors, and safe configuration parsers.
|
|
4
4
|
|
|
@@ -7,7 +7,7 @@ import { InMemoryStorage, StampRallyClient, type PublicRallyConfig } from "@stam
|
|
|
7
7
|
|
|
8
8
|
const config: PublicRallyConfig = {
|
|
9
9
|
id: "city-tour",
|
|
10
|
-
version: "0.
|
|
10
|
+
version: "0.15.0",
|
|
11
11
|
title: "City Tour",
|
|
12
12
|
spots: [{ id: "station", orderIndex: 0, name: "Central Station", conditions: [{ type: "passcode" }] }],
|
|
13
13
|
rewards: [],
|
|
@@ -23,7 +23,7 @@ The browser detectors `getCurrentGeoContext`, `readNfcContext`, and `readQrConte
|
|
|
23
23
|
|
|
24
24
|
`InMemoryStorage`, `LocalStorageAdapter`, and `IndexedDBAdapter` implement `StampStorage`. `updateLocalizedField` updates one locale without dropping existing translations.
|
|
25
25
|
|
|
26
|
-
## v0.
|
|
26
|
+
## v0.15.0 offline synchronization
|
|
27
27
|
|
|
28
28
|
Configure `OfflineQueue` with `rallyId` and `userId` to persist pending work under
|
|
29
29
|
`stamprally:queue:<rallyId>:<userId-or-anonymous>`. `switchUser` loads the other
|
|
@@ -32,6 +32,10 @@ timestamps, and gives `CONSUMED` reward states priority; `server_wins` uses the
|
|
|
32
32
|
server state unchanged. Sync adapters may return `ACCEPTED`, `REJECTED_PERMANENT`,
|
|
33
33
|
or `RETRYABLE_ERROR`; only the first two remove an operation.
|
|
34
34
|
|
|
35
|
+
When no authenticated user is supplied, the client creates a persistent UUID v4
|
|
36
|
+
`anonymousSessionId` and includes it in sync requests. Queue replay uses Web Locks
|
|
37
|
+
when available and supports bounded exponential backoff through `retryOptions`.
|
|
38
|
+
|
|
35
39
|
`evaluateSpotStatus` derives `UNCLAIMED`, `CLAIMED`, `LOCKED`, or `VERIFYING`
|
|
36
40
|
without mutating state. A spot with incomplete `prerequisites` is `LOCKED` and
|
|
37
41
|
must not be verified by a client or viewer.
|
package/dist/index.cjs
CHANGED
|
@@ -617,7 +617,13 @@ function cloneState(state) {
|
|
|
617
617
|
return {
|
|
618
618
|
...state,
|
|
619
619
|
records: state.records.map(cloneRecord),
|
|
620
|
-
...state.rewards === void 0 ? {} : { rewards: state.rewards.map(cloneRewardState) }
|
|
620
|
+
...state.rewards === void 0 ? {} : { rewards: state.rewards.map(cloneRewardState) },
|
|
621
|
+
...state.inventory === void 0 ? {} : {
|
|
622
|
+
inventory: {
|
|
623
|
+
...state.inventory,
|
|
624
|
+
...state.inventory.rewardRemaining === void 0 ? {} : { rewardRemaining: { ...state.inventory.rewardRemaining } }
|
|
625
|
+
}
|
|
626
|
+
}
|
|
621
627
|
};
|
|
622
628
|
}
|
|
623
629
|
function isRecord(value) {
|
|
@@ -637,7 +643,7 @@ function isRewardState(value) {
|
|
|
637
643
|
function isStampRallyState(value) {
|
|
638
644
|
if (typeof value !== "object" || value === null) return false;
|
|
639
645
|
const state = value;
|
|
640
|
-
return typeof state.rallyId === "string" && (typeof state.userId === "string" || state.userId === null) && typeof state.updatedAt === "string" && Array.isArray(state.records) && state.records.every(isRecord) && (state.rewards === void 0 || Array.isArray(state.rewards) && state.rewards.every(isRewardState));
|
|
646
|
+
return typeof state.rallyId === "string" && (typeof state.userId === "string" || state.userId === null) && typeof state.updatedAt === "string" && Array.isArray(state.records) && state.records.every(isRecord) && (state.rewards === void 0 || Array.isArray(state.rewards) && state.rewards.every(isRewardState)) && (state.inventory === void 0 || typeof state.inventory === "object" && state.inventory !== null && !Array.isArray(state.inventory) && (state.inventory.sharedRemaining === void 0 || typeof state.inventory.sharedRemaining === "number" && Number.isInteger(state.inventory.sharedRemaining) && state.inventory.sharedRemaining >= 0) && (state.inventory.rewardRemaining === void 0 || typeof state.inventory.rewardRemaining === "object" && state.inventory.rewardRemaining !== null && !Array.isArray(state.inventory.rewardRemaining)));
|
|
641
647
|
}
|
|
642
648
|
function isValidDate(value) {
|
|
643
649
|
return typeof value === "string" && !Number.isNaN(Date.parse(value));
|
|
@@ -682,6 +688,35 @@ var InMemoryStorage = class {
|
|
|
682
688
|
function storageKey(rallyId, userId) {
|
|
683
689
|
return `stamprally:${rallyId}:${userId ?? "anonymous"}`;
|
|
684
690
|
}
|
|
691
|
+
function createAnonymousSessionId(storage) {
|
|
692
|
+
const key = "stamprally:anonymous-session-id";
|
|
693
|
+
try {
|
|
694
|
+
const browserStorage = typeof window === "undefined" ? null : window.localStorage;
|
|
695
|
+
const value = storage?.getItem(key) ?? browserStorage?.getItem(key);
|
|
696
|
+
if (value !== null && value !== void 0 && isUuidV4(value)) return value;
|
|
697
|
+
const generated = randomUuidV4();
|
|
698
|
+
(storage ?? browserStorage)?.setItem(key, generated);
|
|
699
|
+
return generated;
|
|
700
|
+
} catch {
|
|
701
|
+
return randomUuidV4();
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
function isUuidV4(value) {
|
|
705
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
|
|
706
|
+
}
|
|
707
|
+
function randomUuidV4() {
|
|
708
|
+
const cryptoApi3 = globalThis.crypto;
|
|
709
|
+
if (cryptoApi3?.randomUUID !== void 0) return cryptoApi3.randomUUID();
|
|
710
|
+
if (cryptoApi3?.getRandomValues !== void 0) {
|
|
711
|
+
const bytes2 = cryptoApi3.getRandomValues(new Uint8Array(16));
|
|
712
|
+
bytes2[6] = (bytes2[6] ?? 0) & 15 | 64;
|
|
713
|
+
bytes2[8] = (bytes2[8] ?? 0) & 63 | 128;
|
|
714
|
+
const hex = Array.from(bytes2, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
715
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
716
|
+
}
|
|
717
|
+
const random = `${Date.now().toString(16)}${Math.random().toString(16).slice(2)}`.padEnd(32, "0").slice(0, 32);
|
|
718
|
+
return `${random.slice(0, 8)}-${random.slice(8, 12)}-4${random.slice(13, 16)}-8${random.slice(17, 20)}-${random.slice(20)}`;
|
|
719
|
+
}
|
|
685
720
|
var defaultStorageWarningHandler = (error) => {
|
|
686
721
|
console.warn(`[@stamprally/core] ${error.message}`, error);
|
|
687
722
|
};
|
|
@@ -1071,6 +1106,7 @@ var StampRallyClient = class {
|
|
|
1071
1106
|
#config;
|
|
1072
1107
|
#offlineQueue;
|
|
1073
1108
|
#userId;
|
|
1109
|
+
#anonymousSessionId;
|
|
1074
1110
|
#state = null;
|
|
1075
1111
|
#initialization = null;
|
|
1076
1112
|
#queue = Promise.resolve();
|
|
@@ -1078,7 +1114,8 @@ var StampRallyClient = class {
|
|
|
1078
1114
|
this.#config = config;
|
|
1079
1115
|
this.#options = isStorage(storageOrOptions) ? { storage: storageOrOptions } : storageOrOptions;
|
|
1080
1116
|
this.#storage = this.#options.storage ?? new InMemoryStorage();
|
|
1081
|
-
this.#
|
|
1117
|
+
this.#anonymousSessionId = this.#options.anonymousSessionId ?? createAnonymousSessionId();
|
|
1118
|
+
this.#userId = this.#options.userId ?? this.#anonymousSessionId;
|
|
1082
1119
|
this.#offlineQueue = this.#options.offlineQueue;
|
|
1083
1120
|
this.#offlineQueue?.setSyncResultListener((event) => this.#handleOfflineSyncResult(event));
|
|
1084
1121
|
}
|
|
@@ -1091,6 +1128,9 @@ var StampRallyClient = class {
|
|
|
1091
1128
|
getUserId() {
|
|
1092
1129
|
return this.#userId;
|
|
1093
1130
|
}
|
|
1131
|
+
getAnonymousSessionId() {
|
|
1132
|
+
return this.#anonymousSessionId;
|
|
1133
|
+
}
|
|
1094
1134
|
get syncState() {
|
|
1095
1135
|
return this.#offlineQueue?.syncState ?? "idle";
|
|
1096
1136
|
}
|
|
@@ -1128,11 +1168,12 @@ var StampRallyClient = class {
|
|
|
1128
1168
|
}
|
|
1129
1169
|
switchUser(newUserId) {
|
|
1130
1170
|
return this.#enqueue(async () => {
|
|
1131
|
-
|
|
1132
|
-
this.#userId
|
|
1171
|
+
const nextUserId = newUserId ?? this.#anonymousSessionId;
|
|
1172
|
+
if (this.#userId === nextUserId && this.#state !== null) return this.#state;
|
|
1173
|
+
this.#userId = nextUserId;
|
|
1133
1174
|
this.#state = null;
|
|
1134
1175
|
this.#initialization = null;
|
|
1135
|
-
await this.#offlineQueue?.switchUser(
|
|
1176
|
+
await this.#offlineQueue?.switchUser(nextUserId);
|
|
1136
1177
|
return this.initialize();
|
|
1137
1178
|
});
|
|
1138
1179
|
}
|
|
@@ -1200,7 +1241,8 @@ var StampRallyClient = class {
|
|
|
1200
1241
|
proofData,
|
|
1201
1242
|
idempotencyKey: options.idempotencyKey ?? id("check-in"),
|
|
1202
1243
|
now,
|
|
1203
|
-
state: current
|
|
1244
|
+
state: current,
|
|
1245
|
+
...this.#userId === this.#anonymousSessionId ? { anonymousSessionId: this.#anonymousSessionId } : {}
|
|
1204
1246
|
};
|
|
1205
1247
|
const remote = this.#options.syncAdapter?.checkIn;
|
|
1206
1248
|
if (options.sync !== false && remote !== void 0) {
|
|
@@ -1256,7 +1298,8 @@ var StampRallyClient = class {
|
|
|
1256
1298
|
idempotencyKey: options.idempotencyKey ?? id("claim"),
|
|
1257
1299
|
now,
|
|
1258
1300
|
options,
|
|
1259
|
-
state: current
|
|
1301
|
+
state: current,
|
|
1302
|
+
...this.#userId === this.#anonymousSessionId ? { anonymousSessionId: this.#anonymousSessionId } : {}
|
|
1260
1303
|
};
|
|
1261
1304
|
const remote = this.#options.syncAdapter?.claimReward;
|
|
1262
1305
|
if (options.sync !== false && remote !== void 0) {
|
|
@@ -1308,13 +1351,10 @@ var StampRallyClient = class {
|
|
|
1308
1351
|
const serverState = await adapter.sync({
|
|
1309
1352
|
rallyId: this.#config.id,
|
|
1310
1353
|
userId: this.#userId,
|
|
1311
|
-
state: this.#state ?? current
|
|
1312
|
-
|
|
1313
|
-
const localState = this.#state ?? current;
|
|
1314
|
-
const merged = this.#offlineQueue === void 0 ? serverState : resolveRallyStateConflict(serverState, localState, {
|
|
1315
|
-
policy: this.#offlineQueue.conflictPolicy
|
|
1354
|
+
state: this.#state ?? current,
|
|
1355
|
+
...this.#userId === this.#anonymousSessionId ? { anonymousSessionId: this.#anonymousSessionId } : {}
|
|
1316
1356
|
});
|
|
1317
|
-
const next = this.#reconcile(
|
|
1357
|
+
const next = this.#reconcile(serverState);
|
|
1318
1358
|
await this.#storage.save(next);
|
|
1319
1359
|
this.#state = next;
|
|
1320
1360
|
this.#emit(next);
|
|
@@ -1528,6 +1568,11 @@ function errorValue(value, fallbackCode) {
|
|
|
1528
1568
|
}
|
|
1529
1569
|
var syncLocks = /* @__PURE__ */ new Map();
|
|
1530
1570
|
var SYNC_LOCK_TTL_MS = 3e4;
|
|
1571
|
+
var DEFAULT_RETRY_OPTIONS = {
|
|
1572
|
+
maxRetries: 0,
|
|
1573
|
+
initialIntervalMs: 250,
|
|
1574
|
+
backoffMultiplier: 2
|
|
1575
|
+
};
|
|
1531
1576
|
function randomId() {
|
|
1532
1577
|
return globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
1533
1578
|
}
|
|
@@ -1546,6 +1591,7 @@ var OfflineQueue = class {
|
|
|
1546
1591
|
#syncPromise = null;
|
|
1547
1592
|
#syncResultListener;
|
|
1548
1593
|
#synchronizeInstances;
|
|
1594
|
+
#retryOptions;
|
|
1549
1595
|
#instanceId = randomId();
|
|
1550
1596
|
#lockStorage;
|
|
1551
1597
|
#storageListener;
|
|
@@ -1561,6 +1607,15 @@ var OfflineQueue = class {
|
|
|
1561
1607
|
this.#conflictPolicy = options.conflictPolicy ?? "server_wins";
|
|
1562
1608
|
this.#onSyncConflict = options.onSyncConflict;
|
|
1563
1609
|
this.#synchronizeInstances = options.synchronizeInstances ?? true;
|
|
1610
|
+
const retryOptions = {
|
|
1611
|
+
...DEFAULT_RETRY_OPTIONS,
|
|
1612
|
+
...options.retryOptions ?? options.syncRetryOptions ?? options.retry ?? {}
|
|
1613
|
+
};
|
|
1614
|
+
this.#retryOptions = {
|
|
1615
|
+
maxRetries: Math.max(0, Math.floor(retryOptions.maxRetries)),
|
|
1616
|
+
initialIntervalMs: Math.max(0, retryOptions.initialIntervalMs),
|
|
1617
|
+
backoffMultiplier: Math.max(1, retryOptions.backoffMultiplier)
|
|
1618
|
+
};
|
|
1564
1619
|
this.#lockStorage = options.storageLike ?? globalThis.localStorage ?? null;
|
|
1565
1620
|
if (this.#synchronizeInstances) this.#subscribeToExternalChanges();
|
|
1566
1621
|
}
|
|
@@ -1593,7 +1648,7 @@ var OfflineQueue = class {
|
|
|
1593
1648
|
}
|
|
1594
1649
|
async initialize() {
|
|
1595
1650
|
if (this.#loaded) return;
|
|
1596
|
-
this.#operations =
|
|
1651
|
+
this.#operations = (await this.#storage.load(this.#storageKey())).map(normalizeOperation);
|
|
1597
1652
|
this.#loaded = true;
|
|
1598
1653
|
}
|
|
1599
1654
|
/** Releases browser listeners when the queue is no longer used. */
|
|
@@ -1638,7 +1693,7 @@ var OfflineQueue = class {
|
|
|
1638
1693
|
await this.initialize();
|
|
1639
1694
|
const id2 = operationId(operation);
|
|
1640
1695
|
if (this.#operations.some((item) => operationId(item) === id2)) return;
|
|
1641
|
-
this.#operations = [...this.#operations, operation];
|
|
1696
|
+
this.#operations = [...this.#operations, { ...operation, status: "PENDING", attempts: 0 }];
|
|
1642
1697
|
await this.#storage.save(this.#storageKey(), this.#operations);
|
|
1643
1698
|
this.#announceChange();
|
|
1644
1699
|
}
|
|
@@ -1668,6 +1723,33 @@ var OfflineQueue = class {
|
|
|
1668
1723
|
return this.sync(sender);
|
|
1669
1724
|
}
|
|
1670
1725
|
async #run(sender) {
|
|
1726
|
+
const locks = globalThis.navigator?.locks;
|
|
1727
|
+
if (locks !== void 0) {
|
|
1728
|
+
let callbackStarted = false;
|
|
1729
|
+
try {
|
|
1730
|
+
const acquired = await locks.request(
|
|
1731
|
+
`stamprally:${this.#storageKey()}:sync`,
|
|
1732
|
+
{ ifAvailable: true },
|
|
1733
|
+
async (lock) => {
|
|
1734
|
+
if (lock === null) {
|
|
1735
|
+
await this.#reloadFromStorage();
|
|
1736
|
+
this.#state = "idle";
|
|
1737
|
+
return false;
|
|
1738
|
+
}
|
|
1739
|
+
callbackStarted = true;
|
|
1740
|
+
await this.#runWithStorageLock(sender);
|
|
1741
|
+
return true;
|
|
1742
|
+
}
|
|
1743
|
+
);
|
|
1744
|
+
if (!acquired) return;
|
|
1745
|
+
return;
|
|
1746
|
+
} catch (error) {
|
|
1747
|
+
if (callbackStarted) throw error;
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
await this.#runWithStorageLock(sender);
|
|
1751
|
+
}
|
|
1752
|
+
async #runWithStorageLock(sender) {
|
|
1671
1753
|
this.#state = "syncing";
|
|
1672
1754
|
this.#error = null;
|
|
1673
1755
|
if (!this.#acquireSyncLock()) {
|
|
@@ -1679,21 +1761,36 @@ var OfflineQueue = class {
|
|
|
1679
1761
|
while (this.#operations.length > 0) {
|
|
1680
1762
|
const operation = this.#operations[0];
|
|
1681
1763
|
if (operation === void 0) break;
|
|
1682
|
-
let
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1764
|
+
let attempt = 0;
|
|
1765
|
+
let response;
|
|
1766
|
+
while (true) {
|
|
1767
|
+
await this.#updateOperationStatus("IN_FLIGHT", attempt);
|
|
1768
|
+
try {
|
|
1769
|
+
response = this.#normalizeResponse(await sender(operation));
|
|
1770
|
+
} catch (cause) {
|
|
1771
|
+
response = {
|
|
1772
|
+
status: "RETRYABLE_ERROR",
|
|
1773
|
+
error: errorValue(cause, "RETRYABLE_ERROR")
|
|
1774
|
+
};
|
|
1775
|
+
}
|
|
1776
|
+
if (response.status !== "RETRYABLE_ERROR") break;
|
|
1690
1777
|
const error2 = errorValue(response.error ?? response.reason, "RETRYABLE_ERROR");
|
|
1778
|
+
await this.#updateOperationStatus("PENDING", attempt + 1);
|
|
1691
1779
|
await this.#syncResultListener?.({ operation, status: response.status, error: error2 });
|
|
1692
|
-
throw new Error(error2.message);
|
|
1780
|
+
if (attempt >= this.#retryOptions.maxRetries) throw new Error(error2.message);
|
|
1781
|
+
const interval = this.#retryOptions.initialIntervalMs * this.#retryOptions.backoffMultiplier ** attempt;
|
|
1782
|
+
await new Promise((resolve) => setTimeout(resolve, Math.max(0, interval)));
|
|
1783
|
+
attempt += 1;
|
|
1693
1784
|
}
|
|
1694
1785
|
const result = response.result;
|
|
1695
1786
|
const state = response.state ?? (result !== void 0 && "conflict" in result && result.conflict === true ? await this.resolveConflict(operation, result.localState, result.serverState) : result !== void 0 && "ok" in result && result.ok ? result.value.state : void 0);
|
|
1696
1787
|
const error = response.status === "REJECTED_PERMANENT" ? errorValue(response.error ?? response.reason, "REJECTED_PERMANENT") : void 0;
|
|
1788
|
+
await this.#updateOperationStatus(
|
|
1789
|
+
response.status === "ACCEPTED" ? "ACCEPTED" : "REJECTED",
|
|
1790
|
+
attempt + 1
|
|
1791
|
+
);
|
|
1792
|
+
const fallbackState = response.status === "REJECTED_PERMANENT" ? operation.request.state : void 0;
|
|
1793
|
+
const eventState = state ?? fallbackState;
|
|
1697
1794
|
this.#operations = this.#operations.slice(1);
|
|
1698
1795
|
await this.#storage.save(this.#storageKey(), this.#operations);
|
|
1699
1796
|
this.#announceChange();
|
|
@@ -1702,7 +1799,7 @@ var OfflineQueue = class {
|
|
|
1702
1799
|
...result === void 0 ? {} : { result },
|
|
1703
1800
|
status: response.status,
|
|
1704
1801
|
...error === void 0 ? {} : { error },
|
|
1705
|
-
...
|
|
1802
|
+
...eventState === void 0 ? {} : { state: eventState }
|
|
1706
1803
|
});
|
|
1707
1804
|
}
|
|
1708
1805
|
this.#state = "idle";
|
|
@@ -1714,6 +1811,13 @@ var OfflineQueue = class {
|
|
|
1714
1811
|
this.#releaseSyncLock();
|
|
1715
1812
|
}
|
|
1716
1813
|
}
|
|
1814
|
+
async #updateOperationStatus(status, attempts) {
|
|
1815
|
+
const operation = this.#operations[0];
|
|
1816
|
+
if (operation === void 0) return;
|
|
1817
|
+
this.#operations = [{ ...operation, status, attempts }, ...this.#operations.slice(1)];
|
|
1818
|
+
await this.#storage.save(this.#storageKey(), this.#operations);
|
|
1819
|
+
this.#announceChange();
|
|
1820
|
+
}
|
|
1717
1821
|
#subscribeToExternalChanges() {
|
|
1718
1822
|
const windowLike = globalThis.window;
|
|
1719
1823
|
if (windowLike !== void 0) {
|
|
@@ -1741,7 +1845,7 @@ var OfflineQueue = class {
|
|
|
1741
1845
|
async #reloadFromStorage() {
|
|
1742
1846
|
if (this.#state === "syncing") return;
|
|
1743
1847
|
try {
|
|
1744
|
-
this.#operations =
|
|
1848
|
+
this.#operations = (await this.#storage.load(this.#storageKey())).map(normalizeOperation);
|
|
1745
1849
|
this.#loaded = true;
|
|
1746
1850
|
} catch {
|
|
1747
1851
|
}
|
|
@@ -1811,6 +1915,13 @@ var OfflineQueue = class {
|
|
|
1811
1915
|
return resolveRallyStateConflict(serverState, localState, { policy });
|
|
1812
1916
|
}
|
|
1813
1917
|
};
|
|
1918
|
+
function normalizeOperation(operation) {
|
|
1919
|
+
return {
|
|
1920
|
+
...operation,
|
|
1921
|
+
status: operation.status === "IN_FLIGHT" ? "PENDING" : operation.status ?? "PENDING",
|
|
1922
|
+
attempts: operation.attempts ?? 0
|
|
1923
|
+
};
|
|
1924
|
+
}
|
|
1814
1925
|
|
|
1815
1926
|
// src/crypto/token.ts
|
|
1816
1927
|
var encoder = new TextEncoder();
|
|
@@ -2379,6 +2490,8 @@ function condition(value, path, errors, isPublic) {
|
|
|
2379
2490
|
finiteNumber(value, "latitude", path, errors);
|
|
2380
2491
|
finiteNumber(value, "longitude", path, errors);
|
|
2381
2492
|
finiteNumber(value, "radiusMeters", path, errors, 0);
|
|
2493
|
+
if (typeof value.radiusMeters === "number" && value.radiusMeters <= 0)
|
|
2494
|
+
add(errors, `${path}.radiusMeters`, "Expected a radius greater than 0.", "out_of_range");
|
|
2382
2495
|
if (typeof value.latitude === "number" && (value.latitude < -90 || value.latitude > 90))
|
|
2383
2496
|
add(errors, `${path}.latitude`, "Expected a latitude between -90 and 90.", "out_of_range");
|
|
2384
2497
|
if (typeof value.longitude === "number" && (value.longitude < -180 || value.longitude > 180))
|
|
@@ -2804,6 +2917,7 @@ exports.assertPublicConfig = assertPublicConfig;
|
|
|
2804
2917
|
exports.calculateDistanceMeters = calculateDistanceMeters;
|
|
2805
2918
|
exports.calculateProgress = calculateProgress;
|
|
2806
2919
|
exports.consumeReward = consumeReward;
|
|
2920
|
+
exports.createAnonymousSessionId = createAnonymousSessionId;
|
|
2807
2921
|
exports.createClaimTicketNumber = createClaimTicketNumber;
|
|
2808
2922
|
exports.createSecureToken = createSecureToken;
|
|
2809
2923
|
exports.createSignedSnapshotToken = createSignedSnapshotToken;
|