@stamprally/core 0.1.0 → 0.2.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.d.cts CHANGED
@@ -94,6 +94,13 @@ type StampError = {
94
94
  readonly code: "CONDITION_MISMATCH";
95
95
  readonly stampId: string;
96
96
  readonly mismatch: ConditionMismatch;
97
+ } | {
98
+ readonly code: "OFFLINE_QUEUED";
99
+ readonly stampId: string;
100
+ readonly idempotencyKey: string;
101
+ } | {
102
+ readonly code: "INVALID_PROOF";
103
+ readonly stampId: string;
97
104
  };
98
105
  type Result<T, E> = {
99
106
  readonly ok: true;
@@ -104,8 +111,8 @@ type Result<T, E> = {
104
111
  };
105
112
 
106
113
  type SupportedLocale = "ja" | "en";
107
- type LocalizedString = Record<SupportedLocale, string>;
108
- type LocalizedText = string | LocalizedString;
114
+ type LocalizedString<TLocale extends string = SupportedLocale> = Record<TLocale, string>;
115
+ type LocalizedText<TLocale extends string = string> = string | Partial<Record<TLocale, string>>;
109
116
  type SlotShape = "circle" | "square" | "rounded";
110
117
  type FontFamily = "system-ui" | "serif" | "rounded-sans" | "monospace" | "handwritten";
111
118
  interface SheetTheme {
@@ -121,23 +128,33 @@ interface SheetTheme {
121
128
  readonly fontFamily?: FontFamily;
122
129
  }
123
130
  type ThemePresetId = "default" | "modern_dark" | "pop_candy" | "retro_craft" | "cyber";
124
- interface ThemePreset {
131
+ interface ThemePreset<TLocale extends string = SupportedLocale> {
125
132
  readonly id: ThemePresetId;
126
- readonly name: LocalizedString;
127
- readonly description: LocalizedString;
133
+ readonly name: LocalizedString<TLocale>;
134
+ readonly description: LocalizedString<TLocale>;
128
135
  readonly theme: SheetTheme;
129
136
  }
130
137
  declare const DEFAULT_SHEET_THEME: SheetTheme;
131
- interface SpotItem {
138
+ interface SpotItem<TLocale extends string = SupportedLocale, TMeta extends Record<string, unknown> = Record<string, unknown>> {
132
139
  readonly id: string;
133
- readonly name: LocalizedText;
134
- readonly description?: LocalizedText;
135
- readonly hint?: LocalizedText;
140
+ readonly name: LocalizedText<TLocale>;
141
+ readonly description?: LocalizedText<TLocale>;
142
+ readonly hint?: LocalizedText<TLocale>;
136
143
  readonly condition: StampCondition;
137
144
  readonly order?: number;
145
+ readonly deckId?: string;
146
+ readonly groupId?: string;
147
+ readonly guideId?: string;
148
+ readonly iconUrl?: string;
149
+ readonly imageUrl?: string;
150
+ readonly externalUrl?: string;
151
+ readonly redirectUrlAfterClaim?: string;
152
+ readonly metadata?: TMeta;
153
+ readonly dependsOn?: ReadonlyArray<string>;
154
+ readonly requiresStampIds?: ReadonlyArray<string>;
138
155
  }
139
156
  /** @deprecated Use SpotItem instead. */
140
- type StampDefinition = SpotItem;
157
+ type StampDefinition<TLocale extends string = SupportedLocale, TMeta extends Record<string, unknown> = Record<string, unknown>> = SpotItem<TLocale, TMeta>;
141
158
  interface StampRecord {
142
159
  readonly stampId: string;
143
160
  readonly acquiredAt: string;
@@ -146,15 +163,19 @@ interface StampRecord {
146
163
  type RewardType = "digital" | "in_person";
147
164
  type RedemptionMethod = "manual_slide" | "staff_passcode" | "view_only";
148
165
  type RewardStatus = "LOCKED" | "AVAILABLE" | "CONSUMED" | "EXPIRED";
149
- interface RewardItem {
166
+ interface RewardItem<TLocale extends string = SupportedLocale> {
150
167
  readonly id: string;
151
- readonly title: LocalizedText;
152
- readonly description: LocalizedText;
168
+ readonly title: LocalizedText<TLocale>;
169
+ readonly description: LocalizedText<TLocale>;
153
170
  readonly type: RewardType;
154
171
  readonly redemptionMethod: RedemptionMethod;
155
172
  readonly requiredStampCount: number;
156
173
  readonly digitalContentUrl?: string;
157
174
  readonly staffPasscode?: string;
175
+ readonly validUntil?: string;
176
+ readonly maxStock?: number;
177
+ readonly limitPerUser?: number;
178
+ readonly claimTicketNumber?: string;
158
179
  }
159
180
  interface RewardState {
160
181
  readonly rewardId: string;
@@ -162,6 +183,9 @@ interface RewardState {
162
183
  readonly unlockedAt?: string;
163
184
  readonly consumedAt?: string;
164
185
  readonly consumedByStaffId?: string;
186
+ readonly claimTicketNumber?: string;
187
+ readonly redeemedCount?: number;
188
+ readonly userRedemptionCount?: number;
165
189
  }
166
190
  interface StampRallyState {
167
191
  readonly rallyId: string;
@@ -169,21 +193,72 @@ interface StampRallyState {
169
193
  readonly rewards?: ReadonlyArray<RewardState>;
170
194
  readonly updatedAt: string;
171
195
  }
172
- interface RallyConfig {
196
+ interface RallyConfig<TLocale extends string = SupportedLocale, TMeta extends Record<string, unknown> = Record<string, unknown>> {
173
197
  readonly id: string;
174
- readonly title?: LocalizedText;
175
- readonly description?: LocalizedText;
176
- readonly stamps: ReadonlyArray<StampDefinition>;
177
- readonly rewards?: ReadonlyArray<RewardItem>;
198
+ readonly title?: LocalizedText<TLocale>;
199
+ readonly description?: LocalizedText<TLocale>;
200
+ readonly stamps: ReadonlyArray<StampDefinition<TLocale, TMeta>>;
201
+ readonly rewards?: ReadonlyArray<RewardItem<TLocale>>;
178
202
  readonly isSequential?: boolean;
179
203
  readonly theme?: SheetTheme;
204
+ readonly version?: number;
205
+ readonly startDate?: string;
206
+ readonly endDate?: string;
180
207
  }
181
208
 
182
- declare function resolveLocalizedText(text: LocalizedText | undefined, locale: SupportedLocale, fallbackLocale?: SupportedLocale): string;
209
+ declare function resolveLocalizedText<TLocale extends string = SupportedLocale>(text: LocalizedText<TLocale> | undefined, locale: string, fallbackLocale?: string): string;
183
210
  declare function toLocalizedString(text: LocalizedText | undefined): LocalizedString;
211
+ declare function toLocalizedString<TLocale extends string>(text: LocalizedText<TLocale> | undefined): LocalizedString<TLocale>;
212
+
213
+ declare function migrateRallyConfig(raw: unknown): RallyConfig;
214
+
215
+ type PublicRewardItem<TLocale extends string = string> = Omit<RewardItem<TLocale>, "staffPasscode">;
216
+ type PublicRallyConfig<TLocale extends string = string, TMeta extends Record<string, unknown> = Record<string, unknown>> = Omit<RallyConfig<TLocale, TMeta>, "rewards"> & {
217
+ readonly rewards?: ReadonlyArray<PublicRewardItem<TLocale>>;
218
+ };
219
+ declare function stripSensitiveConfig<TLocale extends string, TMeta extends Record<string, unknown>>(config: RallyConfig<TLocale, TMeta>): PublicRallyConfig<TLocale, TMeta>;
184
220
 
185
221
  declare const THEME_PRESETS: ReadonlyArray<ThemePreset>;
186
222
 
223
+ declare const CURRENT_RALLY_CONFIG_VERSION = 2;
224
+ interface ValidationError {
225
+ readonly path: string;
226
+ readonly code: "INVALID_TYPE" | "REQUIRED" | "EMPTY_STRING" | "DUPLICATE_ID" | "INVALID_COORDINATES" | "INVALID_RADIUS" | "INVALID_DATE" | "INVALID_VERSION" | "INVALID_REWARD" | "CYCLE_DETECTED";
227
+ readonly message: string;
228
+ }
229
+ interface RallyConfigValidationResult {
230
+ readonly valid: boolean;
231
+ readonly errors: ReadonlyArray<ValidationError>;
232
+ }
233
+ declare function validateRallyConfig(config: unknown): RallyConfigValidationResult;
234
+
235
+ type CheckInErrorCode = "ORDER_VIOLATION" | "OUT_OF_RANGE" | "EXPIRED" | "INVALID_PROOF" | "INVALID_CONTEXT" | "ALREADY_CLAIMED";
236
+ interface CheckInContext {
237
+ readonly verificationContext: VerificationContext;
238
+ readonly now?: string;
239
+ readonly expectedStampId?: string;
240
+ readonly proof?: string;
241
+ readonly alreadyClaimed?: boolean;
242
+ }
243
+ type CheckInInput = VerificationContext | (VerificationContext & {
244
+ readonly now?: string;
245
+ readonly expectedStampId?: string;
246
+ readonly alreadyClaimed?: boolean;
247
+ }) | CheckInContext;
248
+ type CheckInResult$1 = {
249
+ readonly ok: true;
250
+ readonly success: true;
251
+ readonly stampId: string;
252
+ readonly checkedAt: string;
253
+ } | {
254
+ readonly ok: false;
255
+ readonly success: false;
256
+ readonly code: CheckInErrorCode;
257
+ readonly stampId: string;
258
+ readonly message: string;
259
+ };
260
+ declare function evaluateCheckIn(spot: SpotItem, input: CheckInInput): CheckInResult$1;
261
+
187
262
  declare function calculateDistanceMeters(latitudeA: number, longitudeA: number, latitudeB: number, longitudeB: number): number;
188
263
  declare function evaluateConditionDetailed(condition: StampCondition, context: VerificationContext, now: string): Result<ConditionMatch, ConditionMismatch>;
189
264
  declare function evaluateCondition(condition: StampCondition, context: VerificationContext, now?: string): boolean;
@@ -199,6 +274,13 @@ interface StampRallyProgress {
199
274
  }
200
275
  declare function calculateProgress(state: StampRallyState, config: RallyConfig): StampRallyProgress;
201
276
 
277
+ interface ClaimTicketOptions {
278
+ readonly issuedAt?: string;
279
+ readonly sequence?: number;
280
+ }
281
+ declare function createClaimTicketNumber(rewardId: string, options?: ClaimTicketOptions): string;
282
+ declare function issueClaimTicketNumber(reward: RewardItem, currentState: RewardState, options?: ClaimTicketOptions): RewardState;
283
+
202
284
  type StampRallyEvent = {
203
285
  readonly type: "stampAcquired";
204
286
  readonly record: StampRecord;
@@ -206,6 +288,10 @@ type StampRallyEvent = {
206
288
  readonly type: "rallyCompleted";
207
289
  readonly rallyId: string;
208
290
  readonly completedAt: string;
291
+ } | {
292
+ readonly type: "rewardUnlocked";
293
+ readonly rewardId: string;
294
+ readonly unlockedAt: string;
209
295
  };
210
296
  interface ProcessStampValue {
211
297
  readonly nextState: StampRallyState;
@@ -221,6 +307,15 @@ type RewardConsumeError = {
221
307
  readonly code: "INVALID_PASSCODE";
222
308
  readonly rewardId: string;
223
309
  readonly message: string;
310
+ } | {
311
+ readonly code: "EXPIRED";
312
+ readonly rewardId: string;
313
+ } | {
314
+ readonly code: "OUT_OF_STOCK";
315
+ readonly rewardId: string;
316
+ } | {
317
+ readonly code: "USER_LIMIT_REACHED";
318
+ readonly rewardId: string;
224
319
  } | {
225
320
  readonly code: "REWARD_NOT_FOUND";
226
321
  readonly rewardId: string;
@@ -231,6 +326,8 @@ interface ConsumeRewardParams {
231
326
  readonly inputPasscode?: string;
232
327
  readonly staffId?: string;
233
328
  readonly now: string;
329
+ readonly userId?: string;
330
+ readonly userRedemptionCount?: number;
234
331
  }
235
332
  type ConsumeResult = Result<RewardState, RewardConsumeError>;
236
333
  declare function reconcileRewardStates(rewards: ReadonlyArray<RewardItem>, currentStates: ReadonlyArray<RewardState>, acquiredStampCount: number, now: string): ReadonlyArray<RewardState>;
@@ -369,4 +466,27 @@ interface QrDetectorOptions {
369
466
  declare function isQrSupported(): boolean;
370
467
  declare function readQrContext(videoElement: HTMLVideoElement, options?: QrDetectorOptions): Promise<DetectorResult<TokenVerificationContext>>;
371
468
 
372
- export { type CheckInResult, type Clock, type CompositeConditionFailure, type ConditionMatch, type ConditionMismatch, type ConsumeResult, type ConsumeRewardParams, DEFAULT_SHEET_THEME, type DetectorError, type DetectorErrorCode, type DetectorKind, type DetectorResult, type FontFamily, type GeoDetectorOptions, type GeoVerificationContext, InMemoryStorage, IndexedDBAdapter, type IndexedDBAdapterOptions, LocalStorageAdapter, type LocalStorageAdapterOptions, type LocalStorageFailureMode, type LocalizedString, type LocalizedText, type NfcDetectorOptions, type PasscodeCondition, type ProcessStampValue, type QrDetectorOptions, type RallyConfig, type RallySnapshot, type RedemptionMethod, type Result, type RewardConsumeError, type RewardItem, type RewardState, type RewardStatus, type RewardType, type SheetTheme, type SlotShape, type SpotItem, type StampCondition, type StampDefinition, type StampError, StampRallyClient, type StampRallyEvent, type StampRallyListener, type StampRallyProgress, type StampRallyState, type StampRecord, type StampStorage, StorageAdapterError, type StorageAdapterErrorCode, type StorageLike, type StorageOperation, type StorageWarningHandler, type SupportedLocale, THEME_PRESETS, type ThemePreset, type ThemePresetId, type TokenVerificationContext, type VerificationContext, calculateDistanceMeters, calculateProgress, consumeReward, evaluateCondition, evaluateConditionDetailed, exportProgressToken, getCurrentGeoContext, importProgressToken, isGeolocationSupported, isNfcSupported, isQrSupported, isRewardState, isStampRallyState, normalizePasscode, processStamp, readNfcContext, readQrContext, reconcileRewardStates, resolveLocalizedText, toLocalizedString, verifyPasscode };
469
+ type SnapshotSecretKey = string | Uint8Array;
470
+ interface SnapshotTokenPayload {
471
+ readonly [key: string]: unknown;
472
+ readonly expiresAt?: string;
473
+ readonly exp?: number;
474
+ }
475
+ type SnapshotTokenErrorCode = "MALFORMED" | "INVALID_SIGNATURE" | "DECRYPTION_FAILED" | "EXPIRED" | "UNSUPPORTED";
476
+ interface SnapshotTokenError {
477
+ readonly code: SnapshotTokenErrorCode;
478
+ readonly message: string;
479
+ }
480
+ type SnapshotTokenVerification<T extends SnapshotTokenPayload = SnapshotTokenPayload> = {
481
+ readonly ok: true;
482
+ readonly valid: true;
483
+ readonly payload: T;
484
+ } | {
485
+ readonly ok: false;
486
+ readonly valid: false;
487
+ readonly error: SnapshotTokenError;
488
+ };
489
+ declare function createSignedSnapshotToken<T extends SnapshotTokenPayload>(payload: T, secretKey: SnapshotSecretKey): Promise<string>;
490
+ declare function verifySnapshotToken<T extends SnapshotTokenPayload = SnapshotTokenPayload>(token: string, secretKey: SnapshotSecretKey, now?: number): Promise<SnapshotTokenVerification<T>>;
491
+
492
+ export { CURRENT_RALLY_CONFIG_VERSION, type CheckInContext, type CheckInErrorCode, type CheckInInput, type CheckInResult$1 as CheckInResult, type ClaimTicketOptions, type Clock, type CompositeConditionFailure, type ConditionMatch, type ConditionMismatch, type ConsumeResult, type ConsumeRewardParams, DEFAULT_SHEET_THEME, type DetectorError, type DetectorErrorCode, type DetectorKind, type DetectorResult, type FontFamily, type GeoDetectorOptions, type GeoVerificationContext, InMemoryStorage, IndexedDBAdapter, type IndexedDBAdapterOptions, LocalStorageAdapter, type LocalStorageAdapterOptions, type LocalStorageFailureMode, type LocalizedString, type LocalizedText, type NfcDetectorOptions, type PasscodeCondition, type ProcessStampValue, type PublicRallyConfig, type PublicRewardItem, type QrDetectorOptions, type RallyConfig, type RallyConfigValidationResult, type RallySnapshot, type RedemptionMethod, type Result, type RewardConsumeError, type RewardItem, type RewardState, type RewardStatus, type RewardType, type SheetTheme, type SlotShape, type SnapshotSecretKey, type SnapshotTokenError, type SnapshotTokenErrorCode, type SnapshotTokenPayload, type SnapshotTokenVerification, type SpotItem, type StampCondition, type StampDefinition, type StampError, StampRallyClient, type StampRallyEvent, type StampRallyListener, type StampRallyProgress, type StampRallyState, type StampRecord, type StampStorage, StorageAdapterError, type StorageAdapterErrorCode, type StorageLike, type StorageOperation, type StorageWarningHandler, type SupportedLocale, THEME_PRESETS, type ThemePreset, type ThemePresetId, type TokenVerificationContext, type ValidationError, type VerificationContext, calculateDistanceMeters, calculateProgress, consumeReward, createClaimTicketNumber, createSignedSnapshotToken, evaluateCheckIn, evaluateCondition, evaluateConditionDetailed, exportProgressToken, getCurrentGeoContext, importProgressToken, isGeolocationSupported, isNfcSupported, isQrSupported, isRewardState, isStampRallyState, issueClaimTicketNumber, migrateRallyConfig, normalizePasscode, processStamp, readNfcContext, readQrContext, reconcileRewardStates, resolveLocalizedText, stripSensitiveConfig, toLocalizedString, validateRallyConfig, verifyPasscode, verifySnapshotToken };
package/dist/index.d.ts CHANGED
@@ -94,6 +94,13 @@ type StampError = {
94
94
  readonly code: "CONDITION_MISMATCH";
95
95
  readonly stampId: string;
96
96
  readonly mismatch: ConditionMismatch;
97
+ } | {
98
+ readonly code: "OFFLINE_QUEUED";
99
+ readonly stampId: string;
100
+ readonly idempotencyKey: string;
101
+ } | {
102
+ readonly code: "INVALID_PROOF";
103
+ readonly stampId: string;
97
104
  };
98
105
  type Result<T, E> = {
99
106
  readonly ok: true;
@@ -104,8 +111,8 @@ type Result<T, E> = {
104
111
  };
105
112
 
106
113
  type SupportedLocale = "ja" | "en";
107
- type LocalizedString = Record<SupportedLocale, string>;
108
- type LocalizedText = string | LocalizedString;
114
+ type LocalizedString<TLocale extends string = SupportedLocale> = Record<TLocale, string>;
115
+ type LocalizedText<TLocale extends string = string> = string | Partial<Record<TLocale, string>>;
109
116
  type SlotShape = "circle" | "square" | "rounded";
110
117
  type FontFamily = "system-ui" | "serif" | "rounded-sans" | "monospace" | "handwritten";
111
118
  interface SheetTheme {
@@ -121,23 +128,33 @@ interface SheetTheme {
121
128
  readonly fontFamily?: FontFamily;
122
129
  }
123
130
  type ThemePresetId = "default" | "modern_dark" | "pop_candy" | "retro_craft" | "cyber";
124
- interface ThemePreset {
131
+ interface ThemePreset<TLocale extends string = SupportedLocale> {
125
132
  readonly id: ThemePresetId;
126
- readonly name: LocalizedString;
127
- readonly description: LocalizedString;
133
+ readonly name: LocalizedString<TLocale>;
134
+ readonly description: LocalizedString<TLocale>;
128
135
  readonly theme: SheetTheme;
129
136
  }
130
137
  declare const DEFAULT_SHEET_THEME: SheetTheme;
131
- interface SpotItem {
138
+ interface SpotItem<TLocale extends string = SupportedLocale, TMeta extends Record<string, unknown> = Record<string, unknown>> {
132
139
  readonly id: string;
133
- readonly name: LocalizedText;
134
- readonly description?: LocalizedText;
135
- readonly hint?: LocalizedText;
140
+ readonly name: LocalizedText<TLocale>;
141
+ readonly description?: LocalizedText<TLocale>;
142
+ readonly hint?: LocalizedText<TLocale>;
136
143
  readonly condition: StampCondition;
137
144
  readonly order?: number;
145
+ readonly deckId?: string;
146
+ readonly groupId?: string;
147
+ readonly guideId?: string;
148
+ readonly iconUrl?: string;
149
+ readonly imageUrl?: string;
150
+ readonly externalUrl?: string;
151
+ readonly redirectUrlAfterClaim?: string;
152
+ readonly metadata?: TMeta;
153
+ readonly dependsOn?: ReadonlyArray<string>;
154
+ readonly requiresStampIds?: ReadonlyArray<string>;
138
155
  }
139
156
  /** @deprecated Use SpotItem instead. */
140
- type StampDefinition = SpotItem;
157
+ type StampDefinition<TLocale extends string = SupportedLocale, TMeta extends Record<string, unknown> = Record<string, unknown>> = SpotItem<TLocale, TMeta>;
141
158
  interface StampRecord {
142
159
  readonly stampId: string;
143
160
  readonly acquiredAt: string;
@@ -146,15 +163,19 @@ interface StampRecord {
146
163
  type RewardType = "digital" | "in_person";
147
164
  type RedemptionMethod = "manual_slide" | "staff_passcode" | "view_only";
148
165
  type RewardStatus = "LOCKED" | "AVAILABLE" | "CONSUMED" | "EXPIRED";
149
- interface RewardItem {
166
+ interface RewardItem<TLocale extends string = SupportedLocale> {
150
167
  readonly id: string;
151
- readonly title: LocalizedText;
152
- readonly description: LocalizedText;
168
+ readonly title: LocalizedText<TLocale>;
169
+ readonly description: LocalizedText<TLocale>;
153
170
  readonly type: RewardType;
154
171
  readonly redemptionMethod: RedemptionMethod;
155
172
  readonly requiredStampCount: number;
156
173
  readonly digitalContentUrl?: string;
157
174
  readonly staffPasscode?: string;
175
+ readonly validUntil?: string;
176
+ readonly maxStock?: number;
177
+ readonly limitPerUser?: number;
178
+ readonly claimTicketNumber?: string;
158
179
  }
159
180
  interface RewardState {
160
181
  readonly rewardId: string;
@@ -162,6 +183,9 @@ interface RewardState {
162
183
  readonly unlockedAt?: string;
163
184
  readonly consumedAt?: string;
164
185
  readonly consumedByStaffId?: string;
186
+ readonly claimTicketNumber?: string;
187
+ readonly redeemedCount?: number;
188
+ readonly userRedemptionCount?: number;
165
189
  }
166
190
  interface StampRallyState {
167
191
  readonly rallyId: string;
@@ -169,21 +193,72 @@ interface StampRallyState {
169
193
  readonly rewards?: ReadonlyArray<RewardState>;
170
194
  readonly updatedAt: string;
171
195
  }
172
- interface RallyConfig {
196
+ interface RallyConfig<TLocale extends string = SupportedLocale, TMeta extends Record<string, unknown> = Record<string, unknown>> {
173
197
  readonly id: string;
174
- readonly title?: LocalizedText;
175
- readonly description?: LocalizedText;
176
- readonly stamps: ReadonlyArray<StampDefinition>;
177
- readonly rewards?: ReadonlyArray<RewardItem>;
198
+ readonly title?: LocalizedText<TLocale>;
199
+ readonly description?: LocalizedText<TLocale>;
200
+ readonly stamps: ReadonlyArray<StampDefinition<TLocale, TMeta>>;
201
+ readonly rewards?: ReadonlyArray<RewardItem<TLocale>>;
178
202
  readonly isSequential?: boolean;
179
203
  readonly theme?: SheetTheme;
204
+ readonly version?: number;
205
+ readonly startDate?: string;
206
+ readonly endDate?: string;
180
207
  }
181
208
 
182
- declare function resolveLocalizedText(text: LocalizedText | undefined, locale: SupportedLocale, fallbackLocale?: SupportedLocale): string;
209
+ declare function resolveLocalizedText<TLocale extends string = SupportedLocale>(text: LocalizedText<TLocale> | undefined, locale: string, fallbackLocale?: string): string;
183
210
  declare function toLocalizedString(text: LocalizedText | undefined): LocalizedString;
211
+ declare function toLocalizedString<TLocale extends string>(text: LocalizedText<TLocale> | undefined): LocalizedString<TLocale>;
212
+
213
+ declare function migrateRallyConfig(raw: unknown): RallyConfig;
214
+
215
+ type PublicRewardItem<TLocale extends string = string> = Omit<RewardItem<TLocale>, "staffPasscode">;
216
+ type PublicRallyConfig<TLocale extends string = string, TMeta extends Record<string, unknown> = Record<string, unknown>> = Omit<RallyConfig<TLocale, TMeta>, "rewards"> & {
217
+ readonly rewards?: ReadonlyArray<PublicRewardItem<TLocale>>;
218
+ };
219
+ declare function stripSensitiveConfig<TLocale extends string, TMeta extends Record<string, unknown>>(config: RallyConfig<TLocale, TMeta>): PublicRallyConfig<TLocale, TMeta>;
184
220
 
185
221
  declare const THEME_PRESETS: ReadonlyArray<ThemePreset>;
186
222
 
223
+ declare const CURRENT_RALLY_CONFIG_VERSION = 2;
224
+ interface ValidationError {
225
+ readonly path: string;
226
+ readonly code: "INVALID_TYPE" | "REQUIRED" | "EMPTY_STRING" | "DUPLICATE_ID" | "INVALID_COORDINATES" | "INVALID_RADIUS" | "INVALID_DATE" | "INVALID_VERSION" | "INVALID_REWARD" | "CYCLE_DETECTED";
227
+ readonly message: string;
228
+ }
229
+ interface RallyConfigValidationResult {
230
+ readonly valid: boolean;
231
+ readonly errors: ReadonlyArray<ValidationError>;
232
+ }
233
+ declare function validateRallyConfig(config: unknown): RallyConfigValidationResult;
234
+
235
+ type CheckInErrorCode = "ORDER_VIOLATION" | "OUT_OF_RANGE" | "EXPIRED" | "INVALID_PROOF" | "INVALID_CONTEXT" | "ALREADY_CLAIMED";
236
+ interface CheckInContext {
237
+ readonly verificationContext: VerificationContext;
238
+ readonly now?: string;
239
+ readonly expectedStampId?: string;
240
+ readonly proof?: string;
241
+ readonly alreadyClaimed?: boolean;
242
+ }
243
+ type CheckInInput = VerificationContext | (VerificationContext & {
244
+ readonly now?: string;
245
+ readonly expectedStampId?: string;
246
+ readonly alreadyClaimed?: boolean;
247
+ }) | CheckInContext;
248
+ type CheckInResult$1 = {
249
+ readonly ok: true;
250
+ readonly success: true;
251
+ readonly stampId: string;
252
+ readonly checkedAt: string;
253
+ } | {
254
+ readonly ok: false;
255
+ readonly success: false;
256
+ readonly code: CheckInErrorCode;
257
+ readonly stampId: string;
258
+ readonly message: string;
259
+ };
260
+ declare function evaluateCheckIn(spot: SpotItem, input: CheckInInput): CheckInResult$1;
261
+
187
262
  declare function calculateDistanceMeters(latitudeA: number, longitudeA: number, latitudeB: number, longitudeB: number): number;
188
263
  declare function evaluateConditionDetailed(condition: StampCondition, context: VerificationContext, now: string): Result<ConditionMatch, ConditionMismatch>;
189
264
  declare function evaluateCondition(condition: StampCondition, context: VerificationContext, now?: string): boolean;
@@ -199,6 +274,13 @@ interface StampRallyProgress {
199
274
  }
200
275
  declare function calculateProgress(state: StampRallyState, config: RallyConfig): StampRallyProgress;
201
276
 
277
+ interface ClaimTicketOptions {
278
+ readonly issuedAt?: string;
279
+ readonly sequence?: number;
280
+ }
281
+ declare function createClaimTicketNumber(rewardId: string, options?: ClaimTicketOptions): string;
282
+ declare function issueClaimTicketNumber(reward: RewardItem, currentState: RewardState, options?: ClaimTicketOptions): RewardState;
283
+
202
284
  type StampRallyEvent = {
203
285
  readonly type: "stampAcquired";
204
286
  readonly record: StampRecord;
@@ -206,6 +288,10 @@ type StampRallyEvent = {
206
288
  readonly type: "rallyCompleted";
207
289
  readonly rallyId: string;
208
290
  readonly completedAt: string;
291
+ } | {
292
+ readonly type: "rewardUnlocked";
293
+ readonly rewardId: string;
294
+ readonly unlockedAt: string;
209
295
  };
210
296
  interface ProcessStampValue {
211
297
  readonly nextState: StampRallyState;
@@ -221,6 +307,15 @@ type RewardConsumeError = {
221
307
  readonly code: "INVALID_PASSCODE";
222
308
  readonly rewardId: string;
223
309
  readonly message: string;
310
+ } | {
311
+ readonly code: "EXPIRED";
312
+ readonly rewardId: string;
313
+ } | {
314
+ readonly code: "OUT_OF_STOCK";
315
+ readonly rewardId: string;
316
+ } | {
317
+ readonly code: "USER_LIMIT_REACHED";
318
+ readonly rewardId: string;
224
319
  } | {
225
320
  readonly code: "REWARD_NOT_FOUND";
226
321
  readonly rewardId: string;
@@ -231,6 +326,8 @@ interface ConsumeRewardParams {
231
326
  readonly inputPasscode?: string;
232
327
  readonly staffId?: string;
233
328
  readonly now: string;
329
+ readonly userId?: string;
330
+ readonly userRedemptionCount?: number;
234
331
  }
235
332
  type ConsumeResult = Result<RewardState, RewardConsumeError>;
236
333
  declare function reconcileRewardStates(rewards: ReadonlyArray<RewardItem>, currentStates: ReadonlyArray<RewardState>, acquiredStampCount: number, now: string): ReadonlyArray<RewardState>;
@@ -369,4 +466,27 @@ interface QrDetectorOptions {
369
466
  declare function isQrSupported(): boolean;
370
467
  declare function readQrContext(videoElement: HTMLVideoElement, options?: QrDetectorOptions): Promise<DetectorResult<TokenVerificationContext>>;
371
468
 
372
- export { type CheckInResult, type Clock, type CompositeConditionFailure, type ConditionMatch, type ConditionMismatch, type ConsumeResult, type ConsumeRewardParams, DEFAULT_SHEET_THEME, type DetectorError, type DetectorErrorCode, type DetectorKind, type DetectorResult, type FontFamily, type GeoDetectorOptions, type GeoVerificationContext, InMemoryStorage, IndexedDBAdapter, type IndexedDBAdapterOptions, LocalStorageAdapter, type LocalStorageAdapterOptions, type LocalStorageFailureMode, type LocalizedString, type LocalizedText, type NfcDetectorOptions, type PasscodeCondition, type ProcessStampValue, type QrDetectorOptions, type RallyConfig, type RallySnapshot, type RedemptionMethod, type Result, type RewardConsumeError, type RewardItem, type RewardState, type RewardStatus, type RewardType, type SheetTheme, type SlotShape, type SpotItem, type StampCondition, type StampDefinition, type StampError, StampRallyClient, type StampRallyEvent, type StampRallyListener, type StampRallyProgress, type StampRallyState, type StampRecord, type StampStorage, StorageAdapterError, type StorageAdapterErrorCode, type StorageLike, type StorageOperation, type StorageWarningHandler, type SupportedLocale, THEME_PRESETS, type ThemePreset, type ThemePresetId, type TokenVerificationContext, type VerificationContext, calculateDistanceMeters, calculateProgress, consumeReward, evaluateCondition, evaluateConditionDetailed, exportProgressToken, getCurrentGeoContext, importProgressToken, isGeolocationSupported, isNfcSupported, isQrSupported, isRewardState, isStampRallyState, normalizePasscode, processStamp, readNfcContext, readQrContext, reconcileRewardStates, resolveLocalizedText, toLocalizedString, verifyPasscode };
469
+ type SnapshotSecretKey = string | Uint8Array;
470
+ interface SnapshotTokenPayload {
471
+ readonly [key: string]: unknown;
472
+ readonly expiresAt?: string;
473
+ readonly exp?: number;
474
+ }
475
+ type SnapshotTokenErrorCode = "MALFORMED" | "INVALID_SIGNATURE" | "DECRYPTION_FAILED" | "EXPIRED" | "UNSUPPORTED";
476
+ interface SnapshotTokenError {
477
+ readonly code: SnapshotTokenErrorCode;
478
+ readonly message: string;
479
+ }
480
+ type SnapshotTokenVerification<T extends SnapshotTokenPayload = SnapshotTokenPayload> = {
481
+ readonly ok: true;
482
+ readonly valid: true;
483
+ readonly payload: T;
484
+ } | {
485
+ readonly ok: false;
486
+ readonly valid: false;
487
+ readonly error: SnapshotTokenError;
488
+ };
489
+ declare function createSignedSnapshotToken<T extends SnapshotTokenPayload>(payload: T, secretKey: SnapshotSecretKey): Promise<string>;
490
+ declare function verifySnapshotToken<T extends SnapshotTokenPayload = SnapshotTokenPayload>(token: string, secretKey: SnapshotSecretKey, now?: number): Promise<SnapshotTokenVerification<T>>;
491
+
492
+ export { CURRENT_RALLY_CONFIG_VERSION, type CheckInContext, type CheckInErrorCode, type CheckInInput, type CheckInResult$1 as CheckInResult, type ClaimTicketOptions, type Clock, type CompositeConditionFailure, type ConditionMatch, type ConditionMismatch, type ConsumeResult, type ConsumeRewardParams, DEFAULT_SHEET_THEME, type DetectorError, type DetectorErrorCode, type DetectorKind, type DetectorResult, type FontFamily, type GeoDetectorOptions, type GeoVerificationContext, InMemoryStorage, IndexedDBAdapter, type IndexedDBAdapterOptions, LocalStorageAdapter, type LocalStorageAdapterOptions, type LocalStorageFailureMode, type LocalizedString, type LocalizedText, type NfcDetectorOptions, type PasscodeCondition, type ProcessStampValue, type PublicRallyConfig, type PublicRewardItem, type QrDetectorOptions, type RallyConfig, type RallyConfigValidationResult, type RallySnapshot, type RedemptionMethod, type Result, type RewardConsumeError, type RewardItem, type RewardState, type RewardStatus, type RewardType, type SheetTheme, type SlotShape, type SnapshotSecretKey, type SnapshotTokenError, type SnapshotTokenErrorCode, type SnapshotTokenPayload, type SnapshotTokenVerification, type SpotItem, type StampCondition, type StampDefinition, type StampError, StampRallyClient, type StampRallyEvent, type StampRallyListener, type StampRallyProgress, type StampRallyState, type StampRecord, type StampStorage, StorageAdapterError, type StorageAdapterErrorCode, type StorageLike, type StorageOperation, type StorageWarningHandler, type SupportedLocale, THEME_PRESETS, type ThemePreset, type ThemePresetId, type TokenVerificationContext, type ValidationError, type VerificationContext, calculateDistanceMeters, calculateProgress, consumeReward, createClaimTicketNumber, createSignedSnapshotToken, evaluateCheckIn, evaluateCondition, evaluateConditionDetailed, exportProgressToken, getCurrentGeoContext, importProgressToken, isGeolocationSupported, isNfcSupported, isQrSupported, isRewardState, isStampRallyState, issueClaimTicketNumber, migrateRallyConfig, normalizePasscode, processStamp, readNfcContext, readQrContext, reconcileRewardStates, resolveLocalizedText, stripSensitiveConfig, toLocalizedString, validateRallyConfig, verifyPasscode, verifySnapshotToken };