@stamprally/server 0.16.0 → 0.17.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 +56 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +28 -25
- package/dist/index.d.ts +28 -25
- package/dist/index.js +56 -15
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -7,7 +7,16 @@ async function executeClaimRewardTransaction(database, store, params2, mutation2
|
|
|
7
7
|
try {
|
|
8
8
|
return await database.transaction(async (transaction) => {
|
|
9
9
|
const current = await store.readContext(transaction, params2);
|
|
10
|
-
const
|
|
10
|
+
const secondaryStock = current.secondaryStock ?? null;
|
|
11
|
+
const rewardStock2 = params2.stockKey === "__shared__" ? secondaryStock : current.stock;
|
|
12
|
+
const next = mutation2({
|
|
13
|
+
...current,
|
|
14
|
+
rewardStock: rewardStock2,
|
|
15
|
+
sharedStock: params2.stockKey === "__shared__" ? current.stock : null,
|
|
16
|
+
primaryStock: rewardStock2,
|
|
17
|
+
secondaryStock,
|
|
18
|
+
stock: current.stock
|
|
19
|
+
});
|
|
11
20
|
if (next.error !== void 0) {
|
|
12
21
|
await store.writeAudit(transaction, next.auditLog);
|
|
13
22
|
if (params2.idempotencyKey !== void 0 && next.result !== void 0)
|
|
@@ -15,7 +24,7 @@ async function executeClaimRewardTransaction(database, store, params2, mutation2
|
|
|
15
24
|
return { success: false, error: next.error };
|
|
16
25
|
}
|
|
17
26
|
if (next.nextSecondaryStock !== void 0 && store.writeSecondaryStock === void 0)
|
|
18
|
-
return { success: false, error: "
|
|
27
|
+
return { success: false, error: "INVENTORY_STORAGE_NOT_IMPLEMENTED" };
|
|
19
28
|
if (next.nextStock !== null) await store.writeStock(transaction, params2, next.nextStock);
|
|
20
29
|
if (next.nextSecondaryStock !== void 0 && store.writeSecondaryStock !== void 0) {
|
|
21
30
|
if (next.nextSecondaryStock !== null)
|
|
@@ -146,9 +155,16 @@ var InMemoryServerPersistenceAdapter = class {
|
|
|
146
155
|
params2.stockKey ?? params2.rewardId
|
|
147
156
|
);
|
|
148
157
|
const storedSecondaryStock = params2.secondaryStockKey === void 0 ? null : await this.getRewardStock(params2.rallyId, params2.secondaryStockKey);
|
|
158
|
+
const stock = storedStock ?? initialStock;
|
|
159
|
+
const secondaryStock = storedSecondaryStock ?? initialSecondaryStock;
|
|
160
|
+
const rewardStock2 = params2.stockKey === "__shared__" ? secondaryStock : stock;
|
|
161
|
+
const sharedStock2 = params2.stockKey === "__shared__" ? stock : null;
|
|
149
162
|
const mutationResult = mutation2({
|
|
150
|
-
|
|
151
|
-
|
|
163
|
+
rewardStock: rewardStock2,
|
|
164
|
+
sharedStock: sharedStock2,
|
|
165
|
+
primaryStock: rewardStock2,
|
|
166
|
+
secondaryStock,
|
|
167
|
+
stock,
|
|
152
168
|
claimCount: await this.getUserClaimCount(params2.rallyId, params2.userId, params2.rewardId),
|
|
153
169
|
userState
|
|
154
170
|
});
|
|
@@ -680,7 +696,18 @@ function operationStatus(result) {
|
|
|
680
696
|
if (result.ok) return "ACCEPTED";
|
|
681
697
|
return result.code === "CONFLICT" || result.code === "PERSISTENCE_FAILED" ? "RETRYABLE_ERROR" : "REJECTED_PERMANENT";
|
|
682
698
|
}
|
|
683
|
-
function withDirectIdentity(request) {
|
|
699
|
+
function withDirectIdentity(request, authContext) {
|
|
700
|
+
if (authContext !== void 0) {
|
|
701
|
+
if (authContext.authenticatedUserId.trim() === "")
|
|
702
|
+
throw new RequestValidationException([
|
|
703
|
+
{
|
|
704
|
+
path: "authContext.authenticatedUserId",
|
|
705
|
+
message: "An authenticated userId is required.",
|
|
706
|
+
code: "REQUIRED"
|
|
707
|
+
}
|
|
708
|
+
]);
|
|
709
|
+
return { ...request, userId: authContext.authenticatedUserId };
|
|
710
|
+
}
|
|
684
711
|
if (request.userId !== void 0) return { ...request, userId: request.userId };
|
|
685
712
|
if (request.anonymousSessionId !== void 0 && isUuidV4(request.anonymousSessionId))
|
|
686
713
|
return { ...request, userId: request.anonymousSessionId };
|
|
@@ -810,8 +837,8 @@ var StampRallyServer = class {
|
|
|
810
837
|
throw error;
|
|
811
838
|
}
|
|
812
839
|
}
|
|
813
|
-
async checkIn(request) {
|
|
814
|
-
const directRequest = withDirectIdentity(request);
|
|
840
|
+
async checkIn(request, authContext) {
|
|
841
|
+
const directRequest = withDirectIdentity(request, authContext);
|
|
815
842
|
assertValidCheckInParams(directRequest, this.#config);
|
|
816
843
|
const { userId } = directRequest;
|
|
817
844
|
const key = `check-in:${request.rallyId}:${userId}:${request.idempotencyKey}`;
|
|
@@ -944,8 +971,8 @@ var StampRallyServer = class {
|
|
|
944
971
|
await this.#persistence.releaseLock(request.rallyId, lockKey);
|
|
945
972
|
}
|
|
946
973
|
}
|
|
947
|
-
async claimReward(request) {
|
|
948
|
-
const directRequest = withDirectIdentity(request);
|
|
974
|
+
async claimReward(request, authContext) {
|
|
975
|
+
const directRequest = withDirectIdentity(request, authContext);
|
|
949
976
|
assertValidClaimParams(directRequest, this.#config);
|
|
950
977
|
const { userId } = directRequest;
|
|
951
978
|
const key = `claim:${request.rallyId}:${userId}:${request.rewardId}:${request.idempotencyKey}`;
|
|
@@ -963,13 +990,14 @@ var StampRallyServer = class {
|
|
|
963
990
|
now(this.#options)
|
|
964
991
|
);
|
|
965
992
|
const plan = inventoryPlan(this.#config, reward.id, reward.stockLimit);
|
|
966
|
-
|
|
993
|
+
const inventoryEnabled = plan.primaryInitial !== null || plan.secondaryInitial !== void 0 && plan.secondaryInitial !== null;
|
|
994
|
+
if (inventoryEnabled && (this.#persistence.supportsRewardStock === false || typeof this.#persistence.getRewardStock !== "function" || typeof this.#persistence.executeClaimRewardTransaction !== "function"))
|
|
967
995
|
return this.#rememberClaim(
|
|
968
996
|
key,
|
|
969
997
|
{
|
|
970
998
|
ok: false,
|
|
971
|
-
code: "
|
|
972
|
-
message: "This persistence adapter cannot store
|
|
999
|
+
code: "INVENTORY_STORAGE_NOT_IMPLEMENTED",
|
|
1000
|
+
message: "This persistence adapter cannot atomically store inventory."
|
|
973
1001
|
},
|
|
974
1002
|
directRequest,
|
|
975
1003
|
now(this.#options)
|
|
@@ -1100,11 +1128,11 @@ var StampRallyServer = class {
|
|
|
1100
1128
|
const response = responseHolder.value;
|
|
1101
1129
|
if (!result.success) {
|
|
1102
1130
|
if (response !== null && !response.ok && response.code === result.error) return response;
|
|
1103
|
-
if (result.error === "
|
|
1131
|
+
if (result.error === "INVENTORY_STORAGE_NOT_IMPLEMENTED")
|
|
1104
1132
|
return {
|
|
1105
1133
|
ok: false,
|
|
1106
|
-
code: "
|
|
1107
|
-
message: "This persistence adapter cannot store
|
|
1134
|
+
code: "INVENTORY_STORAGE_NOT_IMPLEMENTED",
|
|
1135
|
+
message: "This persistence adapter cannot atomically store inventory."
|
|
1108
1136
|
};
|
|
1109
1137
|
return {
|
|
1110
1138
|
ok: false,
|
|
@@ -1312,6 +1340,15 @@ async function runPersistenceAdapterComplianceTests(createAdapter) {
|
|
|
1312
1340
|
await adapter.getRewardStock("compliance-rally", "reward") === 0,
|
|
1313
1341
|
"per-reward stock was not decremented atomically"
|
|
1314
1342
|
);
|
|
1343
|
+
const exhausted = await adapter.executeClaimRewardTransaction(
|
|
1344
|
+
params("carol", "boundary"),
|
|
1345
|
+
mutation
|
|
1346
|
+
);
|
|
1347
|
+
assert(!exhausted.success, "a zero-stock claim was accepted");
|
|
1348
|
+
assert(
|
|
1349
|
+
await adapter.getRewardStock("compliance-rally", "__shared__") === 0 && await adapter.getRewardStock("compliance-rally", "reward") === 0,
|
|
1350
|
+
"zero-stock rejection did not preserve both stock boundaries"
|
|
1351
|
+
);
|
|
1315
1352
|
const idempotentAdapter = await createAdapter();
|
|
1316
1353
|
const idempotentParams = params("alice", "same-key");
|
|
1317
1354
|
const firstClaim = await idempotentAdapter.executeClaimRewardTransaction(
|
|
@@ -1327,6 +1364,10 @@ async function runPersistenceAdapterComplianceTests(createAdapter) {
|
|
|
1327
1364
|
await idempotentAdapter.getRewardStock("compliance-rally", "__shared__") === 0,
|
|
1328
1365
|
"idempotent retry decremented shared stock twice"
|
|
1329
1366
|
);
|
|
1367
|
+
assert(
|
|
1368
|
+
await idempotentAdapter.getRewardStock("compliance-rally", "reward") === 0,
|
|
1369
|
+
"idempotent retry decremented per-reward stock twice"
|
|
1370
|
+
);
|
|
1330
1371
|
const rollbackAdapter = await createAdapter();
|
|
1331
1372
|
const rollbackParams = params("alice", "rollback");
|
|
1332
1373
|
const rollback = await rollbackAdapter.executeClaimRewardTransaction(rollbackParams, () => {
|