@stamprally/server 0.19.0 → 0.20.1
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 +26 -1
- package/dist/index.cjs +82 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +82 -12
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as _stamprally_core from '@stamprally/core';
|
|
2
|
-
import { UserRallyState, AdminRallyConfig } from '@stamprally/core';
|
|
3
|
-
export { UserRallyState } from '@stamprally/core';
|
|
2
|
+
import { UserRallyState, AdminRallyConfig, SyncProgressResponse } from '@stamprally/core';
|
|
3
|
+
export { SyncOperationResult, SyncProgressResponse, UserRallyState } from '@stamprally/core';
|
|
4
4
|
|
|
5
5
|
/** Authentication already verified by the host application's middleware. */
|
|
6
6
|
interface TrustedAuthContext {
|
|
@@ -276,7 +276,7 @@ declare class StampRallyServer {
|
|
|
276
276
|
checkIn(request: CheckInRequest, authContext?: TrustedAuthContext): Promise<CheckInResponse>;
|
|
277
277
|
claimReward(request: ClaimRewardRequest, authContext?: TrustedAuthContext): Promise<ClaimResponse>;
|
|
278
278
|
sync(rallyId: string, identity: string | TrustedAuthContext): Promise<UserRallyState>;
|
|
279
|
-
syncProgress(request: SyncProgressRequest, authContext: TrustedAuthContext): Promise<
|
|
279
|
+
syncProgress(request: SyncProgressRequest, authContext: TrustedAuthContext): Promise<SyncProgressResponse>;
|
|
280
280
|
}
|
|
281
281
|
|
|
282
282
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as _stamprally_core from '@stamprally/core';
|
|
2
|
-
import { UserRallyState, AdminRallyConfig } from '@stamprally/core';
|
|
3
|
-
export { UserRallyState } from '@stamprally/core';
|
|
2
|
+
import { UserRallyState, AdminRallyConfig, SyncProgressResponse } from '@stamprally/core';
|
|
3
|
+
export { SyncOperationResult, SyncProgressResponse, UserRallyState } from '@stamprally/core';
|
|
4
4
|
|
|
5
5
|
/** Authentication already verified by the host application's middleware. */
|
|
6
6
|
interface TrustedAuthContext {
|
|
@@ -276,7 +276,7 @@ declare class StampRallyServer {
|
|
|
276
276
|
checkIn(request: CheckInRequest, authContext?: TrustedAuthContext): Promise<CheckInResponse>;
|
|
277
277
|
claimReward(request: ClaimRewardRequest, authContext?: TrustedAuthContext): Promise<ClaimResponse>;
|
|
278
278
|
sync(rallyId: string, identity: string | TrustedAuthContext): Promise<UserRallyState>;
|
|
279
|
-
syncProgress(request: SyncProgressRequest, authContext: TrustedAuthContext): Promise<
|
|
279
|
+
syncProgress(request: SyncProgressRequest, authContext: TrustedAuthContext): Promise<SyncProgressResponse>;
|
|
280
280
|
}
|
|
281
281
|
|
|
282
282
|
/**
|
package/dist/index.js
CHANGED
|
@@ -661,6 +661,15 @@ function timestampMillis(timestamp) {
|
|
|
661
661
|
const value = Date.parse(timestamp);
|
|
662
662
|
return Number.isFinite(value) ? value : Date.now();
|
|
663
663
|
}
|
|
664
|
+
function operationTimestamp(operation) {
|
|
665
|
+
const value = operation.request.now;
|
|
666
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
667
|
+
if (typeof value === "string") {
|
|
668
|
+
const parsed = Date.parse(value);
|
|
669
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
670
|
+
}
|
|
671
|
+
return Number.MAX_SAFE_INTEGER;
|
|
672
|
+
}
|
|
664
673
|
function validationResponse(errors2) {
|
|
665
674
|
return json(
|
|
666
675
|
{
|
|
@@ -748,6 +757,38 @@ function operationStatus(result) {
|
|
|
748
757
|
if (result.ok) return "ACCEPTED";
|
|
749
758
|
return result.code === "CONFLICT" || result.code === "PERSISTENCE_FAILED" ? "RETRYABLE_ERROR" : "REJECTED_PERMANENT";
|
|
750
759
|
}
|
|
760
|
+
function syncOperationId(operation, userId) {
|
|
761
|
+
return `${operation.kind}:${operation.request.rallyId}:${userId}:${operation.request.idempotencyKey}`;
|
|
762
|
+
}
|
|
763
|
+
function syncOperationResult(operation, userId, result) {
|
|
764
|
+
const operationId = syncOperationId(operation, userId);
|
|
765
|
+
const resourceId = operation.kind === "checkIn" ? operation.request.spotId : operation.request.rewardId;
|
|
766
|
+
const action = operation.kind === "checkIn" ? "CHECK_IN" : "CLAIM_REWARD";
|
|
767
|
+
const status = operationStatus(result);
|
|
768
|
+
if (status === "ACCEPTED") {
|
|
769
|
+
return {
|
|
770
|
+
operationId,
|
|
771
|
+
status,
|
|
772
|
+
resourceId,
|
|
773
|
+
action,
|
|
774
|
+
appliedAt: timestampMillis(result.ok ? result.state.updatedAt : "")
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
if (status === "RETRYABLE_ERROR")
|
|
778
|
+
return {
|
|
779
|
+
operationId,
|
|
780
|
+
status: "FAILED_RETRYABLE",
|
|
781
|
+
resourceId,
|
|
782
|
+
error: result.ok ? "The operation can be retried." : result.message
|
|
783
|
+
};
|
|
784
|
+
return {
|
|
785
|
+
operationId,
|
|
786
|
+
status,
|
|
787
|
+
resourceId,
|
|
788
|
+
errorCode: result.ok ? "REJECTED_PERMANENT" : result.code,
|
|
789
|
+
reason: result.ok ? "The operation was rejected." : result.message
|
|
790
|
+
};
|
|
791
|
+
}
|
|
751
792
|
function withDirectIdentity(request, authContext) {
|
|
752
793
|
if (authContext !== void 0) {
|
|
753
794
|
if (authContext.authenticatedUserId.trim() === "")
|
|
@@ -880,16 +921,17 @@ var StampRallyServer = class {
|
|
|
880
921
|
...sessionId === null ? {} : { isAnonymous: true, sessionId }
|
|
881
922
|
};
|
|
882
923
|
try {
|
|
924
|
+
const progress = await this.syncProgress(
|
|
925
|
+
{
|
|
926
|
+
rallyId: body.data.rallyId,
|
|
927
|
+
...body.data.operations === void 0 ? {} : { operations: body.data.operations },
|
|
928
|
+
...sessionId === null ? {} : { anonymousSessionId: sessionId }
|
|
929
|
+
},
|
|
930
|
+
authContext
|
|
931
|
+
);
|
|
883
932
|
return json({
|
|
884
933
|
ok: true,
|
|
885
|
-
|
|
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
|
-
)
|
|
934
|
+
...progress
|
|
893
935
|
});
|
|
894
936
|
} catch (error) {
|
|
895
937
|
if (error instanceof RequestValidationException) return validationResponse(error.errors);
|
|
@@ -1226,11 +1268,39 @@ var StampRallyServer = class {
|
|
|
1226
1268
|
async syncProgress(request, authContext) {
|
|
1227
1269
|
const directRequest = withDirectIdentity(request, authContext);
|
|
1228
1270
|
assertValidSyncParams(directRequest, this.#config);
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1271
|
+
const results = [];
|
|
1272
|
+
const rejectedCheckIns = /* @__PURE__ */ new Set();
|
|
1273
|
+
const operations = [...request.operations ?? []].map((operation, index) => ({ operation, index })).sort(
|
|
1274
|
+
(left, right) => operationTimestamp(left.operation) - operationTimestamp(right.operation) || left.index - right.index
|
|
1275
|
+
).map(({ operation }) => operation);
|
|
1276
|
+
for (const operation of operations) {
|
|
1277
|
+
const resourceId = operation.kind === "checkIn" ? operation.request.spotId : operation.request.rewardId;
|
|
1278
|
+
if (operation.kind === "checkIn") {
|
|
1279
|
+
const spot = this.#config.spots.find((candidate) => candidate.id === resourceId);
|
|
1280
|
+
if (spot?.prerequisites?.some((prerequisite) => rejectedCheckIns.has(prerequisite))) {
|
|
1281
|
+
results.push({
|
|
1282
|
+
operationId: syncOperationId(operation, directRequest.userId),
|
|
1283
|
+
status: "REJECTED_PERMANENT",
|
|
1284
|
+
resourceId,
|
|
1285
|
+
errorCode: "REJECTED_PREREQUISITE_FAILED",
|
|
1286
|
+
reason: "A prerequisite operation was rejected by the server."
|
|
1287
|
+
});
|
|
1288
|
+
rejectedCheckIns.add(resourceId);
|
|
1289
|
+
continue;
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
const result = operation.kind === "checkIn" ? await this.checkIn(operation.request, authContext) : await this.claimReward(operation.request, authContext);
|
|
1293
|
+
const operationResult = syncOperationResult(operation, directRequest.userId, result);
|
|
1294
|
+
results.push(operationResult);
|
|
1295
|
+
if (operation.kind === "checkIn" && operationResult.status === "REJECTED_PERMANENT")
|
|
1296
|
+
rejectedCheckIns.add(resourceId);
|
|
1232
1297
|
}
|
|
1233
|
-
|
|
1298
|
+
const currentState = await this.sync(directRequest.rallyId, authContext);
|
|
1299
|
+
return {
|
|
1300
|
+
results,
|
|
1301
|
+
currentState,
|
|
1302
|
+
syncTimestamp: timestampMillis(now(this.#options))
|
|
1303
|
+
};
|
|
1234
1304
|
}
|
|
1235
1305
|
async #attachInventory(state2) {
|
|
1236
1306
|
const rewardRemaining = {};
|