@stamprally/server 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/index.cjs +289 -118
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +93 -2
- package/dist/index.d.ts +93 -2
- package/dist/index.js +286 -119
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/persistence.ts","../src/server.ts"],"names":["now","reconcileRewardStates","evaluateConditionDetailed","consumeReward"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { UserRallyState } from \"@stamprally/core\";\nimport type { AuditLog } from \"./index.js\";\n\nexport interface UserClaimRecord {\n readonly rallyId: string;\n readonly userId: string;\n readonly rewardId: string;\n readonly ticketNumber: string;\n readonly timestamp: number;\n}\n\nexport interface ClaimRewardTransactionParams {\n readonly rallyId: string;\n readonly userId: string;\n readonly rewardId: string;\n readonly ticketNumber: string;\n readonly timestamp: number;\n readonly idempotencyKey?: string;\n readonly proofData?: unknown;\n readonly idempotencyTtlMs?: number;\n /** Used when the first claim is made before a user state has been stored. */\n readonly initialUserState?: UserRallyState;\n}\n\nexport interface ClaimRewardTransactionMutation {\n readonly nextStock: number | null;\n readonly nextUserState: UserRallyState;\n readonly auditLog: AuditLog;\n /** The server response is persisted by the adapter with the idempotency key. */\n readonly result?: unknown;\n /** A domain rejection is committed as an audit/idempotency record without mutations. */\n readonly error?: string;\n}\n\nexport interface ServerPersistenceAdapter {\n /**\n * Atomically commits stock, user state, claim count, audit log, and idempotency data.\n * Adapters must roll back every write when the callback or any commit operation fails.\n */\n executeClaimRewardTransaction(\n params: ClaimRewardTransactionParams,\n mutation: (current: {\n readonly stock: number | null;\n readonly claimCount: number;\n readonly userState: UserRallyState;\n }) => ClaimRewardTransactionMutation,\n ): Promise<{ readonly success: boolean; readonly error?: string }>;\n acquireLock(rallyId: string, lockKey: string, ttlMs: number): Promise<boolean>;\n releaseLock(rallyId: string, lockKey: string): Promise<void>;\n getRewardStock(rallyId: string, rewardId: string): Promise<number | null>;\n decrementRewardStock(\n rallyId: string,\n rewardId: string,\n ): Promise<{ readonly success: boolean; readonly remainingStock: number }>;\n getIdempotentResult<T>(rallyId: string, key: string): Promise<T | null>;\n saveIdempotentResult<T>(rallyId: string, key: string, result: T, ttlMs: number): Promise<void>;\n getUserClaimCount(rallyId: string, userId: string, rewardId: string): Promise<number>;\n recordUserClaim(params: UserClaimRecord): Promise<void>;\n recordClaim?(params: UserClaimRecord): Promise<void>;\n incrementUserClaimCount?(rallyId: string, userId: string, rewardId: string): Promise<void>;\n getUserState(rallyId: string, userId: string): Promise<UserRallyState | null>;\n saveUserState(rallyId: string, userId: string, state: UserRallyState): Promise<void>;\n recordAuditLog(log: AuditLog): Promise<void>;\n removeAuditLog?(auditId: string): Promise<void>;\n}\ninterface TimedValue<T> {\n readonly value: T;\n readonly expiresAt: number;\n}\nexport interface InMemoryServerPersistenceOptions {\n readonly stocks?: Readonly<Record<string, number>>;\n}\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 #stockDefaults: Map<string, number>;\n readonly #claims = new Map<string, number>();\n readonly #claimRecords: UserClaimRecord[] = [];\n readonly #auditLogs: AuditLog[] = [];\n constructor(options: InMemoryServerPersistenceOptions = {}) {\n this.#stocks = new Map();\n this.#stockDefaults = new Map();\n for (const [key, stock] of Object.entries(options.stocks ?? {})) {\n if (key.includes(\":\")) this.#stocks.set(key, stock);\n else this.#stockDefaults.set(key, stock);\n }\n }\n #key(rallyId: string, key: string): string {\n return `${rallyId}:${key}`;\n }\n #stockKey(rallyId: string, rewardId: string): string {\n const scopedKey = `${rallyId}:${rewardId}`;\n if (this.#stocks.has(scopedKey)) return scopedKey;\n const defaultStock = this.#stockDefaults.get(rewardId);\n if (defaultStock !== undefined) {\n this.#stocks.set(scopedKey, defaultStock);\n return scopedKey;\n }\n return scopedKey;\n }\n async acquireLock(rallyId: string, key: string, ttlMs: number): Promise<boolean> {\n const scopedKey = this.#key(rallyId, key);\n const now = Date.now();\n const until = this.#locks.get(scopedKey);\n if (until !== undefined && until > now) return false;\n this.#locks.set(scopedKey, now + Math.max(1, ttlMs));\n return true;\n }\n async releaseLock(rallyId: string, key: string): Promise<void> {\n this.#locks.delete(this.#key(rallyId, key));\n }\n async getRewardStock(rallyId: string, rewardId: string): Promise<number | null> {\n return this.#stocks.get(this.#stockKey(rallyId, rewardId)) ?? null;\n }\n async decrementRewardStock(\n rallyId: string,\n rewardId: string,\n ): Promise<{ success: boolean; remainingStock: number }> {\n const key = this.#stockKey(rallyId, rewardId);\n const current = this.#stocks.get(key);\n if (current === undefined) return { success: true, remainingStock: Number.POSITIVE_INFINITY };\n if (current <= 0) return { success: false, remainingStock: 0 };\n this.#stocks.set(key, current - 1);\n return { success: true, remainingStock: current - 1 };\n }\n async restoreRewardStock(rallyId: string, rewardId: string, count = 1): Promise<void> {\n const key = this.#stockKey(rallyId, rewardId);\n const current = this.#stocks.get(key);\n if (current !== undefined) this.#stocks.set(key, current + Math.max(0, count));\n }\n\n async executeClaimRewardTransaction(\n params: ClaimRewardTransactionParams,\n mutation: (current: {\n readonly stock: number | null;\n readonly claimCount: number;\n readonly userState: UserRallyState;\n }) => ClaimRewardTransactionMutation,\n ): Promise<{ readonly success: boolean; readonly error?: string }> {\n try {\n return await this.runTransaction(params.rallyId, async () => {\n const userState =\n (await this.getUserState(params.rallyId, params.userId)) ?? params.initialUserState;\n if (userState === undefined)\n return { success: false, error: \"A user state is required for this transaction.\" };\n const mutationResult = mutation({\n stock: await this.getRewardStock(params.rallyId, params.rewardId),\n claimCount: await this.getUserClaimCount(params.rallyId, params.userId, params.rewardId),\n userState,\n });\n const idempotencyKey = params.idempotencyKey;\n if (mutationResult.error !== undefined) {\n await this.recordAuditLog(mutationResult.auditLog);\n if (idempotencyKey !== undefined && mutationResult.result !== undefined)\n await this.saveIdempotentResult(\n params.rallyId,\n idempotencyKey,\n mutationResult.result,\n params.idempotencyTtlMs ?? 86_400_000,\n );\n return { success: false, error: mutationResult.error };\n }\n const currentStock = await this.getRewardStock(params.rallyId, params.rewardId);\n if (\n currentStock !== null &&\n (mutationResult.nextStock === null || mutationResult.nextStock < 0)\n )\n throw new Error(\"The transaction produced an invalid stock value.\");\n if (currentStock === null && mutationResult.nextStock !== null)\n throw new Error(\"The transaction changed an unlimited stock to a limited stock.\");\n if (mutationResult.nextStock !== null)\n this.#stocks.set(\n this.#stockKey(params.rallyId, params.rewardId),\n mutationResult.nextStock,\n );\n await this.saveUserState(params.rallyId, params.userId, mutationResult.nextUserState);\n const reward = mutationResult.nextUserState.rewards.find(\n (item) => item.rewardId === params.rewardId,\n );\n if (reward?.claimTicketNumber !== undefined)\n await this.recordUserClaim({\n rallyId: params.rallyId,\n userId: params.userId,\n rewardId: params.rewardId,\n ticketNumber: reward.claimTicketNumber,\n timestamp: params.timestamp,\n });\n await this.recordAuditLog(mutationResult.auditLog);\n if (idempotencyKey !== undefined && mutationResult.result !== undefined)\n await this.saveIdempotentResult(\n params.rallyId,\n idempotencyKey,\n mutationResult.result,\n params.idempotencyTtlMs ?? 86_400_000,\n );\n return { success: true };\n });\n } catch (error) {\n return { success: false, error: error instanceof Error ? error.message : String(error) };\n }\n }\n\n async rollbackUserState(\n rallyId: string,\n userId: string,\n previousState: UserRallyState | null,\n ): Promise<void> {\n const key = `${rallyId}:${userId}`;\n if (previousState === null) this.#states.delete(key);\n else this.#states.set(key, structuredClone(previousState));\n }\n async getIdempotentResult<T>(rallyId: string, key: string): Promise<T | null> {\n const value = this.#idempotent.get(this.#key(rallyId, key));\n if (value === undefined || value.expiresAt <= Date.now()) {\n this.#idempotent.delete(this.#key(rallyId, key));\n return null;\n }\n return structuredClone(value.value) as T;\n }\n async saveIdempotentResult<T>(\n rallyId: string,\n key: string,\n result: T,\n ttlMs: number,\n ): Promise<void> {\n this.#idempotent.set(this.#key(rallyId, key), {\n value: structuredClone(result),\n expiresAt: Date.now() + Math.max(1, ttlMs),\n });\n }\n async getUserClaimCount(rallyId: string, userId: string, rewardId: string): Promise<number> {\n return this.#claims.get(`${rallyId}:${userId}:${rewardId}`) ?? 0;\n }\n async getUserState(rallyId: string, userId: string): Promise<UserRallyState | null> {\n const value = this.#states.get(`${rallyId}:${userId}`);\n return value === undefined ? null : structuredClone(value);\n }\n async saveUserState(rallyId: string, userId: string, state: UserRallyState): Promise<void> {\n this.#states.set(`${rallyId}:${userId}`, structuredClone(state));\n }\n async recordAuditLog(log: AuditLog): Promise<void> {\n this.#auditLogs.push(structuredClone(log));\n }\n async removeAuditLog(auditId: string): Promise<void> {\n const index = this.#auditLogs.findIndex((log) => log.id === auditId);\n if (index >= 0) this.#auditLogs.splice(index, 1);\n }\n getAuditLogs(): ReadonlyArray<AuditLog> {\n return structuredClone(this.#auditLogs);\n }\n async recordUserClaim(params: UserClaimRecord): Promise<void> {\n const { rallyId, userId, rewardId } = params;\n const key = `${rallyId}:${userId}:${rewardId}`;\n this.#claims.set(key, (this.#claims.get(key) ?? 0) + 1);\n this.#claimRecords.push(structuredClone(params));\n }\n async rollbackUserClaim(\n rallyId: string,\n userId: string,\n rewardId: string,\n ticketNumber: string,\n ): Promise<void> {\n const key = `${rallyId}:${userId}:${rewardId}`;\n const count = this.#claims.get(key) ?? 0;\n if (count <= 1) this.#claims.delete(key);\n else this.#claims.set(key, count - 1);\n const index = this.#claimRecords.findIndex(\n (record) =>\n record.rallyId === rallyId &&\n record.userId === userId &&\n record.rewardId === rewardId &&\n record.ticketNumber === ticketNumber,\n );\n if (index >= 0) this.#claimRecords.splice(index, 1);\n }\n async incrementUserClaimCount(rallyId: string, userId: string, rewardId: string): Promise<void> {\n const key = `${rallyId}:${userId}:${rewardId}`;\n this.#claims.set(key, (this.#claims.get(key) ?? 0) + 1);\n }\n getClaimRecords(): ReadonlyArray<UserClaimRecord> {\n return structuredClone(this.#claimRecords);\n }\n /** @deprecated Use recordUserClaim with a ticket number and timestamp. */\n recordClaim(params: UserClaimRecord): Promise<void>;\n recordClaim(rallyId: string, userId: string, rewardId: string): Promise<void>;\n recordClaim(\n paramsOrRallyId: UserClaimRecord | string,\n userId?: string,\n rewardId?: string,\n ): Promise<void> {\n const params: UserClaimRecord =\n typeof paramsOrRallyId === \"string\"\n ? {\n rallyId: paramsOrRallyId,\n userId: userId ?? \"\",\n rewardId: rewardId ?? \"\",\n ticketNumber: \"\",\n timestamp: Date.now(),\n }\n : paramsOrRallyId;\n return this.recordUserClaim(params);\n }\n\n async runTransaction<T>(\n _rallyId: string,\n operation: (transaction: ServerPersistenceAdapter) => Promise<T>,\n ): Promise<T> {\n const snapshot = {\n stocks: new Map(this.#stocks),\n idempotent: new Map(this.#idempotent),\n states: new Map(this.#states),\n claims: new Map(this.#claims),\n claimRecords: structuredClone(this.#claimRecords),\n auditLogs: structuredClone(this.#auditLogs),\n };\n try {\n return await operation(this);\n } catch (error) {\n this.#stocks.clear();\n for (const [key, value] of snapshot.stocks) this.#stocks.set(key, value);\n this.#idempotent.clear();\n for (const [key, value] of snapshot.idempotent)\n this.#idempotent.set(key, structuredClone(value));\n this.#states.clear();\n for (const [key, value] of snapshot.states) this.#states.set(key, structuredClone(value));\n this.#claims.clear();\n for (const [key, value] of snapshot.claims) this.#claims.set(key, value);\n this.#claimRecords.splice(0, this.#claimRecords.length, ...snapshot.claimRecords);\n this.#auditLogs.splice(0, this.#auditLogs.length, ...snapshot.auditLogs);\n throw error;\n }\n }\n}\n","import {\n type AdminRallyConfig,\n type CheckInCondition,\n consumeReward,\n evaluateConditionDetailed,\n reconcileRewardStates,\n type UserRallyState,\n type Validator,\n type VerificationContext,\n} from \"@stamprally/core\";\nimport type {\n AuditLog,\n CheckInRequest,\n CheckInResponse,\n ClaimRewardRequest,\n ServerOptions,\n} from \"./index.js\";\nimport type { ServerPersistenceAdapter } from \"./persistence.js\";\n\ntype ErrorResponse = { readonly ok: false; readonly code: string; readonly message: string };\ntype ClaimResponse =\n | { readonly ok: true; readonly state: UserRallyState; readonly claimTicketNumber?: string }\n | ErrorResponse;\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}\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\nfunction requestId(prefix: string): string {\n return `${prefix}-${globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random().toString(36).slice(2)}`}`;\n}\nfunction now(options: ServerOptions, requested?: string): string {\n return requested ?? options.now?.() ?? new Date().toISOString();\n}\nfunction initialState(config: AdminRallyConfig, userId: string, timestamp: string): UserRallyState {\n return {\n rallyId: config.id,\n userId,\n records: [],\n rewards: reconcileRewardStates(config.rewards, [], 0, timestamp),\n updatedAt: timestamp,\n };\n}\nfunction getProof(context: VerificationContext): unknown {\n return context.type === \"qr\"\n ? context.token\n : context.type === \"passcode\"\n ? context.code\n : context.type === \"gps\"\n ? { latitude: context.latitude, longitude: context.longitude }\n : context.type === \"nfc\"\n ? context.tagId\n : context.value;\n}\nasync function evaluate(\n condition: CheckInCondition,\n context: VerificationContext,\n validator: Validator | undefined,\n base: { rallyId: string; spotId: string; state: UserRallyState },\n): Promise<boolean> {\n if (condition.type !== \"custom\") return evaluateConditionDetailed(condition, context).ok;\n if (validator === undefined) return false;\n const validationContext = {\n rallyId: base.rallyId,\n spotId: base.spotId,\n proofData: getProof(context),\n condition,\n userState: base.state,\n };\n const result =\n typeof validator === \"function\"\n ? await validator(validationContext)\n : await validator.validate(validationContext);\n return result === true || (typeof result === \"object\" && result.valid);\n}\nfunction audit(\n rallyId: string,\n userId: string,\n action: AuditLog[\"action\"],\n resourceId: string,\n key: string,\n status: AuditLog[\"status\"],\n timestamp: string,\n code?: string,\n): AuditLog {\n return {\n id: requestId(\"audit\"),\n timestamp,\n rallyId,\n userId,\n action,\n resourceId,\n status,\n idempotencyKey: key,\n ...(code === undefined ? {} : { metadata: { errorCode: code } }),\n };\n}\n\nexport class StampRallyServer {\n readonly #config: AdminRallyConfig;\n readonly #persistence: ServerPersistenceAdapter;\n readonly #options: ServerOptions;\n constructor(\n config: AdminRallyConfig,\n persistence: ServerPersistenceAdapter,\n options: ServerOptions = {},\n ) {\n this.#config = config;\n this.#persistence = persistence;\n this.#options = options;\n }\n async handle(request: Request): Promise<Response> {\n if (request.method !== \"POST\")\n return json({ ok: false, code: \"METHOD_NOT_ALLOWED\", message: \"POST is required.\" }, 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, code: \"NOT_FOUND\", message: \"Route not found.\" }, 404);\n }\n async handleCheckIn(request: Request): Promise<Response> {\n const body = await this.#body<CheckInRequest>(request);\n const userId = await this.#user(request, body?.userId);\n if (\n body === null ||\n userId === null ||\n body.rallyId !== this.#config.id ||\n body.spotId === \"\" ||\n body.idempotencyKey === \"\" ||\n body.context === undefined\n )\n return json(\n {\n ok: false,\n code: \"INVALID_REQUEST\",\n message: \"rallyId, spotId, context, and idempotencyKey are required.\",\n },\n 400,\n );\n const result = await this.checkIn({ ...body, userId });\n return json(result, result.ok ? 200 : result.code === \"SPOT_NOT_FOUND\" ? 404 : 422);\n }\n async handleClaimReward(request: Request): Promise<Response> {\n const body = await this.#body<ClaimRewardRequest>(request);\n const userId = await this.#user(request, body?.userId);\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 code: \"INVALID_REQUEST\",\n message: \"rallyId, rewardId, and idempotencyKey are required.\",\n },\n 400,\n );\n const result = await this.claimReward({ ...body, userId });\n return json(result, result.ok ? 200 : result.code === \"REWARD_NOT_FOUND\" ? 404 : 422);\n }\n async handleSync(request: Request): Promise<Response> {\n const body = await this.#body<{ readonly rallyId: string; readonly userId?: string }>(request);\n const userId = await this.#user(request, body?.userId);\n if (body === null || userId === null || body.rallyId !== this.#config.id)\n return json({ ok: false, code: \"INVALID_REQUEST\", message: \"rallyId is required.\" }, 400);\n return json({ ok: true, state: await this.sync(body.rallyId, userId) });\n }\n async checkIn(request: CheckInRequest & { readonly userId: string }): Promise<CheckInResponse> {\n const key = `check-in:${request.rallyId}:${request.userId}:${request.idempotencyKey}`;\n const previous = await this.#persistence.getIdempotentResult<CheckInResponse>(\n request.rallyId,\n key,\n );\n if (previous !== null) return previous;\n const lockKey = `state:${request.rallyId}:${request.userId}`;\n if (\n !(await this.#persistence.acquireLock(\n request.rallyId,\n lockKey,\n this.#options.lockTtlMs ?? 5_000,\n ))\n )\n return { ok: false, code: \"CONFLICT\", message: \"The user state is being updated.\" };\n const timestamp = now(this.#options, request.now);\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.rallyId,\n request.userId,\n \"CHECK_IN\",\n request.spotId,\n request.idempotencyKey,\n timestamp,\n );\n const current =\n (await this.#persistence.getUserState(request.rallyId, request.userId)) ??\n initialState(this.#config, request.userId, timestamp);\n if (current.records.some((record) => record.stampId === request.spotId))\n return this.#remember(\n key,\n { ok: false, code: \"STAMP_ALREADY_ACQUIRED\", message: \"Spot was already claimed.\" },\n request.rallyId,\n request.userId,\n \"CHECK_IN\",\n request.spotId,\n request.idempotencyKey,\n timestamp,\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.rallyId,\n request.userId,\n \"CHECK_IN\",\n request.spotId,\n request.idempotencyKey,\n timestamp,\n );\n for (const condition of spot.conditions)\n if (\n !(await evaluate(\n condition,\n request.context,\n condition.type === \"custom\"\n ? this.#options.customValidators?.[condition.validatorName]\n : undefined,\n { rallyId: request.rallyId, spotId: request.spotId, state: current },\n ))\n )\n return this.#remember(\n key,\n { ok: false, code: \"INVALID_PROOF\", message: \"Verification failed.\" },\n request.rallyId,\n request.userId,\n \"CHECK_IN\",\n request.spotId,\n request.idempotencyKey,\n timestamp,\n );\n const next: UserRallyState = {\n ...current,\n records: [...current.records, { stampId: request.spotId, acquiredAt: timestamp }],\n rewards: reconcileRewardStates(\n this.#config.rewards,\n current.rewards,\n current.records.length + 1,\n timestamp,\n ),\n updatedAt: timestamp,\n };\n await this.#persistence.saveUserState(request.rallyId, request.userId, next);\n return this.#remember(\n key,\n { ok: true, state: next },\n request.rallyId,\n request.userId,\n \"CHECK_IN\",\n request.spotId,\n request.idempotencyKey,\n timestamp,\n );\n } finally {\n await this.#persistence.releaseLock(request.rallyId, lockKey);\n }\n }\n async claimReward(\n request: ClaimRewardRequest & { readonly userId: string },\n ): Promise<ClaimResponse> {\n const key = `claim:${request.rallyId}:${request.userId}:${request.rewardId}:${request.idempotencyKey}`;\n const previous = await this.#persistence.getIdempotentResult<ClaimResponse>(\n request.rallyId,\n key,\n );\n if (previous !== null) return previous;\n const reward = this.#config.rewards.find((item) => item.id === request.rewardId);\n if (reward === undefined)\n return this.#rememberClaim(\n key,\n { ok: false, code: \"REWARD_NOT_FOUND\", message: \"Reward was not found.\" },\n request,\n now(this.#options, request.now),\n );\n const lockKey = `reward:${request.rallyId}:${reward.id}`;\n if (\n !(await this.#persistence.acquireLock(\n request.rallyId,\n lockKey,\n this.#options.lockTtlMs ?? 5_000,\n ))\n )\n return { ok: false, code: \"CONFLICT\", message: \"The reward is being claimed.\" };\n const timestamp = now(this.#options, request.now);\n try {\n const checked = await this.#persistence.getIdempotentResult<ClaimResponse>(\n request.rallyId,\n key,\n );\n if (checked !== null) return checked;\n const responseHolder: { value: ClaimResponse | null } = { value: null };\n const result = await this.#persistence.executeClaimRewardTransaction(\n {\n rallyId: request.rallyId,\n userId: request.userId,\n rewardId: reward.id,\n ticketNumber: request.idempotencyKey,\n timestamp: Number.isNaN(Date.parse(timestamp)) ? Date.now() : Date.parse(timestamp),\n idempotencyKey: key,\n ...(request.staffPasscode === undefined ? {} : { proofData: request.staffPasscode }),\n ...(this.#options.idempotencyTtlMs === undefined\n ? {}\n : { idempotencyTtlMs: this.#options.idempotencyTtlMs }),\n initialUserState: initialState(this.#config, request.userId, timestamp),\n },\n ({ stock, claimCount, userState }) => {\n const storedReward = userState.rewards.find((item) => item.rewardId === reward.id) ?? {\n rewardId: reward.id,\n status: \"LOCKED\" as const,\n };\n const currentReward =\n reward.redemptionMethod === \"server_claim\" &&\n storedReward.status === \"CONSUMED\" &&\n (reward.userClaimLimit === undefined || claimCount < reward.userClaimLimit)\n ? { ...storedReward, status: \"AVAILABLE\" as const }\n : storedReward;\n const makeAudit = (status: \"SUCCESS\" | \"REJECTED\", code?: string): AuditLog =>\n audit(\n request.rallyId,\n request.userId,\n \"CLAIM_REWARD\",\n reward.id,\n request.idempotencyKey,\n status,\n timestamp,\n code,\n );\n if (stock !== null && stock <= 0) {\n responseHolder.value = {\n ok: false,\n code: \"OUT_OF_STOCK\",\n message: \"Reward is out of stock.\",\n };\n return {\n nextStock: stock,\n nextUserState: userState,\n auditLog: makeAudit(\"REJECTED\", \"OUT_OF_STOCK\"),\n result: responseHolder.value,\n error: \"OUT_OF_STOCK\",\n };\n }\n const local = consumeReward({\n reward,\n currentState: currentReward,\n now: timestamp,\n userRedemptionCount: claimCount,\n ...(request.staffPasscode === undefined\n ? {}\n : { inputPasscode: request.staffPasscode }),\n ...(request.staffId === undefined ? {} : { staffId: request.staffId }),\n });\n if (!local.ok) {\n responseHolder.value = {\n ok: false,\n code: local.error.code,\n message: \"Reward cannot be claimed.\",\n };\n return {\n nextStock: stock,\n nextUserState: userState,\n auditLog: makeAudit(\"REJECTED\", local.error.code),\n result: responseHolder.value,\n error: local.error.code,\n };\n }\n const nextRewards = userState.rewards.some((item) => item.rewardId === reward.id)\n ? userState.rewards.map((item) => (item.rewardId === reward.id ? local.value : item))\n : [...userState.rewards, local.value];\n const next: UserRallyState = {\n ...userState,\n rewards: nextRewards,\n updatedAt: timestamp,\n };\n responseHolder.value =\n local.value.claimTicketNumber === undefined\n ? { ok: true, state: next }\n : { ok: true, state: next, claimTicketNumber: local.value.claimTicketNumber };\n return {\n nextStock: stock === null ? null : stock - 1,\n nextUserState: next,\n auditLog: makeAudit(\"SUCCESS\"),\n result: responseHolder.value,\n };\n },\n );\n const response = responseHolder.value;\n if (!result.success) {\n if (response !== null && !response.ok && response.code === result.error) return response;\n return {\n ok: false,\n code: \"PERSISTENCE_FAILED\",\n message: result.error ?? \"Reward claim failed.\",\n };\n }\n if (response !== null && result.success) return response;\n return {\n ok: false,\n code: \"PERSISTENCE_FAILED\",\n message: result.error ?? \"Reward claim failed.\",\n };\n } catch (error) {\n return {\n ok: false,\n code: \"PERSISTENCE_FAILED\",\n message: error instanceof Error ? error.message : \"Reward claim failed.\",\n };\n } finally {\n await this.#persistence.releaseLock(request.rallyId, lockKey);\n }\n }\n async sync(rallyId: string, userId: string): Promise<UserRallyState> {\n return (\n (await this.#persistence.getUserState(rallyId, userId)) ??\n initialState(this.#config, userId, now(this.#options))\n );\n }\n async #body<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 async #user(request: Request, requested: string | undefined): Promise<string | null> {\n if (this.#options.authenticate !== undefined)\n return (await this.#options.authenticate(request)) ?? null;\n return requested ?? null;\n }\n async #remember(\n key: string,\n result: CheckInResponse,\n rallyId: string,\n userId: string,\n action: AuditLog[\"action\"],\n resourceId: string,\n idempotencyKey: string,\n timestamp: string,\n ): Promise<CheckInResponse> {\n await this.#persistence.recordAuditLog(\n audit(\n rallyId,\n userId,\n action,\n resourceId,\n idempotencyKey,\n result.ok ? \"SUCCESS\" : \"REJECTED\",\n timestamp,\n result.ok ? undefined : result.code,\n ),\n );\n await this.#persistence.saveIdempotentResult(\n rallyId,\n key,\n result,\n this.#options.idempotencyTtlMs ?? 86_400_000,\n );\n return result;\n }\n async #rememberClaim(\n key: string,\n result: ClaimResponse,\n request: ClaimRewardRequest & { readonly userId: string },\n timestamp: string,\n ): Promise<ClaimResponse> {\n await this.#persistence.recordAuditLog(\n audit(\n request.rallyId,\n request.userId,\n \"CLAIM_REWARD\",\n request.rewardId,\n request.idempotencyKey,\n \"REJECTED\",\n timestamp,\n result.ok ? undefined : result.code,\n ),\n );\n await this.#persistence.saveIdempotentResult(\n request.rallyId,\n key,\n result,\n this.#options.idempotencyTtlMs ?? 86_400_000,\n );\n return result;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/examples/transaction.ts","../src/persistence.ts","../src/security.ts","../src/server.ts"],"names":["error","now","record","reconcileRewardStates","context","evaluateConditionDetailed","consumeReward"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { UserRallyState } from \"@stamprally/core\";\nimport type { AuditLog } from \"../index.js\";\nimport type {\n ClaimRewardTransactionMutation,\n ClaimRewardTransactionParams,\n} from \"../persistence.js\";\n\n/**\n * Minimal database contract for the SQL reference implementation.\n *\n * `readContext` should use SELECT ... FOR UPDATE for the stock, claim-count,\n * and user-state rows. The adapter's transaction implementation must commit\n * or roll back every operation performed through the transaction handle.\n */\nexport interface SqlTransactionDatabase<Tx> {\n transaction<T>(operation: (transaction: Tx) => Promise<T>): Promise<T>;\n}\n\nexport interface SqlClaimRewardStore<Tx> {\n readContext(\n transaction: Tx,\n params: ClaimRewardTransactionParams,\n ): Promise<{\n readonly stock: number | null;\n readonly claimCount: number;\n readonly userState: UserRallyState;\n }>;\n writeStock(\n transaction: Tx,\n params: ClaimRewardTransactionParams,\n nextStock: number,\n ): Promise<void>;\n writeUserState(\n transaction: Tx,\n params: ClaimRewardTransactionParams,\n state: UserRallyState,\n ): Promise<void>;\n writeClaimRecord(\n transaction: Tx,\n params: ClaimRewardTransactionParams,\n state: UserRallyState,\n ): Promise<void>;\n writeAudit(transaction: Tx, log: AuditLog): Promise<void>;\n writeIdempotency(\n transaction: Tx,\n params: ClaimRewardTransactionParams,\n result: unknown,\n ): Promise<void>;\n}\n\n/**\n * Reference implementation for PostgreSQL, MySQL, or another transactional\n * SQL store. Domain rejections still write their audit/idempotency records,\n * while unexpected write failures reject the transaction and roll everything\n * back.\n */\nexport async function executeClaimRewardTransaction<Tx>(\n database: SqlTransactionDatabase<Tx>,\n store: SqlClaimRewardStore<Tx>,\n params: ClaimRewardTransactionParams,\n mutation: (current: {\n readonly stock: number | null;\n readonly claimCount: number;\n readonly userState: UserRallyState;\n }) => ClaimRewardTransactionMutation,\n): Promise<{ readonly success: boolean; readonly error?: string }> {\n try {\n return await database.transaction(async (transaction) => {\n const current = await store.readContext(transaction, params);\n const next = mutation(current);\n if (next.error !== undefined) {\n await store.writeAudit(transaction, next.auditLog);\n if (params.idempotencyKey !== undefined && next.result !== undefined)\n await store.writeIdempotency(transaction, params, next.result);\n return { success: false, error: next.error };\n }\n if (next.nextStock !== null) await store.writeStock(transaction, params, next.nextStock);\n await store.writeUserState(transaction, params, next.nextUserState);\n await store.writeClaimRecord(transaction, params, next.nextUserState);\n await store.writeAudit(transaction, next.auditLog);\n if (params.idempotencyKey !== undefined && next.result !== undefined)\n await store.writeIdempotency(transaction, params, next.result);\n return { success: true };\n });\n } catch (error) {\n return { success: false, error: error instanceof Error ? error.message : String(error) };\n }\n}\n","import type { UserRallyState } from \"@stamprally/core\";\nimport type { AuditLog } from \"./index.js\";\n\nexport interface UserClaimRecord {\n readonly rallyId: string;\n readonly userId: string;\n readonly rewardId: string;\n readonly ticketNumber: string;\n readonly timestamp: number;\n}\n\nexport interface ClaimRewardTransactionParams {\n readonly rallyId: string;\n readonly userId: string;\n readonly rewardId: string;\n readonly ticketNumber: string;\n readonly timestamp: number;\n readonly idempotencyKey?: string;\n readonly proofData?: unknown;\n readonly idempotencyTtlMs?: number;\n /** Used when the first claim is made before a user state has been stored. */\n readonly initialUserState?: UserRallyState;\n}\n\nexport interface ClaimRewardTransactionMutation {\n readonly nextStock: number | null;\n readonly nextUserState: UserRallyState;\n readonly auditLog: AuditLog;\n /** The server response is persisted by the adapter with the idempotency key. */\n readonly result?: unknown;\n /** A domain rejection is committed as an audit/idempotency record without mutations. */\n readonly error?: string;\n}\n\nexport interface CheckInTransactionParams {\n readonly rallyId: string;\n readonly userId: string;\n readonly spotId: string;\n readonly timestamp: number;\n readonly idempotencyKey?: string;\n readonly proofData?: unknown;\n readonly idempotencyTtlMs?: number;\n readonly initialUserState?: UserRallyState;\n}\n\nexport interface CheckInTransactionMutation {\n readonly nextUserState: UserRallyState;\n readonly auditLog: AuditLog;\n readonly result?: unknown;\n readonly error?: string;\n}\n\nexport interface ServerPersistenceAdapter {\n /** Atomically commits user state, audit log, and idempotency data. */\n executeCheckInTransaction(\n params: CheckInTransactionParams,\n mutation: (current: { readonly userState: UserRallyState }) => CheckInTransactionMutation,\n ): Promise<{ readonly success: boolean; readonly error?: string }>;\n /**\n * Atomically commits stock, user state, claim count, audit log, and idempotency data.\n * Adapters must roll back every write when the callback or any commit operation fails.\n */\n executeClaimRewardTransaction(\n params: ClaimRewardTransactionParams,\n mutation: (current: {\n readonly stock: number | null;\n readonly claimCount: number;\n readonly userState: UserRallyState;\n }) => ClaimRewardTransactionMutation,\n ): Promise<{ readonly success: boolean; readonly error?: string }>;\n acquireLock(rallyId: string, lockKey: string, ttlMs: number): Promise<boolean>;\n releaseLock(rallyId: string, lockKey: string): Promise<void>;\n getRewardStock(rallyId: string, rewardId: string): Promise<number | null>;\n decrementRewardStock(\n rallyId: string,\n rewardId: string,\n ): Promise<{ readonly success: boolean; readonly remainingStock: number }>;\n getIdempotentResult<T>(rallyId: string, key: string): Promise<T | null>;\n saveIdempotentResult<T>(rallyId: string, key: string, result: T, ttlMs: number): Promise<void>;\n getUserClaimCount(rallyId: string, userId: string, rewardId: string): Promise<number>;\n recordUserClaim(params: UserClaimRecord): Promise<void>;\n recordClaim?(params: UserClaimRecord): Promise<void>;\n incrementUserClaimCount?(rallyId: string, userId: string, rewardId: string): Promise<void>;\n getUserState(rallyId: string, userId: string): Promise<UserRallyState | null>;\n saveUserState(rallyId: string, userId: string, state: UserRallyState): Promise<void>;\n recordAuditLog(log: AuditLog): Promise<void>;\n removeAuditLog?(auditId: string): Promise<void>;\n}\ninterface TimedValue<T> {\n readonly value: T;\n readonly expiresAt: number;\n}\nexport interface InMemoryServerPersistenceOptions {\n readonly stocks?: Readonly<Record<string, number>>;\n}\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 #stockDefaults: Map<string, number>;\n readonly #claims = new Map<string, number>();\n readonly #claimRecords: UserClaimRecord[] = [];\n readonly #auditLogs: AuditLog[] = [];\n constructor(options: InMemoryServerPersistenceOptions = {}) {\n this.#stocks = new Map();\n this.#stockDefaults = new Map();\n for (const [key, stock] of Object.entries(options.stocks ?? {})) {\n if (key.includes(\":\")) this.#stocks.set(key, stock);\n else this.#stockDefaults.set(key, stock);\n }\n }\n #key(rallyId: string, key: string): string {\n return `${rallyId}:${key}`;\n }\n #stockKey(rallyId: string, rewardId: string): string {\n const scopedKey = `${rallyId}:${rewardId}`;\n if (this.#stocks.has(scopedKey)) return scopedKey;\n const defaultStock = this.#stockDefaults.get(rewardId);\n if (defaultStock !== undefined) {\n this.#stocks.set(scopedKey, defaultStock);\n return scopedKey;\n }\n return scopedKey;\n }\n async acquireLock(rallyId: string, key: string, ttlMs: number): Promise<boolean> {\n const scopedKey = this.#key(rallyId, key);\n const now = Date.now();\n const until = this.#locks.get(scopedKey);\n if (until !== undefined && until > now) return false;\n this.#locks.set(scopedKey, now + Math.max(1, ttlMs));\n return true;\n }\n async releaseLock(rallyId: string, key: string): Promise<void> {\n this.#locks.delete(this.#key(rallyId, key));\n }\n async getRewardStock(rallyId: string, rewardId: string): Promise<number | null> {\n return this.#stocks.get(this.#stockKey(rallyId, rewardId)) ?? null;\n }\n async decrementRewardStock(\n rallyId: string,\n rewardId: string,\n ): Promise<{ success: boolean; remainingStock: number }> {\n const key = this.#stockKey(rallyId, rewardId);\n const current = this.#stocks.get(key);\n if (current === undefined) return { success: true, remainingStock: Number.POSITIVE_INFINITY };\n if (current <= 0) return { success: false, remainingStock: 0 };\n this.#stocks.set(key, current - 1);\n return { success: true, remainingStock: current - 1 };\n }\n async restoreRewardStock(rallyId: string, rewardId: string, count = 1): Promise<void> {\n const key = this.#stockKey(rallyId, rewardId);\n const current = this.#stocks.get(key);\n if (current !== undefined) this.#stocks.set(key, current + Math.max(0, count));\n }\n\n async executeClaimRewardTransaction(\n params: ClaimRewardTransactionParams,\n mutation: (current: {\n readonly stock: number | null;\n readonly claimCount: number;\n readonly userState: UserRallyState;\n }) => ClaimRewardTransactionMutation,\n ): Promise<{ readonly success: boolean; readonly error?: string }> {\n try {\n return await this.runTransaction(params.rallyId, async () => {\n const userState =\n (await this.getUserState(params.rallyId, params.userId)) ?? params.initialUserState;\n if (userState === undefined)\n return { success: false, error: \"A user state is required for this transaction.\" };\n const mutationResult = mutation({\n stock: await this.getRewardStock(params.rallyId, params.rewardId),\n claimCount: await this.getUserClaimCount(params.rallyId, params.userId, params.rewardId),\n userState,\n });\n const idempotencyKey = params.idempotencyKey;\n if (mutationResult.error !== undefined) {\n await this.recordAuditLog(mutationResult.auditLog);\n if (idempotencyKey !== undefined && mutationResult.result !== undefined)\n await this.saveIdempotentResult(\n params.rallyId,\n idempotencyKey,\n mutationResult.result,\n params.idempotencyTtlMs ?? 86_400_000,\n );\n return { success: false, error: mutationResult.error };\n }\n const currentStock = await this.getRewardStock(params.rallyId, params.rewardId);\n if (\n currentStock !== null &&\n (mutationResult.nextStock === null || mutationResult.nextStock < 0)\n )\n throw new Error(\"The transaction produced an invalid stock value.\");\n if (currentStock === null && mutationResult.nextStock !== null)\n throw new Error(\"The transaction changed an unlimited stock to a limited stock.\");\n if (mutationResult.nextStock !== null)\n this.#stocks.set(\n this.#stockKey(params.rallyId, params.rewardId),\n mutationResult.nextStock,\n );\n await this.saveUserState(params.rallyId, params.userId, mutationResult.nextUserState);\n const reward = mutationResult.nextUserState.rewards.find(\n (item) => item.rewardId === params.rewardId,\n );\n if (reward?.claimTicketNumber !== undefined)\n await this.recordUserClaim({\n rallyId: params.rallyId,\n userId: params.userId,\n rewardId: params.rewardId,\n ticketNumber: reward.claimTicketNumber,\n timestamp: params.timestamp,\n });\n await this.recordAuditLog(mutationResult.auditLog);\n if (idempotencyKey !== undefined && mutationResult.result !== undefined)\n await this.saveIdempotentResult(\n params.rallyId,\n idempotencyKey,\n mutationResult.result,\n params.idempotencyTtlMs ?? 86_400_000,\n );\n return { success: true };\n });\n } catch (error) {\n return { success: false, error: error instanceof Error ? error.message : String(error) };\n }\n }\n\n async executeCheckInTransaction(\n params: CheckInTransactionParams,\n mutation: (current: { readonly userState: UserRallyState }) => CheckInTransactionMutation,\n ): Promise<{ readonly success: boolean; readonly error?: string }> {\n try {\n return await this.runTransaction(params.rallyId, async () => {\n const userState =\n (await this.getUserState(params.rallyId, params.userId)) ?? params.initialUserState;\n if (userState === undefined)\n return { success: false, error: \"A user state is required for this transaction.\" };\n const mutationResult = mutation({ userState });\n if (mutationResult.error !== undefined) {\n await this.recordAuditLog(mutationResult.auditLog);\n if (params.idempotencyKey !== undefined && mutationResult.result !== undefined)\n await this.saveIdempotentResult(\n params.rallyId,\n params.idempotencyKey,\n mutationResult.result,\n params.idempotencyTtlMs ?? 86_400_000,\n );\n return { success: false, error: mutationResult.error };\n }\n await this.saveUserState(params.rallyId, params.userId, mutationResult.nextUserState);\n await this.recordAuditLog(mutationResult.auditLog);\n if (params.idempotencyKey !== undefined && mutationResult.result !== undefined)\n await this.saveIdempotentResult(\n params.rallyId,\n params.idempotencyKey,\n mutationResult.result,\n params.idempotencyTtlMs ?? 86_400_000,\n );\n return { success: true };\n });\n } catch (error) {\n return { success: false, error: error instanceof Error ? error.message : String(error) };\n }\n }\n\n async rollbackUserState(\n rallyId: string,\n userId: string,\n previousState: UserRallyState | null,\n ): Promise<void> {\n const key = `${rallyId}:${userId}`;\n if (previousState === null) this.#states.delete(key);\n else this.#states.set(key, structuredClone(previousState));\n }\n async getIdempotentResult<T>(rallyId: string, key: string): Promise<T | null> {\n const value = this.#idempotent.get(this.#key(rallyId, key));\n if (value === undefined || value.expiresAt <= Date.now()) {\n this.#idempotent.delete(this.#key(rallyId, key));\n return null;\n }\n return structuredClone(value.value) as T;\n }\n async saveIdempotentResult<T>(\n rallyId: string,\n key: string,\n result: T,\n ttlMs: number,\n ): Promise<void> {\n this.#idempotent.set(this.#key(rallyId, key), {\n value: structuredClone(result),\n expiresAt: Date.now() + Math.max(1, ttlMs),\n });\n }\n async getUserClaimCount(rallyId: string, userId: string, rewardId: string): Promise<number> {\n return this.#claims.get(`${rallyId}:${userId}:${rewardId}`) ?? 0;\n }\n async getUserState(rallyId: string, userId: string): Promise<UserRallyState | null> {\n const value = this.#states.get(`${rallyId}:${userId}`);\n return value === undefined ? null : structuredClone(value);\n }\n async saveUserState(rallyId: string, userId: string, state: UserRallyState): Promise<void> {\n this.#states.set(`${rallyId}:${userId}`, structuredClone(state));\n }\n async recordAuditLog(log: AuditLog): Promise<void> {\n this.#auditLogs.push(structuredClone(log));\n }\n async removeAuditLog(auditId: string): Promise<void> {\n const index = this.#auditLogs.findIndex((log) => log.id === auditId);\n if (index >= 0) this.#auditLogs.splice(index, 1);\n }\n getAuditLogs(): ReadonlyArray<AuditLog> {\n return structuredClone(this.#auditLogs);\n }\n async recordUserClaim(params: UserClaimRecord): Promise<void> {\n const { rallyId, userId, rewardId } = params;\n const key = `${rallyId}:${userId}:${rewardId}`;\n this.#claims.set(key, (this.#claims.get(key) ?? 0) + 1);\n this.#claimRecords.push(structuredClone(params));\n }\n async rollbackUserClaim(\n rallyId: string,\n userId: string,\n rewardId: string,\n ticketNumber: string,\n ): Promise<void> {\n const key = `${rallyId}:${userId}:${rewardId}`;\n const count = this.#claims.get(key) ?? 0;\n if (count <= 1) this.#claims.delete(key);\n else this.#claims.set(key, count - 1);\n const index = this.#claimRecords.findIndex(\n (record) =>\n record.rallyId === rallyId &&\n record.userId === userId &&\n record.rewardId === rewardId &&\n record.ticketNumber === ticketNumber,\n );\n if (index >= 0) this.#claimRecords.splice(index, 1);\n }\n async incrementUserClaimCount(rallyId: string, userId: string, rewardId: string): Promise<void> {\n const key = `${rallyId}:${userId}:${rewardId}`;\n this.#claims.set(key, (this.#claims.get(key) ?? 0) + 1);\n }\n getClaimRecords(): ReadonlyArray<UserClaimRecord> {\n return structuredClone(this.#claimRecords);\n }\n /** @deprecated Use recordUserClaim with a ticket number and timestamp. */\n recordClaim(params: UserClaimRecord): Promise<void>;\n recordClaim(rallyId: string, userId: string, rewardId: string): Promise<void>;\n recordClaim(\n paramsOrRallyId: UserClaimRecord | string,\n userId?: string,\n rewardId?: string,\n ): Promise<void> {\n const params: UserClaimRecord =\n typeof paramsOrRallyId === \"string\"\n ? {\n rallyId: paramsOrRallyId,\n userId: userId ?? \"\",\n rewardId: rewardId ?? \"\",\n ticketNumber: \"\",\n timestamp: Date.now(),\n }\n : paramsOrRallyId;\n return this.recordUserClaim(params);\n }\n\n async runTransaction<T>(\n _rallyId: string,\n operation: (transaction: ServerPersistenceAdapter) => Promise<T>,\n ): Promise<T> {\n const snapshot = {\n stocks: new Map(this.#stocks),\n idempotent: new Map(this.#idempotent),\n states: new Map(this.#states),\n claims: new Map(this.#claims),\n claimRecords: structuredClone(this.#claimRecords),\n auditLogs: structuredClone(this.#auditLogs),\n };\n try {\n return await operation(this);\n } catch (error) {\n this.#stocks.clear();\n for (const [key, value] of snapshot.stocks) this.#stocks.set(key, value);\n this.#idempotent.clear();\n for (const [key, value] of snapshot.idempotent)\n this.#idempotent.set(key, structuredClone(value));\n this.#states.clear();\n for (const [key, value] of snapshot.states) this.#states.set(key, structuredClone(value));\n this.#claims.clear();\n for (const [key, value] of snapshot.claims) this.#claims.set(key, value);\n this.#claimRecords.splice(0, this.#claimRecords.length, ...snapshot.claimRecords);\n this.#auditLogs.splice(0, this.#auditLogs.length, ...snapshot.auditLogs);\n throw error;\n }\n }\n}\n","import type { VerificationContext } from \"@stamprally/core\";\nimport type { CheckInRequest, ClaimRewardRequest } from \"./index.js\";\n\nexport interface RequestValidationError {\n readonly path: string;\n readonly message: string;\n readonly code: \"invalid_request\";\n}\nexport type RequestValidationResult<T> =\n | { readonly success: true; readonly data: T }\n | { readonly success: false; readonly errors: ReadonlyArray<RequestValidationError> };\n\nfunction error(path: string, message: string): RequestValidationResult<never> {\n return { success: false, errors: [{ path, message, code: \"invalid_request\" }] };\n}\nfunction record(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\nfunction nonEmpty(value: unknown): value is string {\n return typeof value === \"string\" && value.trim().length > 0;\n}\nfunction context(value: unknown): value is VerificationContext {\n if (!record(value) || typeof value.type !== \"string\") return false;\n if (value.type === \"qr\") return nonEmpty(value.token);\n if (value.type === \"passcode\") return nonEmpty(value.code);\n if (value.type === \"nfc\") return nonEmpty(value.tagId);\n if (value.type === \"custom\") return \"value\" in value;\n return (\n value.type === \"gps\" &&\n typeof value.latitude === \"number\" &&\n Number.isFinite(value.latitude) &&\n typeof value.longitude === \"number\" &&\n Number.isFinite(value.longitude)\n );\n}\nfunction common(value: unknown, fields: ReadonlyArray<string>): value is Record<string, unknown> {\n if (!record(value)) return false;\n return fields.every((field) => nonEmpty(value[field]));\n}\n\nexport function validateCheckInRequest(value: unknown): RequestValidationResult<CheckInRequest> {\n if (!common(value, [\"rallyId\", \"spotId\", \"idempotencyKey\"]))\n return error(\"$\", \"rallyId, spotId, and idempotencyKey must be non-empty strings.\");\n if (!context(value.context))\n return error(\"context\", \"context is not a valid verification context.\");\n if (value.userId !== undefined && !nonEmpty(value.userId))\n return error(\"userId\", \"userId must be non-empty.\");\n if (value.now !== undefined && !nonEmpty(value.now))\n return error(\"now\", \"now must be non-empty when provided.\");\n return { success: true, data: value as unknown as CheckInRequest };\n}\n\nexport function validateClaimRewardRequest(\n value: unknown,\n): RequestValidationResult<ClaimRewardRequest> {\n if (!common(value, [\"rallyId\", \"rewardId\", \"idempotencyKey\"]))\n return error(\"$\", \"rallyId, rewardId, and idempotencyKey must be non-empty strings.\");\n if (value.userId !== undefined && !nonEmpty(value.userId))\n return error(\"userId\", \"userId must be non-empty.\");\n if (value.staffPasscode !== undefined && !nonEmpty(value.staffPasscode))\n return error(\"staffPasscode\", \"staffPasscode must be non-empty.\");\n if (value.staffId !== undefined && !nonEmpty(value.staffId))\n return error(\"staffId\", \"staffId must be non-empty.\");\n if (value.now !== undefined && !nonEmpty(value.now))\n return error(\"now\", \"now must be non-empty when provided.\");\n return { success: true, data: value as unknown as ClaimRewardRequest };\n}\n\nexport function validateSyncRequest(\n value: unknown,\n): RequestValidationResult<{ readonly rallyId: string; readonly userId?: string }> {\n if (!common(value, [\"rallyId\"])) return error(\"rallyId\", \"rallyId must be a non-empty string.\");\n if (value.userId !== undefined && !nonEmpty(value.userId))\n return error(\"userId\", \"userId must be non-empty.\");\n return { success: true, data: value as { readonly rallyId: string; readonly userId?: string } };\n}\n","import {\n type AdminRallyConfig,\n type CheckInCondition,\n consumeReward,\n evaluateConditionDetailed,\n reconcileRewardStates,\n type UserRallyState,\n type Validator,\n type VerificationContext,\n} from \"@stamprally/core\";\nimport type {\n AuditLog,\n CheckInRequest,\n CheckInResponse,\n ClaimRewardRequest,\n ServerOptions,\n SyncOperationStatus,\n} from \"./index.js\";\nimport type { ServerPersistenceAdapter } from \"./persistence.js\";\nimport {\n validateCheckInRequest,\n validateClaimRewardRequest,\n validateSyncRequest,\n} from \"./security.js\";\n\ntype ErrorResponse = { readonly ok: false; readonly code: string; readonly message: string };\ntype ClaimResponse =\n | { readonly ok: true; readonly state: UserRallyState; readonly claimTicketNumber?: string }\n | ErrorResponse;\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}\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\nfunction requestId(prefix: string): string {\n return `${prefix}-${globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random().toString(36).slice(2)}`}`;\n}\nfunction now(options: ServerOptions): string {\n return options.now?.() ?? new Date().toISOString();\n}\nfunction timestampMillis(timestamp: string): number {\n const value = Date.parse(timestamp);\n return Number.isFinite(value) ? value : Date.now();\n}\nfunction initialState(config: AdminRallyConfig, userId: string, timestamp: string): UserRallyState {\n return {\n rallyId: config.id,\n userId,\n records: [],\n rewards: reconcileRewardStates(config.rewards, [], 0, timestamp),\n updatedAt: timestamp,\n };\n}\nfunction getProof(context: VerificationContext): unknown {\n return context.type === \"qr\"\n ? context.token\n : context.type === \"passcode\"\n ? context.code\n : context.type === \"gps\"\n ? { latitude: context.latitude, longitude: context.longitude }\n : context.type === \"nfc\"\n ? context.tagId\n : context.value;\n}\nasync function evaluate(\n condition: CheckInCondition,\n context: VerificationContext,\n validator: Validator | undefined,\n base: { rallyId: string; spotId: string; state: UserRallyState },\n): Promise<boolean> {\n if (condition.type !== \"custom\") return evaluateConditionDetailed(condition, context).ok;\n if (validator === undefined) return false;\n const validationContext = {\n rallyId: base.rallyId,\n spotId: base.spotId,\n proofData: getProof(context),\n condition,\n userState: base.state,\n };\n const result =\n typeof validator === \"function\"\n ? await validator(validationContext)\n : await validator.validate(validationContext);\n return result === true || (typeof result === \"object\" && result.valid);\n}\nfunction audit(\n rallyId: string,\n userId: string,\n action: AuditLog[\"action\"],\n resourceId: string,\n key: string,\n status: AuditLog[\"status\"],\n timestamp: string,\n code?: string,\n): AuditLog {\n return {\n id: requestId(\"audit\"),\n timestamp,\n rallyId,\n userId,\n action,\n resourceId,\n status,\n idempotencyKey: key,\n ...(code === undefined ? {} : { metadata: { errorCode: code } }),\n };\n}\nfunction operationStatus(result: {\n readonly ok: boolean;\n readonly code?: string;\n}): SyncOperationStatus {\n if (result.ok) return \"ACCEPTED\";\n return result.code === \"CONFLICT\" || result.code === \"PERSISTENCE_FAILED\"\n ? \"RETRYABLE_ERROR\"\n : \"REJECTED_PERMANENT\";\n}\n\nexport class StampRallyServer {\n readonly #config: AdminRallyConfig;\n readonly #persistence: ServerPersistenceAdapter;\n readonly #options: ServerOptions;\n constructor(\n config: AdminRallyConfig,\n persistence: ServerPersistenceAdapter,\n options: ServerOptions = {},\n ) {\n this.#config = config;\n this.#persistence = persistence;\n this.#options = options;\n }\n async handle(request: Request): Promise<Response> {\n if (request.method !== \"POST\")\n return json({ ok: false, code: \"METHOD_NOT_ALLOWED\", message: \"POST is required.\" }, 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, code: \"NOT_FOUND\", message: \"Route not found.\" }, 404);\n }\n async handleCheckIn(request: Request): Promise<Response> {\n const body = validateCheckInRequest(await this.#body(request));\n if (!body.success || body.data.rallyId !== this.#config.id)\n return json(\n { ok: false, code: \"INVALID_REQUEST\", message: \"Invalid check-in request.\" },\n 400,\n );\n const userId = await this.#user(request);\n if (userId === null)\n return json(\n { ok: false, code: \"UNAUTHENTICATED\", message: \"Authentication is required.\" },\n 401,\n );\n const result = await this.checkIn({ ...body.data, userId });\n return json(\n { ...result, status: operationStatus(result) },\n result.ok ? 200 : result.code === \"SPOT_NOT_FOUND\" ? 404 : 422,\n );\n }\n async handleClaimReward(request: Request): Promise<Response> {\n const body = validateClaimRewardRequest(await this.#body(request));\n if (!body.success || body.data.rallyId !== this.#config.id)\n return json({ ok: false, code: \"INVALID_REQUEST\", message: \"Invalid reward request.\" }, 400);\n const userId = await this.#user(request);\n if (userId === null)\n return json(\n { ok: false, code: \"UNAUTHENTICATED\", message: \"Authentication is required.\" },\n 401,\n );\n const result = await this.claimReward({ ...body.data, userId });\n return json(\n { ...result, status: operationStatus(result) },\n result.ok ? 200 : result.code === \"REWARD_NOT_FOUND\" ? 404 : 422,\n );\n }\n async handleSync(request: Request): Promise<Response> {\n const body = validateSyncRequest(await this.#body(request));\n if (!body.success || body.data.rallyId !== this.#config.id)\n return json({ ok: false, code: \"INVALID_REQUEST\", message: \"Invalid sync request.\" }, 400);\n const userId = await this.#user(request);\n if (userId === null)\n return json(\n { ok: false, code: \"UNAUTHENTICATED\", message: \"Authentication is required.\" },\n 401,\n );\n return json({ ok: true, state: await this.sync(body.data.rallyId, userId) });\n }\n async checkIn(request: CheckInRequest & { readonly userId: string }): Promise<CheckInResponse> {\n const key = `check-in:${request.rallyId}:${request.userId}:${request.idempotencyKey}`;\n const previous = await this.#persistence.getIdempotentResult<CheckInResponse>(\n request.rallyId,\n key,\n );\n if (previous !== null) return previous;\n const lockKey = `state:${request.rallyId}:${request.userId}`;\n if (\n !(await this.#persistence.acquireLock(\n request.rallyId,\n lockKey,\n this.#options.lockTtlMs ?? 5_000,\n ))\n )\n return { ok: false, code: \"CONFLICT\", message: \"The user state is being updated.\" };\n const timestamp = now(this.#options);\n try {\n const current =\n (await this.#persistence.getUserState(request.rallyId, request.userId)) ??\n initialState(this.#config, request.userId, timestamp);\n const responseHolder: { value: CheckInResponse | null } = { value: null };\n const makeAudit = (status: \"SUCCESS\" | \"REJECTED\", code?: string): AuditLog =>\n audit(\n request.rallyId,\n request.userId,\n \"CHECK_IN\",\n request.spotId,\n request.idempotencyKey,\n status,\n timestamp,\n code,\n );\n const spot = this.#config.spots.find((item) => item.id === request.spotId);\n if (spot === undefined) {\n responseHolder.value = {\n ok: false,\n code: \"SPOT_NOT_FOUND\",\n message: \"Spot was not found.\",\n };\n return await this.#rememberCheckInTransaction(\n request,\n timestamp,\n key,\n current,\n {\n nextUserState: current,\n auditLog: makeAudit(\"REJECTED\", \"SPOT_NOT_FOUND\"),\n result: responseHolder.value,\n error: \"SPOT_NOT_FOUND\",\n },\n responseHolder,\n );\n }\n const rejected = (code: string, message: string) => {\n responseHolder.value = { ok: false, code, message };\n return {\n nextUserState: current,\n auditLog: makeAudit(\"REJECTED\", code),\n result: responseHolder.value,\n error: code,\n };\n };\n if (current.records.some((record) => record.stampId === request.spotId))\n return await this.#rememberCheckInTransaction(\n request,\n timestamp,\n key,\n current,\n rejected(\"STAMP_ALREADY_ACQUIRED\", \"Spot was already claimed.\"),\n responseHolder,\n );\n const acquired = new Set(current.records.map((record) => record.stampId));\n if (spot.prerequisites?.some((id) => !acquired.has(id)))\n return await this.#rememberCheckInTransaction(\n request,\n timestamp,\n key,\n current,\n rejected(\"PREREQUISITES_NOT_MET\", \"Prerequisite spots are not complete.\"),\n responseHolder,\n );\n for (const condition of spot.conditions)\n if (\n !(await evaluate(\n condition,\n request.context,\n condition.type === \"custom\"\n ? this.#options.customValidators?.[condition.validatorName]\n : undefined,\n { rallyId: request.rallyId, spotId: request.spotId, state: current },\n ))\n )\n return await this.#rememberCheckInTransaction(\n request,\n timestamp,\n key,\n current,\n rejected(\"INVALID_PROOF\", \"Verification failed.\"),\n responseHolder,\n );\n const next: UserRallyState = {\n ...current,\n records: [...current.records, { stampId: request.spotId, acquiredAt: timestamp }],\n rewards: reconcileRewardStates(\n this.#config.rewards,\n current.rewards,\n current.records.length + 1,\n timestamp,\n ),\n updatedAt: timestamp,\n };\n responseHolder.value = { ok: true, state: next };\n const transaction = await this.#persistence.executeCheckInTransaction(\n {\n rallyId: request.rallyId,\n userId: request.userId,\n spotId: request.spotId,\n timestamp: timestampMillis(timestamp),\n idempotencyKey: key,\n proofData: request.context,\n ...(this.#options.idempotencyTtlMs === undefined\n ? {}\n : { idempotencyTtlMs: this.#options.idempotencyTtlMs }),\n initialUserState: current,\n },\n () => ({\n nextUserState: next,\n auditLog: makeAudit(\"SUCCESS\"),\n result: responseHolder.value,\n }),\n );\n if (transaction.success && responseHolder.value !== null) return responseHolder.value;\n return {\n ok: false,\n code: \"PERSISTENCE_FAILED\",\n message: transaction.error ?? \"Check-in failed.\",\n };\n } finally {\n await this.#persistence.releaseLock(request.rallyId, lockKey);\n }\n }\n async claimReward(\n request: ClaimRewardRequest & { readonly userId: string },\n ): Promise<ClaimResponse> {\n const key = `claim:${request.rallyId}:${request.userId}:${request.rewardId}:${request.idempotencyKey}`;\n const previous = await this.#persistence.getIdempotentResult<ClaimResponse>(\n request.rallyId,\n key,\n );\n if (previous !== null) return previous;\n const reward = this.#config.rewards.find((item) => item.id === request.rewardId);\n if (reward === undefined)\n return this.#rememberClaim(\n key,\n { ok: false, code: \"REWARD_NOT_FOUND\", message: \"Reward was not found.\" },\n request,\n now(this.#options),\n );\n const lockKey = `reward:${request.rallyId}:${reward.id}`;\n if (\n !(await this.#persistence.acquireLock(\n request.rallyId,\n lockKey,\n this.#options.lockTtlMs ?? 5_000,\n ))\n )\n return { ok: false, code: \"CONFLICT\", message: \"The reward is being claimed.\" };\n const timestamp = now(this.#options);\n try {\n const checked = await this.#persistence.getIdempotentResult<ClaimResponse>(\n request.rallyId,\n key,\n );\n if (checked !== null) return checked;\n const responseHolder: { value: ClaimResponse | null } = { value: null };\n const result = await this.#persistence.executeClaimRewardTransaction(\n {\n rallyId: request.rallyId,\n userId: request.userId,\n rewardId: reward.id,\n ticketNumber: request.idempotencyKey,\n timestamp: Number.isNaN(Date.parse(timestamp)) ? Date.now() : Date.parse(timestamp),\n idempotencyKey: key,\n ...(request.staffPasscode === undefined ? {} : { proofData: request.staffPasscode }),\n ...(this.#options.idempotencyTtlMs === undefined\n ? {}\n : { idempotencyTtlMs: this.#options.idempotencyTtlMs }),\n initialUserState: initialState(this.#config, request.userId, timestamp),\n },\n ({ stock, claimCount, userState }) => {\n const storedReward = userState.rewards.find((item) => item.rewardId === reward.id) ?? {\n rewardId: reward.id,\n status: \"LOCKED\" as const,\n };\n const currentReward =\n reward.redemptionMethod === \"server_claim\" &&\n storedReward.status === \"CONSUMED\" &&\n (reward.userClaimLimit === undefined || claimCount < reward.userClaimLimit)\n ? { ...storedReward, status: \"AVAILABLE\" as const }\n : storedReward;\n const makeAudit = (status: \"SUCCESS\" | \"REJECTED\", code?: string): AuditLog =>\n audit(\n request.rallyId,\n request.userId,\n \"CLAIM_REWARD\",\n reward.id,\n request.idempotencyKey,\n status,\n timestamp,\n code,\n );\n if (stock !== null && stock <= 0) {\n responseHolder.value = {\n ok: false,\n code: \"OUT_OF_STOCK\",\n message: \"Reward is out of stock.\",\n };\n return {\n nextStock: stock,\n nextUserState: userState,\n auditLog: makeAudit(\"REJECTED\", \"OUT_OF_STOCK\"),\n result: responseHolder.value,\n error: \"OUT_OF_STOCK\",\n };\n }\n const effectiveReward =\n reward.staffPasscode === undefined && this.#config.staffPasscode !== undefined\n ? { ...reward, staffPasscode: this.#config.staffPasscode }\n : reward;\n const local = consumeReward({\n reward: effectiveReward,\n currentState: currentReward,\n now: timestamp,\n userRedemptionCount: claimCount,\n ...(request.staffPasscode === undefined\n ? {}\n : { inputPasscode: request.staffPasscode }),\n ...(request.staffId === undefined ? {} : { staffId: request.staffId }),\n });\n if (!local.ok) {\n responseHolder.value = {\n ok: false,\n code: local.error.code,\n message: \"Reward cannot be claimed.\",\n };\n return {\n nextStock: stock,\n nextUserState: userState,\n auditLog: makeAudit(\"REJECTED\", local.error.code),\n result: responseHolder.value,\n error: local.error.code,\n };\n }\n const nextRewards = userState.rewards.some((item) => item.rewardId === reward.id)\n ? userState.rewards.map((item) => (item.rewardId === reward.id ? local.value : item))\n : [...userState.rewards, local.value];\n const next: UserRallyState = {\n ...userState,\n rewards: nextRewards,\n updatedAt: timestamp,\n };\n responseHolder.value =\n local.value.claimTicketNumber === undefined\n ? { ok: true, state: next }\n : { ok: true, state: next, claimTicketNumber: local.value.claimTicketNumber };\n return {\n nextStock: stock === null ? null : stock - 1,\n nextUserState: next,\n auditLog: makeAudit(\"SUCCESS\"),\n result: responseHolder.value,\n };\n },\n );\n const response = responseHolder.value;\n if (!result.success) {\n if (response !== null && !response.ok && response.code === result.error) return response;\n return {\n ok: false,\n code: \"PERSISTENCE_FAILED\",\n message: result.error ?? \"Reward claim failed.\",\n };\n }\n if (response !== null && result.success) return response;\n return {\n ok: false,\n code: \"PERSISTENCE_FAILED\",\n message: result.error ?? \"Reward claim failed.\",\n };\n } catch (error) {\n return {\n ok: false,\n code: \"PERSISTENCE_FAILED\",\n message: error instanceof Error ? error.message : \"Reward claim failed.\",\n };\n } finally {\n await this.#persistence.releaseLock(request.rallyId, lockKey);\n }\n }\n async sync(rallyId: string, userId: string): Promise<UserRallyState> {\n return (\n (await this.#persistence.getUserState(rallyId, userId)) ??\n initialState(this.#config, userId, now(this.#options))\n );\n }\n async #body(request: Request): Promise<unknown> {\n try {\n const value: unknown = await request.json();\n return isObject(value) ? value : null;\n } catch {\n return null;\n }\n }\n async #user(request: Request): Promise<string | null> {\n if (this.#options.authenticate !== undefined) {\n const identity = await this.#options.authenticate(request);\n if (typeof identity === \"string\") return identity.length > 0 ? identity : null;\n if (identity === null) return null;\n const authenticatedUserId = identity.authenticatedUserId;\n return authenticatedUserId.length > 0 ? authenticatedUserId : null;\n }\n return \"anonymous\";\n }\n\n async #rememberCheckInTransaction(\n request: CheckInRequest & { readonly userId: string },\n timestamp: string,\n key: string,\n current: UserRallyState,\n mutation: {\n readonly nextUserState: UserRallyState;\n readonly auditLog: AuditLog;\n readonly result?: unknown;\n readonly error?: string;\n },\n responseHolder: { value: CheckInResponse | null },\n ): Promise<CheckInResponse> {\n const transaction = await this.#persistence.executeCheckInTransaction(\n {\n rallyId: request.rallyId,\n userId: request.userId,\n spotId: request.spotId,\n timestamp: timestampMillis(timestamp),\n idempotencyKey: key,\n ...(this.#options.idempotencyTtlMs === undefined\n ? {}\n : { idempotencyTtlMs: this.#options.idempotencyTtlMs }),\n initialUserState: current,\n },\n () => mutation,\n );\n if (\n responseHolder.value !== null &&\n (transaction.success || transaction.error === mutation.error)\n )\n return responseHolder.value;\n return {\n ok: false,\n code: \"PERSISTENCE_FAILED\",\n message: transaction.error ?? \"Check-in failed.\",\n };\n }\n async #rememberClaim(\n key: string,\n result: ClaimResponse,\n request: ClaimRewardRequest & { readonly userId: string },\n timestamp: string,\n ): Promise<ClaimResponse> {\n await this.#persistence.recordAuditLog(\n audit(\n request.rallyId,\n request.userId,\n \"CLAIM_REWARD\",\n request.rewardId,\n request.idempotencyKey,\n \"REJECTED\",\n timestamp,\n result.ok ? undefined : result.code,\n ),\n );\n await this.#persistence.saveIdempotentResult(\n request.rallyId,\n key,\n result,\n this.#options.idempotencyTtlMs ?? 86_400_000,\n );\n return result;\n }\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -30,7 +30,30 @@ interface ClaimRewardTransactionMutation {
|
|
|
30
30
|
/** A domain rejection is committed as an audit/idempotency record without mutations. */
|
|
31
31
|
readonly error?: string;
|
|
32
32
|
}
|
|
33
|
+
interface CheckInTransactionParams {
|
|
34
|
+
readonly rallyId: string;
|
|
35
|
+
readonly userId: string;
|
|
36
|
+
readonly spotId: string;
|
|
37
|
+
readonly timestamp: number;
|
|
38
|
+
readonly idempotencyKey?: string;
|
|
39
|
+
readonly proofData?: unknown;
|
|
40
|
+
readonly idempotencyTtlMs?: number;
|
|
41
|
+
readonly initialUserState?: UserRallyState;
|
|
42
|
+
}
|
|
43
|
+
interface CheckInTransactionMutation {
|
|
44
|
+
readonly nextUserState: UserRallyState;
|
|
45
|
+
readonly auditLog: AuditLog;
|
|
46
|
+
readonly result?: unknown;
|
|
47
|
+
readonly error?: string;
|
|
48
|
+
}
|
|
33
49
|
interface ServerPersistenceAdapter {
|
|
50
|
+
/** Atomically commits user state, audit log, and idempotency data. */
|
|
51
|
+
executeCheckInTransaction(params: CheckInTransactionParams, mutation: (current: {
|
|
52
|
+
readonly userState: UserRallyState;
|
|
53
|
+
}) => CheckInTransactionMutation): Promise<{
|
|
54
|
+
readonly success: boolean;
|
|
55
|
+
readonly error?: string;
|
|
56
|
+
}>;
|
|
34
57
|
/**
|
|
35
58
|
* Atomically commits stock, user state, claim count, audit log, and idempotency data.
|
|
36
59
|
* Adapters must roll back every write when the callback or any commit operation fails.
|
|
@@ -83,6 +106,12 @@ declare class InMemoryServerPersistenceAdapter implements ServerPersistenceAdapt
|
|
|
83
106
|
readonly success: boolean;
|
|
84
107
|
readonly error?: string;
|
|
85
108
|
}>;
|
|
109
|
+
executeCheckInTransaction(params: CheckInTransactionParams, mutation: (current: {
|
|
110
|
+
readonly userState: UserRallyState;
|
|
111
|
+
}) => CheckInTransactionMutation): Promise<{
|
|
112
|
+
readonly success: boolean;
|
|
113
|
+
readonly error?: string;
|
|
114
|
+
}>;
|
|
86
115
|
rollbackUserState(rallyId: string, userId: string, previousState: UserRallyState | null): Promise<void>;
|
|
87
116
|
getIdempotentResult<T>(rallyId: string, key: string): Promise<T | null>;
|
|
88
117
|
saveIdempotentResult<T>(rallyId: string, key: string, result: T, ttlMs: number): Promise<void>;
|
|
@@ -102,6 +131,62 @@ declare class InMemoryServerPersistenceAdapter implements ServerPersistenceAdapt
|
|
|
102
131
|
runTransaction<T>(_rallyId: string, operation: (transaction: ServerPersistenceAdapter) => Promise<T>): Promise<T>;
|
|
103
132
|
}
|
|
104
133
|
|
|
134
|
+
/**
|
|
135
|
+
* Minimal database contract for the SQL reference implementation.
|
|
136
|
+
*
|
|
137
|
+
* `readContext` should use SELECT ... FOR UPDATE for the stock, claim-count,
|
|
138
|
+
* and user-state rows. The adapter's transaction implementation must commit
|
|
139
|
+
* or roll back every operation performed through the transaction handle.
|
|
140
|
+
*/
|
|
141
|
+
interface SqlTransactionDatabase<Tx> {
|
|
142
|
+
transaction<T>(operation: (transaction: Tx) => Promise<T>): Promise<T>;
|
|
143
|
+
}
|
|
144
|
+
interface SqlClaimRewardStore<Tx> {
|
|
145
|
+
readContext(transaction: Tx, params: ClaimRewardTransactionParams): Promise<{
|
|
146
|
+
readonly stock: number | null;
|
|
147
|
+
readonly claimCount: number;
|
|
148
|
+
readonly userState: UserRallyState;
|
|
149
|
+
}>;
|
|
150
|
+
writeStock(transaction: Tx, params: ClaimRewardTransactionParams, nextStock: number): Promise<void>;
|
|
151
|
+
writeUserState(transaction: Tx, params: ClaimRewardTransactionParams, state: UserRallyState): Promise<void>;
|
|
152
|
+
writeClaimRecord(transaction: Tx, params: ClaimRewardTransactionParams, state: UserRallyState): Promise<void>;
|
|
153
|
+
writeAudit(transaction: Tx, log: AuditLog): Promise<void>;
|
|
154
|
+
writeIdempotency(transaction: Tx, params: ClaimRewardTransactionParams, result: unknown): Promise<void>;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Reference implementation for PostgreSQL, MySQL, or another transactional
|
|
158
|
+
* SQL store. Domain rejections still write their audit/idempotency records,
|
|
159
|
+
* while unexpected write failures reject the transaction and roll everything
|
|
160
|
+
* back.
|
|
161
|
+
*/
|
|
162
|
+
declare function executeClaimRewardTransaction<Tx>(database: SqlTransactionDatabase<Tx>, store: SqlClaimRewardStore<Tx>, params: ClaimRewardTransactionParams, mutation: (current: {
|
|
163
|
+
readonly stock: number | null;
|
|
164
|
+
readonly claimCount: number;
|
|
165
|
+
readonly userState: UserRallyState;
|
|
166
|
+
}) => ClaimRewardTransactionMutation): Promise<{
|
|
167
|
+
readonly success: boolean;
|
|
168
|
+
readonly error?: string;
|
|
169
|
+
}>;
|
|
170
|
+
|
|
171
|
+
interface RequestValidationError {
|
|
172
|
+
readonly path: string;
|
|
173
|
+
readonly message: string;
|
|
174
|
+
readonly code: "invalid_request";
|
|
175
|
+
}
|
|
176
|
+
type RequestValidationResult<T> = {
|
|
177
|
+
readonly success: true;
|
|
178
|
+
readonly data: T;
|
|
179
|
+
} | {
|
|
180
|
+
readonly success: false;
|
|
181
|
+
readonly errors: ReadonlyArray<RequestValidationError>;
|
|
182
|
+
};
|
|
183
|
+
declare function validateCheckInRequest(value: unknown): RequestValidationResult<CheckInRequest>;
|
|
184
|
+
declare function validateClaimRewardRequest(value: unknown): RequestValidationResult<ClaimRewardRequest>;
|
|
185
|
+
declare function validateSyncRequest(value: unknown): RequestValidationResult<{
|
|
186
|
+
readonly rallyId: string;
|
|
187
|
+
readonly userId?: string;
|
|
188
|
+
}>;
|
|
189
|
+
|
|
105
190
|
type ErrorResponse = {
|
|
106
191
|
readonly ok: false;
|
|
107
192
|
readonly code: string;
|
|
@@ -139,6 +224,7 @@ interface AuditLog {
|
|
|
139
224
|
readonly idempotencyKey: string;
|
|
140
225
|
readonly metadata?: Readonly<Record<string, unknown>>;
|
|
141
226
|
}
|
|
227
|
+
type SyncOperationStatus = "ACCEPTED" | "REJECTED_PERMANENT" | "RETRYABLE_ERROR";
|
|
142
228
|
interface CheckInRequest {
|
|
143
229
|
readonly rallyId: string;
|
|
144
230
|
readonly userId?: string;
|
|
@@ -159,17 +245,22 @@ interface ClaimRewardRequest {
|
|
|
159
245
|
type CheckInResponse = {
|
|
160
246
|
readonly ok: true;
|
|
161
247
|
readonly state: UserRallyState;
|
|
248
|
+
readonly status?: "ACCEPTED";
|
|
162
249
|
} | {
|
|
163
250
|
readonly ok: false;
|
|
164
251
|
readonly code: string;
|
|
165
252
|
readonly message: string;
|
|
253
|
+
readonly status?: "REJECTED_PERMANENT" | "RETRYABLE_ERROR";
|
|
166
254
|
};
|
|
167
255
|
interface ServerOptions {
|
|
168
256
|
readonly lockTtlMs?: number;
|
|
169
257
|
readonly idempotencyTtlMs?: number;
|
|
170
|
-
readonly authenticate?: (request: Request) => Promise<string | null> | string | null;
|
|
258
|
+
readonly authenticate?: (request: Request) => Promise<string | AuthenticationContext | null> | string | AuthenticationContext | null;
|
|
171
259
|
readonly customValidators?: Readonly<Record<string, _stamprally_core.Validator>>;
|
|
172
260
|
readonly now?: () => string;
|
|
173
261
|
}
|
|
262
|
+
interface AuthenticationContext {
|
|
263
|
+
readonly authenticatedUserId: string;
|
|
264
|
+
}
|
|
174
265
|
|
|
175
|
-
export { type AuditLog, type CheckInRequest, type CheckInResponse, type ClaimRewardRequest, type ClaimRewardTransactionMutation, type ClaimRewardTransactionParams, InMemoryServerPersistenceAdapter, type InMemoryServerPersistenceOptions, type ServerOptions, type ServerPersistenceAdapter, StampRallyServer, type UserClaimRecord };
|
|
266
|
+
export { type AuditLog, type AuthenticationContext, type CheckInRequest, type CheckInResponse, type CheckInTransactionMutation, type CheckInTransactionParams, type ClaimRewardRequest, type ClaimRewardTransactionMutation, type ClaimRewardTransactionParams, InMemoryServerPersistenceAdapter, type InMemoryServerPersistenceOptions, type RequestValidationError, type RequestValidationResult, type ServerOptions, type ServerPersistenceAdapter, type SqlClaimRewardStore, type SqlTransactionDatabase, StampRallyServer, type SyncOperationStatus, type UserClaimRecord, executeClaimRewardTransaction, validateCheckInRequest, validateClaimRewardRequest, validateSyncRequest };
|
package/dist/index.d.ts
CHANGED
|
@@ -30,7 +30,30 @@ interface ClaimRewardTransactionMutation {
|
|
|
30
30
|
/** A domain rejection is committed as an audit/idempotency record without mutations. */
|
|
31
31
|
readonly error?: string;
|
|
32
32
|
}
|
|
33
|
+
interface CheckInTransactionParams {
|
|
34
|
+
readonly rallyId: string;
|
|
35
|
+
readonly userId: string;
|
|
36
|
+
readonly spotId: string;
|
|
37
|
+
readonly timestamp: number;
|
|
38
|
+
readonly idempotencyKey?: string;
|
|
39
|
+
readonly proofData?: unknown;
|
|
40
|
+
readonly idempotencyTtlMs?: number;
|
|
41
|
+
readonly initialUserState?: UserRallyState;
|
|
42
|
+
}
|
|
43
|
+
interface CheckInTransactionMutation {
|
|
44
|
+
readonly nextUserState: UserRallyState;
|
|
45
|
+
readonly auditLog: AuditLog;
|
|
46
|
+
readonly result?: unknown;
|
|
47
|
+
readonly error?: string;
|
|
48
|
+
}
|
|
33
49
|
interface ServerPersistenceAdapter {
|
|
50
|
+
/** Atomically commits user state, audit log, and idempotency data. */
|
|
51
|
+
executeCheckInTransaction(params: CheckInTransactionParams, mutation: (current: {
|
|
52
|
+
readonly userState: UserRallyState;
|
|
53
|
+
}) => CheckInTransactionMutation): Promise<{
|
|
54
|
+
readonly success: boolean;
|
|
55
|
+
readonly error?: string;
|
|
56
|
+
}>;
|
|
34
57
|
/**
|
|
35
58
|
* Atomically commits stock, user state, claim count, audit log, and idempotency data.
|
|
36
59
|
* Adapters must roll back every write when the callback or any commit operation fails.
|
|
@@ -83,6 +106,12 @@ declare class InMemoryServerPersistenceAdapter implements ServerPersistenceAdapt
|
|
|
83
106
|
readonly success: boolean;
|
|
84
107
|
readonly error?: string;
|
|
85
108
|
}>;
|
|
109
|
+
executeCheckInTransaction(params: CheckInTransactionParams, mutation: (current: {
|
|
110
|
+
readonly userState: UserRallyState;
|
|
111
|
+
}) => CheckInTransactionMutation): Promise<{
|
|
112
|
+
readonly success: boolean;
|
|
113
|
+
readonly error?: string;
|
|
114
|
+
}>;
|
|
86
115
|
rollbackUserState(rallyId: string, userId: string, previousState: UserRallyState | null): Promise<void>;
|
|
87
116
|
getIdempotentResult<T>(rallyId: string, key: string): Promise<T | null>;
|
|
88
117
|
saveIdempotentResult<T>(rallyId: string, key: string, result: T, ttlMs: number): Promise<void>;
|
|
@@ -102,6 +131,62 @@ declare class InMemoryServerPersistenceAdapter implements ServerPersistenceAdapt
|
|
|
102
131
|
runTransaction<T>(_rallyId: string, operation: (transaction: ServerPersistenceAdapter) => Promise<T>): Promise<T>;
|
|
103
132
|
}
|
|
104
133
|
|
|
134
|
+
/**
|
|
135
|
+
* Minimal database contract for the SQL reference implementation.
|
|
136
|
+
*
|
|
137
|
+
* `readContext` should use SELECT ... FOR UPDATE for the stock, claim-count,
|
|
138
|
+
* and user-state rows. The adapter's transaction implementation must commit
|
|
139
|
+
* or roll back every operation performed through the transaction handle.
|
|
140
|
+
*/
|
|
141
|
+
interface SqlTransactionDatabase<Tx> {
|
|
142
|
+
transaction<T>(operation: (transaction: Tx) => Promise<T>): Promise<T>;
|
|
143
|
+
}
|
|
144
|
+
interface SqlClaimRewardStore<Tx> {
|
|
145
|
+
readContext(transaction: Tx, params: ClaimRewardTransactionParams): Promise<{
|
|
146
|
+
readonly stock: number | null;
|
|
147
|
+
readonly claimCount: number;
|
|
148
|
+
readonly userState: UserRallyState;
|
|
149
|
+
}>;
|
|
150
|
+
writeStock(transaction: Tx, params: ClaimRewardTransactionParams, nextStock: number): Promise<void>;
|
|
151
|
+
writeUserState(transaction: Tx, params: ClaimRewardTransactionParams, state: UserRallyState): Promise<void>;
|
|
152
|
+
writeClaimRecord(transaction: Tx, params: ClaimRewardTransactionParams, state: UserRallyState): Promise<void>;
|
|
153
|
+
writeAudit(transaction: Tx, log: AuditLog): Promise<void>;
|
|
154
|
+
writeIdempotency(transaction: Tx, params: ClaimRewardTransactionParams, result: unknown): Promise<void>;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Reference implementation for PostgreSQL, MySQL, or another transactional
|
|
158
|
+
* SQL store. Domain rejections still write their audit/idempotency records,
|
|
159
|
+
* while unexpected write failures reject the transaction and roll everything
|
|
160
|
+
* back.
|
|
161
|
+
*/
|
|
162
|
+
declare function executeClaimRewardTransaction<Tx>(database: SqlTransactionDatabase<Tx>, store: SqlClaimRewardStore<Tx>, params: ClaimRewardTransactionParams, mutation: (current: {
|
|
163
|
+
readonly stock: number | null;
|
|
164
|
+
readonly claimCount: number;
|
|
165
|
+
readonly userState: UserRallyState;
|
|
166
|
+
}) => ClaimRewardTransactionMutation): Promise<{
|
|
167
|
+
readonly success: boolean;
|
|
168
|
+
readonly error?: string;
|
|
169
|
+
}>;
|
|
170
|
+
|
|
171
|
+
interface RequestValidationError {
|
|
172
|
+
readonly path: string;
|
|
173
|
+
readonly message: string;
|
|
174
|
+
readonly code: "invalid_request";
|
|
175
|
+
}
|
|
176
|
+
type RequestValidationResult<T> = {
|
|
177
|
+
readonly success: true;
|
|
178
|
+
readonly data: T;
|
|
179
|
+
} | {
|
|
180
|
+
readonly success: false;
|
|
181
|
+
readonly errors: ReadonlyArray<RequestValidationError>;
|
|
182
|
+
};
|
|
183
|
+
declare function validateCheckInRequest(value: unknown): RequestValidationResult<CheckInRequest>;
|
|
184
|
+
declare function validateClaimRewardRequest(value: unknown): RequestValidationResult<ClaimRewardRequest>;
|
|
185
|
+
declare function validateSyncRequest(value: unknown): RequestValidationResult<{
|
|
186
|
+
readonly rallyId: string;
|
|
187
|
+
readonly userId?: string;
|
|
188
|
+
}>;
|
|
189
|
+
|
|
105
190
|
type ErrorResponse = {
|
|
106
191
|
readonly ok: false;
|
|
107
192
|
readonly code: string;
|
|
@@ -139,6 +224,7 @@ interface AuditLog {
|
|
|
139
224
|
readonly idempotencyKey: string;
|
|
140
225
|
readonly metadata?: Readonly<Record<string, unknown>>;
|
|
141
226
|
}
|
|
227
|
+
type SyncOperationStatus = "ACCEPTED" | "REJECTED_PERMANENT" | "RETRYABLE_ERROR";
|
|
142
228
|
interface CheckInRequest {
|
|
143
229
|
readonly rallyId: string;
|
|
144
230
|
readonly userId?: string;
|
|
@@ -159,17 +245,22 @@ interface ClaimRewardRequest {
|
|
|
159
245
|
type CheckInResponse = {
|
|
160
246
|
readonly ok: true;
|
|
161
247
|
readonly state: UserRallyState;
|
|
248
|
+
readonly status?: "ACCEPTED";
|
|
162
249
|
} | {
|
|
163
250
|
readonly ok: false;
|
|
164
251
|
readonly code: string;
|
|
165
252
|
readonly message: string;
|
|
253
|
+
readonly status?: "REJECTED_PERMANENT" | "RETRYABLE_ERROR";
|
|
166
254
|
};
|
|
167
255
|
interface ServerOptions {
|
|
168
256
|
readonly lockTtlMs?: number;
|
|
169
257
|
readonly idempotencyTtlMs?: number;
|
|
170
|
-
readonly authenticate?: (request: Request) => Promise<string | null> | string | null;
|
|
258
|
+
readonly authenticate?: (request: Request) => Promise<string | AuthenticationContext | null> | string | AuthenticationContext | null;
|
|
171
259
|
readonly customValidators?: Readonly<Record<string, _stamprally_core.Validator>>;
|
|
172
260
|
readonly now?: () => string;
|
|
173
261
|
}
|
|
262
|
+
interface AuthenticationContext {
|
|
263
|
+
readonly authenticatedUserId: string;
|
|
264
|
+
}
|
|
174
265
|
|
|
175
|
-
export { type AuditLog, type CheckInRequest, type CheckInResponse, type ClaimRewardRequest, type ClaimRewardTransactionMutation, type ClaimRewardTransactionParams, InMemoryServerPersistenceAdapter, type InMemoryServerPersistenceOptions, type ServerOptions, type ServerPersistenceAdapter, StampRallyServer, type UserClaimRecord };
|
|
266
|
+
export { type AuditLog, type AuthenticationContext, type CheckInRequest, type CheckInResponse, type CheckInTransactionMutation, type CheckInTransactionParams, type ClaimRewardRequest, type ClaimRewardTransactionMutation, type ClaimRewardTransactionParams, InMemoryServerPersistenceAdapter, type InMemoryServerPersistenceOptions, type RequestValidationError, type RequestValidationResult, type ServerOptions, type ServerPersistenceAdapter, type SqlClaimRewardStore, type SqlTransactionDatabase, StampRallyServer, type SyncOperationStatus, type UserClaimRecord, executeClaimRewardTransaction, validateCheckInRequest, validateClaimRewardRequest, validateSyncRequest };
|