@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/README.md +161 -0
- package/dist/index.cjs +617 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +140 -20
- package/dist/index.d.ts +140 -20
- package/dist/index.js +609 -39
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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<
|
|
108
|
-
type LocalizedText = string |
|
|
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:
|
|
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
|
-
|
|
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<
|
|
108
|
-
type LocalizedText = string |
|
|
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:
|
|
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
|
-
|
|
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 };
|