@stamprally/server 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -61,8 +61,6 @@ var InMemoryServerPersistenceAdapter = class {
61
61
  return structuredClone(this.#auditLogs);
62
62
  }
63
63
  };
64
-
65
- // src/universalServer.ts
66
64
  function distanceMeters(aLat, aLon, bLat, bLon) {
67
65
  const radians = (degrees) => degrees * Math.PI / 180;
68
66
  const dLat = radians(bLat - aLat);
@@ -73,7 +71,32 @@ function distanceMeters(aLat, aLon, bLat, bLon) {
73
71
  return 6371e3 * 2 * Math.asin(Math.sqrt(Math.min(1, value)));
74
72
  }
75
73
  function initialState(config, now2) {
76
- return { rallyId: config.id, records: [], updatedAt: now2 };
74
+ return {
75
+ rallyId: config.id,
76
+ records: [],
77
+ rewards: core.reconcileRewardStates(
78
+ config.rewards.map((reward) => ({ ...reward, description: reward.description ?? "" })),
79
+ [],
80
+ 0,
81
+ now2
82
+ ),
83
+ updatedAt: now2
84
+ };
85
+ }
86
+ function json(body, status = 200) {
87
+ return new Response(JSON.stringify(body), {
88
+ status,
89
+ headers: { "content-type": "application/json; charset=utf-8" }
90
+ });
91
+ }
92
+ function requestBody(request) {
93
+ return request.json().then((value) => {
94
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
95
+ return value;
96
+ }).catch(() => null);
97
+ }
98
+ function adminReward(reward) {
99
+ return { ...reward, description: reward.description ?? "" };
77
100
  }
78
101
  function matchesCondition(condition, context, customValidators) {
79
102
  switch (condition.type) {
@@ -117,6 +140,157 @@ var UniversalRallyServer = class {
117
140
  this.#persistence = persistence;
118
141
  this.#options = options;
119
142
  }
143
+ /** Web Standard endpoint handler. Authentication, when configured, supplies the user identity. */
144
+ async handleCheckIn(request) {
145
+ const body = await requestBody(request);
146
+ const userId = await this.#authenticatedUser(request, body?.userId);
147
+ if (this.#options.authenticate !== void 0 && userId === null)
148
+ return json(
149
+ { ok: false, error: { code: "UNAUTHORIZED", message: "Authentication is required." } },
150
+ 401
151
+ );
152
+ if (body === null || userId === null || body.rallyId !== this.#config.id || body.spotId === "" || body.idempotencyKey === "")
153
+ return json(
154
+ {
155
+ ok: false,
156
+ error: {
157
+ code: "INVALID_REQUEST",
158
+ message: "rallyId, spotId, and idempotencyKey are required."
159
+ }
160
+ },
161
+ 400
162
+ );
163
+ if (body.context === void 0)
164
+ return json(
165
+ { ok: false, error: { code: "INVALID_REQUEST", message: "context is required." } },
166
+ 400
167
+ );
168
+ const result = await this.checkIn({ ...body, userId });
169
+ return result.ok ? json(result) : json(result, result.code === "SPOT_NOT_FOUND" ? 404 : 422);
170
+ }
171
+ async handleClaimReward(request) {
172
+ const body = await requestBody(request);
173
+ const userId = await this.#authenticatedUser(request, body?.userId);
174
+ if (this.#options.authenticate !== void 0 && userId === null)
175
+ return json(
176
+ { ok: false, error: { code: "UNAUTHORIZED", message: "Authentication is required." } },
177
+ 401
178
+ );
179
+ if (body === null || userId === null || body.rallyId !== this.#config.id || body.rewardId === "" || body.idempotencyKey === "")
180
+ return json(
181
+ {
182
+ ok: false,
183
+ error: {
184
+ code: "INVALID_REQUEST",
185
+ message: "rallyId, rewardId, and idempotencyKey are required."
186
+ }
187
+ },
188
+ 400
189
+ );
190
+ const key = `claim-reward:${body.rallyId}:${userId}:${body.rewardId}:${body.idempotencyKey}`;
191
+ const previous = await this.#persistence.getIdempotentResult(key);
192
+ if (previous !== null) return json(previous, previous.ok ? 200 : 422);
193
+ const reward = this.#config.rewards.find((item) => item.id === body.rewardId);
194
+ if (reward === void 0) {
195
+ const failure = {
196
+ ok: false,
197
+ code: "REWARD_NOT_FOUND",
198
+ message: "Reward was not found."
199
+ };
200
+ return json(await this.#rememberClaim(key, failure, body, userId), 404);
201
+ }
202
+ const timestamp = body.now ?? this.#options.now?.() ?? (/* @__PURE__ */ new Date()).toISOString();
203
+ const current = await this.sync(body.rallyId, userId);
204
+ const currentReward = current.rewards?.find((item) => item.rewardId === reward.id) ?? {
205
+ rewardId: reward.id,
206
+ status: "LOCKED"
207
+ };
208
+ const userClaims = await this.#getUserClaimCount(userId, reward.id);
209
+ const local = core.consumeReward({
210
+ reward: adminReward(reward),
211
+ currentState: currentReward,
212
+ now: timestamp,
213
+ userId,
214
+ userRedemptionCount: userClaims,
215
+ ...body.staffPasscode === void 0 ? {} : { inputPasscode: body.staffPasscode },
216
+ ...body.staffId === void 0 ? {} : { staffId: body.staffId }
217
+ });
218
+ if (!local.ok)
219
+ return json(
220
+ await this.#rememberClaim(
221
+ key,
222
+ {
223
+ ok: false,
224
+ code: local.error.code,
225
+ message: "Reward cannot be claimed.",
226
+ error: local.error
227
+ },
228
+ body,
229
+ userId
230
+ ),
231
+ 422
232
+ );
233
+ const stock = await this.#persistence.decrementRewardStock(reward.id);
234
+ if (!stock.success)
235
+ return json(
236
+ await this.#rememberClaim(
237
+ key,
238
+ { ok: false, code: "OUT_OF_STOCK", message: "Reward is out of stock." },
239
+ body,
240
+ userId
241
+ ),
242
+ 422
243
+ );
244
+ const next = {
245
+ ...current,
246
+ rewards: (current.rewards ?? []).map(
247
+ (item) => item.rewardId === reward.id ? local.value : item
248
+ ),
249
+ updatedAt: timestamp
250
+ };
251
+ await this.#persistence.saveUserState(body.rallyId, userId, next);
252
+ const success = local.value.claimTicketNumber === void 0 ? { ok: true, state: next } : { ok: true, state: next, claimTicketNumber: local.value.claimTicketNumber };
253
+ await this.#persistence.saveIdempotentResult(
254
+ key,
255
+ success,
256
+ this.#options.idempotencyTtlMs ?? 864e5
257
+ );
258
+ await this.#persistence.recordAuditLog({
259
+ id: `audit-${globalThis.crypto?.randomUUID?.() ?? Date.now()}`,
260
+ timestamp,
261
+ rallyId: body.rallyId,
262
+ userId,
263
+ action: "CLAIM_REWARD",
264
+ resourceId: reward.id,
265
+ status: "SUCCESS",
266
+ idempotencyKey: body.idempotencyKey
267
+ });
268
+ return json(success);
269
+ }
270
+ async handleSync(request) {
271
+ const body = await requestBody(request);
272
+ const userId = await this.#authenticatedUser(request, body?.userId);
273
+ if (this.#options.authenticate !== void 0 && userId === null)
274
+ return json(
275
+ { ok: false, error: { code: "UNAUTHORIZED", message: "Authentication is required." } },
276
+ 401
277
+ );
278
+ if (body === null || userId === null || body.rallyId !== this.#config.id)
279
+ return json(
280
+ { ok: false, error: { code: "INVALID_REQUEST", message: "rallyId is required." } },
281
+ 400
282
+ );
283
+ return json({ ok: true, state: await this.sync(body.rallyId, userId) });
284
+ }
285
+ async handle(request) {
286
+ if (request.method !== "POST")
287
+ return json({ ok: false, error: { code: "METHOD_NOT_ALLOWED" } }, 405);
288
+ const path = new URL(request.url).pathname;
289
+ if (path.endsWith("/check-in")) return this.handleCheckIn(request);
290
+ if (path.endsWith("/claim-reward")) return this.handleClaimReward(request);
291
+ if (path.endsWith("/sync")) return this.handleSync(request);
292
+ return json({ ok: false, error: { code: "NOT_FOUND" } }, 404);
293
+ }
120
294
  async checkIn(request) {
121
295
  const key = `check-in:${request.rallyId}:${request.userId}:${request.idempotencyKey}`;
122
296
  const previous = await this.#persistence.getIdempotentResult(key);
@@ -170,7 +344,6 @@ var UniversalRallyServer = class {
170
344
  updatedAt: now2
171
345
  };
172
346
  await this.#persistence.saveUserState(request.rallyId, request.userId, state);
173
- await this.#persistence.recordAuditLog(audit(request, "SUCCESS", now2));
174
347
  return this.#remember(key, { ok: true, state }, request, now2);
175
348
  } finally {
176
349
  await this.#persistence.releaseLock(lockKey);
@@ -180,9 +353,36 @@ var UniversalRallyServer = class {
180
353
  const now2 = this.#options.now?.() ?? (/* @__PURE__ */ new Date()).toISOString();
181
354
  return await this.#persistence.getUserState(rallyId, userId) ?? initialState(this.#config, now2);
182
355
  }
356
+ async #authenticatedUser(request, requestedUserId) {
357
+ const authenticated = await this.#options.authenticate?.(request);
358
+ if (this.#options.authenticate !== void 0) return authenticated ?? null;
359
+ return requestedUserId ?? null;
360
+ }
361
+ async #getUserClaimCount(userId, rewardId) {
362
+ const state = await this.#persistence.getUserState(this.#config.id, userId);
363
+ return state?.rewards?.find((item) => item.rewardId === rewardId)?.userRedemptionCount ?? 0;
364
+ }
365
+ async #rememberClaim(key, result, request, userId) {
366
+ await this.#persistence.saveIdempotentResult(
367
+ key,
368
+ result,
369
+ this.#options.idempotencyTtlMs ?? 864e5
370
+ );
371
+ await this.#persistence.recordAuditLog({
372
+ id: `audit-${globalThis.crypto?.randomUUID?.() ?? Date.now()}`,
373
+ timestamp: request.now ?? this.#options.now?.() ?? (/* @__PURE__ */ new Date()).toISOString(),
374
+ rallyId: request.rallyId,
375
+ userId,
376
+ action: "CLAIM_REWARD",
377
+ resourceId: request.rewardId,
378
+ status: "REJECTED",
379
+ idempotencyKey: request.idempotencyKey
380
+ });
381
+ return result;
382
+ }
183
383
  async #remember(key, result, request, now2) {
184
384
  await this.#persistence.recordAuditLog(
185
- audit(request, "REJECTED", now2, result.ok ? void 0 : result.code)
385
+ audit(request, result.ok ? "SUCCESS" : "REJECTED", now2, result.ok ? void 0 : result.code)
186
386
  );
187
387
  await this.#persistence.saveIdempotentResult(
188
388
  key,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/persistence.ts","../src/universalServer.ts","../src/index.ts"],"names":["now","id","processStamp","createSecureToken","consumeReward","reconcileRewardStates"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { RallyAuditLog, UserRallyState } from \"./index.js\";\n\n/** Persistence contract for implementations backed by Redis, SQL, or another RDB. */\nexport interface ServerPersistenceAdapter {\n acquireLock(lockKey: string, ttlMs: number): Promise<boolean>;\n releaseLock(lockKey: string): Promise<void>;\n decrementRewardStock(rewardId: string): Promise<{ success: boolean; remainingStock: number }>;\n getIdempotentResult<T>(idempotencyKey: string): Promise<T | null>;\n saveIdempotentResult<T>(idempotencyKey: string, result: T, ttlMs: number): Promise<void>;\n getUserState(rallyId: string, userId: string): Promise<UserRallyState | null>;\n saveUserState(rallyId: string, userId: string, state: UserRallyState): Promise<void>;\n recordAuditLog(log: RallyAuditLog): Promise<void>;\n}\n\ninterface TimedValue<T> {\n readonly value: T;\n readonly expiresAt: number;\n}\n\nexport interface InMemoryServerPersistenceOptions {\n readonly stocks?: Readonly<Record<string, number>>;\n}\n\n/** Deterministic adapter for tests and small single-process deployments. */\nexport class InMemoryServerPersistenceAdapter implements ServerPersistenceAdapter {\n readonly #locks = new Map<string, number>();\n readonly #idempotent = new Map<string, TimedValue<unknown>>();\n readonly #states = new Map<string, UserRallyState>();\n readonly #stocks: Map<string, number>;\n readonly #auditLogs: RallyAuditLog[] = [];\n\n constructor(options: InMemoryServerPersistenceOptions = {}) {\n this.#stocks = new Map(Object.entries(options.stocks ?? {}));\n }\n\n async acquireLock(lockKey: string, ttlMs: number): Promise<boolean> {\n const now = Date.now();\n const expiresAt = this.#locks.get(lockKey);\n if (expiresAt !== undefined && expiresAt > now) return false;\n this.#locks.set(lockKey, now + Math.max(1, ttlMs));\n return true;\n }\n\n async releaseLock(lockKey: string): Promise<void> {\n this.#locks.delete(lockKey);\n }\n\n async decrementRewardStock(\n rewardId: string,\n ): Promise<{ success: boolean; remainingStock: number }> {\n const stock = this.#stocks.get(rewardId);\n if (stock === undefined) return { success: true, remainingStock: Number.POSITIVE_INFINITY };\n if (stock <= 0) return { success: false, remainingStock: 0 };\n const remainingStock = stock - 1;\n this.#stocks.set(rewardId, remainingStock);\n return { success: true, remainingStock };\n }\n\n async getIdempotentResult<T>(idempotencyKey: string): Promise<T | null> {\n const entry = this.#idempotent.get(idempotencyKey);\n if (entry === undefined) return null;\n if (entry.expiresAt <= Date.now()) {\n this.#idempotent.delete(idempotencyKey);\n return null;\n }\n return structuredClone(entry.value) as T;\n }\n\n async saveIdempotentResult<T>(idempotencyKey: string, result: T, ttlMs: number): Promise<void> {\n this.#idempotent.set(idempotencyKey, {\n value: structuredClone(result),\n expiresAt: Date.now() + Math.max(1, ttlMs),\n });\n }\n\n async getUserState(rallyId: string, userId: string): Promise<UserRallyState | null> {\n const state = this.#states.get(`${rallyId}:${userId}`);\n return state === undefined ? null : structuredClone(state);\n }\n\n async saveUserState(rallyId: string, userId: string, state: UserRallyState): Promise<void> {\n this.#states.set(`${rallyId}:${userId}`, structuredClone(state));\n }\n\n async recordAuditLog(log: RallyAuditLog): Promise<void> {\n this.#auditLogs.push(structuredClone(log));\n }\n\n getAuditLogs(): ReadonlyArray<RallyAuditLog> {\n return structuredClone(this.#auditLogs);\n }\n}\n","import type { AdminRallyConfig, StampRallyState, VerificationCondition } from \"@stamprally/core\";\nimport type { RallyAuditLog, UserRallyState } from \"./index.js\";\nimport type { ServerPersistenceAdapter } from \"./persistence.js\";\n\nexport type UniversalVerificationContext =\n | { readonly type: \"qr\"; readonly token: string }\n | { readonly type: \"passcode\"; readonly code: string }\n | { readonly type: \"gps\"; readonly latitude: number; readonly longitude: number }\n | { readonly type: \"custom\"; readonly value: unknown };\n\nexport interface UniversalCheckInRequest {\n readonly rallyId: string;\n readonly userId: string;\n readonly spotId: string;\n readonly context: UniversalVerificationContext;\n readonly idempotencyKey: string;\n readonly now?: string;\n}\n\nexport type UniversalCheckInResult =\n | { readonly ok: true; readonly state: UserRallyState }\n | { readonly ok: false; readonly code: string; readonly message: string };\n\nexport interface UniversalRallyServerOptions {\n readonly idempotencyTtlMs?: number;\n readonly lockTtlMs?: number;\n readonly customValidators?: Readonly<\n Record<string, (value: unknown, condition: VerificationCondition) => boolean>\n >;\n readonly now?: () => string;\n}\n\nfunction distanceMeters(aLat: number, aLon: number, bLat: number, bLon: number): number {\n const radians = (degrees: number): number => (degrees * Math.PI) / 180;\n const dLat = radians(bLat - aLat);\n const dLon = radians(bLon - aLon);\n const latA = radians(aLat);\n const latB = radians(bLat);\n const value = Math.sin(dLat / 2) ** 2 + Math.cos(latA) * Math.cos(latB) * Math.sin(dLon / 2) ** 2;\n return 6_371_000 * 2 * Math.asin(Math.sqrt(Math.min(1, value)));\n}\n\nfunction initialState(config: AdminRallyConfig, now: string): UserRallyState {\n return { rallyId: config.id, records: [], updatedAt: now };\n}\n\nfunction matchesCondition(\n condition: VerificationCondition,\n context: UniversalVerificationContext,\n customValidators: UniversalRallyServerOptions[\"customValidators\"],\n): boolean {\n switch (condition.type) {\n case \"qr\":\n return context.type === \"qr\" && context.token === condition.secretToken;\n case \"passcode\":\n return (\n context.type === \"passcode\" &&\n (condition.caseSensitive === false\n ? context.code.toLocaleLowerCase() === condition.code.toLocaleLowerCase()\n : context.code === condition.code)\n );\n case \"gps\":\n return (\n context.type === \"gps\" &&\n distanceMeters(\n condition.latitude,\n condition.longitude,\n context.latitude,\n context.longitude,\n ) <= condition.radiusMeters\n );\n case \"custom\":\n return (\n customValidators?.[condition.validatorName]?.(\n context.type === \"custom\" ? context.value : undefined,\n condition,\n ) ?? false\n );\n }\n}\n\nfunction audit(\n request: UniversalCheckInRequest,\n status: RallyAuditLog[\"status\"],\n now: string,\n errorCode?: string,\n): RallyAuditLog {\n return {\n id: `audit-${globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random().toString(36).slice(2)}`}`,\n timestamp: now,\n rallyId: request.rallyId,\n userId: request.userId,\n action: \"CHECK_IN\",\n resourceId: request.spotId,\n status,\n idempotencyKey: request.idempotencyKey,\n ...(errorCode === undefined ? {} : { metadata: { errorCode } }),\n };\n}\n\n/** Atomic, adapter-backed check-in service for the universal model. */\nexport class UniversalRallyServer {\n readonly #config: AdminRallyConfig;\n readonly #persistence: ServerPersistenceAdapter;\n readonly #options: UniversalRallyServerOptions;\n\n constructor(\n config: AdminRallyConfig,\n persistence: ServerPersistenceAdapter,\n options: UniversalRallyServerOptions = {},\n ) {\n this.#config = config;\n this.#persistence = persistence;\n this.#options = options;\n }\n\n async checkIn(request: UniversalCheckInRequest): Promise<UniversalCheckInResult> {\n const key = `check-in:${request.rallyId}:${request.userId}:${request.idempotencyKey}`;\n const previous = await this.#persistence.getIdempotentResult<UniversalCheckInResult>(key);\n if (previous !== null) return previous;\n const lockKey = `state:${request.rallyId}:${request.userId}`;\n const locked = await this.#persistence.acquireLock(lockKey, this.#options.lockTtlMs ?? 5000);\n if (!locked)\n return { ok: false, code: \"CONFLICT\", message: \"The user state is being updated.\" };\n const now = request.now ?? this.#options.now?.() ?? new Date().toISOString();\n try {\n const spot = this.#config.spots.find((item) => item.id === request.spotId);\n if (spot === undefined)\n return this.#remember(\n key,\n { ok: false, code: \"SPOT_NOT_FOUND\", message: \"Spot was not found.\" },\n request,\n now,\n );\n const current =\n (await this.#persistence.getUserState(request.rallyId, request.userId)) ??\n initialState(this.#config, now);\n if (current.records.some((record) => record.stampId === request.spotId))\n return this.#remember(\n key,\n { ok: false, code: \"ALREADY_CLAIMED\", message: \"Spot was already claimed.\" },\n request,\n now,\n );\n const acquired = new Set(current.records.map((record) => record.stampId));\n if (spot.prerequisites?.some((id) => !acquired.has(id)))\n return this.#remember(\n key,\n {\n ok: false,\n code: \"PREREQUISITES_NOT_MET\",\n message: \"Prerequisite spots are not complete.\",\n },\n request,\n now,\n );\n if (\n !spot.conditions.every((condition) =>\n matchesCondition(condition, request.context, this.#options.customValidators),\n )\n )\n return this.#remember(\n key,\n { ok: false, code: \"INVALID_PROOF\", message: \"Verification failed.\" },\n request,\n now,\n );\n const state: UserRallyState = {\n ...current,\n records: [...current.records, { stampId: request.spotId, acquiredAt: now }],\n updatedAt: now,\n };\n await this.#persistence.saveUserState(request.rallyId, request.userId, state);\n await this.#persistence.recordAuditLog(audit(request, \"SUCCESS\", now));\n return this.#remember(key, { ok: true, state }, request, now);\n } finally {\n await this.#persistence.releaseLock(lockKey);\n }\n }\n\n async sync(rallyId: string, userId: string): Promise<UserRallyState> {\n const now = this.#options.now?.() ?? new Date().toISOString();\n return (\n (await this.#persistence.getUserState(rallyId, userId)) ?? initialState(this.#config, now)\n );\n }\n\n async #remember(\n key: string,\n result: UniversalCheckInResult,\n request: UniversalCheckInRequest,\n now: string,\n ): Promise<UniversalCheckInResult> {\n await this.#persistence.recordAuditLog(\n audit(request, \"REJECTED\", now, result.ok ? undefined : result.code),\n );\n await this.#persistence.saveIdempotentResult(\n key,\n result,\n this.#options.idempotencyTtlMs ?? 86_400_000,\n );\n return result;\n }\n}\n\nexport type { StampRallyState };\n","import {\n consumeReward,\n createSecureToken,\n processStamp,\n type RallyConfig,\n type RewardConsumeError,\n reconcileRewardStates,\n type SecureTokenSecretKey,\n type StampError,\n type StampRallyState,\n type StampRecord,\n type VerificationContext,\n} from \"@stamprally/core\";\n\nexport interface UserRallyState extends StampRallyState {\n readonly userId?: string;\n}\n\nexport interface RallyAuditLog {\n readonly id: string;\n readonly timestamp: string;\n readonly rallyId: string;\n readonly userId: string;\n readonly action: \"CHECK_IN\" | \"CLAIM_REWARD\";\n readonly resourceId: string;\n readonly status: \"SUCCESS\" | \"REJECTED\";\n readonly idempotencyKey: string;\n readonly proofData?: unknown;\n readonly metadata?: Readonly<Record<string, unknown>>;\n}\n\nexport interface ServerStorageAdapter {\n getRewardStock(rewardId: string): Promise<number | null>;\n decrementRewardStock(rewardId: string): Promise<boolean>;\n getUserClaims(userId: string, rewardId: string): Promise<number>;\n recordAuditLog(log: RallyAuditLog): Promise<void>;\n saveUserState(userId: string, state: UserRallyState): Promise<void>;\n getUserState(userId: string): Promise<UserRallyState | null>;\n}\n\nexport interface AdminRallyConfig extends RallyConfig {\n readonly secretKey: SecureTokenSecretKey;\n readonly proofTtlSeconds?: number;\n readonly authenticate?: (request: Request) => Promise<string | null> | string | null;\n}\n\nexport interface CheckInRequest {\n readonly userId: string;\n readonly spotId: string;\n readonly claimMethod: string;\n readonly proofData?: unknown;\n readonly idempotencyKey: string;\n}\n\nexport interface ClaimRewardRequest {\n readonly userId: string;\n readonly rewardId: string;\n readonly staffPasscode?: string;\n readonly idempotencyKey: string;\n readonly staffId?: string;\n}\n\nexport interface SyncRequest {\n readonly userId: string;\n readonly queue?: ReadonlyArray<SyncCheckInOperation>;\n readonly operations?: ReadonlyArray<SyncCheckInOperation>;\n}\n\nexport interface SyncCheckInOperation {\n readonly userId?: string;\n readonly spotId?: string;\n readonly stampId?: string;\n readonly claimMethod?: string;\n readonly proofData?: unknown;\n readonly context?: VerificationContext;\n readonly idempotencyKey: string;\n}\n\nexport interface StampClaimProof {\n readonly token: string;\n readonly rallyId: string;\n readonly userId: string;\n readonly spotId: string;\n readonly acquiredAt: string;\n}\n\nexport interface InMemoryServerStorageOptions {\n readonly stocks?: Readonly<Record<string, number>>;\n}\n\nexport class InMemoryServerStorage implements ServerStorageAdapter {\n readonly #states = new Map<string, UserRallyState>();\n readonly #stocks: Map<string, number>;\n readonly #claims = new Map<string, number>();\n readonly #auditLogs: RallyAuditLog[] = [];\n\n constructor(options: InMemoryServerStorageOptions = {}) {\n this.#stocks = new Map(Object.entries(options.stocks ?? {}));\n }\n\n async getRewardStock(rewardId: string): Promise<number | null> {\n return this.#stocks.get(rewardId) ?? null;\n }\n\n async decrementRewardStock(rewardId: string): Promise<boolean> {\n const stock = this.#stocks.get(rewardId);\n if (stock === undefined) return true;\n if (stock <= 0) return false;\n this.#stocks.set(rewardId, stock - 1);\n return true;\n }\n\n async getUserClaims(userId: string, rewardId: string): Promise<number> {\n return this.#claims.get(`${userId}:${rewardId}`) ?? 0;\n }\n\n async recordAuditLog(log: RallyAuditLog): Promise<void> {\n this.#auditLogs.push({ ...log });\n }\n\n async saveUserState(userId: string, state: UserRallyState): Promise<void> {\n this.#states.set(userId, cloneUserState(state));\n }\n\n async getUserState(userId: string): Promise<UserRallyState | null> {\n const state = this.#states.get(userId);\n return state === undefined ? null : cloneUserState(state);\n }\n\n getAuditLogs(): ReadonlyArray<RallyAuditLog> {\n return this.#auditLogs.map((log) => ({ ...log }));\n }\n\n recordClaim(userId: string, rewardId: string): void {\n const key = `${userId}:${rewardId}`;\n this.#claims.set(key, (this.#claims.get(key) ?? 0) + 1);\n }\n}\n\nfunction cloneUserState(state: UserRallyState): UserRallyState {\n return {\n ...state,\n records: state.records.map((record) => ({\n ...record,\n ...(record.metadata === undefined ? {} : { metadata: { ...record.metadata } }),\n })),\n ...(state.rewards === undefined\n ? {}\n : { rewards: state.rewards.map((reward) => ({ ...reward })) }),\n };\n}\n\nfunction jsonResponse(body: unknown, status = 200): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { \"content-type\": \"application/json; charset=utf-8\" },\n });\n}\n\nfunction errorResponse(code: string, message: string, status: number): Response {\n return jsonResponse({ ok: false, error: { code, message } }, status);\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction contextForClaim(method: string, proofData: unknown): VerificationContext {\n if (method === \"token\" || method === \"qr\" || method === \"passcode\") {\n return {\n type: \"token\",\n token:\n isObject(proofData) && typeof proofData.token === \"string\"\n ? proofData.token\n : String(proofData ?? \"\"),\n };\n }\n if (method === \"geo\" || method === \"geolocation\") {\n const value = isObject(proofData) ? proofData : {};\n return {\n type: \"geo\",\n currentLatitude:\n typeof value.latitude === \"number\"\n ? value.latitude\n : typeof value.currentLatitude === \"number\"\n ? value.currentLatitude\n : Number.NaN,\n currentLongitude:\n typeof value.longitude === \"number\"\n ? value.longitude\n : typeof value.currentLongitude === \"number\"\n ? value.currentLongitude\n : Number.NaN,\n };\n }\n return { type: \"instant\" };\n}\n\nfunction now(): string {\n return new Date().toISOString();\n}\n\nfunction hasText(value: unknown): value is string {\n return typeof value === \"string\" && value.trim() !== \"\";\n}\n\nfunction safeProofData(value: unknown): unknown {\n return isObject(value) && typeof value.token === \"string\" ? { type: \"token\" } : value;\n}\n\nfunction proofFromContext(context: VerificationContext | undefined): unknown {\n if (context?.type === \"token\") return { token: context.token };\n if (context?.type === \"geo\") {\n return { latitude: context.currentLatitude, longitude: context.currentLongitude };\n }\n return undefined;\n}\n\nfunction id(prefix: string): string {\n return `${prefix}-${globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random().toString(36).slice(2)}`}`;\n}\n\nexport class StampRallyServer {\n readonly #config: AdminRallyConfig;\n readonly #storage: ServerStorageAdapter;\n readonly #idempotent = new Map<string, Response>();\n readonly #claims = new Map<string, number>();\n #queue: Promise<unknown> = Promise.resolve();\n\n constructor(config: AdminRallyConfig, storage: ServerStorageAdapter) {\n this.#config = config;\n this.#storage = storage;\n }\n\n handle(request: Request): Promise<Response> {\n const path = new URL(request.url).pathname;\n if (request.method !== \"POST\")\n return Promise.resolve(errorResponse(\"METHOD_NOT_ALLOWED\", \"POST is required.\", 405));\n if (path.endsWith(\"/check-in\")) return this.verifyCheckIn(request);\n if (path.endsWith(\"/claim-reward\")) return this.claimReward(request);\n if (path.endsWith(\"/sync\")) return this.syncProgress(request);\n return Promise.resolve(errorResponse(\"NOT_FOUND\", \"Route not found.\", 404));\n }\n\n verifyCheckIn(request: Request): Promise<Response> {\n return this.#enqueue(() => this.#verifyCheckIn(request));\n }\n\n claimReward(request: Request): Promise<Response> {\n return this.#enqueue(() => this.#claimReward(request));\n }\n\n syncProgress(request: Request): Promise<Response> {\n return this.#enqueue(() => this.#syncProgress(request));\n }\n\n #enqueue<T>(operation: () => Promise<T>): Promise<T> {\n const next = this.#queue.then(operation, operation);\n this.#queue = next.then(\n () => undefined,\n () => undefined,\n );\n return next;\n }\n\n async #authenticate(request: Request): Promise<string | null> {\n return this.#config.authenticate === undefined ? null : this.#config.authenticate(request);\n }\n\n async #parse<T>(request: Request): Promise<T | null> {\n try {\n const value: unknown = await request.json();\n return isObject(value) ? (value as T) : null;\n } catch {\n return null;\n }\n }\n\n async #verifyCheckIn(request: Request): Promise<Response> {\n const authenticatedUser = await this.#authenticate(request);\n const body = await this.#parse<CheckInRequest>(request);\n const userId = body?.userId ?? authenticatedUser;\n if (\n userId === null ||\n userId === undefined ||\n body === null ||\n !hasText(userId) ||\n !hasText(body.spotId) ||\n !hasText(body.claimMethod) ||\n !hasText(body.idempotencyKey)\n ) {\n return errorResponse(\n \"INVALID_REQUEST\",\n \"userId, spotId, and idempotencyKey are required.\",\n 400,\n );\n }\n if (authenticatedUser !== null && authenticatedUser !== userId)\n return errorResponse(\"UNAUTHORIZED\", \"User identity does not match authentication.\", 401);\n const key = `check-in:${userId}:${body.idempotencyKey}`;\n const previous = this.#idempotent.get(key);\n if (previous !== undefined) return previous.clone();\n const timestamp = now();\n const current =\n (await this.#storage.getUserState(userId)) ?? emptyState(this.#config, timestamp);\n const result = processStamp(\n current,\n this.#config,\n body.spotId,\n contextForClaim(body.claimMethod, body.proofData),\n timestamp,\n );\n if (!result.ok) {\n await this.#audit(\n userId,\n \"CHECK_IN\",\n body.spotId,\n body.idempotencyKey,\n \"REJECTED\",\n safeProofData(body.proofData),\n result.error,\n );\n return this.#remember(key, jsonResponse({ ok: false, error: result.error }, 422));\n }\n const ttl = this.#config.proofTtlSeconds ?? 3600;\n const token = await createSecureToken(\n {\n type: \"stamp_claim\",\n rallyId: this.#config.id,\n userId,\n spotId: body.spotId,\n acquiredAt: timestamp,\n exp: Math.floor(Date.now() / 1000) + ttl,\n },\n this.#config.secretKey,\n { encrypt: true },\n );\n await this.#storage.saveUserState(userId, result.value.nextState);\n await this.#audit(\n userId,\n \"CHECK_IN\",\n body.spotId,\n body.idempotencyKey,\n \"SUCCESS\",\n safeProofData(body.proofData),\n );\n return this.#remember(\n key,\n jsonResponse({\n ok: true,\n state: result.value.nextState,\n proof: {\n token,\n rallyId: this.#config.id,\n userId,\n spotId: body.spotId,\n acquiredAt: timestamp,\n },\n }),\n );\n }\n\n async #claimReward(request: Request): Promise<Response> {\n const authenticatedUser = await this.#authenticate(request);\n const body = await this.#parse<ClaimRewardRequest>(request);\n const userId = body?.userId ?? authenticatedUser;\n if (\n userId === null ||\n userId === undefined ||\n body === null ||\n !hasText(userId) ||\n !hasText(body.rewardId) ||\n !hasText(body.idempotencyKey)\n )\n return errorResponse(\n \"INVALID_REQUEST\",\n \"userId, rewardId, and idempotencyKey are required.\",\n 400,\n );\n if (authenticatedUser !== null && authenticatedUser !== userId)\n return errorResponse(\"UNAUTHORIZED\", \"User identity does not match authentication.\", 401);\n const key = `claim-reward:${userId}:${body.rewardId}:${body.idempotencyKey}`;\n const previous = this.#idempotent.get(key);\n if (previous !== undefined) return previous.clone();\n const reward = this.#config.rewards?.find((item) => item.id === body.rewardId);\n if (reward === undefined) {\n await this.#audit(userId, \"CLAIM_REWARD\", body.rewardId, body.idempotencyKey, \"REJECTED\");\n return this.#remember(key, errorResponse(\"REWARD_NOT_FOUND\", \"Reward was not found.\", 404));\n }\n const timestamp = now();\n const current =\n (await this.#storage.getUserState(userId)) ?? emptyState(this.#config, timestamp);\n const rewardState = current.rewards?.find((item) => item.rewardId === reward.id);\n if (rewardState === undefined) {\n await this.#audit(userId, \"CLAIM_REWARD\", reward.id, body.idempotencyKey, \"REJECTED\");\n return this.#remember(key, errorResponse(\"NOT_AVAILABLE\", \"Reward is not available.\", 422));\n }\n const userClaims = Math.max(\n await this.#storage.getUserClaims(userId, reward.id),\n this.#claims.get(`${userId}:${reward.id}`) ?? 0,\n );\n const stock = await this.#storage.getRewardStock(reward.id);\n const userLimit = reward.userClaimLimit ?? reward.limitPerUser;\n const canReclaimServerReward =\n reward.redemptionMethod === \"server_claim\" &&\n (userLimit === undefined || userClaims < userLimit) &&\n (stock === null || stock > 0);\n const claimableState =\n canReclaimServerReward && rewardState.status === \"CONSUMED\"\n ? { ...rewardState, status: \"AVAILABLE\" as const }\n : rewardState;\n const local = consumeReward({\n reward,\n currentState: claimableState,\n now: timestamp,\n ...(body.staffPasscode === undefined ? {} : { inputPasscode: body.staffPasscode }),\n ...(body.staffId === undefined ? {} : { staffId: body.staffId }),\n userId,\n userRedemptionCount: userClaims,\n });\n if (!local.ok)\n return this.#remember(\n key,\n await this.#rewardError(userId, reward.id, body.idempotencyKey, local.error),\n );\n if (stock !== null && !(await this.#storage.decrementRewardStock(reward.id)))\n return this.#remember(\n key,\n await this.#rewardError(userId, reward.id, body.idempotencyKey, {\n code: \"OUT_OF_STOCK\",\n rewardId: reward.id,\n }),\n );\n const nextState: StampRallyState = {\n ...current,\n rewards: (current.rewards ?? []).map((item) =>\n item.rewardId === reward.id ? local.value : item,\n ),\n updatedAt: timestamp,\n };\n await this.#storage.saveUserState(userId, nextState);\n const claimKey = `${userId}:${reward.id}`;\n this.#claims.set(claimKey, userClaims + 1);\n if (this.#storage instanceof InMemoryServerStorage)\n this.#storage.recordClaim(userId, reward.id);\n await this.#audit(userId, \"CLAIM_REWARD\", reward.id, body.idempotencyKey, \"SUCCESS\", {\n staffId: body.staffId,\n });\n return this.#remember(\n key,\n jsonResponse({\n ok: true,\n state: nextState,\n claimTicketNumber: local.value.claimTicketNumber,\n }),\n );\n }\n\n async #rewardError(\n userId: string,\n rewardId: string,\n key: string,\n error: RewardConsumeError,\n ): Promise<Response> {\n await this.#audit(userId, \"CLAIM_REWARD\", rewardId, key, \"REJECTED\", undefined, error);\n return jsonResponse({ ok: false, error }, 422);\n }\n\n async #syncProgress(request: Request): Promise<Response> {\n const authenticatedUser = await this.#authenticate(request);\n const body = await this.#parse<SyncRequest>(request);\n if (body === null || body.userId === undefined)\n return errorResponse(\"INVALID_REQUEST\", \"userId is required.\", 400);\n if (authenticatedUser !== null && authenticatedUser !== body.userId)\n return errorResponse(\"UNAUTHORIZED\", \"User identity does not match authentication.\", 401);\n const queue = body.queue ?? body.operations ?? [];\n for (const operation of queue) {\n const userId = operation.userId ?? body.userId;\n const spotId = operation.spotId ?? operation.stampId;\n const claimMethod = operation.claimMethod ?? operation.context?.type;\n if (!hasText(userId) || !hasText(spotId) || !hasText(claimMethod)) continue;\n const synthetic = new Request(new URL(\"/api/check-in\", request.url), {\n method: \"POST\",\n body: JSON.stringify({\n userId,\n spotId,\n claimMethod,\n proofData: operation.proofData ?? proofFromContext(operation.context),\n idempotencyKey: operation.idempotencyKey,\n }),\n headers: { \"content-type\": \"application/json\" },\n });\n await this.#verifyCheckIn(synthetic);\n }\n const timestamp = now();\n const state =\n (await this.#storage.getUserState(body.userId)) ?? emptyState(this.#config, timestamp);\n await this.#storage.saveUserState(body.userId, state);\n return jsonResponse({ ok: true, state, accepted: queue.length });\n }\n\n async #audit(\n userId: string,\n action: RallyAuditLog[\"action\"],\n resourceId: string,\n idempotencyKey: string,\n status: RallyAuditLog[\"status\"],\n proofData?: unknown,\n error?: unknown,\n ): Promise<void> {\n await this.#storage.recordAuditLog({\n id: id(\"audit\"),\n timestamp: now(),\n rallyId: this.#config.id,\n userId,\n action,\n resourceId,\n status,\n idempotencyKey,\n ...(proofData === undefined ? {} : { proofData }),\n ...(error === undefined\n ? {}\n : {\n metadata: {\n errorCode: isObject(error) && typeof error.code === \"string\" ? error.code : \"UNKNOWN\",\n },\n }),\n });\n }\n\n #remember(key: string, response: Response): Response {\n this.#idempotent.set(key, response.clone());\n return response;\n }\n}\n\nfunction emptyState(config: RallyConfig, timestamp: string): UserRallyState {\n return {\n rallyId: config.id,\n records: [],\n ...(config.rewards === undefined\n ? {}\n : { rewards: reconcileRewardStates(config.rewards, [], 0, timestamp) }),\n updatedAt: timestamp,\n };\n}\n\nexport type { InMemoryServerPersistenceOptions, ServerPersistenceAdapter } from \"./persistence.js\";\nexport { InMemoryServerPersistenceAdapter } from \"./persistence.js\";\nexport type {\n UniversalCheckInRequest,\n UniversalCheckInResult,\n UniversalRallyServerOptions,\n UniversalVerificationContext,\n} from \"./universalServer.js\";\nexport { UniversalRallyServer } from \"./universalServer.js\";\nexport type { StampError, StampRecord };\n"]}
1
+ {"version":3,"sources":["../src/persistence.ts","../src/universalServer.ts","../src/index.ts"],"names":["now","reconcileRewardStates","consumeReward","id","processStamp","createSecureToken"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { RallyAuditLog, UserRallyState } from \"./index.js\";\n\n/** Persistence contract for implementations backed by Redis, SQL, or another RDB. */\nexport interface ServerPersistenceAdapter {\n acquireLock(lockKey: string, ttlMs: number): Promise<boolean>;\n releaseLock(lockKey: string): Promise<void>;\n decrementRewardStock(rewardId: string): Promise<{ success: boolean; remainingStock: number }>;\n getIdempotentResult<T>(idempotencyKey: string): Promise<T | null>;\n saveIdempotentResult<T>(idempotencyKey: string, result: T, ttlMs: number): Promise<void>;\n getUserState(rallyId: string, userId: string): Promise<UserRallyState | null>;\n saveUserState(rallyId: string, userId: string, state: UserRallyState): Promise<void>;\n recordAuditLog(log: RallyAuditLog): Promise<void>;\n}\n\ninterface TimedValue<T> {\n readonly value: T;\n readonly expiresAt: number;\n}\n\nexport interface InMemoryServerPersistenceOptions {\n readonly stocks?: Readonly<Record<string, number>>;\n}\n\n/** Deterministic adapter for tests and small single-process deployments. */\nexport class InMemoryServerPersistenceAdapter implements ServerPersistenceAdapter {\n readonly #locks = new Map<string, number>();\n readonly #idempotent = new Map<string, TimedValue<unknown>>();\n readonly #states = new Map<string, UserRallyState>();\n readonly #stocks: Map<string, number>;\n readonly #auditLogs: RallyAuditLog[] = [];\n\n constructor(options: InMemoryServerPersistenceOptions = {}) {\n this.#stocks = new Map(Object.entries(options.stocks ?? {}));\n }\n\n async acquireLock(lockKey: string, ttlMs: number): Promise<boolean> {\n const now = Date.now();\n const expiresAt = this.#locks.get(lockKey);\n if (expiresAt !== undefined && expiresAt > now) return false;\n this.#locks.set(lockKey, now + Math.max(1, ttlMs));\n return true;\n }\n\n async releaseLock(lockKey: string): Promise<void> {\n this.#locks.delete(lockKey);\n }\n\n async decrementRewardStock(\n rewardId: string,\n ): Promise<{ success: boolean; remainingStock: number }> {\n const stock = this.#stocks.get(rewardId);\n if (stock === undefined) return { success: true, remainingStock: Number.POSITIVE_INFINITY };\n if (stock <= 0) return { success: false, remainingStock: 0 };\n const remainingStock = stock - 1;\n this.#stocks.set(rewardId, remainingStock);\n return { success: true, remainingStock };\n }\n\n async getIdempotentResult<T>(idempotencyKey: string): Promise<T | null> {\n const entry = this.#idempotent.get(idempotencyKey);\n if (entry === undefined) return null;\n if (entry.expiresAt <= Date.now()) {\n this.#idempotent.delete(idempotencyKey);\n return null;\n }\n return structuredClone(entry.value) as T;\n }\n\n async saveIdempotentResult<T>(idempotencyKey: string, result: T, ttlMs: number): Promise<void> {\n this.#idempotent.set(idempotencyKey, {\n value: structuredClone(result),\n expiresAt: Date.now() + Math.max(1, ttlMs),\n });\n }\n\n async getUserState(rallyId: string, userId: string): Promise<UserRallyState | null> {\n const state = this.#states.get(`${rallyId}:${userId}`);\n return state === undefined ? null : structuredClone(state);\n }\n\n async saveUserState(rallyId: string, userId: string, state: UserRallyState): Promise<void> {\n this.#states.set(`${rallyId}:${userId}`, structuredClone(state));\n }\n\n async recordAuditLog(log: RallyAuditLog): Promise<void> {\n this.#auditLogs.push(structuredClone(log));\n }\n\n getAuditLogs(): ReadonlyArray<RallyAuditLog> {\n return structuredClone(this.#auditLogs);\n }\n}\n","import {\n type AdminRallyConfig,\n type AdminReward,\n consumeReward,\n type RewardItem,\n reconcileRewardStates,\n type StampRallyState,\n type VerificationCondition,\n} from \"@stamprally/core\";\nimport type { RallyAuditLog, UserRallyState } from \"./index.js\";\nimport type { ServerPersistenceAdapter } from \"./persistence.js\";\n\nexport type UniversalVerificationContext =\n | { readonly type: \"qr\"; readonly token: string }\n | { readonly type: \"passcode\"; readonly code: string }\n | { readonly type: \"gps\"; readonly latitude: number; readonly longitude: number }\n | { readonly type: \"custom\"; readonly value: unknown };\n\nexport interface UniversalCheckInRequest {\n readonly rallyId: string;\n readonly userId: string;\n readonly spotId: string;\n readonly context: UniversalVerificationContext;\n readonly idempotencyKey: string;\n readonly now?: string;\n}\n\nexport interface UniversalClaimRewardRequest {\n readonly rallyId: string;\n readonly userId: string;\n readonly rewardId: string;\n readonly idempotencyKey: string;\n readonly staffPasscode?: string;\n readonly staffId?: string;\n readonly now?: string;\n}\n\nexport type UniversalCheckInResult =\n | { readonly ok: true; readonly state: UserRallyState }\n | { readonly ok: false; readonly code: string; readonly message: string };\n\nexport interface UniversalRallyServerOptions {\n readonly idempotencyTtlMs?: number;\n readonly lockTtlMs?: number;\n readonly customValidators?: Readonly<\n Record<string, (value: unknown, condition: VerificationCondition) => boolean>\n >;\n readonly now?: () => string;\n /** Extract the authenticated identity; request-body userId is never trusted when this is set. */\n readonly authenticate?: (request: Request) => Promise<string | null> | string | null;\n}\n\nfunction distanceMeters(aLat: number, aLon: number, bLat: number, bLon: number): number {\n const radians = (degrees: number): number => (degrees * Math.PI) / 180;\n const dLat = radians(bLat - aLat);\n const dLon = radians(bLon - aLon);\n const latA = radians(aLat);\n const latB = radians(bLat);\n const value = Math.sin(dLat / 2) ** 2 + Math.cos(latA) * Math.cos(latB) * Math.sin(dLon / 2) ** 2;\n return 6_371_000 * 2 * Math.asin(Math.sqrt(Math.min(1, value)));\n}\n\nfunction initialState(config: AdminRallyConfig, now: string): UserRallyState {\n return {\n rallyId: config.id,\n records: [],\n rewards: reconcileRewardStates(\n config.rewards.map((reward) => ({ ...reward, description: reward.description ?? \"\" })),\n [],\n 0,\n now,\n ),\n updatedAt: now,\n };\n}\n\nfunction json(body: unknown, status = 200): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { \"content-type\": \"application/json; charset=utf-8\" },\n });\n}\n\nfunction requestBody<T>(request: Request): Promise<T | null> {\n return request\n .json()\n .then((value: unknown) => {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return null;\n return value as T;\n })\n .catch(() => null);\n}\n\nfunction adminReward(reward: AdminReward): RewardItem {\n return { ...reward, description: reward.description ?? \"\" };\n}\n\nfunction matchesCondition(\n condition: VerificationCondition,\n context: UniversalVerificationContext,\n customValidators: UniversalRallyServerOptions[\"customValidators\"],\n): boolean {\n switch (condition.type) {\n case \"qr\":\n return context.type === \"qr\" && context.token === condition.secretToken;\n case \"passcode\":\n return (\n context.type === \"passcode\" &&\n (condition.caseSensitive === false\n ? context.code.toLocaleLowerCase() === condition.code.toLocaleLowerCase()\n : context.code === condition.code)\n );\n case \"gps\":\n return (\n context.type === \"gps\" &&\n distanceMeters(\n condition.latitude,\n condition.longitude,\n context.latitude,\n context.longitude,\n ) <= condition.radiusMeters\n );\n case \"custom\":\n return (\n customValidators?.[condition.validatorName]?.(\n context.type === \"custom\" ? context.value : undefined,\n condition,\n ) ?? false\n );\n }\n}\n\nfunction audit(\n request: UniversalCheckInRequest,\n status: RallyAuditLog[\"status\"],\n now: string,\n errorCode?: string,\n): RallyAuditLog {\n return {\n id: `audit-${globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random().toString(36).slice(2)}`}`,\n timestamp: now,\n rallyId: request.rallyId,\n userId: request.userId,\n action: \"CHECK_IN\",\n resourceId: request.spotId,\n status,\n idempotencyKey: request.idempotencyKey,\n ...(errorCode === undefined ? {} : { metadata: { errorCode } }),\n };\n}\n\n/** Atomic, adapter-backed check-in service for the universal model. */\nexport class UniversalRallyServer {\n readonly #config: AdminRallyConfig;\n readonly #persistence: ServerPersistenceAdapter;\n readonly #options: UniversalRallyServerOptions;\n\n constructor(\n config: AdminRallyConfig,\n persistence: ServerPersistenceAdapter,\n options: UniversalRallyServerOptions = {},\n ) {\n this.#config = config;\n this.#persistence = persistence;\n this.#options = options;\n }\n\n /** Web Standard endpoint handler. Authentication, when configured, supplies the user identity. */\n async handleCheckIn(request: Request): Promise<Response> {\n const body = await requestBody<UniversalCheckInRequest>(request);\n const userId = await this.#authenticatedUser(request, body?.userId);\n if (this.#options.authenticate !== undefined && userId === null)\n return json(\n { ok: false, error: { code: \"UNAUTHORIZED\", message: \"Authentication is required.\" } },\n 401,\n );\n if (\n body === null ||\n userId === null ||\n body.rallyId !== this.#config.id ||\n body.spotId === \"\" ||\n body.idempotencyKey === \"\"\n )\n return json(\n {\n ok: false,\n error: {\n code: \"INVALID_REQUEST\",\n message: \"rallyId, spotId, and idempotencyKey are required.\",\n },\n },\n 400,\n );\n if (body.context === undefined)\n return json(\n { ok: false, error: { code: \"INVALID_REQUEST\", message: \"context is required.\" } },\n 400,\n );\n const result = await this.checkIn({ ...body, userId });\n return result.ok ? json(result) : json(result, result.code === \"SPOT_NOT_FOUND\" ? 404 : 422);\n }\n\n async handleClaimReward(request: Request): Promise<Response> {\n const body = await requestBody<UniversalClaimRewardRequest>(request);\n const userId = await this.#authenticatedUser(request, body?.userId);\n if (this.#options.authenticate !== undefined && userId === null)\n return json(\n { ok: false, error: { code: \"UNAUTHORIZED\", message: \"Authentication is required.\" } },\n 401,\n );\n if (\n body === null ||\n userId === null ||\n body.rallyId !== this.#config.id ||\n body.rewardId === \"\" ||\n body.idempotencyKey === \"\"\n )\n return json(\n {\n ok: false,\n error: {\n code: \"INVALID_REQUEST\",\n message: \"rallyId, rewardId, and idempotencyKey are required.\",\n },\n },\n 400,\n );\n const key = `claim-reward:${body.rallyId}:${userId}:${body.rewardId}:${body.idempotencyKey}`;\n const previous = await this.#persistence.getIdempotentResult<UniversalClaimRewardResponse>(key);\n if (previous !== null) return json(previous, previous.ok ? 200 : 422);\n const reward = this.#config.rewards.find((item) => item.id === body.rewardId);\n if (reward === undefined) {\n const failure: UniversalClaimRewardResponse = {\n ok: false,\n code: \"REWARD_NOT_FOUND\",\n message: \"Reward was not found.\",\n };\n return json(await this.#rememberClaim(key, failure, body, userId), 404);\n }\n const timestamp = body.now ?? this.#options.now?.() ?? new Date().toISOString();\n const current = await this.sync(body.rallyId, userId);\n const currentReward = current.rewards?.find((item) => item.rewardId === reward.id) ?? {\n rewardId: reward.id,\n status: \"LOCKED\" as const,\n };\n const userClaims = await this.#getUserClaimCount(userId, reward.id);\n const local = consumeReward({\n reward: adminReward(reward),\n currentState: currentReward,\n now: timestamp,\n userId,\n userRedemptionCount: userClaims,\n ...(body.staffPasscode === undefined ? {} : { inputPasscode: body.staffPasscode }),\n ...(body.staffId === undefined ? {} : { staffId: body.staffId }),\n });\n if (!local.ok)\n return json(\n await this.#rememberClaim(\n key,\n {\n ok: false,\n code: local.error.code,\n message: \"Reward cannot be claimed.\",\n error: local.error,\n },\n body,\n userId,\n ),\n 422,\n );\n const stock = await this.#persistence.decrementRewardStock(reward.id);\n if (!stock.success)\n return json(\n await this.#rememberClaim(\n key,\n { ok: false, code: \"OUT_OF_STOCK\", message: \"Reward is out of stock.\" },\n body,\n userId,\n ),\n 422,\n );\n const next: UserRallyState = {\n ...current,\n rewards: (current.rewards ?? []).map((item) =>\n item.rewardId === reward.id ? local.value : item,\n ),\n updatedAt: timestamp,\n };\n await this.#persistence.saveUserState(body.rallyId, userId, next);\n const success: UniversalClaimRewardResponse =\n local.value.claimTicketNumber === undefined\n ? { ok: true, state: next }\n : { ok: true, state: next, claimTicketNumber: local.value.claimTicketNumber };\n await this.#persistence.saveIdempotentResult(\n key,\n success,\n this.#options.idempotencyTtlMs ?? 86_400_000,\n );\n await this.#persistence.recordAuditLog({\n id: `audit-${globalThis.crypto?.randomUUID?.() ?? Date.now()}`,\n timestamp,\n rallyId: body.rallyId,\n userId,\n action: \"CLAIM_REWARD\",\n resourceId: reward.id,\n status: \"SUCCESS\",\n idempotencyKey: body.idempotencyKey,\n });\n return json(success);\n }\n\n async handleSync(request: Request): Promise<Response> {\n const body = await requestBody<{ readonly rallyId: string; readonly userId?: string }>(request);\n const userId = await this.#authenticatedUser(request, body?.userId);\n if (this.#options.authenticate !== undefined && userId === null)\n return json(\n { ok: false, error: { code: \"UNAUTHORIZED\", message: \"Authentication is required.\" } },\n 401,\n );\n if (body === null || userId === null || body.rallyId !== this.#config.id)\n return json(\n { ok: false, error: { code: \"INVALID_REQUEST\", message: \"rallyId is required.\" } },\n 400,\n );\n return json({ ok: true, state: await this.sync(body.rallyId, userId) });\n }\n\n async handle(request: Request): Promise<Response> {\n if (request.method !== \"POST\")\n return json({ ok: false, error: { code: \"METHOD_NOT_ALLOWED\" } }, 405);\n const path = new URL(request.url).pathname;\n if (path.endsWith(\"/check-in\")) return this.handleCheckIn(request);\n if (path.endsWith(\"/claim-reward\")) return this.handleClaimReward(request);\n if (path.endsWith(\"/sync\")) return this.handleSync(request);\n return json({ ok: false, error: { code: \"NOT_FOUND\" } }, 404);\n }\n\n async checkIn(request: UniversalCheckInRequest): Promise<UniversalCheckInResult> {\n const key = `check-in:${request.rallyId}:${request.userId}:${request.idempotencyKey}`;\n const previous = await this.#persistence.getIdempotentResult<UniversalCheckInResult>(key);\n if (previous !== null) return previous;\n const lockKey = `state:${request.rallyId}:${request.userId}`;\n const locked = await this.#persistence.acquireLock(lockKey, this.#options.lockTtlMs ?? 5000);\n if (!locked)\n return { ok: false, code: \"CONFLICT\", message: \"The user state is being updated.\" };\n const now = request.now ?? this.#options.now?.() ?? new Date().toISOString();\n try {\n const spot = this.#config.spots.find((item) => item.id === request.spotId);\n if (spot === undefined)\n return this.#remember(\n key,\n { ok: false, code: \"SPOT_NOT_FOUND\", message: \"Spot was not found.\" },\n request,\n now,\n );\n const current =\n (await this.#persistence.getUserState(request.rallyId, request.userId)) ??\n initialState(this.#config, now);\n if (current.records.some((record) => record.stampId === request.spotId))\n return this.#remember(\n key,\n { ok: false, code: \"ALREADY_CLAIMED\", message: \"Spot was already claimed.\" },\n request,\n now,\n );\n const acquired = new Set(current.records.map((record) => record.stampId));\n if (spot.prerequisites?.some((id) => !acquired.has(id)))\n return this.#remember(\n key,\n {\n ok: false,\n code: \"PREREQUISITES_NOT_MET\",\n message: \"Prerequisite spots are not complete.\",\n },\n request,\n now,\n );\n if (\n !spot.conditions.every((condition) =>\n matchesCondition(condition, request.context, this.#options.customValidators),\n )\n )\n return this.#remember(\n key,\n { ok: false, code: \"INVALID_PROOF\", message: \"Verification failed.\" },\n request,\n now,\n );\n const state: UserRallyState = {\n ...current,\n records: [...current.records, { stampId: request.spotId, acquiredAt: now }],\n updatedAt: now,\n };\n await this.#persistence.saveUserState(request.rallyId, request.userId, state);\n return this.#remember(key, { ok: true, state }, request, now);\n } finally {\n await this.#persistence.releaseLock(lockKey);\n }\n }\n\n async sync(rallyId: string, userId: string): Promise<UserRallyState> {\n const now = this.#options.now?.() ?? new Date().toISOString();\n return (\n (await this.#persistence.getUserState(rallyId, userId)) ?? initialState(this.#config, now)\n );\n }\n\n async #authenticatedUser(\n request: Request,\n requestedUserId: string | undefined,\n ): Promise<string | null> {\n const authenticated = await this.#options.authenticate?.(request);\n if (this.#options.authenticate !== undefined) return authenticated ?? null;\n return requestedUserId ?? null;\n }\n\n async #getUserClaimCount(userId: string, rewardId: string): Promise<number> {\n const state = await this.#persistence.getUserState(this.#config.id, userId);\n return state?.rewards?.find((item) => item.rewardId === rewardId)?.userRedemptionCount ?? 0;\n }\n\n async #rememberClaim(\n key: string,\n result: UniversalClaimRewardResponse,\n request: UniversalClaimRewardRequest,\n userId: string,\n ): Promise<UniversalClaimRewardResponse> {\n await this.#persistence.saveIdempotentResult(\n key,\n result,\n this.#options.idempotencyTtlMs ?? 86_400_000,\n );\n await this.#persistence.recordAuditLog({\n id: `audit-${globalThis.crypto?.randomUUID?.() ?? Date.now()}`,\n timestamp: request.now ?? this.#options.now?.() ?? new Date().toISOString(),\n rallyId: request.rallyId,\n userId,\n action: \"CLAIM_REWARD\",\n resourceId: request.rewardId,\n status: \"REJECTED\",\n idempotencyKey: request.idempotencyKey,\n });\n return result;\n }\n\n async #remember(\n key: string,\n result: UniversalCheckInResult,\n request: UniversalCheckInRequest,\n now: string,\n ): Promise<UniversalCheckInResult> {\n await this.#persistence.recordAuditLog(\n audit(request, result.ok ? \"SUCCESS\" : \"REJECTED\", now, result.ok ? undefined : result.code),\n );\n await this.#persistence.saveIdempotentResult(\n key,\n result,\n this.#options.idempotencyTtlMs ?? 86_400_000,\n );\n return result;\n }\n}\n\ntype UniversalClaimRewardResponse =\n | { readonly ok: true; readonly state: UserRallyState; readonly claimTicketNumber?: string }\n | {\n readonly ok: false;\n readonly code: string;\n readonly message: string;\n readonly error?: unknown;\n };\n\nexport type { StampRallyState };\n","import {\n consumeReward,\n createSecureToken,\n processStamp,\n type RallyConfig,\n type RewardConsumeError,\n reconcileRewardStates,\n type SecureTokenSecretKey,\n type StampError,\n type StampRallyState,\n type StampRecord,\n type VerificationContext,\n} from \"@stamprally/core\";\n\nexport interface UserRallyState extends StampRallyState {\n readonly userId?: string;\n}\n\nexport interface RallyAuditLog {\n readonly id: string;\n readonly timestamp: string;\n readonly rallyId: string;\n readonly userId: string;\n readonly action: \"CHECK_IN\" | \"CLAIM_REWARD\";\n readonly resourceId: string;\n readonly status: \"SUCCESS\" | \"REJECTED\";\n readonly idempotencyKey: string;\n readonly proofData?: unknown;\n readonly metadata?: Readonly<Record<string, unknown>>;\n}\n\nexport interface ServerStorageAdapter {\n getRewardStock(rewardId: string): Promise<number | null>;\n decrementRewardStock(rewardId: string): Promise<boolean>;\n getUserClaims(userId: string, rewardId: string): Promise<number>;\n recordAuditLog(log: RallyAuditLog): Promise<void>;\n saveUserState(userId: string, state: UserRallyState): Promise<void>;\n getUserState(userId: string): Promise<UserRallyState | null>;\n}\n\nexport interface AdminRallyConfig extends RallyConfig {\n readonly secretKey: SecureTokenSecretKey;\n readonly proofTtlSeconds?: number;\n readonly authenticate?: (request: Request) => Promise<string | null> | string | null;\n}\n\nexport interface CheckInRequest {\n readonly userId: string;\n readonly spotId: string;\n readonly claimMethod: string;\n readonly proofData?: unknown;\n readonly idempotencyKey: string;\n}\n\nexport interface ClaimRewardRequest {\n readonly userId: string;\n readonly rewardId: string;\n readonly staffPasscode?: string;\n readonly idempotencyKey: string;\n readonly staffId?: string;\n}\n\nexport interface SyncRequest {\n readonly userId: string;\n readonly queue?: ReadonlyArray<SyncCheckInOperation>;\n readonly operations?: ReadonlyArray<SyncCheckInOperation>;\n}\n\nexport interface SyncCheckInOperation {\n readonly userId?: string;\n readonly spotId?: string;\n readonly stampId?: string;\n readonly claimMethod?: string;\n readonly proofData?: unknown;\n readonly context?: VerificationContext;\n readonly idempotencyKey: string;\n}\n\nexport interface StampClaimProof {\n readonly token: string;\n readonly rallyId: string;\n readonly userId: string;\n readonly spotId: string;\n readonly acquiredAt: string;\n}\n\nexport interface InMemoryServerStorageOptions {\n readonly stocks?: Readonly<Record<string, number>>;\n}\n\nexport class InMemoryServerStorage implements ServerStorageAdapter {\n readonly #states = new Map<string, UserRallyState>();\n readonly #stocks: Map<string, number>;\n readonly #claims = new Map<string, number>();\n readonly #auditLogs: RallyAuditLog[] = [];\n\n constructor(options: InMemoryServerStorageOptions = {}) {\n this.#stocks = new Map(Object.entries(options.stocks ?? {}));\n }\n\n async getRewardStock(rewardId: string): Promise<number | null> {\n return this.#stocks.get(rewardId) ?? null;\n }\n\n async decrementRewardStock(rewardId: string): Promise<boolean> {\n const stock = this.#stocks.get(rewardId);\n if (stock === undefined) return true;\n if (stock <= 0) return false;\n this.#stocks.set(rewardId, stock - 1);\n return true;\n }\n\n async getUserClaims(userId: string, rewardId: string): Promise<number> {\n return this.#claims.get(`${userId}:${rewardId}`) ?? 0;\n }\n\n async recordAuditLog(log: RallyAuditLog): Promise<void> {\n this.#auditLogs.push({ ...log });\n }\n\n async saveUserState(userId: string, state: UserRallyState): Promise<void> {\n this.#states.set(userId, cloneUserState(state));\n }\n\n async getUserState(userId: string): Promise<UserRallyState | null> {\n const state = this.#states.get(userId);\n return state === undefined ? null : cloneUserState(state);\n }\n\n getAuditLogs(): ReadonlyArray<RallyAuditLog> {\n return this.#auditLogs.map((log) => ({ ...log }));\n }\n\n recordClaim(userId: string, rewardId: string): void {\n const key = `${userId}:${rewardId}`;\n this.#claims.set(key, (this.#claims.get(key) ?? 0) + 1);\n }\n}\n\nfunction cloneUserState(state: UserRallyState): UserRallyState {\n return {\n ...state,\n records: state.records.map((record) => ({\n ...record,\n ...(record.metadata === undefined ? {} : { metadata: { ...record.metadata } }),\n })),\n ...(state.rewards === undefined\n ? {}\n : { rewards: state.rewards.map((reward) => ({ ...reward })) }),\n };\n}\n\nfunction jsonResponse(body: unknown, status = 200): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { \"content-type\": \"application/json; charset=utf-8\" },\n });\n}\n\nfunction errorResponse(code: string, message: string, status: number): Response {\n return jsonResponse({ ok: false, error: { code, message } }, status);\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction contextForClaim(method: string, proofData: unknown): VerificationContext {\n if (method === \"token\" || method === \"qr\" || method === \"passcode\") {\n return {\n type: \"token\",\n token:\n isObject(proofData) && typeof proofData.token === \"string\"\n ? proofData.token\n : String(proofData ?? \"\"),\n };\n }\n if (method === \"geo\" || method === \"geolocation\") {\n const value = isObject(proofData) ? proofData : {};\n return {\n type: \"geo\",\n currentLatitude:\n typeof value.latitude === \"number\"\n ? value.latitude\n : typeof value.currentLatitude === \"number\"\n ? value.currentLatitude\n : Number.NaN,\n currentLongitude:\n typeof value.longitude === \"number\"\n ? value.longitude\n : typeof value.currentLongitude === \"number\"\n ? value.currentLongitude\n : Number.NaN,\n };\n }\n return { type: \"instant\" };\n}\n\nfunction now(): string {\n return new Date().toISOString();\n}\n\nfunction hasText(value: unknown): value is string {\n return typeof value === \"string\" && value.trim() !== \"\";\n}\n\nfunction safeProofData(value: unknown): unknown {\n return isObject(value) && typeof value.token === \"string\" ? { type: \"token\" } : value;\n}\n\nfunction proofFromContext(context: VerificationContext | undefined): unknown {\n if (context?.type === \"token\") return { token: context.token };\n if (context?.type === \"geo\") {\n return { latitude: context.currentLatitude, longitude: context.currentLongitude };\n }\n return undefined;\n}\n\nfunction id(prefix: string): string {\n return `${prefix}-${globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random().toString(36).slice(2)}`}`;\n}\n\nexport class StampRallyServer {\n readonly #config: AdminRallyConfig;\n readonly #storage: ServerStorageAdapter;\n readonly #idempotent = new Map<string, Response>();\n readonly #claims = new Map<string, number>();\n #queue: Promise<unknown> = Promise.resolve();\n\n constructor(config: AdminRallyConfig, storage: ServerStorageAdapter) {\n this.#config = config;\n this.#storage = storage;\n }\n\n handle(request: Request): Promise<Response> {\n const path = new URL(request.url).pathname;\n if (request.method !== \"POST\")\n return Promise.resolve(errorResponse(\"METHOD_NOT_ALLOWED\", \"POST is required.\", 405));\n if (path.endsWith(\"/check-in\")) return this.verifyCheckIn(request);\n if (path.endsWith(\"/claim-reward\")) return this.claimReward(request);\n if (path.endsWith(\"/sync\")) return this.syncProgress(request);\n return Promise.resolve(errorResponse(\"NOT_FOUND\", \"Route not found.\", 404));\n }\n\n verifyCheckIn(request: Request): Promise<Response> {\n return this.#enqueue(() => this.#verifyCheckIn(request));\n }\n\n claimReward(request: Request): Promise<Response> {\n return this.#enqueue(() => this.#claimReward(request));\n }\n\n syncProgress(request: Request): Promise<Response> {\n return this.#enqueue(() => this.#syncProgress(request));\n }\n\n #enqueue<T>(operation: () => Promise<T>): Promise<T> {\n const next = this.#queue.then(operation, operation);\n this.#queue = next.then(\n () => undefined,\n () => undefined,\n );\n return next;\n }\n\n async #authenticate(request: Request): Promise<string | null> {\n return this.#config.authenticate === undefined ? null : this.#config.authenticate(request);\n }\n\n async #parse<T>(request: Request): Promise<T | null> {\n try {\n const value: unknown = await request.json();\n return isObject(value) ? (value as T) : null;\n } catch {\n return null;\n }\n }\n\n async #verifyCheckIn(request: Request): Promise<Response> {\n const authenticatedUser = await this.#authenticate(request);\n const body = await this.#parse<CheckInRequest>(request);\n const userId = body?.userId ?? authenticatedUser;\n if (\n userId === null ||\n userId === undefined ||\n body === null ||\n !hasText(userId) ||\n !hasText(body.spotId) ||\n !hasText(body.claimMethod) ||\n !hasText(body.idempotencyKey)\n ) {\n return errorResponse(\n \"INVALID_REQUEST\",\n \"userId, spotId, and idempotencyKey are required.\",\n 400,\n );\n }\n if (authenticatedUser !== null && authenticatedUser !== userId)\n return errorResponse(\"UNAUTHORIZED\", \"User identity does not match authentication.\", 401);\n const key = `check-in:${userId}:${body.idempotencyKey}`;\n const previous = this.#idempotent.get(key);\n if (previous !== undefined) return previous.clone();\n const timestamp = now();\n const current =\n (await this.#storage.getUserState(userId)) ?? emptyState(this.#config, timestamp);\n const result = processStamp(\n current,\n this.#config,\n body.spotId,\n contextForClaim(body.claimMethod, body.proofData),\n timestamp,\n );\n if (!result.ok) {\n await this.#audit(\n userId,\n \"CHECK_IN\",\n body.spotId,\n body.idempotencyKey,\n \"REJECTED\",\n safeProofData(body.proofData),\n result.error,\n );\n return this.#remember(key, jsonResponse({ ok: false, error: result.error }, 422));\n }\n const ttl = this.#config.proofTtlSeconds ?? 3600;\n const token = await createSecureToken(\n {\n type: \"stamp_claim\",\n rallyId: this.#config.id,\n userId,\n spotId: body.spotId,\n acquiredAt: timestamp,\n exp: Math.floor(Date.now() / 1000) + ttl,\n },\n this.#config.secretKey,\n { encrypt: true },\n );\n await this.#storage.saveUserState(userId, result.value.nextState);\n await this.#audit(\n userId,\n \"CHECK_IN\",\n body.spotId,\n body.idempotencyKey,\n \"SUCCESS\",\n safeProofData(body.proofData),\n );\n return this.#remember(\n key,\n jsonResponse({\n ok: true,\n state: result.value.nextState,\n proof: {\n token,\n rallyId: this.#config.id,\n userId,\n spotId: body.spotId,\n acquiredAt: timestamp,\n },\n }),\n );\n }\n\n async #claimReward(request: Request): Promise<Response> {\n const authenticatedUser = await this.#authenticate(request);\n const body = await this.#parse<ClaimRewardRequest>(request);\n const userId = body?.userId ?? authenticatedUser;\n if (\n userId === null ||\n userId === undefined ||\n body === null ||\n !hasText(userId) ||\n !hasText(body.rewardId) ||\n !hasText(body.idempotencyKey)\n )\n return errorResponse(\n \"INVALID_REQUEST\",\n \"userId, rewardId, and idempotencyKey are required.\",\n 400,\n );\n if (authenticatedUser !== null && authenticatedUser !== userId)\n return errorResponse(\"UNAUTHORIZED\", \"User identity does not match authentication.\", 401);\n const key = `claim-reward:${userId}:${body.rewardId}:${body.idempotencyKey}`;\n const previous = this.#idempotent.get(key);\n if (previous !== undefined) return previous.clone();\n const reward = this.#config.rewards?.find((item) => item.id === body.rewardId);\n if (reward === undefined) {\n await this.#audit(userId, \"CLAIM_REWARD\", body.rewardId, body.idempotencyKey, \"REJECTED\");\n return this.#remember(key, errorResponse(\"REWARD_NOT_FOUND\", \"Reward was not found.\", 404));\n }\n const timestamp = now();\n const current =\n (await this.#storage.getUserState(userId)) ?? emptyState(this.#config, timestamp);\n const rewardState = current.rewards?.find((item) => item.rewardId === reward.id);\n if (rewardState === undefined) {\n await this.#audit(userId, \"CLAIM_REWARD\", reward.id, body.idempotencyKey, \"REJECTED\");\n return this.#remember(key, errorResponse(\"NOT_AVAILABLE\", \"Reward is not available.\", 422));\n }\n const userClaims = Math.max(\n await this.#storage.getUserClaims(userId, reward.id),\n this.#claims.get(`${userId}:${reward.id}`) ?? 0,\n );\n const stock = await this.#storage.getRewardStock(reward.id);\n const userLimit = reward.userClaimLimit ?? reward.limitPerUser;\n const canReclaimServerReward =\n reward.redemptionMethod === \"server_claim\" &&\n (userLimit === undefined || userClaims < userLimit) &&\n (stock === null || stock > 0);\n const claimableState =\n canReclaimServerReward && rewardState.status === \"CONSUMED\"\n ? { ...rewardState, status: \"AVAILABLE\" as const }\n : rewardState;\n const local = consumeReward({\n reward,\n currentState: claimableState,\n now: timestamp,\n ...(body.staffPasscode === undefined ? {} : { inputPasscode: body.staffPasscode }),\n ...(body.staffId === undefined ? {} : { staffId: body.staffId }),\n userId,\n userRedemptionCount: userClaims,\n });\n if (!local.ok)\n return this.#remember(\n key,\n await this.#rewardError(userId, reward.id, body.idempotencyKey, local.error),\n );\n if (stock !== null && !(await this.#storage.decrementRewardStock(reward.id)))\n return this.#remember(\n key,\n await this.#rewardError(userId, reward.id, body.idempotencyKey, {\n code: \"OUT_OF_STOCK\",\n rewardId: reward.id,\n }),\n );\n const nextState: StampRallyState = {\n ...current,\n rewards: (current.rewards ?? []).map((item) =>\n item.rewardId === reward.id ? local.value : item,\n ),\n updatedAt: timestamp,\n };\n await this.#storage.saveUserState(userId, nextState);\n const claimKey = `${userId}:${reward.id}`;\n this.#claims.set(claimKey, userClaims + 1);\n if (this.#storage instanceof InMemoryServerStorage)\n this.#storage.recordClaim(userId, reward.id);\n await this.#audit(userId, \"CLAIM_REWARD\", reward.id, body.idempotencyKey, \"SUCCESS\", {\n staffId: body.staffId,\n });\n return this.#remember(\n key,\n jsonResponse({\n ok: true,\n state: nextState,\n claimTicketNumber: local.value.claimTicketNumber,\n }),\n );\n }\n\n async #rewardError(\n userId: string,\n rewardId: string,\n key: string,\n error: RewardConsumeError,\n ): Promise<Response> {\n await this.#audit(userId, \"CLAIM_REWARD\", rewardId, key, \"REJECTED\", undefined, error);\n return jsonResponse({ ok: false, error }, 422);\n }\n\n async #syncProgress(request: Request): Promise<Response> {\n const authenticatedUser = await this.#authenticate(request);\n const body = await this.#parse<SyncRequest>(request);\n if (body === null || body.userId === undefined)\n return errorResponse(\"INVALID_REQUEST\", \"userId is required.\", 400);\n if (authenticatedUser !== null && authenticatedUser !== body.userId)\n return errorResponse(\"UNAUTHORIZED\", \"User identity does not match authentication.\", 401);\n const queue = body.queue ?? body.operations ?? [];\n for (const operation of queue) {\n const userId = operation.userId ?? body.userId;\n const spotId = operation.spotId ?? operation.stampId;\n const claimMethod = operation.claimMethod ?? operation.context?.type;\n if (!hasText(userId) || !hasText(spotId) || !hasText(claimMethod)) continue;\n const synthetic = new Request(new URL(\"/api/check-in\", request.url), {\n method: \"POST\",\n body: JSON.stringify({\n userId,\n spotId,\n claimMethod,\n proofData: operation.proofData ?? proofFromContext(operation.context),\n idempotencyKey: operation.idempotencyKey,\n }),\n headers: { \"content-type\": \"application/json\" },\n });\n await this.#verifyCheckIn(synthetic);\n }\n const timestamp = now();\n const state =\n (await this.#storage.getUserState(body.userId)) ?? emptyState(this.#config, timestamp);\n await this.#storage.saveUserState(body.userId, state);\n return jsonResponse({ ok: true, state, accepted: queue.length });\n }\n\n async #audit(\n userId: string,\n action: RallyAuditLog[\"action\"],\n resourceId: string,\n idempotencyKey: string,\n status: RallyAuditLog[\"status\"],\n proofData?: unknown,\n error?: unknown,\n ): Promise<void> {\n await this.#storage.recordAuditLog({\n id: id(\"audit\"),\n timestamp: now(),\n rallyId: this.#config.id,\n userId,\n action,\n resourceId,\n status,\n idempotencyKey,\n ...(proofData === undefined ? {} : { proofData }),\n ...(error === undefined\n ? {}\n : {\n metadata: {\n errorCode: isObject(error) && typeof error.code === \"string\" ? error.code : \"UNKNOWN\",\n },\n }),\n });\n }\n\n #remember(key: string, response: Response): Response {\n this.#idempotent.set(key, response.clone());\n return response;\n }\n}\n\nfunction emptyState(config: RallyConfig, timestamp: string): UserRallyState {\n return {\n rallyId: config.id,\n records: [],\n ...(config.rewards === undefined\n ? {}\n : { rewards: reconcileRewardStates(config.rewards, [], 0, timestamp) }),\n updatedAt: timestamp,\n };\n}\n\nexport type { InMemoryServerPersistenceOptions, ServerPersistenceAdapter } from \"./persistence.js\";\nexport { InMemoryServerPersistenceAdapter } from \"./persistence.js\";\nexport type {\n UniversalCheckInRequest,\n UniversalCheckInResult,\n UniversalClaimRewardRequest,\n UniversalRallyServerOptions,\n UniversalVerificationContext,\n} from \"./universalServer.js\";\nexport { UniversalRallyServer } from \"./universalServer.js\";\nexport type { StampError, StampRecord };\n"]}
package/dist/index.d.cts CHANGED
@@ -58,6 +58,15 @@ interface UniversalCheckInRequest {
58
58
  readonly idempotencyKey: string;
59
59
  readonly now?: string;
60
60
  }
61
+ interface UniversalClaimRewardRequest {
62
+ readonly rallyId: string;
63
+ readonly userId: string;
64
+ readonly rewardId: string;
65
+ readonly idempotencyKey: string;
66
+ readonly staffPasscode?: string;
67
+ readonly staffId?: string;
68
+ readonly now?: string;
69
+ }
61
70
  type UniversalCheckInResult = {
62
71
  readonly ok: true;
63
72
  readonly state: UserRallyState;
@@ -71,11 +80,18 @@ interface UniversalRallyServerOptions {
71
80
  readonly lockTtlMs?: number;
72
81
  readonly customValidators?: Readonly<Record<string, (value: unknown, condition: VerificationCondition) => boolean>>;
73
82
  readonly now?: () => string;
83
+ /** Extract the authenticated identity; request-body userId is never trusted when this is set. */
84
+ readonly authenticate?: (request: Request) => Promise<string | null> | string | null;
74
85
  }
75
86
  /** Atomic, adapter-backed check-in service for the universal model. */
76
87
  declare class UniversalRallyServer {
77
88
  #private;
78
89
  constructor(config: AdminRallyConfig$1, persistence: ServerPersistenceAdapter, options?: UniversalRallyServerOptions);
90
+ /** Web Standard endpoint handler. Authentication, when configured, supplies the user identity. */
91
+ handleCheckIn(request: Request): Promise<Response>;
92
+ handleClaimReward(request: Request): Promise<Response>;
93
+ handleSync(request: Request): Promise<Response>;
94
+ handle(request: Request): Promise<Response>;
79
95
  checkIn(request: UniversalCheckInRequest): Promise<UniversalCheckInResult>;
80
96
  sync(rallyId: string, userId: string): Promise<UserRallyState>;
81
97
  }
@@ -167,4 +183,4 @@ declare class StampRallyServer {
167
183
  syncProgress(request: Request): Promise<Response>;
168
184
  }
169
185
 
170
- export { type AdminRallyConfig, type CheckInRequest, type ClaimRewardRequest, InMemoryServerPersistenceAdapter, type InMemoryServerPersistenceOptions, InMemoryServerStorage, type InMemoryServerStorageOptions, type RallyAuditLog, type ServerPersistenceAdapter, type ServerStorageAdapter, type StampClaimProof, StampRallyServer, type SyncCheckInOperation, type SyncRequest, type UniversalCheckInRequest, type UniversalCheckInResult, UniversalRallyServer, type UniversalRallyServerOptions, type UniversalVerificationContext, type UserRallyState };
186
+ export { type AdminRallyConfig, type CheckInRequest, type ClaimRewardRequest, InMemoryServerPersistenceAdapter, type InMemoryServerPersistenceOptions, InMemoryServerStorage, type InMemoryServerStorageOptions, type RallyAuditLog, type ServerPersistenceAdapter, type ServerStorageAdapter, type StampClaimProof, StampRallyServer, type SyncCheckInOperation, type SyncRequest, type UniversalCheckInRequest, type UniversalCheckInResult, type UniversalClaimRewardRequest, UniversalRallyServer, type UniversalRallyServerOptions, type UniversalVerificationContext, type UserRallyState };
package/dist/index.d.ts CHANGED
@@ -58,6 +58,15 @@ interface UniversalCheckInRequest {
58
58
  readonly idempotencyKey: string;
59
59
  readonly now?: string;
60
60
  }
61
+ interface UniversalClaimRewardRequest {
62
+ readonly rallyId: string;
63
+ readonly userId: string;
64
+ readonly rewardId: string;
65
+ readonly idempotencyKey: string;
66
+ readonly staffPasscode?: string;
67
+ readonly staffId?: string;
68
+ readonly now?: string;
69
+ }
61
70
  type UniversalCheckInResult = {
62
71
  readonly ok: true;
63
72
  readonly state: UserRallyState;
@@ -71,11 +80,18 @@ interface UniversalRallyServerOptions {
71
80
  readonly lockTtlMs?: number;
72
81
  readonly customValidators?: Readonly<Record<string, (value: unknown, condition: VerificationCondition) => boolean>>;
73
82
  readonly now?: () => string;
83
+ /** Extract the authenticated identity; request-body userId is never trusted when this is set. */
84
+ readonly authenticate?: (request: Request) => Promise<string | null> | string | null;
74
85
  }
75
86
  /** Atomic, adapter-backed check-in service for the universal model. */
76
87
  declare class UniversalRallyServer {
77
88
  #private;
78
89
  constructor(config: AdminRallyConfig$1, persistence: ServerPersistenceAdapter, options?: UniversalRallyServerOptions);
90
+ /** Web Standard endpoint handler. Authentication, when configured, supplies the user identity. */
91
+ handleCheckIn(request: Request): Promise<Response>;
92
+ handleClaimReward(request: Request): Promise<Response>;
93
+ handleSync(request: Request): Promise<Response>;
94
+ handle(request: Request): Promise<Response>;
79
95
  checkIn(request: UniversalCheckInRequest): Promise<UniversalCheckInResult>;
80
96
  sync(rallyId: string, userId: string): Promise<UserRallyState>;
81
97
  }
@@ -167,4 +183,4 @@ declare class StampRallyServer {
167
183
  syncProgress(request: Request): Promise<Response>;
168
184
  }
169
185
 
170
- export { type AdminRallyConfig, type CheckInRequest, type ClaimRewardRequest, InMemoryServerPersistenceAdapter, type InMemoryServerPersistenceOptions, InMemoryServerStorage, type InMemoryServerStorageOptions, type RallyAuditLog, type ServerPersistenceAdapter, type ServerStorageAdapter, type StampClaimProof, StampRallyServer, type SyncCheckInOperation, type SyncRequest, type UniversalCheckInRequest, type UniversalCheckInResult, UniversalRallyServer, type UniversalRallyServerOptions, type UniversalVerificationContext, type UserRallyState };
186
+ export { type AdminRallyConfig, type CheckInRequest, type ClaimRewardRequest, InMemoryServerPersistenceAdapter, type InMemoryServerPersistenceOptions, InMemoryServerStorage, type InMemoryServerStorageOptions, type RallyAuditLog, type ServerPersistenceAdapter, type ServerStorageAdapter, type StampClaimProof, StampRallyServer, type SyncCheckInOperation, type SyncRequest, type UniversalCheckInRequest, type UniversalCheckInResult, type UniversalClaimRewardRequest, UniversalRallyServer, type UniversalRallyServerOptions, type UniversalVerificationContext, type UserRallyState };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { processStamp, createSecureToken, consumeReward, reconcileRewardStates } from '@stamprally/core';
1
+ import { consumeReward, processStamp, createSecureToken, reconcileRewardStates } from '@stamprally/core';
2
2
 
3
3
  // src/index.ts
4
4
 
@@ -59,8 +59,6 @@ var InMemoryServerPersistenceAdapter = class {
59
59
  return structuredClone(this.#auditLogs);
60
60
  }
61
61
  };
62
-
63
- // src/universalServer.ts
64
62
  function distanceMeters(aLat, aLon, bLat, bLon) {
65
63
  const radians = (degrees) => degrees * Math.PI / 180;
66
64
  const dLat = radians(bLat - aLat);
@@ -71,7 +69,32 @@ function distanceMeters(aLat, aLon, bLat, bLon) {
71
69
  return 6371e3 * 2 * Math.asin(Math.sqrt(Math.min(1, value)));
72
70
  }
73
71
  function initialState(config, now2) {
74
- return { rallyId: config.id, records: [], updatedAt: now2 };
72
+ return {
73
+ rallyId: config.id,
74
+ records: [],
75
+ rewards: reconcileRewardStates(
76
+ config.rewards.map((reward) => ({ ...reward, description: reward.description ?? "" })),
77
+ [],
78
+ 0,
79
+ now2
80
+ ),
81
+ updatedAt: now2
82
+ };
83
+ }
84
+ function json(body, status = 200) {
85
+ return new Response(JSON.stringify(body), {
86
+ status,
87
+ headers: { "content-type": "application/json; charset=utf-8" }
88
+ });
89
+ }
90
+ function requestBody(request) {
91
+ return request.json().then((value) => {
92
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
93
+ return value;
94
+ }).catch(() => null);
95
+ }
96
+ function adminReward(reward) {
97
+ return { ...reward, description: reward.description ?? "" };
75
98
  }
76
99
  function matchesCondition(condition, context, customValidators) {
77
100
  switch (condition.type) {
@@ -115,6 +138,157 @@ var UniversalRallyServer = class {
115
138
  this.#persistence = persistence;
116
139
  this.#options = options;
117
140
  }
141
+ /** Web Standard endpoint handler. Authentication, when configured, supplies the user identity. */
142
+ async handleCheckIn(request) {
143
+ const body = await requestBody(request);
144
+ const userId = await this.#authenticatedUser(request, body?.userId);
145
+ if (this.#options.authenticate !== void 0 && userId === null)
146
+ return json(
147
+ { ok: false, error: { code: "UNAUTHORIZED", message: "Authentication is required." } },
148
+ 401
149
+ );
150
+ if (body === null || userId === null || body.rallyId !== this.#config.id || body.spotId === "" || body.idempotencyKey === "")
151
+ return json(
152
+ {
153
+ ok: false,
154
+ error: {
155
+ code: "INVALID_REQUEST",
156
+ message: "rallyId, spotId, and idempotencyKey are required."
157
+ }
158
+ },
159
+ 400
160
+ );
161
+ if (body.context === void 0)
162
+ return json(
163
+ { ok: false, error: { code: "INVALID_REQUEST", message: "context is required." } },
164
+ 400
165
+ );
166
+ const result = await this.checkIn({ ...body, userId });
167
+ return result.ok ? json(result) : json(result, result.code === "SPOT_NOT_FOUND" ? 404 : 422);
168
+ }
169
+ async handleClaimReward(request) {
170
+ const body = await requestBody(request);
171
+ const userId = await this.#authenticatedUser(request, body?.userId);
172
+ if (this.#options.authenticate !== void 0 && userId === null)
173
+ return json(
174
+ { ok: false, error: { code: "UNAUTHORIZED", message: "Authentication is required." } },
175
+ 401
176
+ );
177
+ if (body === null || userId === null || body.rallyId !== this.#config.id || body.rewardId === "" || body.idempotencyKey === "")
178
+ return json(
179
+ {
180
+ ok: false,
181
+ error: {
182
+ code: "INVALID_REQUEST",
183
+ message: "rallyId, rewardId, and idempotencyKey are required."
184
+ }
185
+ },
186
+ 400
187
+ );
188
+ const key = `claim-reward:${body.rallyId}:${userId}:${body.rewardId}:${body.idempotencyKey}`;
189
+ const previous = await this.#persistence.getIdempotentResult(key);
190
+ if (previous !== null) return json(previous, previous.ok ? 200 : 422);
191
+ const reward = this.#config.rewards.find((item) => item.id === body.rewardId);
192
+ if (reward === void 0) {
193
+ const failure = {
194
+ ok: false,
195
+ code: "REWARD_NOT_FOUND",
196
+ message: "Reward was not found."
197
+ };
198
+ return json(await this.#rememberClaim(key, failure, body, userId), 404);
199
+ }
200
+ const timestamp = body.now ?? this.#options.now?.() ?? (/* @__PURE__ */ new Date()).toISOString();
201
+ const current = await this.sync(body.rallyId, userId);
202
+ const currentReward = current.rewards?.find((item) => item.rewardId === reward.id) ?? {
203
+ rewardId: reward.id,
204
+ status: "LOCKED"
205
+ };
206
+ const userClaims = await this.#getUserClaimCount(userId, reward.id);
207
+ const local = consumeReward({
208
+ reward: adminReward(reward),
209
+ currentState: currentReward,
210
+ now: timestamp,
211
+ userId,
212
+ userRedemptionCount: userClaims,
213
+ ...body.staffPasscode === void 0 ? {} : { inputPasscode: body.staffPasscode },
214
+ ...body.staffId === void 0 ? {} : { staffId: body.staffId }
215
+ });
216
+ if (!local.ok)
217
+ return json(
218
+ await this.#rememberClaim(
219
+ key,
220
+ {
221
+ ok: false,
222
+ code: local.error.code,
223
+ message: "Reward cannot be claimed.",
224
+ error: local.error
225
+ },
226
+ body,
227
+ userId
228
+ ),
229
+ 422
230
+ );
231
+ const stock = await this.#persistence.decrementRewardStock(reward.id);
232
+ if (!stock.success)
233
+ return json(
234
+ await this.#rememberClaim(
235
+ key,
236
+ { ok: false, code: "OUT_OF_STOCK", message: "Reward is out of stock." },
237
+ body,
238
+ userId
239
+ ),
240
+ 422
241
+ );
242
+ const next = {
243
+ ...current,
244
+ rewards: (current.rewards ?? []).map(
245
+ (item) => item.rewardId === reward.id ? local.value : item
246
+ ),
247
+ updatedAt: timestamp
248
+ };
249
+ await this.#persistence.saveUserState(body.rallyId, userId, next);
250
+ const success = local.value.claimTicketNumber === void 0 ? { ok: true, state: next } : { ok: true, state: next, claimTicketNumber: local.value.claimTicketNumber };
251
+ await this.#persistence.saveIdempotentResult(
252
+ key,
253
+ success,
254
+ this.#options.idempotencyTtlMs ?? 864e5
255
+ );
256
+ await this.#persistence.recordAuditLog({
257
+ id: `audit-${globalThis.crypto?.randomUUID?.() ?? Date.now()}`,
258
+ timestamp,
259
+ rallyId: body.rallyId,
260
+ userId,
261
+ action: "CLAIM_REWARD",
262
+ resourceId: reward.id,
263
+ status: "SUCCESS",
264
+ idempotencyKey: body.idempotencyKey
265
+ });
266
+ return json(success);
267
+ }
268
+ async handleSync(request) {
269
+ const body = await requestBody(request);
270
+ const userId = await this.#authenticatedUser(request, body?.userId);
271
+ if (this.#options.authenticate !== void 0 && userId === null)
272
+ return json(
273
+ { ok: false, error: { code: "UNAUTHORIZED", message: "Authentication is required." } },
274
+ 401
275
+ );
276
+ if (body === null || userId === null || body.rallyId !== this.#config.id)
277
+ return json(
278
+ { ok: false, error: { code: "INVALID_REQUEST", message: "rallyId is required." } },
279
+ 400
280
+ );
281
+ return json({ ok: true, state: await this.sync(body.rallyId, userId) });
282
+ }
283
+ async handle(request) {
284
+ if (request.method !== "POST")
285
+ return json({ ok: false, error: { code: "METHOD_NOT_ALLOWED" } }, 405);
286
+ const path = new URL(request.url).pathname;
287
+ if (path.endsWith("/check-in")) return this.handleCheckIn(request);
288
+ if (path.endsWith("/claim-reward")) return this.handleClaimReward(request);
289
+ if (path.endsWith("/sync")) return this.handleSync(request);
290
+ return json({ ok: false, error: { code: "NOT_FOUND" } }, 404);
291
+ }
118
292
  async checkIn(request) {
119
293
  const key = `check-in:${request.rallyId}:${request.userId}:${request.idempotencyKey}`;
120
294
  const previous = await this.#persistence.getIdempotentResult(key);
@@ -168,7 +342,6 @@ var UniversalRallyServer = class {
168
342
  updatedAt: now2
169
343
  };
170
344
  await this.#persistence.saveUserState(request.rallyId, request.userId, state);
171
- await this.#persistence.recordAuditLog(audit(request, "SUCCESS", now2));
172
345
  return this.#remember(key, { ok: true, state }, request, now2);
173
346
  } finally {
174
347
  await this.#persistence.releaseLock(lockKey);
@@ -178,9 +351,36 @@ var UniversalRallyServer = class {
178
351
  const now2 = this.#options.now?.() ?? (/* @__PURE__ */ new Date()).toISOString();
179
352
  return await this.#persistence.getUserState(rallyId, userId) ?? initialState(this.#config, now2);
180
353
  }
354
+ async #authenticatedUser(request, requestedUserId) {
355
+ const authenticated = await this.#options.authenticate?.(request);
356
+ if (this.#options.authenticate !== void 0) return authenticated ?? null;
357
+ return requestedUserId ?? null;
358
+ }
359
+ async #getUserClaimCount(userId, rewardId) {
360
+ const state = await this.#persistence.getUserState(this.#config.id, userId);
361
+ return state?.rewards?.find((item) => item.rewardId === rewardId)?.userRedemptionCount ?? 0;
362
+ }
363
+ async #rememberClaim(key, result, request, userId) {
364
+ await this.#persistence.saveIdempotentResult(
365
+ key,
366
+ result,
367
+ this.#options.idempotencyTtlMs ?? 864e5
368
+ );
369
+ await this.#persistence.recordAuditLog({
370
+ id: `audit-${globalThis.crypto?.randomUUID?.() ?? Date.now()}`,
371
+ timestamp: request.now ?? this.#options.now?.() ?? (/* @__PURE__ */ new Date()).toISOString(),
372
+ rallyId: request.rallyId,
373
+ userId,
374
+ action: "CLAIM_REWARD",
375
+ resourceId: request.rewardId,
376
+ status: "REJECTED",
377
+ idempotencyKey: request.idempotencyKey
378
+ });
379
+ return result;
380
+ }
181
381
  async #remember(key, result, request, now2) {
182
382
  await this.#persistence.recordAuditLog(
183
- audit(request, "REJECTED", now2, result.ok ? void 0 : result.code)
383
+ audit(request, result.ok ? "SUCCESS" : "REJECTED", now2, result.ok ? void 0 : result.code)
184
384
  );
185
385
  await this.#persistence.saveIdempotentResult(
186
386
  key,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/persistence.ts","../src/universalServer.ts","../src/index.ts"],"names":["now","id"],"mappings":"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","file":"index.js","sourcesContent":["import type { RallyAuditLog, UserRallyState } from \"./index.js\";\n\n/** Persistence contract for implementations backed by Redis, SQL, or another RDB. */\nexport interface ServerPersistenceAdapter {\n acquireLock(lockKey: string, ttlMs: number): Promise<boolean>;\n releaseLock(lockKey: string): Promise<void>;\n decrementRewardStock(rewardId: string): Promise<{ success: boolean; remainingStock: number }>;\n getIdempotentResult<T>(idempotencyKey: string): Promise<T | null>;\n saveIdempotentResult<T>(idempotencyKey: string, result: T, ttlMs: number): Promise<void>;\n getUserState(rallyId: string, userId: string): Promise<UserRallyState | null>;\n saveUserState(rallyId: string, userId: string, state: UserRallyState): Promise<void>;\n recordAuditLog(log: RallyAuditLog): Promise<void>;\n}\n\ninterface TimedValue<T> {\n readonly value: T;\n readonly expiresAt: number;\n}\n\nexport interface InMemoryServerPersistenceOptions {\n readonly stocks?: Readonly<Record<string, number>>;\n}\n\n/** Deterministic adapter for tests and small single-process deployments. */\nexport class InMemoryServerPersistenceAdapter implements ServerPersistenceAdapter {\n readonly #locks = new Map<string, number>();\n readonly #idempotent = new Map<string, TimedValue<unknown>>();\n readonly #states = new Map<string, UserRallyState>();\n readonly #stocks: Map<string, number>;\n readonly #auditLogs: RallyAuditLog[] = [];\n\n constructor(options: InMemoryServerPersistenceOptions = {}) {\n this.#stocks = new Map(Object.entries(options.stocks ?? {}));\n }\n\n async acquireLock(lockKey: string, ttlMs: number): Promise<boolean> {\n const now = Date.now();\n const expiresAt = this.#locks.get(lockKey);\n if (expiresAt !== undefined && expiresAt > now) return false;\n this.#locks.set(lockKey, now + Math.max(1, ttlMs));\n return true;\n }\n\n async releaseLock(lockKey: string): Promise<void> {\n this.#locks.delete(lockKey);\n }\n\n async decrementRewardStock(\n rewardId: string,\n ): Promise<{ success: boolean; remainingStock: number }> {\n const stock = this.#stocks.get(rewardId);\n if (stock === undefined) return { success: true, remainingStock: Number.POSITIVE_INFINITY };\n if (stock <= 0) return { success: false, remainingStock: 0 };\n const remainingStock = stock - 1;\n this.#stocks.set(rewardId, remainingStock);\n return { success: true, remainingStock };\n }\n\n async getIdempotentResult<T>(idempotencyKey: string): Promise<T | null> {\n const entry = this.#idempotent.get(idempotencyKey);\n if (entry === undefined) return null;\n if (entry.expiresAt <= Date.now()) {\n this.#idempotent.delete(idempotencyKey);\n return null;\n }\n return structuredClone(entry.value) as T;\n }\n\n async saveIdempotentResult<T>(idempotencyKey: string, result: T, ttlMs: number): Promise<void> {\n this.#idempotent.set(idempotencyKey, {\n value: structuredClone(result),\n expiresAt: Date.now() + Math.max(1, ttlMs),\n });\n }\n\n async getUserState(rallyId: string, userId: string): Promise<UserRallyState | null> {\n const state = this.#states.get(`${rallyId}:${userId}`);\n return state === undefined ? null : structuredClone(state);\n }\n\n async saveUserState(rallyId: string, userId: string, state: UserRallyState): Promise<void> {\n this.#states.set(`${rallyId}:${userId}`, structuredClone(state));\n }\n\n async recordAuditLog(log: RallyAuditLog): Promise<void> {\n this.#auditLogs.push(structuredClone(log));\n }\n\n getAuditLogs(): ReadonlyArray<RallyAuditLog> {\n return structuredClone(this.#auditLogs);\n }\n}\n","import type { AdminRallyConfig, StampRallyState, VerificationCondition } from \"@stamprally/core\";\nimport type { RallyAuditLog, UserRallyState } from \"./index.js\";\nimport type { ServerPersistenceAdapter } from \"./persistence.js\";\n\nexport type UniversalVerificationContext =\n | { readonly type: \"qr\"; readonly token: string }\n | { readonly type: \"passcode\"; readonly code: string }\n | { readonly type: \"gps\"; readonly latitude: number; readonly longitude: number }\n | { readonly type: \"custom\"; readonly value: unknown };\n\nexport interface UniversalCheckInRequest {\n readonly rallyId: string;\n readonly userId: string;\n readonly spotId: string;\n readonly context: UniversalVerificationContext;\n readonly idempotencyKey: string;\n readonly now?: string;\n}\n\nexport type UniversalCheckInResult =\n | { readonly ok: true; readonly state: UserRallyState }\n | { readonly ok: false; readonly code: string; readonly message: string };\n\nexport interface UniversalRallyServerOptions {\n readonly idempotencyTtlMs?: number;\n readonly lockTtlMs?: number;\n readonly customValidators?: Readonly<\n Record<string, (value: unknown, condition: VerificationCondition) => boolean>\n >;\n readonly now?: () => string;\n}\n\nfunction distanceMeters(aLat: number, aLon: number, bLat: number, bLon: number): number {\n const radians = (degrees: number): number => (degrees * Math.PI) / 180;\n const dLat = radians(bLat - aLat);\n const dLon = radians(bLon - aLon);\n const latA = radians(aLat);\n const latB = radians(bLat);\n const value = Math.sin(dLat / 2) ** 2 + Math.cos(latA) * Math.cos(latB) * Math.sin(dLon / 2) ** 2;\n return 6_371_000 * 2 * Math.asin(Math.sqrt(Math.min(1, value)));\n}\n\nfunction initialState(config: AdminRallyConfig, now: string): UserRallyState {\n return { rallyId: config.id, records: [], updatedAt: now };\n}\n\nfunction matchesCondition(\n condition: VerificationCondition,\n context: UniversalVerificationContext,\n customValidators: UniversalRallyServerOptions[\"customValidators\"],\n): boolean {\n switch (condition.type) {\n case \"qr\":\n return context.type === \"qr\" && context.token === condition.secretToken;\n case \"passcode\":\n return (\n context.type === \"passcode\" &&\n (condition.caseSensitive === false\n ? context.code.toLocaleLowerCase() === condition.code.toLocaleLowerCase()\n : context.code === condition.code)\n );\n case \"gps\":\n return (\n context.type === \"gps\" &&\n distanceMeters(\n condition.latitude,\n condition.longitude,\n context.latitude,\n context.longitude,\n ) <= condition.radiusMeters\n );\n case \"custom\":\n return (\n customValidators?.[condition.validatorName]?.(\n context.type === \"custom\" ? context.value : undefined,\n condition,\n ) ?? false\n );\n }\n}\n\nfunction audit(\n request: UniversalCheckInRequest,\n status: RallyAuditLog[\"status\"],\n now: string,\n errorCode?: string,\n): RallyAuditLog {\n return {\n id: `audit-${globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random().toString(36).slice(2)}`}`,\n timestamp: now,\n rallyId: request.rallyId,\n userId: request.userId,\n action: \"CHECK_IN\",\n resourceId: request.spotId,\n status,\n idempotencyKey: request.idempotencyKey,\n ...(errorCode === undefined ? {} : { metadata: { errorCode } }),\n };\n}\n\n/** Atomic, adapter-backed check-in service for the universal model. */\nexport class UniversalRallyServer {\n readonly #config: AdminRallyConfig;\n readonly #persistence: ServerPersistenceAdapter;\n readonly #options: UniversalRallyServerOptions;\n\n constructor(\n config: AdminRallyConfig,\n persistence: ServerPersistenceAdapter,\n options: UniversalRallyServerOptions = {},\n ) {\n this.#config = config;\n this.#persistence = persistence;\n this.#options = options;\n }\n\n async checkIn(request: UniversalCheckInRequest): Promise<UniversalCheckInResult> {\n const key = `check-in:${request.rallyId}:${request.userId}:${request.idempotencyKey}`;\n const previous = await this.#persistence.getIdempotentResult<UniversalCheckInResult>(key);\n if (previous !== null) return previous;\n const lockKey = `state:${request.rallyId}:${request.userId}`;\n const locked = await this.#persistence.acquireLock(lockKey, this.#options.lockTtlMs ?? 5000);\n if (!locked)\n return { ok: false, code: \"CONFLICT\", message: \"The user state is being updated.\" };\n const now = request.now ?? this.#options.now?.() ?? new Date().toISOString();\n try {\n const spot = this.#config.spots.find((item) => item.id === request.spotId);\n if (spot === undefined)\n return this.#remember(\n key,\n { ok: false, code: \"SPOT_NOT_FOUND\", message: \"Spot was not found.\" },\n request,\n now,\n );\n const current =\n (await this.#persistence.getUserState(request.rallyId, request.userId)) ??\n initialState(this.#config, now);\n if (current.records.some((record) => record.stampId === request.spotId))\n return this.#remember(\n key,\n { ok: false, code: \"ALREADY_CLAIMED\", message: \"Spot was already claimed.\" },\n request,\n now,\n );\n const acquired = new Set(current.records.map((record) => record.stampId));\n if (spot.prerequisites?.some((id) => !acquired.has(id)))\n return this.#remember(\n key,\n {\n ok: false,\n code: \"PREREQUISITES_NOT_MET\",\n message: \"Prerequisite spots are not complete.\",\n },\n request,\n now,\n );\n if (\n !spot.conditions.every((condition) =>\n matchesCondition(condition, request.context, this.#options.customValidators),\n )\n )\n return this.#remember(\n key,\n { ok: false, code: \"INVALID_PROOF\", message: \"Verification failed.\" },\n request,\n now,\n );\n const state: UserRallyState = {\n ...current,\n records: [...current.records, { stampId: request.spotId, acquiredAt: now }],\n updatedAt: now,\n };\n await this.#persistence.saveUserState(request.rallyId, request.userId, state);\n await this.#persistence.recordAuditLog(audit(request, \"SUCCESS\", now));\n return this.#remember(key, { ok: true, state }, request, now);\n } finally {\n await this.#persistence.releaseLock(lockKey);\n }\n }\n\n async sync(rallyId: string, userId: string): Promise<UserRallyState> {\n const now = this.#options.now?.() ?? new Date().toISOString();\n return (\n (await this.#persistence.getUserState(rallyId, userId)) ?? initialState(this.#config, now)\n );\n }\n\n async #remember(\n key: string,\n result: UniversalCheckInResult,\n request: UniversalCheckInRequest,\n now: string,\n ): Promise<UniversalCheckInResult> {\n await this.#persistence.recordAuditLog(\n audit(request, \"REJECTED\", now, result.ok ? undefined : result.code),\n );\n await this.#persistence.saveIdempotentResult(\n key,\n result,\n this.#options.idempotencyTtlMs ?? 86_400_000,\n );\n return result;\n }\n}\n\nexport type { StampRallyState };\n","import {\n consumeReward,\n createSecureToken,\n processStamp,\n type RallyConfig,\n type RewardConsumeError,\n reconcileRewardStates,\n type SecureTokenSecretKey,\n type StampError,\n type StampRallyState,\n type StampRecord,\n type VerificationContext,\n} from \"@stamprally/core\";\n\nexport interface UserRallyState extends StampRallyState {\n readonly userId?: string;\n}\n\nexport interface RallyAuditLog {\n readonly id: string;\n readonly timestamp: string;\n readonly rallyId: string;\n readonly userId: string;\n readonly action: \"CHECK_IN\" | \"CLAIM_REWARD\";\n readonly resourceId: string;\n readonly status: \"SUCCESS\" | \"REJECTED\";\n readonly idempotencyKey: string;\n readonly proofData?: unknown;\n readonly metadata?: Readonly<Record<string, unknown>>;\n}\n\nexport interface ServerStorageAdapter {\n getRewardStock(rewardId: string): Promise<number | null>;\n decrementRewardStock(rewardId: string): Promise<boolean>;\n getUserClaims(userId: string, rewardId: string): Promise<number>;\n recordAuditLog(log: RallyAuditLog): Promise<void>;\n saveUserState(userId: string, state: UserRallyState): Promise<void>;\n getUserState(userId: string): Promise<UserRallyState | null>;\n}\n\nexport interface AdminRallyConfig extends RallyConfig {\n readonly secretKey: SecureTokenSecretKey;\n readonly proofTtlSeconds?: number;\n readonly authenticate?: (request: Request) => Promise<string | null> | string | null;\n}\n\nexport interface CheckInRequest {\n readonly userId: string;\n readonly spotId: string;\n readonly claimMethod: string;\n readonly proofData?: unknown;\n readonly idempotencyKey: string;\n}\n\nexport interface ClaimRewardRequest {\n readonly userId: string;\n readonly rewardId: string;\n readonly staffPasscode?: string;\n readonly idempotencyKey: string;\n readonly staffId?: string;\n}\n\nexport interface SyncRequest {\n readonly userId: string;\n readonly queue?: ReadonlyArray<SyncCheckInOperation>;\n readonly operations?: ReadonlyArray<SyncCheckInOperation>;\n}\n\nexport interface SyncCheckInOperation {\n readonly userId?: string;\n readonly spotId?: string;\n readonly stampId?: string;\n readonly claimMethod?: string;\n readonly proofData?: unknown;\n readonly context?: VerificationContext;\n readonly idempotencyKey: string;\n}\n\nexport interface StampClaimProof {\n readonly token: string;\n readonly rallyId: string;\n readonly userId: string;\n readonly spotId: string;\n readonly acquiredAt: string;\n}\n\nexport interface InMemoryServerStorageOptions {\n readonly stocks?: Readonly<Record<string, number>>;\n}\n\nexport class InMemoryServerStorage implements ServerStorageAdapter {\n readonly #states = new Map<string, UserRallyState>();\n readonly #stocks: Map<string, number>;\n readonly #claims = new Map<string, number>();\n readonly #auditLogs: RallyAuditLog[] = [];\n\n constructor(options: InMemoryServerStorageOptions = {}) {\n this.#stocks = new Map(Object.entries(options.stocks ?? {}));\n }\n\n async getRewardStock(rewardId: string): Promise<number | null> {\n return this.#stocks.get(rewardId) ?? null;\n }\n\n async decrementRewardStock(rewardId: string): Promise<boolean> {\n const stock = this.#stocks.get(rewardId);\n if (stock === undefined) return true;\n if (stock <= 0) return false;\n this.#stocks.set(rewardId, stock - 1);\n return true;\n }\n\n async getUserClaims(userId: string, rewardId: string): Promise<number> {\n return this.#claims.get(`${userId}:${rewardId}`) ?? 0;\n }\n\n async recordAuditLog(log: RallyAuditLog): Promise<void> {\n this.#auditLogs.push({ ...log });\n }\n\n async saveUserState(userId: string, state: UserRallyState): Promise<void> {\n this.#states.set(userId, cloneUserState(state));\n }\n\n async getUserState(userId: string): Promise<UserRallyState | null> {\n const state = this.#states.get(userId);\n return state === undefined ? null : cloneUserState(state);\n }\n\n getAuditLogs(): ReadonlyArray<RallyAuditLog> {\n return this.#auditLogs.map((log) => ({ ...log }));\n }\n\n recordClaim(userId: string, rewardId: string): void {\n const key = `${userId}:${rewardId}`;\n this.#claims.set(key, (this.#claims.get(key) ?? 0) + 1);\n }\n}\n\nfunction cloneUserState(state: UserRallyState): UserRallyState {\n return {\n ...state,\n records: state.records.map((record) => ({\n ...record,\n ...(record.metadata === undefined ? {} : { metadata: { ...record.metadata } }),\n })),\n ...(state.rewards === undefined\n ? {}\n : { rewards: state.rewards.map((reward) => ({ ...reward })) }),\n };\n}\n\nfunction jsonResponse(body: unknown, status = 200): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { \"content-type\": \"application/json; charset=utf-8\" },\n });\n}\n\nfunction errorResponse(code: string, message: string, status: number): Response {\n return jsonResponse({ ok: false, error: { code, message } }, status);\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction contextForClaim(method: string, proofData: unknown): VerificationContext {\n if (method === \"token\" || method === \"qr\" || method === \"passcode\") {\n return {\n type: \"token\",\n token:\n isObject(proofData) && typeof proofData.token === \"string\"\n ? proofData.token\n : String(proofData ?? \"\"),\n };\n }\n if (method === \"geo\" || method === \"geolocation\") {\n const value = isObject(proofData) ? proofData : {};\n return {\n type: \"geo\",\n currentLatitude:\n typeof value.latitude === \"number\"\n ? value.latitude\n : typeof value.currentLatitude === \"number\"\n ? value.currentLatitude\n : Number.NaN,\n currentLongitude:\n typeof value.longitude === \"number\"\n ? value.longitude\n : typeof value.currentLongitude === \"number\"\n ? value.currentLongitude\n : Number.NaN,\n };\n }\n return { type: \"instant\" };\n}\n\nfunction now(): string {\n return new Date().toISOString();\n}\n\nfunction hasText(value: unknown): value is string {\n return typeof value === \"string\" && value.trim() !== \"\";\n}\n\nfunction safeProofData(value: unknown): unknown {\n return isObject(value) && typeof value.token === \"string\" ? { type: \"token\" } : value;\n}\n\nfunction proofFromContext(context: VerificationContext | undefined): unknown {\n if (context?.type === \"token\") return { token: context.token };\n if (context?.type === \"geo\") {\n return { latitude: context.currentLatitude, longitude: context.currentLongitude };\n }\n return undefined;\n}\n\nfunction id(prefix: string): string {\n return `${prefix}-${globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random().toString(36).slice(2)}`}`;\n}\n\nexport class StampRallyServer {\n readonly #config: AdminRallyConfig;\n readonly #storage: ServerStorageAdapter;\n readonly #idempotent = new Map<string, Response>();\n readonly #claims = new Map<string, number>();\n #queue: Promise<unknown> = Promise.resolve();\n\n constructor(config: AdminRallyConfig, storage: ServerStorageAdapter) {\n this.#config = config;\n this.#storage = storage;\n }\n\n handle(request: Request): Promise<Response> {\n const path = new URL(request.url).pathname;\n if (request.method !== \"POST\")\n return Promise.resolve(errorResponse(\"METHOD_NOT_ALLOWED\", \"POST is required.\", 405));\n if (path.endsWith(\"/check-in\")) return this.verifyCheckIn(request);\n if (path.endsWith(\"/claim-reward\")) return this.claimReward(request);\n if (path.endsWith(\"/sync\")) return this.syncProgress(request);\n return Promise.resolve(errorResponse(\"NOT_FOUND\", \"Route not found.\", 404));\n }\n\n verifyCheckIn(request: Request): Promise<Response> {\n return this.#enqueue(() => this.#verifyCheckIn(request));\n }\n\n claimReward(request: Request): Promise<Response> {\n return this.#enqueue(() => this.#claimReward(request));\n }\n\n syncProgress(request: Request): Promise<Response> {\n return this.#enqueue(() => this.#syncProgress(request));\n }\n\n #enqueue<T>(operation: () => Promise<T>): Promise<T> {\n const next = this.#queue.then(operation, operation);\n this.#queue = next.then(\n () => undefined,\n () => undefined,\n );\n return next;\n }\n\n async #authenticate(request: Request): Promise<string | null> {\n return this.#config.authenticate === undefined ? null : this.#config.authenticate(request);\n }\n\n async #parse<T>(request: Request): Promise<T | null> {\n try {\n const value: unknown = await request.json();\n return isObject(value) ? (value as T) : null;\n } catch {\n return null;\n }\n }\n\n async #verifyCheckIn(request: Request): Promise<Response> {\n const authenticatedUser = await this.#authenticate(request);\n const body = await this.#parse<CheckInRequest>(request);\n const userId = body?.userId ?? authenticatedUser;\n if (\n userId === null ||\n userId === undefined ||\n body === null ||\n !hasText(userId) ||\n !hasText(body.spotId) ||\n !hasText(body.claimMethod) ||\n !hasText(body.idempotencyKey)\n ) {\n return errorResponse(\n \"INVALID_REQUEST\",\n \"userId, spotId, and idempotencyKey are required.\",\n 400,\n );\n }\n if (authenticatedUser !== null && authenticatedUser !== userId)\n return errorResponse(\"UNAUTHORIZED\", \"User identity does not match authentication.\", 401);\n const key = `check-in:${userId}:${body.idempotencyKey}`;\n const previous = this.#idempotent.get(key);\n if (previous !== undefined) return previous.clone();\n const timestamp = now();\n const current =\n (await this.#storage.getUserState(userId)) ?? emptyState(this.#config, timestamp);\n const result = processStamp(\n current,\n this.#config,\n body.spotId,\n contextForClaim(body.claimMethod, body.proofData),\n timestamp,\n );\n if (!result.ok) {\n await this.#audit(\n userId,\n \"CHECK_IN\",\n body.spotId,\n body.idempotencyKey,\n \"REJECTED\",\n safeProofData(body.proofData),\n result.error,\n );\n return this.#remember(key, jsonResponse({ ok: false, error: result.error }, 422));\n }\n const ttl = this.#config.proofTtlSeconds ?? 3600;\n const token = await createSecureToken(\n {\n type: \"stamp_claim\",\n rallyId: this.#config.id,\n userId,\n spotId: body.spotId,\n acquiredAt: timestamp,\n exp: Math.floor(Date.now() / 1000) + ttl,\n },\n this.#config.secretKey,\n { encrypt: true },\n );\n await this.#storage.saveUserState(userId, result.value.nextState);\n await this.#audit(\n userId,\n \"CHECK_IN\",\n body.spotId,\n body.idempotencyKey,\n \"SUCCESS\",\n safeProofData(body.proofData),\n );\n return this.#remember(\n key,\n jsonResponse({\n ok: true,\n state: result.value.nextState,\n proof: {\n token,\n rallyId: this.#config.id,\n userId,\n spotId: body.spotId,\n acquiredAt: timestamp,\n },\n }),\n );\n }\n\n async #claimReward(request: Request): Promise<Response> {\n const authenticatedUser = await this.#authenticate(request);\n const body = await this.#parse<ClaimRewardRequest>(request);\n const userId = body?.userId ?? authenticatedUser;\n if (\n userId === null ||\n userId === undefined ||\n body === null ||\n !hasText(userId) ||\n !hasText(body.rewardId) ||\n !hasText(body.idempotencyKey)\n )\n return errorResponse(\n \"INVALID_REQUEST\",\n \"userId, rewardId, and idempotencyKey are required.\",\n 400,\n );\n if (authenticatedUser !== null && authenticatedUser !== userId)\n return errorResponse(\"UNAUTHORIZED\", \"User identity does not match authentication.\", 401);\n const key = `claim-reward:${userId}:${body.rewardId}:${body.idempotencyKey}`;\n const previous = this.#idempotent.get(key);\n if (previous !== undefined) return previous.clone();\n const reward = this.#config.rewards?.find((item) => item.id === body.rewardId);\n if (reward === undefined) {\n await this.#audit(userId, \"CLAIM_REWARD\", body.rewardId, body.idempotencyKey, \"REJECTED\");\n return this.#remember(key, errorResponse(\"REWARD_NOT_FOUND\", \"Reward was not found.\", 404));\n }\n const timestamp = now();\n const current =\n (await this.#storage.getUserState(userId)) ?? emptyState(this.#config, timestamp);\n const rewardState = current.rewards?.find((item) => item.rewardId === reward.id);\n if (rewardState === undefined) {\n await this.#audit(userId, \"CLAIM_REWARD\", reward.id, body.idempotencyKey, \"REJECTED\");\n return this.#remember(key, errorResponse(\"NOT_AVAILABLE\", \"Reward is not available.\", 422));\n }\n const userClaims = Math.max(\n await this.#storage.getUserClaims(userId, reward.id),\n this.#claims.get(`${userId}:${reward.id}`) ?? 0,\n );\n const stock = await this.#storage.getRewardStock(reward.id);\n const userLimit = reward.userClaimLimit ?? reward.limitPerUser;\n const canReclaimServerReward =\n reward.redemptionMethod === \"server_claim\" &&\n (userLimit === undefined || userClaims < userLimit) &&\n (stock === null || stock > 0);\n const claimableState =\n canReclaimServerReward && rewardState.status === \"CONSUMED\"\n ? { ...rewardState, status: \"AVAILABLE\" as const }\n : rewardState;\n const local = consumeReward({\n reward,\n currentState: claimableState,\n now: timestamp,\n ...(body.staffPasscode === undefined ? {} : { inputPasscode: body.staffPasscode }),\n ...(body.staffId === undefined ? {} : { staffId: body.staffId }),\n userId,\n userRedemptionCount: userClaims,\n });\n if (!local.ok)\n return this.#remember(\n key,\n await this.#rewardError(userId, reward.id, body.idempotencyKey, local.error),\n );\n if (stock !== null && !(await this.#storage.decrementRewardStock(reward.id)))\n return this.#remember(\n key,\n await this.#rewardError(userId, reward.id, body.idempotencyKey, {\n code: \"OUT_OF_STOCK\",\n rewardId: reward.id,\n }),\n );\n const nextState: StampRallyState = {\n ...current,\n rewards: (current.rewards ?? []).map((item) =>\n item.rewardId === reward.id ? local.value : item,\n ),\n updatedAt: timestamp,\n };\n await this.#storage.saveUserState(userId, nextState);\n const claimKey = `${userId}:${reward.id}`;\n this.#claims.set(claimKey, userClaims + 1);\n if (this.#storage instanceof InMemoryServerStorage)\n this.#storage.recordClaim(userId, reward.id);\n await this.#audit(userId, \"CLAIM_REWARD\", reward.id, body.idempotencyKey, \"SUCCESS\", {\n staffId: body.staffId,\n });\n return this.#remember(\n key,\n jsonResponse({\n ok: true,\n state: nextState,\n claimTicketNumber: local.value.claimTicketNumber,\n }),\n );\n }\n\n async #rewardError(\n userId: string,\n rewardId: string,\n key: string,\n error: RewardConsumeError,\n ): Promise<Response> {\n await this.#audit(userId, \"CLAIM_REWARD\", rewardId, key, \"REJECTED\", undefined, error);\n return jsonResponse({ ok: false, error }, 422);\n }\n\n async #syncProgress(request: Request): Promise<Response> {\n const authenticatedUser = await this.#authenticate(request);\n const body = await this.#parse<SyncRequest>(request);\n if (body === null || body.userId === undefined)\n return errorResponse(\"INVALID_REQUEST\", \"userId is required.\", 400);\n if (authenticatedUser !== null && authenticatedUser !== body.userId)\n return errorResponse(\"UNAUTHORIZED\", \"User identity does not match authentication.\", 401);\n const queue = body.queue ?? body.operations ?? [];\n for (const operation of queue) {\n const userId = operation.userId ?? body.userId;\n const spotId = operation.spotId ?? operation.stampId;\n const claimMethod = operation.claimMethod ?? operation.context?.type;\n if (!hasText(userId) || !hasText(spotId) || !hasText(claimMethod)) continue;\n const synthetic = new Request(new URL(\"/api/check-in\", request.url), {\n method: \"POST\",\n body: JSON.stringify({\n userId,\n spotId,\n claimMethod,\n proofData: operation.proofData ?? proofFromContext(operation.context),\n idempotencyKey: operation.idempotencyKey,\n }),\n headers: { \"content-type\": \"application/json\" },\n });\n await this.#verifyCheckIn(synthetic);\n }\n const timestamp = now();\n const state =\n (await this.#storage.getUserState(body.userId)) ?? emptyState(this.#config, timestamp);\n await this.#storage.saveUserState(body.userId, state);\n return jsonResponse({ ok: true, state, accepted: queue.length });\n }\n\n async #audit(\n userId: string,\n action: RallyAuditLog[\"action\"],\n resourceId: string,\n idempotencyKey: string,\n status: RallyAuditLog[\"status\"],\n proofData?: unknown,\n error?: unknown,\n ): Promise<void> {\n await this.#storage.recordAuditLog({\n id: id(\"audit\"),\n timestamp: now(),\n rallyId: this.#config.id,\n userId,\n action,\n resourceId,\n status,\n idempotencyKey,\n ...(proofData === undefined ? {} : { proofData }),\n ...(error === undefined\n ? {}\n : {\n metadata: {\n errorCode: isObject(error) && typeof error.code === \"string\" ? error.code : \"UNKNOWN\",\n },\n }),\n });\n }\n\n #remember(key: string, response: Response): Response {\n this.#idempotent.set(key, response.clone());\n return response;\n }\n}\n\nfunction emptyState(config: RallyConfig, timestamp: string): UserRallyState {\n return {\n rallyId: config.id,\n records: [],\n ...(config.rewards === undefined\n ? {}\n : { rewards: reconcileRewardStates(config.rewards, [], 0, timestamp) }),\n updatedAt: timestamp,\n };\n}\n\nexport type { InMemoryServerPersistenceOptions, ServerPersistenceAdapter } from \"./persistence.js\";\nexport { InMemoryServerPersistenceAdapter } from \"./persistence.js\";\nexport type {\n UniversalCheckInRequest,\n UniversalCheckInResult,\n UniversalRallyServerOptions,\n UniversalVerificationContext,\n} from \"./universalServer.js\";\nexport { UniversalRallyServer } from \"./universalServer.js\";\nexport type { StampError, StampRecord };\n"]}
1
+ {"version":3,"sources":["../src/persistence.ts","../src/universalServer.ts","../src/index.ts"],"names":["now","id","consumeReward","reconcileRewardStates"],"mappings":"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","file":"index.js","sourcesContent":["import type { RallyAuditLog, UserRallyState } from \"./index.js\";\n\n/** Persistence contract for implementations backed by Redis, SQL, or another RDB. */\nexport interface ServerPersistenceAdapter {\n acquireLock(lockKey: string, ttlMs: number): Promise<boolean>;\n releaseLock(lockKey: string): Promise<void>;\n decrementRewardStock(rewardId: string): Promise<{ success: boolean; remainingStock: number }>;\n getIdempotentResult<T>(idempotencyKey: string): Promise<T | null>;\n saveIdempotentResult<T>(idempotencyKey: string, result: T, ttlMs: number): Promise<void>;\n getUserState(rallyId: string, userId: string): Promise<UserRallyState | null>;\n saveUserState(rallyId: string, userId: string, state: UserRallyState): Promise<void>;\n recordAuditLog(log: RallyAuditLog): Promise<void>;\n}\n\ninterface TimedValue<T> {\n readonly value: T;\n readonly expiresAt: number;\n}\n\nexport interface InMemoryServerPersistenceOptions {\n readonly stocks?: Readonly<Record<string, number>>;\n}\n\n/** Deterministic adapter for tests and small single-process deployments. */\nexport class InMemoryServerPersistenceAdapter implements ServerPersistenceAdapter {\n readonly #locks = new Map<string, number>();\n readonly #idempotent = new Map<string, TimedValue<unknown>>();\n readonly #states = new Map<string, UserRallyState>();\n readonly #stocks: Map<string, number>;\n readonly #auditLogs: RallyAuditLog[] = [];\n\n constructor(options: InMemoryServerPersistenceOptions = {}) {\n this.#stocks = new Map(Object.entries(options.stocks ?? {}));\n }\n\n async acquireLock(lockKey: string, ttlMs: number): Promise<boolean> {\n const now = Date.now();\n const expiresAt = this.#locks.get(lockKey);\n if (expiresAt !== undefined && expiresAt > now) return false;\n this.#locks.set(lockKey, now + Math.max(1, ttlMs));\n return true;\n }\n\n async releaseLock(lockKey: string): Promise<void> {\n this.#locks.delete(lockKey);\n }\n\n async decrementRewardStock(\n rewardId: string,\n ): Promise<{ success: boolean; remainingStock: number }> {\n const stock = this.#stocks.get(rewardId);\n if (stock === undefined) return { success: true, remainingStock: Number.POSITIVE_INFINITY };\n if (stock <= 0) return { success: false, remainingStock: 0 };\n const remainingStock = stock - 1;\n this.#stocks.set(rewardId, remainingStock);\n return { success: true, remainingStock };\n }\n\n async getIdempotentResult<T>(idempotencyKey: string): Promise<T | null> {\n const entry = this.#idempotent.get(idempotencyKey);\n if (entry === undefined) return null;\n if (entry.expiresAt <= Date.now()) {\n this.#idempotent.delete(idempotencyKey);\n return null;\n }\n return structuredClone(entry.value) as T;\n }\n\n async saveIdempotentResult<T>(idempotencyKey: string, result: T, ttlMs: number): Promise<void> {\n this.#idempotent.set(idempotencyKey, {\n value: structuredClone(result),\n expiresAt: Date.now() + Math.max(1, ttlMs),\n });\n }\n\n async getUserState(rallyId: string, userId: string): Promise<UserRallyState | null> {\n const state = this.#states.get(`${rallyId}:${userId}`);\n return state === undefined ? null : structuredClone(state);\n }\n\n async saveUserState(rallyId: string, userId: string, state: UserRallyState): Promise<void> {\n this.#states.set(`${rallyId}:${userId}`, structuredClone(state));\n }\n\n async recordAuditLog(log: RallyAuditLog): Promise<void> {\n this.#auditLogs.push(structuredClone(log));\n }\n\n getAuditLogs(): ReadonlyArray<RallyAuditLog> {\n return structuredClone(this.#auditLogs);\n }\n}\n","import {\n type AdminRallyConfig,\n type AdminReward,\n consumeReward,\n type RewardItem,\n reconcileRewardStates,\n type StampRallyState,\n type VerificationCondition,\n} from \"@stamprally/core\";\nimport type { RallyAuditLog, UserRallyState } from \"./index.js\";\nimport type { ServerPersistenceAdapter } from \"./persistence.js\";\n\nexport type UniversalVerificationContext =\n | { readonly type: \"qr\"; readonly token: string }\n | { readonly type: \"passcode\"; readonly code: string }\n | { readonly type: \"gps\"; readonly latitude: number; readonly longitude: number }\n | { readonly type: \"custom\"; readonly value: unknown };\n\nexport interface UniversalCheckInRequest {\n readonly rallyId: string;\n readonly userId: string;\n readonly spotId: string;\n readonly context: UniversalVerificationContext;\n readonly idempotencyKey: string;\n readonly now?: string;\n}\n\nexport interface UniversalClaimRewardRequest {\n readonly rallyId: string;\n readonly userId: string;\n readonly rewardId: string;\n readonly idempotencyKey: string;\n readonly staffPasscode?: string;\n readonly staffId?: string;\n readonly now?: string;\n}\n\nexport type UniversalCheckInResult =\n | { readonly ok: true; readonly state: UserRallyState }\n | { readonly ok: false; readonly code: string; readonly message: string };\n\nexport interface UniversalRallyServerOptions {\n readonly idempotencyTtlMs?: number;\n readonly lockTtlMs?: number;\n readonly customValidators?: Readonly<\n Record<string, (value: unknown, condition: VerificationCondition) => boolean>\n >;\n readonly now?: () => string;\n /** Extract the authenticated identity; request-body userId is never trusted when this is set. */\n readonly authenticate?: (request: Request) => Promise<string | null> | string | null;\n}\n\nfunction distanceMeters(aLat: number, aLon: number, bLat: number, bLon: number): number {\n const radians = (degrees: number): number => (degrees * Math.PI) / 180;\n const dLat = radians(bLat - aLat);\n const dLon = radians(bLon - aLon);\n const latA = radians(aLat);\n const latB = radians(bLat);\n const value = Math.sin(dLat / 2) ** 2 + Math.cos(latA) * Math.cos(latB) * Math.sin(dLon / 2) ** 2;\n return 6_371_000 * 2 * Math.asin(Math.sqrt(Math.min(1, value)));\n}\n\nfunction initialState(config: AdminRallyConfig, now: string): UserRallyState {\n return {\n rallyId: config.id,\n records: [],\n rewards: reconcileRewardStates(\n config.rewards.map((reward) => ({ ...reward, description: reward.description ?? \"\" })),\n [],\n 0,\n now,\n ),\n updatedAt: now,\n };\n}\n\nfunction json(body: unknown, status = 200): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { \"content-type\": \"application/json; charset=utf-8\" },\n });\n}\n\nfunction requestBody<T>(request: Request): Promise<T | null> {\n return request\n .json()\n .then((value: unknown) => {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return null;\n return value as T;\n })\n .catch(() => null);\n}\n\nfunction adminReward(reward: AdminReward): RewardItem {\n return { ...reward, description: reward.description ?? \"\" };\n}\n\nfunction matchesCondition(\n condition: VerificationCondition,\n context: UniversalVerificationContext,\n customValidators: UniversalRallyServerOptions[\"customValidators\"],\n): boolean {\n switch (condition.type) {\n case \"qr\":\n return context.type === \"qr\" && context.token === condition.secretToken;\n case \"passcode\":\n return (\n context.type === \"passcode\" &&\n (condition.caseSensitive === false\n ? context.code.toLocaleLowerCase() === condition.code.toLocaleLowerCase()\n : context.code === condition.code)\n );\n case \"gps\":\n return (\n context.type === \"gps\" &&\n distanceMeters(\n condition.latitude,\n condition.longitude,\n context.latitude,\n context.longitude,\n ) <= condition.radiusMeters\n );\n case \"custom\":\n return (\n customValidators?.[condition.validatorName]?.(\n context.type === \"custom\" ? context.value : undefined,\n condition,\n ) ?? false\n );\n }\n}\n\nfunction audit(\n request: UniversalCheckInRequest,\n status: RallyAuditLog[\"status\"],\n now: string,\n errorCode?: string,\n): RallyAuditLog {\n return {\n id: `audit-${globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random().toString(36).slice(2)}`}`,\n timestamp: now,\n rallyId: request.rallyId,\n userId: request.userId,\n action: \"CHECK_IN\",\n resourceId: request.spotId,\n status,\n idempotencyKey: request.idempotencyKey,\n ...(errorCode === undefined ? {} : { metadata: { errorCode } }),\n };\n}\n\n/** Atomic, adapter-backed check-in service for the universal model. */\nexport class UniversalRallyServer {\n readonly #config: AdminRallyConfig;\n readonly #persistence: ServerPersistenceAdapter;\n readonly #options: UniversalRallyServerOptions;\n\n constructor(\n config: AdminRallyConfig,\n persistence: ServerPersistenceAdapter,\n options: UniversalRallyServerOptions = {},\n ) {\n this.#config = config;\n this.#persistence = persistence;\n this.#options = options;\n }\n\n /** Web Standard endpoint handler. Authentication, when configured, supplies the user identity. */\n async handleCheckIn(request: Request): Promise<Response> {\n const body = await requestBody<UniversalCheckInRequest>(request);\n const userId = await this.#authenticatedUser(request, body?.userId);\n if (this.#options.authenticate !== undefined && userId === null)\n return json(\n { ok: false, error: { code: \"UNAUTHORIZED\", message: \"Authentication is required.\" } },\n 401,\n );\n if (\n body === null ||\n userId === null ||\n body.rallyId !== this.#config.id ||\n body.spotId === \"\" ||\n body.idempotencyKey === \"\"\n )\n return json(\n {\n ok: false,\n error: {\n code: \"INVALID_REQUEST\",\n message: \"rallyId, spotId, and idempotencyKey are required.\",\n },\n },\n 400,\n );\n if (body.context === undefined)\n return json(\n { ok: false, error: { code: \"INVALID_REQUEST\", message: \"context is required.\" } },\n 400,\n );\n const result = await this.checkIn({ ...body, userId });\n return result.ok ? json(result) : json(result, result.code === \"SPOT_NOT_FOUND\" ? 404 : 422);\n }\n\n async handleClaimReward(request: Request): Promise<Response> {\n const body = await requestBody<UniversalClaimRewardRequest>(request);\n const userId = await this.#authenticatedUser(request, body?.userId);\n if (this.#options.authenticate !== undefined && userId === null)\n return json(\n { ok: false, error: { code: \"UNAUTHORIZED\", message: \"Authentication is required.\" } },\n 401,\n );\n if (\n body === null ||\n userId === null ||\n body.rallyId !== this.#config.id ||\n body.rewardId === \"\" ||\n body.idempotencyKey === \"\"\n )\n return json(\n {\n ok: false,\n error: {\n code: \"INVALID_REQUEST\",\n message: \"rallyId, rewardId, and idempotencyKey are required.\",\n },\n },\n 400,\n );\n const key = `claim-reward:${body.rallyId}:${userId}:${body.rewardId}:${body.idempotencyKey}`;\n const previous = await this.#persistence.getIdempotentResult<UniversalClaimRewardResponse>(key);\n if (previous !== null) return json(previous, previous.ok ? 200 : 422);\n const reward = this.#config.rewards.find((item) => item.id === body.rewardId);\n if (reward === undefined) {\n const failure: UniversalClaimRewardResponse = {\n ok: false,\n code: \"REWARD_NOT_FOUND\",\n message: \"Reward was not found.\",\n };\n return json(await this.#rememberClaim(key, failure, body, userId), 404);\n }\n const timestamp = body.now ?? this.#options.now?.() ?? new Date().toISOString();\n const current = await this.sync(body.rallyId, userId);\n const currentReward = current.rewards?.find((item) => item.rewardId === reward.id) ?? {\n rewardId: reward.id,\n status: \"LOCKED\" as const,\n };\n const userClaims = await this.#getUserClaimCount(userId, reward.id);\n const local = consumeReward({\n reward: adminReward(reward),\n currentState: currentReward,\n now: timestamp,\n userId,\n userRedemptionCount: userClaims,\n ...(body.staffPasscode === undefined ? {} : { inputPasscode: body.staffPasscode }),\n ...(body.staffId === undefined ? {} : { staffId: body.staffId }),\n });\n if (!local.ok)\n return json(\n await this.#rememberClaim(\n key,\n {\n ok: false,\n code: local.error.code,\n message: \"Reward cannot be claimed.\",\n error: local.error,\n },\n body,\n userId,\n ),\n 422,\n );\n const stock = await this.#persistence.decrementRewardStock(reward.id);\n if (!stock.success)\n return json(\n await this.#rememberClaim(\n key,\n { ok: false, code: \"OUT_OF_STOCK\", message: \"Reward is out of stock.\" },\n body,\n userId,\n ),\n 422,\n );\n const next: UserRallyState = {\n ...current,\n rewards: (current.rewards ?? []).map((item) =>\n item.rewardId === reward.id ? local.value : item,\n ),\n updatedAt: timestamp,\n };\n await this.#persistence.saveUserState(body.rallyId, userId, next);\n const success: UniversalClaimRewardResponse =\n local.value.claimTicketNumber === undefined\n ? { ok: true, state: next }\n : { ok: true, state: next, claimTicketNumber: local.value.claimTicketNumber };\n await this.#persistence.saveIdempotentResult(\n key,\n success,\n this.#options.idempotencyTtlMs ?? 86_400_000,\n );\n await this.#persistence.recordAuditLog({\n id: `audit-${globalThis.crypto?.randomUUID?.() ?? Date.now()}`,\n timestamp,\n rallyId: body.rallyId,\n userId,\n action: \"CLAIM_REWARD\",\n resourceId: reward.id,\n status: \"SUCCESS\",\n idempotencyKey: body.idempotencyKey,\n });\n return json(success);\n }\n\n async handleSync(request: Request): Promise<Response> {\n const body = await requestBody<{ readonly rallyId: string; readonly userId?: string }>(request);\n const userId = await this.#authenticatedUser(request, body?.userId);\n if (this.#options.authenticate !== undefined && userId === null)\n return json(\n { ok: false, error: { code: \"UNAUTHORIZED\", message: \"Authentication is required.\" } },\n 401,\n );\n if (body === null || userId === null || body.rallyId !== this.#config.id)\n return json(\n { ok: false, error: { code: \"INVALID_REQUEST\", message: \"rallyId is required.\" } },\n 400,\n );\n return json({ ok: true, state: await this.sync(body.rallyId, userId) });\n }\n\n async handle(request: Request): Promise<Response> {\n if (request.method !== \"POST\")\n return json({ ok: false, error: { code: \"METHOD_NOT_ALLOWED\" } }, 405);\n const path = new URL(request.url).pathname;\n if (path.endsWith(\"/check-in\")) return this.handleCheckIn(request);\n if (path.endsWith(\"/claim-reward\")) return this.handleClaimReward(request);\n if (path.endsWith(\"/sync\")) return this.handleSync(request);\n return json({ ok: false, error: { code: \"NOT_FOUND\" } }, 404);\n }\n\n async checkIn(request: UniversalCheckInRequest): Promise<UniversalCheckInResult> {\n const key = `check-in:${request.rallyId}:${request.userId}:${request.idempotencyKey}`;\n const previous = await this.#persistence.getIdempotentResult<UniversalCheckInResult>(key);\n if (previous !== null) return previous;\n const lockKey = `state:${request.rallyId}:${request.userId}`;\n const locked = await this.#persistence.acquireLock(lockKey, this.#options.lockTtlMs ?? 5000);\n if (!locked)\n return { ok: false, code: \"CONFLICT\", message: \"The user state is being updated.\" };\n const now = request.now ?? this.#options.now?.() ?? new Date().toISOString();\n try {\n const spot = this.#config.spots.find((item) => item.id === request.spotId);\n if (spot === undefined)\n return this.#remember(\n key,\n { ok: false, code: \"SPOT_NOT_FOUND\", message: \"Spot was not found.\" },\n request,\n now,\n );\n const current =\n (await this.#persistence.getUserState(request.rallyId, request.userId)) ??\n initialState(this.#config, now);\n if (current.records.some((record) => record.stampId === request.spotId))\n return this.#remember(\n key,\n { ok: false, code: \"ALREADY_CLAIMED\", message: \"Spot was already claimed.\" },\n request,\n now,\n );\n const acquired = new Set(current.records.map((record) => record.stampId));\n if (spot.prerequisites?.some((id) => !acquired.has(id)))\n return this.#remember(\n key,\n {\n ok: false,\n code: \"PREREQUISITES_NOT_MET\",\n message: \"Prerequisite spots are not complete.\",\n },\n request,\n now,\n );\n if (\n !spot.conditions.every((condition) =>\n matchesCondition(condition, request.context, this.#options.customValidators),\n )\n )\n return this.#remember(\n key,\n { ok: false, code: \"INVALID_PROOF\", message: \"Verification failed.\" },\n request,\n now,\n );\n const state: UserRallyState = {\n ...current,\n records: [...current.records, { stampId: request.spotId, acquiredAt: now }],\n updatedAt: now,\n };\n await this.#persistence.saveUserState(request.rallyId, request.userId, state);\n return this.#remember(key, { ok: true, state }, request, now);\n } finally {\n await this.#persistence.releaseLock(lockKey);\n }\n }\n\n async sync(rallyId: string, userId: string): Promise<UserRallyState> {\n const now = this.#options.now?.() ?? new Date().toISOString();\n return (\n (await this.#persistence.getUserState(rallyId, userId)) ?? initialState(this.#config, now)\n );\n }\n\n async #authenticatedUser(\n request: Request,\n requestedUserId: string | undefined,\n ): Promise<string | null> {\n const authenticated = await this.#options.authenticate?.(request);\n if (this.#options.authenticate !== undefined) return authenticated ?? null;\n return requestedUserId ?? null;\n }\n\n async #getUserClaimCount(userId: string, rewardId: string): Promise<number> {\n const state = await this.#persistence.getUserState(this.#config.id, userId);\n return state?.rewards?.find((item) => item.rewardId === rewardId)?.userRedemptionCount ?? 0;\n }\n\n async #rememberClaim(\n key: string,\n result: UniversalClaimRewardResponse,\n request: UniversalClaimRewardRequest,\n userId: string,\n ): Promise<UniversalClaimRewardResponse> {\n await this.#persistence.saveIdempotentResult(\n key,\n result,\n this.#options.idempotencyTtlMs ?? 86_400_000,\n );\n await this.#persistence.recordAuditLog({\n id: `audit-${globalThis.crypto?.randomUUID?.() ?? Date.now()}`,\n timestamp: request.now ?? this.#options.now?.() ?? new Date().toISOString(),\n rallyId: request.rallyId,\n userId,\n action: \"CLAIM_REWARD\",\n resourceId: request.rewardId,\n status: \"REJECTED\",\n idempotencyKey: request.idempotencyKey,\n });\n return result;\n }\n\n async #remember(\n key: string,\n result: UniversalCheckInResult,\n request: UniversalCheckInRequest,\n now: string,\n ): Promise<UniversalCheckInResult> {\n await this.#persistence.recordAuditLog(\n audit(request, result.ok ? \"SUCCESS\" : \"REJECTED\", now, result.ok ? undefined : result.code),\n );\n await this.#persistence.saveIdempotentResult(\n key,\n result,\n this.#options.idempotencyTtlMs ?? 86_400_000,\n );\n return result;\n }\n}\n\ntype UniversalClaimRewardResponse =\n | { readonly ok: true; readonly state: UserRallyState; readonly claimTicketNumber?: string }\n | {\n readonly ok: false;\n readonly code: string;\n readonly message: string;\n readonly error?: unknown;\n };\n\nexport type { StampRallyState };\n","import {\n consumeReward,\n createSecureToken,\n processStamp,\n type RallyConfig,\n type RewardConsumeError,\n reconcileRewardStates,\n type SecureTokenSecretKey,\n type StampError,\n type StampRallyState,\n type StampRecord,\n type VerificationContext,\n} from \"@stamprally/core\";\n\nexport interface UserRallyState extends StampRallyState {\n readonly userId?: string;\n}\n\nexport interface RallyAuditLog {\n readonly id: string;\n readonly timestamp: string;\n readonly rallyId: string;\n readonly userId: string;\n readonly action: \"CHECK_IN\" | \"CLAIM_REWARD\";\n readonly resourceId: string;\n readonly status: \"SUCCESS\" | \"REJECTED\";\n readonly idempotencyKey: string;\n readonly proofData?: unknown;\n readonly metadata?: Readonly<Record<string, unknown>>;\n}\n\nexport interface ServerStorageAdapter {\n getRewardStock(rewardId: string): Promise<number | null>;\n decrementRewardStock(rewardId: string): Promise<boolean>;\n getUserClaims(userId: string, rewardId: string): Promise<number>;\n recordAuditLog(log: RallyAuditLog): Promise<void>;\n saveUserState(userId: string, state: UserRallyState): Promise<void>;\n getUserState(userId: string): Promise<UserRallyState | null>;\n}\n\nexport interface AdminRallyConfig extends RallyConfig {\n readonly secretKey: SecureTokenSecretKey;\n readonly proofTtlSeconds?: number;\n readonly authenticate?: (request: Request) => Promise<string | null> | string | null;\n}\n\nexport interface CheckInRequest {\n readonly userId: string;\n readonly spotId: string;\n readonly claimMethod: string;\n readonly proofData?: unknown;\n readonly idempotencyKey: string;\n}\n\nexport interface ClaimRewardRequest {\n readonly userId: string;\n readonly rewardId: string;\n readonly staffPasscode?: string;\n readonly idempotencyKey: string;\n readonly staffId?: string;\n}\n\nexport interface SyncRequest {\n readonly userId: string;\n readonly queue?: ReadonlyArray<SyncCheckInOperation>;\n readonly operations?: ReadonlyArray<SyncCheckInOperation>;\n}\n\nexport interface SyncCheckInOperation {\n readonly userId?: string;\n readonly spotId?: string;\n readonly stampId?: string;\n readonly claimMethod?: string;\n readonly proofData?: unknown;\n readonly context?: VerificationContext;\n readonly idempotencyKey: string;\n}\n\nexport interface StampClaimProof {\n readonly token: string;\n readonly rallyId: string;\n readonly userId: string;\n readonly spotId: string;\n readonly acquiredAt: string;\n}\n\nexport interface InMemoryServerStorageOptions {\n readonly stocks?: Readonly<Record<string, number>>;\n}\n\nexport class InMemoryServerStorage implements ServerStorageAdapter {\n readonly #states = new Map<string, UserRallyState>();\n readonly #stocks: Map<string, number>;\n readonly #claims = new Map<string, number>();\n readonly #auditLogs: RallyAuditLog[] = [];\n\n constructor(options: InMemoryServerStorageOptions = {}) {\n this.#stocks = new Map(Object.entries(options.stocks ?? {}));\n }\n\n async getRewardStock(rewardId: string): Promise<number | null> {\n return this.#stocks.get(rewardId) ?? null;\n }\n\n async decrementRewardStock(rewardId: string): Promise<boolean> {\n const stock = this.#stocks.get(rewardId);\n if (stock === undefined) return true;\n if (stock <= 0) return false;\n this.#stocks.set(rewardId, stock - 1);\n return true;\n }\n\n async getUserClaims(userId: string, rewardId: string): Promise<number> {\n return this.#claims.get(`${userId}:${rewardId}`) ?? 0;\n }\n\n async recordAuditLog(log: RallyAuditLog): Promise<void> {\n this.#auditLogs.push({ ...log });\n }\n\n async saveUserState(userId: string, state: UserRallyState): Promise<void> {\n this.#states.set(userId, cloneUserState(state));\n }\n\n async getUserState(userId: string): Promise<UserRallyState | null> {\n const state = this.#states.get(userId);\n return state === undefined ? null : cloneUserState(state);\n }\n\n getAuditLogs(): ReadonlyArray<RallyAuditLog> {\n return this.#auditLogs.map((log) => ({ ...log }));\n }\n\n recordClaim(userId: string, rewardId: string): void {\n const key = `${userId}:${rewardId}`;\n this.#claims.set(key, (this.#claims.get(key) ?? 0) + 1);\n }\n}\n\nfunction cloneUserState(state: UserRallyState): UserRallyState {\n return {\n ...state,\n records: state.records.map((record) => ({\n ...record,\n ...(record.metadata === undefined ? {} : { metadata: { ...record.metadata } }),\n })),\n ...(state.rewards === undefined\n ? {}\n : { rewards: state.rewards.map((reward) => ({ ...reward })) }),\n };\n}\n\nfunction jsonResponse(body: unknown, status = 200): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { \"content-type\": \"application/json; charset=utf-8\" },\n });\n}\n\nfunction errorResponse(code: string, message: string, status: number): Response {\n return jsonResponse({ ok: false, error: { code, message } }, status);\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction contextForClaim(method: string, proofData: unknown): VerificationContext {\n if (method === \"token\" || method === \"qr\" || method === \"passcode\") {\n return {\n type: \"token\",\n token:\n isObject(proofData) && typeof proofData.token === \"string\"\n ? proofData.token\n : String(proofData ?? \"\"),\n };\n }\n if (method === \"geo\" || method === \"geolocation\") {\n const value = isObject(proofData) ? proofData : {};\n return {\n type: \"geo\",\n currentLatitude:\n typeof value.latitude === \"number\"\n ? value.latitude\n : typeof value.currentLatitude === \"number\"\n ? value.currentLatitude\n : Number.NaN,\n currentLongitude:\n typeof value.longitude === \"number\"\n ? value.longitude\n : typeof value.currentLongitude === \"number\"\n ? value.currentLongitude\n : Number.NaN,\n };\n }\n return { type: \"instant\" };\n}\n\nfunction now(): string {\n return new Date().toISOString();\n}\n\nfunction hasText(value: unknown): value is string {\n return typeof value === \"string\" && value.trim() !== \"\";\n}\n\nfunction safeProofData(value: unknown): unknown {\n return isObject(value) && typeof value.token === \"string\" ? { type: \"token\" } : value;\n}\n\nfunction proofFromContext(context: VerificationContext | undefined): unknown {\n if (context?.type === \"token\") return { token: context.token };\n if (context?.type === \"geo\") {\n return { latitude: context.currentLatitude, longitude: context.currentLongitude };\n }\n return undefined;\n}\n\nfunction id(prefix: string): string {\n return `${prefix}-${globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random().toString(36).slice(2)}`}`;\n}\n\nexport class StampRallyServer {\n readonly #config: AdminRallyConfig;\n readonly #storage: ServerStorageAdapter;\n readonly #idempotent = new Map<string, Response>();\n readonly #claims = new Map<string, number>();\n #queue: Promise<unknown> = Promise.resolve();\n\n constructor(config: AdminRallyConfig, storage: ServerStorageAdapter) {\n this.#config = config;\n this.#storage = storage;\n }\n\n handle(request: Request): Promise<Response> {\n const path = new URL(request.url).pathname;\n if (request.method !== \"POST\")\n return Promise.resolve(errorResponse(\"METHOD_NOT_ALLOWED\", \"POST is required.\", 405));\n if (path.endsWith(\"/check-in\")) return this.verifyCheckIn(request);\n if (path.endsWith(\"/claim-reward\")) return this.claimReward(request);\n if (path.endsWith(\"/sync\")) return this.syncProgress(request);\n return Promise.resolve(errorResponse(\"NOT_FOUND\", \"Route not found.\", 404));\n }\n\n verifyCheckIn(request: Request): Promise<Response> {\n return this.#enqueue(() => this.#verifyCheckIn(request));\n }\n\n claimReward(request: Request): Promise<Response> {\n return this.#enqueue(() => this.#claimReward(request));\n }\n\n syncProgress(request: Request): Promise<Response> {\n return this.#enqueue(() => this.#syncProgress(request));\n }\n\n #enqueue<T>(operation: () => Promise<T>): Promise<T> {\n const next = this.#queue.then(operation, operation);\n this.#queue = next.then(\n () => undefined,\n () => undefined,\n );\n return next;\n }\n\n async #authenticate(request: Request): Promise<string | null> {\n return this.#config.authenticate === undefined ? null : this.#config.authenticate(request);\n }\n\n async #parse<T>(request: Request): Promise<T | null> {\n try {\n const value: unknown = await request.json();\n return isObject(value) ? (value as T) : null;\n } catch {\n return null;\n }\n }\n\n async #verifyCheckIn(request: Request): Promise<Response> {\n const authenticatedUser = await this.#authenticate(request);\n const body = await this.#parse<CheckInRequest>(request);\n const userId = body?.userId ?? authenticatedUser;\n if (\n userId === null ||\n userId === undefined ||\n body === null ||\n !hasText(userId) ||\n !hasText(body.spotId) ||\n !hasText(body.claimMethod) ||\n !hasText(body.idempotencyKey)\n ) {\n return errorResponse(\n \"INVALID_REQUEST\",\n \"userId, spotId, and idempotencyKey are required.\",\n 400,\n );\n }\n if (authenticatedUser !== null && authenticatedUser !== userId)\n return errorResponse(\"UNAUTHORIZED\", \"User identity does not match authentication.\", 401);\n const key = `check-in:${userId}:${body.idempotencyKey}`;\n const previous = this.#idempotent.get(key);\n if (previous !== undefined) return previous.clone();\n const timestamp = now();\n const current =\n (await this.#storage.getUserState(userId)) ?? emptyState(this.#config, timestamp);\n const result = processStamp(\n current,\n this.#config,\n body.spotId,\n contextForClaim(body.claimMethod, body.proofData),\n timestamp,\n );\n if (!result.ok) {\n await this.#audit(\n userId,\n \"CHECK_IN\",\n body.spotId,\n body.idempotencyKey,\n \"REJECTED\",\n safeProofData(body.proofData),\n result.error,\n );\n return this.#remember(key, jsonResponse({ ok: false, error: result.error }, 422));\n }\n const ttl = this.#config.proofTtlSeconds ?? 3600;\n const token = await createSecureToken(\n {\n type: \"stamp_claim\",\n rallyId: this.#config.id,\n userId,\n spotId: body.spotId,\n acquiredAt: timestamp,\n exp: Math.floor(Date.now() / 1000) + ttl,\n },\n this.#config.secretKey,\n { encrypt: true },\n );\n await this.#storage.saveUserState(userId, result.value.nextState);\n await this.#audit(\n userId,\n \"CHECK_IN\",\n body.spotId,\n body.idempotencyKey,\n \"SUCCESS\",\n safeProofData(body.proofData),\n );\n return this.#remember(\n key,\n jsonResponse({\n ok: true,\n state: result.value.nextState,\n proof: {\n token,\n rallyId: this.#config.id,\n userId,\n spotId: body.spotId,\n acquiredAt: timestamp,\n },\n }),\n );\n }\n\n async #claimReward(request: Request): Promise<Response> {\n const authenticatedUser = await this.#authenticate(request);\n const body = await this.#parse<ClaimRewardRequest>(request);\n const userId = body?.userId ?? authenticatedUser;\n if (\n userId === null ||\n userId === undefined ||\n body === null ||\n !hasText(userId) ||\n !hasText(body.rewardId) ||\n !hasText(body.idempotencyKey)\n )\n return errorResponse(\n \"INVALID_REQUEST\",\n \"userId, rewardId, and idempotencyKey are required.\",\n 400,\n );\n if (authenticatedUser !== null && authenticatedUser !== userId)\n return errorResponse(\"UNAUTHORIZED\", \"User identity does not match authentication.\", 401);\n const key = `claim-reward:${userId}:${body.rewardId}:${body.idempotencyKey}`;\n const previous = this.#idempotent.get(key);\n if (previous !== undefined) return previous.clone();\n const reward = this.#config.rewards?.find((item) => item.id === body.rewardId);\n if (reward === undefined) {\n await this.#audit(userId, \"CLAIM_REWARD\", body.rewardId, body.idempotencyKey, \"REJECTED\");\n return this.#remember(key, errorResponse(\"REWARD_NOT_FOUND\", \"Reward was not found.\", 404));\n }\n const timestamp = now();\n const current =\n (await this.#storage.getUserState(userId)) ?? emptyState(this.#config, timestamp);\n const rewardState = current.rewards?.find((item) => item.rewardId === reward.id);\n if (rewardState === undefined) {\n await this.#audit(userId, \"CLAIM_REWARD\", reward.id, body.idempotencyKey, \"REJECTED\");\n return this.#remember(key, errorResponse(\"NOT_AVAILABLE\", \"Reward is not available.\", 422));\n }\n const userClaims = Math.max(\n await this.#storage.getUserClaims(userId, reward.id),\n this.#claims.get(`${userId}:${reward.id}`) ?? 0,\n );\n const stock = await this.#storage.getRewardStock(reward.id);\n const userLimit = reward.userClaimLimit ?? reward.limitPerUser;\n const canReclaimServerReward =\n reward.redemptionMethod === \"server_claim\" &&\n (userLimit === undefined || userClaims < userLimit) &&\n (stock === null || stock > 0);\n const claimableState =\n canReclaimServerReward && rewardState.status === \"CONSUMED\"\n ? { ...rewardState, status: \"AVAILABLE\" as const }\n : rewardState;\n const local = consumeReward({\n reward,\n currentState: claimableState,\n now: timestamp,\n ...(body.staffPasscode === undefined ? {} : { inputPasscode: body.staffPasscode }),\n ...(body.staffId === undefined ? {} : { staffId: body.staffId }),\n userId,\n userRedemptionCount: userClaims,\n });\n if (!local.ok)\n return this.#remember(\n key,\n await this.#rewardError(userId, reward.id, body.idempotencyKey, local.error),\n );\n if (stock !== null && !(await this.#storage.decrementRewardStock(reward.id)))\n return this.#remember(\n key,\n await this.#rewardError(userId, reward.id, body.idempotencyKey, {\n code: \"OUT_OF_STOCK\",\n rewardId: reward.id,\n }),\n );\n const nextState: StampRallyState = {\n ...current,\n rewards: (current.rewards ?? []).map((item) =>\n item.rewardId === reward.id ? local.value : item,\n ),\n updatedAt: timestamp,\n };\n await this.#storage.saveUserState(userId, nextState);\n const claimKey = `${userId}:${reward.id}`;\n this.#claims.set(claimKey, userClaims + 1);\n if (this.#storage instanceof InMemoryServerStorage)\n this.#storage.recordClaim(userId, reward.id);\n await this.#audit(userId, \"CLAIM_REWARD\", reward.id, body.idempotencyKey, \"SUCCESS\", {\n staffId: body.staffId,\n });\n return this.#remember(\n key,\n jsonResponse({\n ok: true,\n state: nextState,\n claimTicketNumber: local.value.claimTicketNumber,\n }),\n );\n }\n\n async #rewardError(\n userId: string,\n rewardId: string,\n key: string,\n error: RewardConsumeError,\n ): Promise<Response> {\n await this.#audit(userId, \"CLAIM_REWARD\", rewardId, key, \"REJECTED\", undefined, error);\n return jsonResponse({ ok: false, error }, 422);\n }\n\n async #syncProgress(request: Request): Promise<Response> {\n const authenticatedUser = await this.#authenticate(request);\n const body = await this.#parse<SyncRequest>(request);\n if (body === null || body.userId === undefined)\n return errorResponse(\"INVALID_REQUEST\", \"userId is required.\", 400);\n if (authenticatedUser !== null && authenticatedUser !== body.userId)\n return errorResponse(\"UNAUTHORIZED\", \"User identity does not match authentication.\", 401);\n const queue = body.queue ?? body.operations ?? [];\n for (const operation of queue) {\n const userId = operation.userId ?? body.userId;\n const spotId = operation.spotId ?? operation.stampId;\n const claimMethod = operation.claimMethod ?? operation.context?.type;\n if (!hasText(userId) || !hasText(spotId) || !hasText(claimMethod)) continue;\n const synthetic = new Request(new URL(\"/api/check-in\", request.url), {\n method: \"POST\",\n body: JSON.stringify({\n userId,\n spotId,\n claimMethod,\n proofData: operation.proofData ?? proofFromContext(operation.context),\n idempotencyKey: operation.idempotencyKey,\n }),\n headers: { \"content-type\": \"application/json\" },\n });\n await this.#verifyCheckIn(synthetic);\n }\n const timestamp = now();\n const state =\n (await this.#storage.getUserState(body.userId)) ?? emptyState(this.#config, timestamp);\n await this.#storage.saveUserState(body.userId, state);\n return jsonResponse({ ok: true, state, accepted: queue.length });\n }\n\n async #audit(\n userId: string,\n action: RallyAuditLog[\"action\"],\n resourceId: string,\n idempotencyKey: string,\n status: RallyAuditLog[\"status\"],\n proofData?: unknown,\n error?: unknown,\n ): Promise<void> {\n await this.#storage.recordAuditLog({\n id: id(\"audit\"),\n timestamp: now(),\n rallyId: this.#config.id,\n userId,\n action,\n resourceId,\n status,\n idempotencyKey,\n ...(proofData === undefined ? {} : { proofData }),\n ...(error === undefined\n ? {}\n : {\n metadata: {\n errorCode: isObject(error) && typeof error.code === \"string\" ? error.code : \"UNKNOWN\",\n },\n }),\n });\n }\n\n #remember(key: string, response: Response): Response {\n this.#idempotent.set(key, response.clone());\n return response;\n }\n}\n\nfunction emptyState(config: RallyConfig, timestamp: string): UserRallyState {\n return {\n rallyId: config.id,\n records: [],\n ...(config.rewards === undefined\n ? {}\n : { rewards: reconcileRewardStates(config.rewards, [], 0, timestamp) }),\n updatedAt: timestamp,\n };\n}\n\nexport type { InMemoryServerPersistenceOptions, ServerPersistenceAdapter } from \"./persistence.js\";\nexport { InMemoryServerPersistenceAdapter } from \"./persistence.js\";\nexport type {\n UniversalCheckInRequest,\n UniversalCheckInResult,\n UniversalClaimRewardRequest,\n UniversalRallyServerOptions,\n UniversalVerificationContext,\n} from \"./universalServer.js\";\nexport { UniversalRallyServer } from \"./universalServer.js\";\nexport type { StampError, StampRecord };\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stamprally/server",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "Web Standard server handlers for stamp rally verification and claims.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -28,7 +28,7 @@
28
28
  }
29
29
  },
30
30
  "dependencies": {
31
- "@stamprally/core": "0.7.0"
31
+ "@stamprally/core": "0.8.0"
32
32
  },
33
33
  "devDependencies": {
34
34
  "tsup": "^8.5.1",