@stamprally/server 0.17.0 → 0.19.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 +1 -1
- package/dist/index.cjs +88 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -7
- package/dist/index.d.ts +20 -7
- package/dist/index.js +88 -18
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -93,6 +93,8 @@ interface ServerPersistenceAdapter {
|
|
|
93
93
|
}>;
|
|
94
94
|
/** Set false when the adapter cannot persist per-reward inventory atomically. */
|
|
95
95
|
readonly supportsRewardStock?: boolean;
|
|
96
|
+
/** Must be true when a claim plan uses `secondaryStockKey`. */
|
|
97
|
+
readonly supportsSecondaryStock?: boolean;
|
|
96
98
|
acquireLock(rallyId: string, lockKey: string, ttlMs: number): Promise<boolean>;
|
|
97
99
|
releaseLock(rallyId: string, lockKey: string): Promise<void>;
|
|
98
100
|
getRewardStock(rallyId: string, rewardId: string): Promise<number | null>;
|
|
@@ -117,6 +119,7 @@ interface InMemoryServerPersistenceOptions {
|
|
|
117
119
|
declare class InMemoryServerPersistenceAdapter implements ServerPersistenceAdapter {
|
|
118
120
|
#private;
|
|
119
121
|
readonly supportsRewardStock: boolean;
|
|
122
|
+
readonly supportsSecondaryStock: boolean;
|
|
120
123
|
constructor(options?: InMemoryServerPersistenceOptions);
|
|
121
124
|
acquireLock(rallyId: string, key: string, ttlMs: number): Promise<boolean>;
|
|
122
125
|
releaseLock(rallyId: string, key: string): Promise<void>;
|
|
@@ -245,6 +248,7 @@ declare function validateSyncRequest(value: unknown): RequestValidationResult<{
|
|
|
245
248
|
readonly rallyId: string;
|
|
246
249
|
readonly userId?: string;
|
|
247
250
|
readonly anonymousSessionId?: string;
|
|
251
|
+
readonly operations?: SyncProgressRequest["operations"];
|
|
248
252
|
}>;
|
|
249
253
|
|
|
250
254
|
type ErrorResponse = {
|
|
@@ -271,12 +275,8 @@ declare class StampRallyServer {
|
|
|
271
275
|
handleSync(request: Request): Promise<Response>;
|
|
272
276
|
checkIn(request: CheckInRequest, authContext?: TrustedAuthContext): Promise<CheckInResponse>;
|
|
273
277
|
claimReward(request: ClaimRewardRequest, authContext?: TrustedAuthContext): Promise<ClaimResponse>;
|
|
274
|
-
sync(rallyId: string,
|
|
275
|
-
syncProgress(request:
|
|
276
|
-
readonly rallyId: string;
|
|
277
|
-
readonly userId?: string;
|
|
278
|
-
readonly anonymousSessionId?: string;
|
|
279
|
-
}): Promise<UserRallyState>;
|
|
278
|
+
sync(rallyId: string, identity: string | TrustedAuthContext): Promise<UserRallyState>;
|
|
279
|
+
syncProgress(request: SyncProgressRequest, authContext: TrustedAuthContext): Promise<UserRallyState>;
|
|
280
280
|
}
|
|
281
281
|
|
|
282
282
|
/**
|
|
@@ -316,6 +316,19 @@ interface ClaimRewardRequest {
|
|
|
316
316
|
readonly staffId?: string;
|
|
317
317
|
readonly now?: string | number;
|
|
318
318
|
}
|
|
319
|
+
type SyncProgressOperation = {
|
|
320
|
+
readonly kind: "checkIn";
|
|
321
|
+
readonly request: CheckInRequest;
|
|
322
|
+
} | {
|
|
323
|
+
readonly kind: "claimReward";
|
|
324
|
+
readonly request: ClaimRewardRequest;
|
|
325
|
+
};
|
|
326
|
+
interface SyncProgressRequest {
|
|
327
|
+
readonly rallyId: string;
|
|
328
|
+
readonly userId?: string;
|
|
329
|
+
readonly anonymousSessionId?: string;
|
|
330
|
+
readonly operations?: ReadonlyArray<SyncProgressOperation>;
|
|
331
|
+
}
|
|
319
332
|
type CheckInResponse = {
|
|
320
333
|
readonly ok: true;
|
|
321
334
|
readonly state: UserRallyState;
|
|
@@ -335,4 +348,4 @@ interface ServerOptions {
|
|
|
335
348
|
readonly anonymousPolicy?: "session_scoped" | "reject" | "shared_global_opt_in_insecure";
|
|
336
349
|
}
|
|
337
350
|
|
|
338
|
-
export { type AuditLog, type AuthenticationContext, type CheckInRequest, type CheckInResponse, type CheckInTransactionMutation, type CheckInTransactionParams, type ClaimRewardMutationContext, type ClaimRewardRequest, type ClaimRewardTransactionMutation, type ClaimRewardTransactionParams, InMemoryServerPersistenceAdapter, type InMemoryServerPersistenceOptions, type RedisMultiExecutor, type RequestValidationError, RequestValidationException, type RequestValidationResult, type ServerOptions, type ServerPersistenceAdapter, type SqlCheckInStore, type SqlClaimRewardStore, type SqlTransactionDatabase, StampRallyServer, type SyncOperationStatus, type TrustedAuthContext, type UserClaimRecord, assertValidCheckInParams, assertValidClaimParams, assertValidSyncParams, executeCheckInTransaction, executeClaimRewardTransaction, executeRedisTransaction, runPersistenceAdapterComplianceTests, validateCheckInRequest, validateClaimRewardRequest, validateSyncRequest };
|
|
351
|
+
export { type AuditLog, type AuthenticationContext, type CheckInRequest, type CheckInResponse, type CheckInTransactionMutation, type CheckInTransactionParams, type ClaimRewardMutationContext, type ClaimRewardRequest, type ClaimRewardTransactionMutation, type ClaimRewardTransactionParams, InMemoryServerPersistenceAdapter, type InMemoryServerPersistenceOptions, type RedisMultiExecutor, type RequestValidationError, RequestValidationException, type RequestValidationResult, type ServerOptions, type ServerPersistenceAdapter, type SqlCheckInStore, type SqlClaimRewardStore, type SqlTransactionDatabase, StampRallyServer, type SyncOperationStatus, type SyncProgressOperation, type SyncProgressRequest, type TrustedAuthContext, type UserClaimRecord, assertValidCheckInParams, assertValidClaimParams, assertValidSyncParams, executeCheckInTransaction, executeClaimRewardTransaction, executeRedisTransaction, runPersistenceAdapterComplianceTests, validateCheckInRequest, validateClaimRewardRequest, validateSyncRequest };
|
package/dist/index.d.ts
CHANGED
|
@@ -93,6 +93,8 @@ interface ServerPersistenceAdapter {
|
|
|
93
93
|
}>;
|
|
94
94
|
/** Set false when the adapter cannot persist per-reward inventory atomically. */
|
|
95
95
|
readonly supportsRewardStock?: boolean;
|
|
96
|
+
/** Must be true when a claim plan uses `secondaryStockKey`. */
|
|
97
|
+
readonly supportsSecondaryStock?: boolean;
|
|
96
98
|
acquireLock(rallyId: string, lockKey: string, ttlMs: number): Promise<boolean>;
|
|
97
99
|
releaseLock(rallyId: string, lockKey: string): Promise<void>;
|
|
98
100
|
getRewardStock(rallyId: string, rewardId: string): Promise<number | null>;
|
|
@@ -117,6 +119,7 @@ interface InMemoryServerPersistenceOptions {
|
|
|
117
119
|
declare class InMemoryServerPersistenceAdapter implements ServerPersistenceAdapter {
|
|
118
120
|
#private;
|
|
119
121
|
readonly supportsRewardStock: boolean;
|
|
122
|
+
readonly supportsSecondaryStock: boolean;
|
|
120
123
|
constructor(options?: InMemoryServerPersistenceOptions);
|
|
121
124
|
acquireLock(rallyId: string, key: string, ttlMs: number): Promise<boolean>;
|
|
122
125
|
releaseLock(rallyId: string, key: string): Promise<void>;
|
|
@@ -245,6 +248,7 @@ declare function validateSyncRequest(value: unknown): RequestValidationResult<{
|
|
|
245
248
|
readonly rallyId: string;
|
|
246
249
|
readonly userId?: string;
|
|
247
250
|
readonly anonymousSessionId?: string;
|
|
251
|
+
readonly operations?: SyncProgressRequest["operations"];
|
|
248
252
|
}>;
|
|
249
253
|
|
|
250
254
|
type ErrorResponse = {
|
|
@@ -271,12 +275,8 @@ declare class StampRallyServer {
|
|
|
271
275
|
handleSync(request: Request): Promise<Response>;
|
|
272
276
|
checkIn(request: CheckInRequest, authContext?: TrustedAuthContext): Promise<CheckInResponse>;
|
|
273
277
|
claimReward(request: ClaimRewardRequest, authContext?: TrustedAuthContext): Promise<ClaimResponse>;
|
|
274
|
-
sync(rallyId: string,
|
|
275
|
-
syncProgress(request:
|
|
276
|
-
readonly rallyId: string;
|
|
277
|
-
readonly userId?: string;
|
|
278
|
-
readonly anonymousSessionId?: string;
|
|
279
|
-
}): Promise<UserRallyState>;
|
|
278
|
+
sync(rallyId: string, identity: string | TrustedAuthContext): Promise<UserRallyState>;
|
|
279
|
+
syncProgress(request: SyncProgressRequest, authContext: TrustedAuthContext): Promise<UserRallyState>;
|
|
280
280
|
}
|
|
281
281
|
|
|
282
282
|
/**
|
|
@@ -316,6 +316,19 @@ interface ClaimRewardRequest {
|
|
|
316
316
|
readonly staffId?: string;
|
|
317
317
|
readonly now?: string | number;
|
|
318
318
|
}
|
|
319
|
+
type SyncProgressOperation = {
|
|
320
|
+
readonly kind: "checkIn";
|
|
321
|
+
readonly request: CheckInRequest;
|
|
322
|
+
} | {
|
|
323
|
+
readonly kind: "claimReward";
|
|
324
|
+
readonly request: ClaimRewardRequest;
|
|
325
|
+
};
|
|
326
|
+
interface SyncProgressRequest {
|
|
327
|
+
readonly rallyId: string;
|
|
328
|
+
readonly userId?: string;
|
|
329
|
+
readonly anonymousSessionId?: string;
|
|
330
|
+
readonly operations?: ReadonlyArray<SyncProgressOperation>;
|
|
331
|
+
}
|
|
319
332
|
type CheckInResponse = {
|
|
320
333
|
readonly ok: true;
|
|
321
334
|
readonly state: UserRallyState;
|
|
@@ -335,4 +348,4 @@ interface ServerOptions {
|
|
|
335
348
|
readonly anonymousPolicy?: "session_scoped" | "reject" | "shared_global_opt_in_insecure";
|
|
336
349
|
}
|
|
337
350
|
|
|
338
|
-
export { type AuditLog, type AuthenticationContext, type CheckInRequest, type CheckInResponse, type CheckInTransactionMutation, type CheckInTransactionParams, type ClaimRewardMutationContext, type ClaimRewardRequest, type ClaimRewardTransactionMutation, type ClaimRewardTransactionParams, InMemoryServerPersistenceAdapter, type InMemoryServerPersistenceOptions, type RedisMultiExecutor, type RequestValidationError, RequestValidationException, type RequestValidationResult, type ServerOptions, type ServerPersistenceAdapter, type SqlCheckInStore, type SqlClaimRewardStore, type SqlTransactionDatabase, StampRallyServer, type SyncOperationStatus, type TrustedAuthContext, type UserClaimRecord, assertValidCheckInParams, assertValidClaimParams, assertValidSyncParams, executeCheckInTransaction, executeClaimRewardTransaction, executeRedisTransaction, runPersistenceAdapterComplianceTests, validateCheckInRequest, validateClaimRewardRequest, validateSyncRequest };
|
|
351
|
+
export { type AuditLog, type AuthenticationContext, type CheckInRequest, type CheckInResponse, type CheckInTransactionMutation, type CheckInTransactionParams, type ClaimRewardMutationContext, type ClaimRewardRequest, type ClaimRewardTransactionMutation, type ClaimRewardTransactionParams, InMemoryServerPersistenceAdapter, type InMemoryServerPersistenceOptions, type RedisMultiExecutor, type RequestValidationError, RequestValidationException, type RequestValidationResult, type ServerOptions, type ServerPersistenceAdapter, type SqlCheckInStore, type SqlClaimRewardStore, type SqlTransactionDatabase, StampRallyServer, type SyncOperationStatus, type SyncProgressOperation, type SyncProgressRequest, type TrustedAuthContext, type UserClaimRecord, assertValidCheckInParams, assertValidClaimParams, assertValidSyncParams, executeCheckInTransaction, executeClaimRewardTransaction, executeRedisTransaction, runPersistenceAdapterComplianceTests, validateCheckInRequest, validateClaimRewardRequest, validateSyncRequest };
|
package/dist/index.js
CHANGED
|
@@ -2,9 +2,13 @@ import { reconcileRewardStates, consumeReward, evaluateConditionDetailed } from
|
|
|
2
2
|
|
|
3
3
|
// src/examples/transaction.ts
|
|
4
4
|
async function executeClaimRewardTransaction(database, store, params2, mutation2) {
|
|
5
|
+
if (params2.secondaryStockKey !== void 0 && store.writeSecondaryStock === void 0)
|
|
6
|
+
return { success: false, error: "SECONDARY_STOCK_UNSUPPORTED" };
|
|
5
7
|
try {
|
|
6
8
|
return await database.transaction(async (transaction) => {
|
|
7
9
|
const current = await store.readContext(transaction, params2);
|
|
10
|
+
if (params2.secondaryStockKey !== void 0 && current.secondaryStock === void 0)
|
|
11
|
+
return { success: false, error: "SECONDARY_STOCK_UNSUPPORTED" };
|
|
8
12
|
const secondaryStock = current.secondaryStock ?? null;
|
|
9
13
|
const rewardStock2 = params2.stockKey === "__shared__" ? secondaryStock : current.stock;
|
|
10
14
|
const next = mutation2({
|
|
@@ -76,6 +80,7 @@ async function executeRedisTransaction(redis, queue) {
|
|
|
76
80
|
// src/persistence.ts
|
|
77
81
|
var InMemoryServerPersistenceAdapter = class {
|
|
78
82
|
supportsRewardStock = true;
|
|
83
|
+
supportsSecondaryStock = true;
|
|
79
84
|
#locks = /* @__PURE__ */ new Map();
|
|
80
85
|
#idempotent = /* @__PURE__ */ new Map();
|
|
81
86
|
#states = /* @__PURE__ */ new Map();
|
|
@@ -595,6 +600,40 @@ function validateSyncRequest(value) {
|
|
|
595
600
|
return errors({ path: "$", message: "Expected an object.", code: "INVALID_TYPE" });
|
|
596
601
|
if (value.userId !== void 0 && !nonEmpty(value.userId))
|
|
597
602
|
return errors({ path: "userId", message: "userId must be non-empty.", code: "INVALID_TYPE" });
|
|
603
|
+
if (value.operations !== void 0) {
|
|
604
|
+
if (!Array.isArray(value.operations))
|
|
605
|
+
return errors({
|
|
606
|
+
path: "operations",
|
|
607
|
+
message: "operations must be an array.",
|
|
608
|
+
code: "INVALID_TYPE"
|
|
609
|
+
});
|
|
610
|
+
const operationErrors = [];
|
|
611
|
+
value.operations.forEach((operation, index) => {
|
|
612
|
+
const path = `operations[${index}]`;
|
|
613
|
+
if (!record(operation)) {
|
|
614
|
+
operationErrors.push({
|
|
615
|
+
path,
|
|
616
|
+
message: "Expected an operation object.",
|
|
617
|
+
code: "INVALID_TYPE"
|
|
618
|
+
});
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
if (operation.kind !== "checkIn" && operation.kind !== "claimReward") {
|
|
622
|
+
operationErrors.push({
|
|
623
|
+
path: `${path}.kind`,
|
|
624
|
+
message: "Unknown sync operation.",
|
|
625
|
+
code: "INVALID_ENUM"
|
|
626
|
+
});
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
const result = operation.kind === "checkIn" ? validateCheckInRequest(operation.request) : validateClaimRewardRequest(operation.request);
|
|
630
|
+
if (!result.success)
|
|
631
|
+
operationErrors.push(
|
|
632
|
+
...result.errors.map((error) => ({ ...error, path: `${path}.request.${error.path}` }))
|
|
633
|
+
);
|
|
634
|
+
});
|
|
635
|
+
if (operationErrors.length > 0) return errors(...operationErrors);
|
|
636
|
+
}
|
|
598
637
|
return {
|
|
599
638
|
success: true,
|
|
600
639
|
data: value
|
|
@@ -640,26 +679,41 @@ function initialState(config, userId, timestamp) {
|
|
|
640
679
|
updatedAt: timestamp
|
|
641
680
|
};
|
|
642
681
|
}
|
|
643
|
-
function rewardStock(config,
|
|
644
|
-
const
|
|
682
|
+
function rewardStock(config, reward) {
|
|
683
|
+
const stockLimit = reward.stockLimit;
|
|
684
|
+
const key = reward.stockKey ?? reward.id;
|
|
685
|
+
const configured = key === "__shared__" ? config.inventory?.sharedStock : config.inventory?.[key];
|
|
645
686
|
if (stockLimit === void 0) return configured ?? null;
|
|
646
687
|
if (configured === void 0) return stockLimit;
|
|
647
688
|
return Math.min(stockLimit, configured);
|
|
648
689
|
}
|
|
649
690
|
function sharedStock(config) {
|
|
650
|
-
return config.inventory?.sharedStock ??
|
|
691
|
+
return config.inventory?.sharedStock ?? null;
|
|
651
692
|
}
|
|
652
693
|
function inventoryPlan(config, rewardId, stockLimit) {
|
|
653
|
-
const
|
|
694
|
+
const reward = config.rewards.find((item) => item.id === rewardId);
|
|
695
|
+
const individual = reward === void 0 ? stockLimit ?? null : rewardStock(config, reward);
|
|
654
696
|
const shared = sharedStock(config);
|
|
697
|
+
const explicitPrimaryKey = reward?.stockKey;
|
|
698
|
+
const explicitSecondaryKey = reward?.secondaryStockKey;
|
|
655
699
|
if (config.inventoryMode === "shared" && shared !== null) {
|
|
656
700
|
return {
|
|
657
|
-
primaryKey: "__shared__",
|
|
658
|
-
primaryInitial: shared,
|
|
659
|
-
...
|
|
701
|
+
primaryKey: explicitPrimaryKey ?? "__shared__",
|
|
702
|
+
primaryInitial: explicitPrimaryKey === void 0 ? shared : individual,
|
|
703
|
+
...explicitSecondaryKey !== void 0 ? {
|
|
704
|
+
secondaryKey: explicitSecondaryKey,
|
|
705
|
+
secondaryInitial: config.inventory?.[explicitSecondaryKey] ?? individual
|
|
706
|
+
} : individual === null ? {} : { secondaryKey: rewardId, secondaryInitial: individual }
|
|
660
707
|
};
|
|
661
708
|
}
|
|
662
|
-
return {
|
|
709
|
+
return {
|
|
710
|
+
primaryKey: explicitPrimaryKey ?? rewardId,
|
|
711
|
+
primaryInitial: individual,
|
|
712
|
+
...explicitSecondaryKey === void 0 ? {} : {
|
|
713
|
+
secondaryKey: explicitSecondaryKey,
|
|
714
|
+
secondaryInitial: config.inventory?.[explicitSecondaryKey] ?? null
|
|
715
|
+
}
|
|
716
|
+
};
|
|
663
717
|
}
|
|
664
718
|
function getProof(context) {
|
|
665
719
|
return context.type === "qr" ? context.token : context.type === "passcode" ? context.code : context.type === "gps" ? { latitude: context.latitude, longitude: context.longitude } : context.type === "nfc" ? context.tagId : context.value;
|
|
@@ -821,14 +875,21 @@ var StampRallyServer = class {
|
|
|
821
875
|
401
|
|
822
876
|
);
|
|
823
877
|
const sessionId = request.headers.get("x-anonymous-session-id");
|
|
878
|
+
const authContext = {
|
|
879
|
+
authenticatedUserId: userId,
|
|
880
|
+
...sessionId === null ? {} : { isAnonymous: true, sessionId }
|
|
881
|
+
};
|
|
824
882
|
try {
|
|
825
883
|
return json({
|
|
826
884
|
ok: true,
|
|
827
|
-
state: await this.syncProgress(
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
885
|
+
state: await this.syncProgress(
|
|
886
|
+
{
|
|
887
|
+
rallyId: body.data.rallyId,
|
|
888
|
+
...body.data.operations === void 0 ? {} : { operations: body.data.operations },
|
|
889
|
+
...sessionId === null ? {} : { anonymousSessionId: sessionId }
|
|
890
|
+
},
|
|
891
|
+
authContext
|
|
892
|
+
)
|
|
832
893
|
});
|
|
833
894
|
} catch (error) {
|
|
834
895
|
if (error instanceof RequestValidationException) return validationResponse(error.errors);
|
|
@@ -988,6 +1049,8 @@ var StampRallyServer = class {
|
|
|
988
1049
|
now(this.#options)
|
|
989
1050
|
);
|
|
990
1051
|
const plan = inventoryPlan(this.#config, reward.id, reward.stockLimit);
|
|
1052
|
+
if (plan.secondaryKey !== void 0 && this.#persistence.supportsSecondaryStock !== true)
|
|
1053
|
+
throw new Error("SECONDARY_STOCK_UNSUPPORTED");
|
|
991
1054
|
const inventoryEnabled = plan.primaryInitial !== null || plan.secondaryInitial !== void 0 && plan.secondaryInitial !== null;
|
|
992
1055
|
if (inventoryEnabled && (this.#persistence.supportsRewardStock === false || typeof this.#persistence.getRewardStock !== "function" || typeof this.#persistence.executeClaimRewardTransaction !== "function"))
|
|
993
1056
|
return this.#rememberClaim(
|
|
@@ -1022,7 +1085,7 @@ var StampRallyServer = class {
|
|
|
1022
1085
|
rewardId: reward.id,
|
|
1023
1086
|
stockKey: plan.primaryKey,
|
|
1024
1087
|
...plan.secondaryKey === void 0 ? {} : { secondaryStockKey: plan.secondaryKey },
|
|
1025
|
-
rewardStockLimit: rewardStock(this.#config, reward
|
|
1088
|
+
rewardStockLimit: rewardStock(this.#config, reward),
|
|
1026
1089
|
sharedStockLimit: this.#config.inventoryMode === "shared" ? sharedStock(this.#config) : null,
|
|
1027
1090
|
initialStock: plan.primaryInitial,
|
|
1028
1091
|
...plan.secondaryInitial === void 0 ? {} : { initialSecondaryStock: plan.secondaryInitial },
|
|
@@ -1154,21 +1217,28 @@ var StampRallyServer = class {
|
|
|
1154
1217
|
await this.#persistence.releaseLock(request.rallyId, lockKey);
|
|
1155
1218
|
}
|
|
1156
1219
|
}
|
|
1157
|
-
async sync(rallyId,
|
|
1220
|
+
async sync(rallyId, identity) {
|
|
1221
|
+
const userId = typeof identity === "string" ? identity : identity.authenticatedUserId;
|
|
1158
1222
|
assertValidSyncParams({ rallyId, userId }, this.#config);
|
|
1159
1223
|
const state2 = await this.#persistence.getUserState(rallyId, userId) ?? initialState(this.#config, userId, now(this.#options));
|
|
1160
1224
|
return this.#attachInventory(state2);
|
|
1161
1225
|
}
|
|
1162
|
-
async syncProgress(request) {
|
|
1163
|
-
const directRequest = withDirectIdentity(request);
|
|
1226
|
+
async syncProgress(request, authContext) {
|
|
1227
|
+
const directRequest = withDirectIdentity(request, authContext);
|
|
1164
1228
|
assertValidSyncParams(directRequest, this.#config);
|
|
1165
|
-
|
|
1229
|
+
for (const operation of request.operations ?? []) {
|
|
1230
|
+
if (operation.kind === "checkIn") await this.checkIn(operation.request, authContext);
|
|
1231
|
+
else await this.claimReward(operation.request, authContext);
|
|
1232
|
+
}
|
|
1233
|
+
return this.sync(directRequest.rallyId, authContext);
|
|
1166
1234
|
}
|
|
1167
1235
|
async #attachInventory(state2) {
|
|
1168
1236
|
const rewardRemaining = {};
|
|
1169
1237
|
for (const reward of this.#config.rewards) {
|
|
1170
1238
|
const plan = inventoryPlan(this.#config, reward.id, reward.stockLimit);
|
|
1171
1239
|
if (plan.secondaryKey !== void 0) {
|
|
1240
|
+
if (this.#persistence.supportsSecondaryStock !== true)
|
|
1241
|
+
throw new Error("SECONDARY_STOCK_UNSUPPORTED");
|
|
1172
1242
|
const stock = await this.#persistence.getRewardStock(state2.rallyId, plan.secondaryKey);
|
|
1173
1243
|
const remaining = stock ?? plan.secondaryInitial ?? null;
|
|
1174
1244
|
if (remaining !== null) rewardRemaining[reward.id] = Math.max(0, remaining);
|