@stardeck-customer-apps/testing 0.15.0 → 0.16.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/SKILL.md +40 -4
- package/dist/index.d.mts +63 -0
- package/dist/index.d.ts +63 -0
- package/dist/index.js +242 -44
- package/dist/index.mjs +242 -44
- package/dist/next/headers-shim.js +9 -0
- package/dist/next/headers-shim.mjs +9 -0
- package/dist/setup.js +163 -26
- package/dist/setup.mjs +163 -26
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -57,6 +57,31 @@ function globalSingleton(key, create) {
|
|
|
57
57
|
return holder[symbol];
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
// src/constants.ts
|
|
61
|
+
var TEST_DOMAIN_SUFFIX = ".stardeck.test";
|
|
62
|
+
var CONTROL_PLANE_TEST_URL = "https://control-plane.stardeck.test";
|
|
63
|
+
var DATA_STORE_TEST_HOST = "db.stardeck.test";
|
|
64
|
+
var DATA_STORE_TEST_URL = `postgresql://test:test@${DATA_STORE_TEST_HOST}/main`;
|
|
65
|
+
var STORAGE_TEST_URL = "https://storage.stardeck.test";
|
|
66
|
+
var STORAGE_TEST_HOST = "storage.stardeck.test";
|
|
67
|
+
var APP_TEST_ORIGIN = "http://localhost:3333";
|
|
68
|
+
var TEST_ENV_DEFAULTS = {
|
|
69
|
+
CONTROL_PLANE_URL: CONTROL_PLANE_TEST_URL,
|
|
70
|
+
DEPLOYMENT_SECRET: "stardeck-test-deployment-secret",
|
|
71
|
+
ORGANIZATION_ID: "00000000-0000-4000-8000-00000000000a",
|
|
72
|
+
PROJECT_ID: "00000000-0000-4000-8000-00000000000b",
|
|
73
|
+
DEPLOYMENT_ID: "00000000-0000-4000-8000-00000000000c",
|
|
74
|
+
STORAGE_URL: STORAGE_TEST_URL
|
|
75
|
+
};
|
|
76
|
+
var DEFAULT_TEST_USER = {
|
|
77
|
+
id: "test-user-1",
|
|
78
|
+
email: "test-user@example.com",
|
|
79
|
+
name: "Test User",
|
|
80
|
+
role: "member",
|
|
81
|
+
permissions: []
|
|
82
|
+
};
|
|
83
|
+
var DEFAULT_SCHEMA_PATH = "./src/generated/data-store-schema.sql";
|
|
84
|
+
|
|
60
85
|
// src/state.ts
|
|
61
86
|
var state = globalSingleton("state", () => ({
|
|
62
87
|
db: null,
|
|
@@ -68,6 +93,10 @@ var state = globalSingleton("state", () => ({
|
|
|
68
93
|
emailCounter: 0,
|
|
69
94
|
identities: /* @__PURE__ */ new Map(),
|
|
70
95
|
identityLinks: [],
|
|
96
|
+
identityClaims: /* @__PURE__ */ new Map(),
|
|
97
|
+
identityStaffAssistEnabled: false,
|
|
98
|
+
identitySuppressClaimDelivery: false,
|
|
99
|
+
identityAllowedReturnOrigins: /* @__PURE__ */ new Set([APP_TEST_ORIGIN]),
|
|
71
100
|
checkouts: [],
|
|
72
101
|
checkoutCounter: 0,
|
|
73
102
|
sessionStatuses: /* @__PURE__ */ new Map(),
|
|
@@ -109,30 +138,6 @@ function requireDb() {
|
|
|
109
138
|
return state.db;
|
|
110
139
|
}
|
|
111
140
|
|
|
112
|
-
// src/constants.ts
|
|
113
|
-
var TEST_DOMAIN_SUFFIX = ".stardeck.test";
|
|
114
|
-
var CONTROL_PLANE_TEST_URL = "https://control-plane.stardeck.test";
|
|
115
|
-
var DATA_STORE_TEST_HOST = "db.stardeck.test";
|
|
116
|
-
var DATA_STORE_TEST_URL = `postgresql://test:test@${DATA_STORE_TEST_HOST}/main`;
|
|
117
|
-
var STORAGE_TEST_URL = "https://storage.stardeck.test";
|
|
118
|
-
var STORAGE_TEST_HOST = "storage.stardeck.test";
|
|
119
|
-
var TEST_ENV_DEFAULTS = {
|
|
120
|
-
CONTROL_PLANE_URL: CONTROL_PLANE_TEST_URL,
|
|
121
|
-
DEPLOYMENT_SECRET: "stardeck-test-deployment-secret",
|
|
122
|
-
ORGANIZATION_ID: "00000000-0000-4000-8000-00000000000a",
|
|
123
|
-
PROJECT_ID: "00000000-0000-4000-8000-00000000000b",
|
|
124
|
-
DEPLOYMENT_ID: "00000000-0000-4000-8000-00000000000c",
|
|
125
|
-
STORAGE_URL: STORAGE_TEST_URL
|
|
126
|
-
};
|
|
127
|
-
var DEFAULT_TEST_USER = {
|
|
128
|
-
id: "test-user-1",
|
|
129
|
-
email: "test-user@example.com",
|
|
130
|
-
name: "Test User",
|
|
131
|
-
role: "member",
|
|
132
|
-
permissions: []
|
|
133
|
-
};
|
|
134
|
-
var DEFAULT_SCHEMA_PATH = "./src/generated/data-store-schema.sql";
|
|
135
|
-
|
|
136
141
|
// src/simulator/hmac.ts
|
|
137
142
|
import crypto from "crypto";
|
|
138
143
|
var TIMESTAMP_TOLERANCE_SECONDS = 300;
|
|
@@ -183,8 +188,8 @@ function json(body, status = 200) {
|
|
|
183
188
|
function success(data) {
|
|
184
189
|
return json({ success: true, data });
|
|
185
190
|
}
|
|
186
|
-
function failure(error, status = 400) {
|
|
187
|
-
return json({ success: false, error }, status);
|
|
191
|
+
function failure(error, status = 400, code) {
|
|
192
|
+
return json({ success: false, error, ...code ? { code } : {} }, status);
|
|
188
193
|
}
|
|
189
194
|
async function readJsonBody(request) {
|
|
190
195
|
try {
|
|
@@ -672,6 +677,12 @@ var TRUSTED_LINK_KINDS = /* @__PURE__ */ new Set([
|
|
|
672
677
|
"dashboard_user"
|
|
673
678
|
]);
|
|
674
679
|
var RESOLVE_KINDS = /* @__PURE__ */ new Set(["phone", "email", "line"]);
|
|
680
|
+
var CLAIM_KINDS = /* @__PURE__ */ new Set(["email", "phone"]);
|
|
681
|
+
var CLAIM_FLOWS = /* @__PURE__ */ new Set(["invite", "assist", "transfer"]);
|
|
682
|
+
var DUPLICABLE_UNVERIFIED_KINDS = /* @__PURE__ */ new Set(["email", "phone"]);
|
|
683
|
+
var CLAIM_TTL_MS = 48 * 60 * 60 * 1e3;
|
|
684
|
+
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
685
|
+
var STAFF_ASSISTANCE_NOT_ENABLED = "STAFF_ASSISTANCE_NOT_ENABLED";
|
|
675
686
|
var E164_PHONE = /^\+[1-9]\d{1,14}$/;
|
|
676
687
|
var SEARCH_DEFAULT_LIMIT = 50;
|
|
677
688
|
var SEARCH_MAX_LIMIT = 100;
|
|
@@ -754,6 +765,11 @@ function matchingLinks(kind, externalId) {
|
|
|
754
765
|
(link) => link.kind === kind && comparableExternalId(link.kind, link.externalId) === comparable
|
|
755
766
|
);
|
|
756
767
|
}
|
|
768
|
+
function sortedMatchingLinks(kind, externalId) {
|
|
769
|
+
return matchingLinks(kind, externalId).sort(
|
|
770
|
+
(a, b) => Number(b.verified) - Number(a.verified) || a.createdAt.localeCompare(b.createdAt) || state.identityLinks.indexOf(a) - state.identityLinks.indexOf(b)
|
|
771
|
+
);
|
|
772
|
+
}
|
|
757
773
|
function normalizedLinkKey(link) {
|
|
758
774
|
return `${link.kind}:${comparableExternalId(link.kind, link.externalId)}`;
|
|
759
775
|
}
|
|
@@ -867,9 +883,10 @@ async function handleAttachLink(identityId, request) {
|
|
|
867
883
|
);
|
|
868
884
|
if ("error" in normalized) return failure(normalized.error);
|
|
869
885
|
const { kind, externalId } = normalized;
|
|
870
|
-
const
|
|
871
|
-
|
|
872
|
-
|
|
886
|
+
const matches = matchingLinks(kind, externalId);
|
|
887
|
+
const ownRow = matches.find((link2) => link2.identityId === identityId);
|
|
888
|
+
if (ownRow) return success({ link: ownRow });
|
|
889
|
+
if (!DUPLICABLE_UNVERIFIED_KINDS.has(kind) && matches.length > 0) {
|
|
873
890
|
return failure("identifier already linked to another identity", 409);
|
|
874
891
|
}
|
|
875
892
|
const link = createUnverifiedLink(identityId, kind, externalId);
|
|
@@ -886,7 +903,7 @@ async function handleResolve(request) {
|
|
|
886
903
|
if (body.displayName !== void 0 && typeof body.displayName !== "string") {
|
|
887
904
|
return failure("displayName must be a string");
|
|
888
905
|
}
|
|
889
|
-
const links =
|
|
906
|
+
const links = sortedMatchingLinks(normalized.kind, normalized.externalId);
|
|
890
907
|
if (links.length > 0) {
|
|
891
908
|
const canonicalIds = /* @__PURE__ */ new Set();
|
|
892
909
|
for (const link of links) {
|
|
@@ -897,12 +914,15 @@ async function handleResolve(request) {
|
|
|
897
914
|
return failure(`cannot resolve identity link: ${message}`, 409);
|
|
898
915
|
}
|
|
899
916
|
}
|
|
900
|
-
|
|
917
|
+
const verifiedOwners = new Set(
|
|
918
|
+
links.filter((link) => link.verified).map((link) => link.identityId)
|
|
919
|
+
);
|
|
920
|
+
if (verifiedOwners.size > 1 || !DUPLICABLE_UNVERIFIED_KINDS.has(normalized.kind) && canonicalIds.size !== 1) {
|
|
901
921
|
return failure("identifier is linked to multiple identities", 409);
|
|
902
922
|
}
|
|
903
|
-
const
|
|
904
|
-
const
|
|
905
|
-
if (provenance === "guest" &&
|
|
923
|
+
const [chosen] = links;
|
|
924
|
+
const canonicalId = canonicalIdentity(chosen.identityId).id;
|
|
925
|
+
if (provenance === "guest" && chosen.verified) {
|
|
906
926
|
return success({ identityId: null, reason: "verified_conflict" });
|
|
907
927
|
}
|
|
908
928
|
return success({ identityId: canonicalId, created: false });
|
|
@@ -1004,6 +1024,116 @@ async function handleAliases(request) {
|
|
|
1004
1024
|
}
|
|
1005
1025
|
return success({ aliases });
|
|
1006
1026
|
}
|
|
1027
|
+
function claimView(claim) {
|
|
1028
|
+
const state_ = claim.state === "pending" && Date.parse(claim.expiresAt) <= Date.now() ? "expired" : claim.state;
|
|
1029
|
+
return {
|
|
1030
|
+
claimId: claim.claimId,
|
|
1031
|
+
flow: claim.flow,
|
|
1032
|
+
state: state_,
|
|
1033
|
+
refusalReason: claim.refusalReason,
|
|
1034
|
+
completedIdentityId: claim.completedIdentityId,
|
|
1035
|
+
expiresAt: claim.expiresAt
|
|
1036
|
+
};
|
|
1037
|
+
}
|
|
1038
|
+
function validateClaimStaff(flow, staff) {
|
|
1039
|
+
if (flow === "invite") {
|
|
1040
|
+
if (staff !== void 0) return { error: "staff is only accepted for assist or transfer" };
|
|
1041
|
+
return { staffActorRef: null };
|
|
1042
|
+
}
|
|
1043
|
+
if (!staff || typeof staff !== "object") {
|
|
1044
|
+
return { error: `staff is required for a ${flow} claim` };
|
|
1045
|
+
}
|
|
1046
|
+
const { actorRef, grant } = staff;
|
|
1047
|
+
if (typeof actorRef !== "string" || actorRef.length === 0) {
|
|
1048
|
+
return { error: "staff.actorRef is required" };
|
|
1049
|
+
}
|
|
1050
|
+
if (grant !== flow) return { error: `staff.grant must be '${flow}' for a ${flow} claim` };
|
|
1051
|
+
return { staffActorRef: actorRef };
|
|
1052
|
+
}
|
|
1053
|
+
async function handleCreateClaim(request) {
|
|
1054
|
+
const body = await readJsonBody(request);
|
|
1055
|
+
const flow = body.flow;
|
|
1056
|
+
if (typeof flow !== "string" || !CLAIM_FLOWS.has(flow)) {
|
|
1057
|
+
return failure("flow must be one of: invite, assist, transfer");
|
|
1058
|
+
}
|
|
1059
|
+
if (typeof body.identityId !== "string" || !UUID.test(body.identityId)) {
|
|
1060
|
+
return failure("identityId must be a uuid");
|
|
1061
|
+
}
|
|
1062
|
+
const normalized = normalizeLinkInput(
|
|
1063
|
+
body.kind,
|
|
1064
|
+
body.externalId,
|
|
1065
|
+
CLAIM_KINDS,
|
|
1066
|
+
"kind must be one of: email, phone"
|
|
1067
|
+
);
|
|
1068
|
+
if ("error" in normalized) return failure(normalized.error);
|
|
1069
|
+
let returnTo = null;
|
|
1070
|
+
if (body.returnTo !== void 0) {
|
|
1071
|
+
if (typeof body.returnTo !== "string") return failure("returnTo must be a string");
|
|
1072
|
+
let parsed;
|
|
1073
|
+
try {
|
|
1074
|
+
parsed = new URL(body.returnTo);
|
|
1075
|
+
} catch {
|
|
1076
|
+
return failure("returnTo must be an absolute URL");
|
|
1077
|
+
}
|
|
1078
|
+
returnTo = state.identityAllowedReturnOrigins.has(parsed.origin) ? body.returnTo : null;
|
|
1079
|
+
}
|
|
1080
|
+
if (body.deliver !== void 0 && typeof body.deliver !== "boolean") {
|
|
1081
|
+
return failure("deliver must be a boolean");
|
|
1082
|
+
}
|
|
1083
|
+
const staff = validateClaimStaff(flow, body.staff);
|
|
1084
|
+
if ("error" in staff) return failure(staff.error);
|
|
1085
|
+
if (flow !== "invite" && !state.identityStaffAssistEnabled) {
|
|
1086
|
+
return failure(
|
|
1087
|
+
"staff-assisted profile claiming is not enabled for this project",
|
|
1088
|
+
403,
|
|
1089
|
+
STAFF_ASSISTANCE_NOT_ENABLED
|
|
1090
|
+
);
|
|
1091
|
+
}
|
|
1092
|
+
const identity = state.identities.get(body.identityId);
|
|
1093
|
+
if (!identity || identity.type !== "person" || identity.status !== "active") {
|
|
1094
|
+
return failure("identity not found", 404);
|
|
1095
|
+
}
|
|
1096
|
+
const holdsContact = matchingLinks(normalized.kind, normalized.externalId).some(
|
|
1097
|
+
(link) => link.identityId === identity.id
|
|
1098
|
+
);
|
|
1099
|
+
if (!holdsContact) {
|
|
1100
|
+
return failure("identity does not hold that contact", 409);
|
|
1101
|
+
}
|
|
1102
|
+
const claimId = crypto3.randomUUID();
|
|
1103
|
+
const deliver = body.deliver === true;
|
|
1104
|
+
const handle = crypto3.randomBytes(32).toString("base64url");
|
|
1105
|
+
const claim = {
|
|
1106
|
+
claimId,
|
|
1107
|
+
flow,
|
|
1108
|
+
identityId: identity.id,
|
|
1109
|
+
kind: normalized.kind,
|
|
1110
|
+
externalId: normalized.externalId,
|
|
1111
|
+
returnTo,
|
|
1112
|
+
staffActorRef: staff.staffActorRef,
|
|
1113
|
+
state: "pending",
|
|
1114
|
+
refusalReason: null,
|
|
1115
|
+
completedIdentityId: null,
|
|
1116
|
+
claimantClerkUserId: null,
|
|
1117
|
+
claimUrl: `${CONTROL_PLANE_TEST_URL}/claim/${handle}`,
|
|
1118
|
+
delivered: deliver ? !state.identitySuppressClaimDelivery : null,
|
|
1119
|
+
expiresAt: new Date(Date.now() + CLAIM_TTL_MS).toISOString(),
|
|
1120
|
+
createdAt: now()
|
|
1121
|
+
};
|
|
1122
|
+
state.identityClaims.set(claimId, claim);
|
|
1123
|
+
return success({
|
|
1124
|
+
claimId,
|
|
1125
|
+
claimUrl: claim.claimUrl,
|
|
1126
|
+
expiresAt: claim.expiresAt,
|
|
1127
|
+
// Absent unless asked for: an app delivering its own link must not read a
|
|
1128
|
+
// missing field as "nothing was sent".
|
|
1129
|
+
...deliver ? { delivered: claim.delivered } : {}
|
|
1130
|
+
});
|
|
1131
|
+
}
|
|
1132
|
+
function handleGetClaim(claimId) {
|
|
1133
|
+
const claim = state.identityClaims.get(claimId);
|
|
1134
|
+
if (!claim) return failure("claim not found", 404);
|
|
1135
|
+
return success(claimView(claim));
|
|
1136
|
+
}
|
|
1007
1137
|
async function handleIdentitiesRequest(request, subPath) {
|
|
1008
1138
|
const method = request.method;
|
|
1009
1139
|
if (subPath === "" || subPath === "/") {
|
|
@@ -1019,6 +1149,13 @@ async function handleIdentitiesRequest(request, subPath) {
|
|
|
1019
1149
|
if (subPath === "/search" && method === "GET") {
|
|
1020
1150
|
return handleSearch(request);
|
|
1021
1151
|
}
|
|
1152
|
+
if (subPath === "/claims" && method === "POST") {
|
|
1153
|
+
return handleCreateClaim(request);
|
|
1154
|
+
}
|
|
1155
|
+
const claimMatch = subPath.match(/^\/claims\/([^/]+)$/);
|
|
1156
|
+
if (claimMatch && method === "GET") {
|
|
1157
|
+
return handleGetClaim(decodeURIComponent(claimMatch[1]));
|
|
1158
|
+
}
|
|
1022
1159
|
const linksMatch = subPath.match(/^\/([^/]+)\/links$/);
|
|
1023
1160
|
if (linksMatch && method === "POST") {
|
|
1024
1161
|
return handleAttachLink(linksMatch[1], request);
|
|
@@ -1043,11 +1180,13 @@ function seedVerifiedLink(identityId, params) {
|
|
|
1043
1180
|
);
|
|
1044
1181
|
if ("error" in normalized) throw new Error(normalized.error);
|
|
1045
1182
|
const { kind, externalId } = normalized;
|
|
1046
|
-
const
|
|
1183
|
+
const matches = matchingLinks(kind, externalId);
|
|
1184
|
+
const blocker = matches.find(
|
|
1185
|
+
(link2) => link2.identityId !== identityId && (link2.verified || !DUPLICABLE_UNVERIFIED_KINDS.has(link2.kind))
|
|
1186
|
+
);
|
|
1187
|
+
if (blocker) throw new Error("identifier already linked to another identity");
|
|
1188
|
+
const existing = matches.find((link2) => link2.identityId === identityId);
|
|
1047
1189
|
if (existing) {
|
|
1048
|
-
if (existing.identityId !== identityId) {
|
|
1049
|
-
throw new Error("identifier already linked to another identity");
|
|
1050
|
-
}
|
|
1051
1190
|
existing.verified = true;
|
|
1052
1191
|
return existing;
|
|
1053
1192
|
}
|
|
@@ -1077,7 +1216,12 @@ function merge(sourceIdentityId, canonicalIdentityId) {
|
|
|
1077
1216
|
);
|
|
1078
1217
|
for (const sourceLink of sourceLinks) {
|
|
1079
1218
|
const key = normalizedLinkKey(sourceLink);
|
|
1080
|
-
|
|
1219
|
+
const conflicting = otherLinks.some(
|
|
1220
|
+
(otherLink) => normalizedLinkKey(otherLink) === key && // Same partial rule as the index: a third identity's UNVERIFIED copy of a
|
|
1221
|
+
// shared email/phone is allowed to coexist and must not block the merge.
|
|
1222
|
+
(!DUPLICABLE_UNVERIFIED_KINDS.has(key.split(":")[0]) || otherLink.verified && sourceLink.verified)
|
|
1223
|
+
);
|
|
1224
|
+
if (conflicting) {
|
|
1081
1225
|
throw new Error("merge would conflict with a link owned by another identity");
|
|
1082
1226
|
}
|
|
1083
1227
|
}
|
|
@@ -1104,6 +1248,45 @@ function merge(sourceIdentityId, canonicalIdentityId) {
|
|
|
1104
1248
|
source.mergedIntoId = canonical.id;
|
|
1105
1249
|
source.updatedAt = now();
|
|
1106
1250
|
}
|
|
1251
|
+
function requireClaim(claimId) {
|
|
1252
|
+
const claim = state.identityClaims.get(claimId);
|
|
1253
|
+
if (!claim) throw new Error("claim not found");
|
|
1254
|
+
return claim;
|
|
1255
|
+
}
|
|
1256
|
+
function completeClaim(claimId, params) {
|
|
1257
|
+
const claim = requireClaim(claimId);
|
|
1258
|
+
if (claim.state !== "pending") return claim;
|
|
1259
|
+
if (Date.parse(claim.expiresAt) <= Date.now()) {
|
|
1260
|
+
claim.state = "expired";
|
|
1261
|
+
return claim;
|
|
1262
|
+
}
|
|
1263
|
+
if (!params.clerkUserId) throw new Error("clerkUserId is required");
|
|
1264
|
+
const rows = matchingLinks(claim.kind, claim.externalId);
|
|
1265
|
+
const target = rows.find((link) => link.identityId === claim.identityId);
|
|
1266
|
+
if (!target) {
|
|
1267
|
+
claim.state = "invalidated";
|
|
1268
|
+
return claim;
|
|
1269
|
+
}
|
|
1270
|
+
const otherVerified = rows.find((link) => link.verified && link.identityId !== claim.identityId);
|
|
1271
|
+
if (otherVerified) {
|
|
1272
|
+
claim.state = "refused";
|
|
1273
|
+
claim.refusalReason = "contact_owned";
|
|
1274
|
+
return claim;
|
|
1275
|
+
}
|
|
1276
|
+
target.verified = true;
|
|
1277
|
+
claim.state = "completed";
|
|
1278
|
+
claim.completedIdentityId = claim.identityId;
|
|
1279
|
+
claim.claimantClerkUserId = params.clerkUserId;
|
|
1280
|
+
return claim;
|
|
1281
|
+
}
|
|
1282
|
+
function refuseClaim(claimId, reason) {
|
|
1283
|
+
const claim = requireClaim(claimId);
|
|
1284
|
+
if (claim.state !== "pending") return claim;
|
|
1285
|
+
if (!reason) throw new Error("a refusal reason is required");
|
|
1286
|
+
claim.state = "refused";
|
|
1287
|
+
claim.refusalReason = reason;
|
|
1288
|
+
return claim;
|
|
1289
|
+
}
|
|
1107
1290
|
function createDirectory() {
|
|
1108
1291
|
return {
|
|
1109
1292
|
all: () => [...state.identities.values()],
|
|
@@ -1111,9 +1294,26 @@ function createDirectory() {
|
|
|
1111
1294
|
links: (identityId) => linksFor(identityId),
|
|
1112
1295
|
seedVerifiedLink,
|
|
1113
1296
|
merge,
|
|
1297
|
+
claims: () => [...state.identityClaims.values()],
|
|
1298
|
+
claim: (claimId) => state.identityClaims.get(claimId),
|
|
1299
|
+
enableStaffAssist: (enabled = true) => {
|
|
1300
|
+
state.identityStaffAssistEnabled = enabled;
|
|
1301
|
+
},
|
|
1302
|
+
suppressClaimDelivery: (enabled = true) => {
|
|
1303
|
+
state.identitySuppressClaimDelivery = enabled;
|
|
1304
|
+
},
|
|
1305
|
+
allowReturnOrigin: (origin) => {
|
|
1306
|
+
state.identityAllowedReturnOrigins.add(new URL(origin).origin);
|
|
1307
|
+
},
|
|
1308
|
+
completeClaim,
|
|
1309
|
+
refuseClaim,
|
|
1114
1310
|
clear: () => {
|
|
1115
1311
|
state.identities.clear();
|
|
1116
1312
|
state.identityLinks = [];
|
|
1313
|
+
state.identityClaims.clear();
|
|
1314
|
+
state.identityStaffAssistEnabled = false;
|
|
1315
|
+
state.identitySuppressClaimDelivery = false;
|
|
1316
|
+
state.identityAllowedReturnOrigins = /* @__PURE__ */ new Set([APP_TEST_ORIGIN]);
|
|
1117
1317
|
},
|
|
1118
1318
|
get count() {
|
|
1119
1319
|
return state.identities.size;
|
|
@@ -1152,7 +1352,7 @@ async function importNextServer() {
|
|
|
1152
1352
|
async function callRoute(handler, options = {}) {
|
|
1153
1353
|
const { NextRequest } = await importNextServer();
|
|
1154
1354
|
const path = options.path ?? "/api/test-route";
|
|
1155
|
-
const url = new URL(
|
|
1355
|
+
const url = new URL(`${APP_TEST_ORIGIN}${path}`);
|
|
1156
1356
|
for (const [key, value] of Object.entries(options.searchParams ?? {})) {
|
|
1157
1357
|
url.searchParams.set(key, value);
|
|
1158
1358
|
}
|
|
@@ -2927,8 +3127,7 @@ async function createTestApp(options = {}) {
|
|
|
2927
3127
|
state.refreshSessions.clear();
|
|
2928
3128
|
state.emails = [];
|
|
2929
3129
|
state.emailCounter = 0;
|
|
2930
|
-
|
|
2931
|
-
state.identityLinks = [];
|
|
3130
|
+
directory.clear();
|
|
2932
3131
|
payments.clear();
|
|
2933
3132
|
storage.clear();
|
|
2934
3133
|
messages.clear();
|
|
@@ -2942,8 +3141,7 @@ async function createTestApp(options = {}) {
|
|
|
2942
3141
|
state.refreshSessions.clear();
|
|
2943
3142
|
state.emails = [];
|
|
2944
3143
|
state.emailCounter = 0;
|
|
2945
|
-
|
|
2946
|
-
state.identityLinks = [];
|
|
3144
|
+
directory.clear();
|
|
2947
3145
|
payments.clear();
|
|
2948
3146
|
storage.clear();
|
|
2949
3147
|
messages.clear();
|
|
@@ -36,6 +36,11 @@ function globalSingleton(key, create) {
|
|
|
36
36
|
return holder[symbol];
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
// src/constants.ts
|
|
40
|
+
var DATA_STORE_TEST_HOST = "db.stardeck.test";
|
|
41
|
+
var DATA_STORE_TEST_URL = `postgresql://test:test@${DATA_STORE_TEST_HOST}/main`;
|
|
42
|
+
var APP_TEST_ORIGIN = "http://localhost:3333";
|
|
43
|
+
|
|
39
44
|
// src/state.ts
|
|
40
45
|
var state = globalSingleton("state", () => ({
|
|
41
46
|
db: null,
|
|
@@ -47,6 +52,10 @@ var state = globalSingleton("state", () => ({
|
|
|
47
52
|
emailCounter: 0,
|
|
48
53
|
identities: /* @__PURE__ */ new Map(),
|
|
49
54
|
identityLinks: [],
|
|
55
|
+
identityClaims: /* @__PURE__ */ new Map(),
|
|
56
|
+
identityStaffAssistEnabled: false,
|
|
57
|
+
identitySuppressClaimDelivery: false,
|
|
58
|
+
identityAllowedReturnOrigins: /* @__PURE__ */ new Set([APP_TEST_ORIGIN]),
|
|
50
59
|
checkouts: [],
|
|
51
60
|
checkoutCounter: 0,
|
|
52
61
|
sessionStatuses: /* @__PURE__ */ new Map(),
|
|
@@ -9,6 +9,11 @@ function globalSingleton(key, create) {
|
|
|
9
9
|
return holder[symbol];
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
// src/constants.ts
|
|
13
|
+
var DATA_STORE_TEST_HOST = "db.stardeck.test";
|
|
14
|
+
var DATA_STORE_TEST_URL = `postgresql://test:test@${DATA_STORE_TEST_HOST}/main`;
|
|
15
|
+
var APP_TEST_ORIGIN = "http://localhost:3333";
|
|
16
|
+
|
|
12
17
|
// src/state.ts
|
|
13
18
|
var state = globalSingleton("state", () => ({
|
|
14
19
|
db: null,
|
|
@@ -20,6 +25,10 @@ var state = globalSingleton("state", () => ({
|
|
|
20
25
|
emailCounter: 0,
|
|
21
26
|
identities: /* @__PURE__ */ new Map(),
|
|
22
27
|
identityLinks: [],
|
|
28
|
+
identityClaims: /* @__PURE__ */ new Map(),
|
|
29
|
+
identityStaffAssistEnabled: false,
|
|
30
|
+
identitySuppressClaimDelivery: false,
|
|
31
|
+
identityAllowedReturnOrigins: /* @__PURE__ */ new Set([APP_TEST_ORIGIN]),
|
|
23
32
|
checkouts: [],
|
|
24
33
|
checkoutCounter: 0,
|
|
25
34
|
sessionStatuses: /* @__PURE__ */ new Map(),
|