@stardeck-customer-apps/testing 0.14.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 +48 -4
- package/dist/index.d.mts +104 -1
- package/dist/index.d.ts +104 -1
- package/dist/index.js +355 -46
- package/dist/index.mjs +355 -46
- package/dist/next/headers-shim.js +12 -0
- package/dist/next/headers-shim.mjs +12 -0
- package/dist/setup.js +245 -28
- package/dist/setup.mjs +245 -28
- package/package.json +10 -4
package/dist/setup.js
CHANGED
|
@@ -30,6 +30,23 @@ function globalSingleton(key, create) {
|
|
|
30
30
|
return holder[symbol];
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
// src/constants.ts
|
|
34
|
+
var TEST_DOMAIN_SUFFIX = ".stardeck.test";
|
|
35
|
+
var CONTROL_PLANE_TEST_URL = "https://control-plane.stardeck.test";
|
|
36
|
+
var DATA_STORE_TEST_HOST = "db.stardeck.test";
|
|
37
|
+
var DATA_STORE_TEST_URL = `postgresql://test:test@${DATA_STORE_TEST_HOST}/main`;
|
|
38
|
+
var STORAGE_TEST_URL = "https://storage.stardeck.test";
|
|
39
|
+
var STORAGE_TEST_HOST = "storage.stardeck.test";
|
|
40
|
+
var APP_TEST_ORIGIN = "http://localhost:3333";
|
|
41
|
+
var TEST_ENV_DEFAULTS = {
|
|
42
|
+
CONTROL_PLANE_URL: CONTROL_PLANE_TEST_URL,
|
|
43
|
+
DEPLOYMENT_SECRET: "stardeck-test-deployment-secret",
|
|
44
|
+
ORGANIZATION_ID: "00000000-0000-4000-8000-00000000000a",
|
|
45
|
+
PROJECT_ID: "00000000-0000-4000-8000-00000000000b",
|
|
46
|
+
DEPLOYMENT_ID: "00000000-0000-4000-8000-00000000000c",
|
|
47
|
+
STORAGE_URL: STORAGE_TEST_URL
|
|
48
|
+
};
|
|
49
|
+
|
|
33
50
|
// src/state.ts
|
|
34
51
|
var state = globalSingleton("state", () => ({
|
|
35
52
|
db: null,
|
|
@@ -41,6 +58,10 @@ var state = globalSingleton("state", () => ({
|
|
|
41
58
|
emailCounter: 0,
|
|
42
59
|
identities: /* @__PURE__ */ new Map(),
|
|
43
60
|
identityLinks: [],
|
|
61
|
+
identityClaims: /* @__PURE__ */ new Map(),
|
|
62
|
+
identityStaffAssistEnabled: false,
|
|
63
|
+
identitySuppressClaimDelivery: false,
|
|
64
|
+
identityAllowedReturnOrigins: /* @__PURE__ */ new Set([APP_TEST_ORIGIN]),
|
|
44
65
|
checkouts: [],
|
|
45
66
|
checkoutCounter: 0,
|
|
46
67
|
sessionStatuses: /* @__PURE__ */ new Map(),
|
|
@@ -68,6 +89,9 @@ var state = globalSingleton("state", () => ({
|
|
|
68
89
|
edgeBindings: /* @__PURE__ */ new Map(),
|
|
69
90
|
edgeCapabilities: null,
|
|
70
91
|
edgePeripheralCapabilities: /* @__PURE__ */ new Map(),
|
|
92
|
+
starlensCalls: [],
|
|
93
|
+
starlensReplies: [],
|
|
94
|
+
starlensDefaultReply: null,
|
|
71
95
|
allowNetwork: false
|
|
72
96
|
}));
|
|
73
97
|
function requireDb() {
|
|
@@ -79,22 +103,6 @@ function requireDb() {
|
|
|
79
103
|
return state.db;
|
|
80
104
|
}
|
|
81
105
|
|
|
82
|
-
// src/constants.ts
|
|
83
|
-
var TEST_DOMAIN_SUFFIX = ".stardeck.test";
|
|
84
|
-
var CONTROL_PLANE_TEST_URL = "https://control-plane.stardeck.test";
|
|
85
|
-
var DATA_STORE_TEST_HOST = "db.stardeck.test";
|
|
86
|
-
var DATA_STORE_TEST_URL = `postgresql://test:test@${DATA_STORE_TEST_HOST}/main`;
|
|
87
|
-
var STORAGE_TEST_URL = "https://storage.stardeck.test";
|
|
88
|
-
var STORAGE_TEST_HOST = "storage.stardeck.test";
|
|
89
|
-
var TEST_ENV_DEFAULTS = {
|
|
90
|
-
CONTROL_PLANE_URL: CONTROL_PLANE_TEST_URL,
|
|
91
|
-
DEPLOYMENT_SECRET: "stardeck-test-deployment-secret",
|
|
92
|
-
ORGANIZATION_ID: "00000000-0000-4000-8000-00000000000a",
|
|
93
|
-
PROJECT_ID: "00000000-0000-4000-8000-00000000000b",
|
|
94
|
-
DEPLOYMENT_ID: "00000000-0000-4000-8000-00000000000c",
|
|
95
|
-
STORAGE_URL: STORAGE_TEST_URL
|
|
96
|
-
};
|
|
97
|
-
|
|
98
106
|
// src/simulator/hmac.ts
|
|
99
107
|
var import_node_crypto = __toESM(require("crypto"));
|
|
100
108
|
var TIMESTAMP_TOLERANCE_SECONDS = 300;
|
|
@@ -145,8 +153,8 @@ function json(body, status = 200) {
|
|
|
145
153
|
function success(data) {
|
|
146
154
|
return json({ success: true, data });
|
|
147
155
|
}
|
|
148
|
-
function failure(error, status = 400) {
|
|
149
|
-
return json({ success: false, error }, status);
|
|
156
|
+
function failure(error, status = 400, code) {
|
|
157
|
+
return json({ success: false, error, ...code ? { code } : {} }, status);
|
|
150
158
|
}
|
|
151
159
|
async function readJsonBody(request) {
|
|
152
160
|
try {
|
|
@@ -623,6 +631,12 @@ async function handleEmailSend(request) {
|
|
|
623
631
|
var import_node_crypto3 = __toESM(require("crypto"));
|
|
624
632
|
var ATTACH_LINK_KINDS = /* @__PURE__ */ new Set(["line", "facebook", "instagram", "email", "phone"]);
|
|
625
633
|
var RESOLVE_KINDS = /* @__PURE__ */ new Set(["phone", "email", "line"]);
|
|
634
|
+
var CLAIM_KINDS = /* @__PURE__ */ new Set(["email", "phone"]);
|
|
635
|
+
var CLAIM_FLOWS = /* @__PURE__ */ new Set(["invite", "assist", "transfer"]);
|
|
636
|
+
var DUPLICABLE_UNVERIFIED_KINDS = /* @__PURE__ */ new Set(["email", "phone"]);
|
|
637
|
+
var CLAIM_TTL_MS = 48 * 60 * 60 * 1e3;
|
|
638
|
+
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
639
|
+
var STAFF_ASSISTANCE_NOT_ENABLED = "STAFF_ASSISTANCE_NOT_ENABLED";
|
|
626
640
|
var E164_PHONE = /^\+[1-9]\d{1,14}$/;
|
|
627
641
|
var SEARCH_DEFAULT_LIMIT = 50;
|
|
628
642
|
var SEARCH_MAX_LIMIT = 100;
|
|
@@ -705,6 +719,11 @@ function matchingLinks(kind, externalId) {
|
|
|
705
719
|
(link) => link.kind === kind && comparableExternalId(link.kind, link.externalId) === comparable
|
|
706
720
|
);
|
|
707
721
|
}
|
|
722
|
+
function sortedMatchingLinks(kind, externalId) {
|
|
723
|
+
return matchingLinks(kind, externalId).sort(
|
|
724
|
+
(a, b) => Number(b.verified) - Number(a.verified) || a.createdAt.localeCompare(b.createdAt) || state.identityLinks.indexOf(a) - state.identityLinks.indexOf(b)
|
|
725
|
+
);
|
|
726
|
+
}
|
|
708
727
|
function createPerson(displayName) {
|
|
709
728
|
const createdAt = now();
|
|
710
729
|
const identity = {
|
|
@@ -815,9 +834,10 @@ async function handleAttachLink(identityId, request) {
|
|
|
815
834
|
);
|
|
816
835
|
if ("error" in normalized) return failure(normalized.error);
|
|
817
836
|
const { kind, externalId } = normalized;
|
|
818
|
-
const
|
|
819
|
-
|
|
820
|
-
|
|
837
|
+
const matches = matchingLinks(kind, externalId);
|
|
838
|
+
const ownRow = matches.find((link2) => link2.identityId === identityId);
|
|
839
|
+
if (ownRow) return success({ link: ownRow });
|
|
840
|
+
if (!DUPLICABLE_UNVERIFIED_KINDS.has(kind) && matches.length > 0) {
|
|
821
841
|
return failure("identifier already linked to another identity", 409);
|
|
822
842
|
}
|
|
823
843
|
const link = createUnverifiedLink(identityId, kind, externalId);
|
|
@@ -834,7 +854,7 @@ async function handleResolve(request) {
|
|
|
834
854
|
if (body.displayName !== void 0 && typeof body.displayName !== "string") {
|
|
835
855
|
return failure("displayName must be a string");
|
|
836
856
|
}
|
|
837
|
-
const links =
|
|
857
|
+
const links = sortedMatchingLinks(normalized.kind, normalized.externalId);
|
|
838
858
|
if (links.length > 0) {
|
|
839
859
|
const canonicalIds = /* @__PURE__ */ new Set();
|
|
840
860
|
for (const link of links) {
|
|
@@ -845,12 +865,15 @@ async function handleResolve(request) {
|
|
|
845
865
|
return failure(`cannot resolve identity link: ${message}`, 409);
|
|
846
866
|
}
|
|
847
867
|
}
|
|
848
|
-
|
|
868
|
+
const verifiedOwners = new Set(
|
|
869
|
+
links.filter((link) => link.verified).map((link) => link.identityId)
|
|
870
|
+
);
|
|
871
|
+
if (verifiedOwners.size > 1 || !DUPLICABLE_UNVERIFIED_KINDS.has(normalized.kind) && canonicalIds.size !== 1) {
|
|
849
872
|
return failure("identifier is linked to multiple identities", 409);
|
|
850
873
|
}
|
|
851
|
-
const
|
|
852
|
-
const
|
|
853
|
-
if (provenance === "guest" &&
|
|
874
|
+
const [chosen] = links;
|
|
875
|
+
const canonicalId = canonicalIdentity(chosen.identityId).id;
|
|
876
|
+
if (provenance === "guest" && chosen.verified) {
|
|
854
877
|
return success({ identityId: null, reason: "verified_conflict" });
|
|
855
878
|
}
|
|
856
879
|
return success({ identityId: canonicalId, created: false });
|
|
@@ -952,6 +975,116 @@ async function handleAliases(request) {
|
|
|
952
975
|
}
|
|
953
976
|
return success({ aliases });
|
|
954
977
|
}
|
|
978
|
+
function claimView(claim) {
|
|
979
|
+
const state_ = claim.state === "pending" && Date.parse(claim.expiresAt) <= Date.now() ? "expired" : claim.state;
|
|
980
|
+
return {
|
|
981
|
+
claimId: claim.claimId,
|
|
982
|
+
flow: claim.flow,
|
|
983
|
+
state: state_,
|
|
984
|
+
refusalReason: claim.refusalReason,
|
|
985
|
+
completedIdentityId: claim.completedIdentityId,
|
|
986
|
+
expiresAt: claim.expiresAt
|
|
987
|
+
};
|
|
988
|
+
}
|
|
989
|
+
function validateClaimStaff(flow, staff) {
|
|
990
|
+
if (flow === "invite") {
|
|
991
|
+
if (staff !== void 0) return { error: "staff is only accepted for assist or transfer" };
|
|
992
|
+
return { staffActorRef: null };
|
|
993
|
+
}
|
|
994
|
+
if (!staff || typeof staff !== "object") {
|
|
995
|
+
return { error: `staff is required for a ${flow} claim` };
|
|
996
|
+
}
|
|
997
|
+
const { actorRef, grant } = staff;
|
|
998
|
+
if (typeof actorRef !== "string" || actorRef.length === 0) {
|
|
999
|
+
return { error: "staff.actorRef is required" };
|
|
1000
|
+
}
|
|
1001
|
+
if (grant !== flow) return { error: `staff.grant must be '${flow}' for a ${flow} claim` };
|
|
1002
|
+
return { staffActorRef: actorRef };
|
|
1003
|
+
}
|
|
1004
|
+
async function handleCreateClaim(request) {
|
|
1005
|
+
const body = await readJsonBody(request);
|
|
1006
|
+
const flow = body.flow;
|
|
1007
|
+
if (typeof flow !== "string" || !CLAIM_FLOWS.has(flow)) {
|
|
1008
|
+
return failure("flow must be one of: invite, assist, transfer");
|
|
1009
|
+
}
|
|
1010
|
+
if (typeof body.identityId !== "string" || !UUID.test(body.identityId)) {
|
|
1011
|
+
return failure("identityId must be a uuid");
|
|
1012
|
+
}
|
|
1013
|
+
const normalized = normalizeLinkInput(
|
|
1014
|
+
body.kind,
|
|
1015
|
+
body.externalId,
|
|
1016
|
+
CLAIM_KINDS,
|
|
1017
|
+
"kind must be one of: email, phone"
|
|
1018
|
+
);
|
|
1019
|
+
if ("error" in normalized) return failure(normalized.error);
|
|
1020
|
+
let returnTo = null;
|
|
1021
|
+
if (body.returnTo !== void 0) {
|
|
1022
|
+
if (typeof body.returnTo !== "string") return failure("returnTo must be a string");
|
|
1023
|
+
let parsed;
|
|
1024
|
+
try {
|
|
1025
|
+
parsed = new URL(body.returnTo);
|
|
1026
|
+
} catch {
|
|
1027
|
+
return failure("returnTo must be an absolute URL");
|
|
1028
|
+
}
|
|
1029
|
+
returnTo = state.identityAllowedReturnOrigins.has(parsed.origin) ? body.returnTo : null;
|
|
1030
|
+
}
|
|
1031
|
+
if (body.deliver !== void 0 && typeof body.deliver !== "boolean") {
|
|
1032
|
+
return failure("deliver must be a boolean");
|
|
1033
|
+
}
|
|
1034
|
+
const staff = validateClaimStaff(flow, body.staff);
|
|
1035
|
+
if ("error" in staff) return failure(staff.error);
|
|
1036
|
+
if (flow !== "invite" && !state.identityStaffAssistEnabled) {
|
|
1037
|
+
return failure(
|
|
1038
|
+
"staff-assisted profile claiming is not enabled for this project",
|
|
1039
|
+
403,
|
|
1040
|
+
STAFF_ASSISTANCE_NOT_ENABLED
|
|
1041
|
+
);
|
|
1042
|
+
}
|
|
1043
|
+
const identity = state.identities.get(body.identityId);
|
|
1044
|
+
if (!identity || identity.type !== "person" || identity.status !== "active") {
|
|
1045
|
+
return failure("identity not found", 404);
|
|
1046
|
+
}
|
|
1047
|
+
const holdsContact = matchingLinks(normalized.kind, normalized.externalId).some(
|
|
1048
|
+
(link) => link.identityId === identity.id
|
|
1049
|
+
);
|
|
1050
|
+
if (!holdsContact) {
|
|
1051
|
+
return failure("identity does not hold that contact", 409);
|
|
1052
|
+
}
|
|
1053
|
+
const claimId = import_node_crypto3.default.randomUUID();
|
|
1054
|
+
const deliver = body.deliver === true;
|
|
1055
|
+
const handle = import_node_crypto3.default.randomBytes(32).toString("base64url");
|
|
1056
|
+
const claim = {
|
|
1057
|
+
claimId,
|
|
1058
|
+
flow,
|
|
1059
|
+
identityId: identity.id,
|
|
1060
|
+
kind: normalized.kind,
|
|
1061
|
+
externalId: normalized.externalId,
|
|
1062
|
+
returnTo,
|
|
1063
|
+
staffActorRef: staff.staffActorRef,
|
|
1064
|
+
state: "pending",
|
|
1065
|
+
refusalReason: null,
|
|
1066
|
+
completedIdentityId: null,
|
|
1067
|
+
claimantClerkUserId: null,
|
|
1068
|
+
claimUrl: `${CONTROL_PLANE_TEST_URL}/claim/${handle}`,
|
|
1069
|
+
delivered: deliver ? !state.identitySuppressClaimDelivery : null,
|
|
1070
|
+
expiresAt: new Date(Date.now() + CLAIM_TTL_MS).toISOString(),
|
|
1071
|
+
createdAt: now()
|
|
1072
|
+
};
|
|
1073
|
+
state.identityClaims.set(claimId, claim);
|
|
1074
|
+
return success({
|
|
1075
|
+
claimId,
|
|
1076
|
+
claimUrl: claim.claimUrl,
|
|
1077
|
+
expiresAt: claim.expiresAt,
|
|
1078
|
+
// Absent unless asked for: an app delivering its own link must not read a
|
|
1079
|
+
// missing field as "nothing was sent".
|
|
1080
|
+
...deliver ? { delivered: claim.delivered } : {}
|
|
1081
|
+
});
|
|
1082
|
+
}
|
|
1083
|
+
function handleGetClaim(claimId) {
|
|
1084
|
+
const claim = state.identityClaims.get(claimId);
|
|
1085
|
+
if (!claim) return failure("claim not found", 404);
|
|
1086
|
+
return success(claimView(claim));
|
|
1087
|
+
}
|
|
955
1088
|
async function handleIdentitiesRequest(request, subPath) {
|
|
956
1089
|
const method = request.method;
|
|
957
1090
|
if (subPath === "" || subPath === "/") {
|
|
@@ -967,6 +1100,13 @@ async function handleIdentitiesRequest(request, subPath) {
|
|
|
967
1100
|
if (subPath === "/search" && method === "GET") {
|
|
968
1101
|
return handleSearch(request);
|
|
969
1102
|
}
|
|
1103
|
+
if (subPath === "/claims" && method === "POST") {
|
|
1104
|
+
return handleCreateClaim(request);
|
|
1105
|
+
}
|
|
1106
|
+
const claimMatch = subPath.match(/^\/claims\/([^/]+)$/);
|
|
1107
|
+
if (claimMatch && method === "GET") {
|
|
1108
|
+
return handleGetClaim(decodeURIComponent(claimMatch[1]));
|
|
1109
|
+
}
|
|
970
1110
|
const linksMatch = subPath.match(/^\/([^/]+)\/links$/);
|
|
971
1111
|
if (linksMatch && method === "POST") {
|
|
972
1112
|
return handleAttachLink(linksMatch[1], request);
|
|
@@ -2000,6 +2140,77 @@ async function handleEdgeRequest(request, subPath) {
|
|
|
2000
2140
|
return edgeFailure(`No edge simulator for ${method} .../edge${subPath}`, 404);
|
|
2001
2141
|
}
|
|
2002
2142
|
|
|
2143
|
+
// src/simulator/starlens.ts
|
|
2144
|
+
function starlensFailure(error, status, code) {
|
|
2145
|
+
return json({ success: false, error, code }, status);
|
|
2146
|
+
}
|
|
2147
|
+
function nullFlagged(output, issues) {
|
|
2148
|
+
const copy = JSON.parse(JSON.stringify(output ?? null));
|
|
2149
|
+
for (const { path } of issues) {
|
|
2150
|
+
const keys = path.replace(/^output\./, "").replace(/\[(\d+)\]/g, ".$1").split(".");
|
|
2151
|
+
const last = keys.pop();
|
|
2152
|
+
const parent = keys.reduce(
|
|
2153
|
+
(o, k) => o != null && typeof o === "object" ? o[k] : void 0,
|
|
2154
|
+
copy
|
|
2155
|
+
);
|
|
2156
|
+
if (parent != null && typeof parent === "object" && Object.prototype.hasOwnProperty.call(parent, last)) {
|
|
2157
|
+
parent[last] = null;
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
return copy;
|
|
2161
|
+
}
|
|
2162
|
+
async function handleStarlensExtract(request, signedDeploymentId) {
|
|
2163
|
+
const body = await readJsonBody(request);
|
|
2164
|
+
if (signedDeploymentId !== null && body.deploymentId !== signedDeploymentId) {
|
|
2165
|
+
return starlensFailure("Deployment ID mismatch", 401, "AUTHENTICATION_ERROR");
|
|
2166
|
+
}
|
|
2167
|
+
const inputs = Array.isArray(body.inputs) ? body.inputs : [];
|
|
2168
|
+
if (inputs.length === 0) {
|
|
2169
|
+
return starlensFailure(
|
|
2170
|
+
"Validation failed \u2014 inputs: at least one input",
|
|
2171
|
+
400,
|
|
2172
|
+
"VALIDATION_ERROR"
|
|
2173
|
+
);
|
|
2174
|
+
}
|
|
2175
|
+
const schema = body.schema;
|
|
2176
|
+
if (!schema || schema.type !== "object" || typeof schema.properties !== "object") {
|
|
2177
|
+
return starlensFailure(
|
|
2178
|
+
"Validation failed \u2014 schema.properties is required",
|
|
2179
|
+
400,
|
|
2180
|
+
"VALIDATION_ERROR"
|
|
2181
|
+
);
|
|
2182
|
+
}
|
|
2183
|
+
const call = {
|
|
2184
|
+
version: typeof body.version === "string" ? body.version : "latest",
|
|
2185
|
+
inputs: inputs.map((i) => ({
|
|
2186
|
+
name: typeof i.filename === "string" ? i.filename : typeof i.url === "string" ? i.url : "upload",
|
|
2187
|
+
mediaType: typeof i.mediaType === "string" ? i.mediaType : null,
|
|
2188
|
+
source: typeof i.url === "string" ? "url" : "data"
|
|
2189
|
+
})),
|
|
2190
|
+
schema,
|
|
2191
|
+
instructions: typeof body.instructions === "string" ? body.instructions : void 0
|
|
2192
|
+
};
|
|
2193
|
+
state.starlensCalls.push(call);
|
|
2194
|
+
const reply = state.starlensReplies.shift() ?? state.starlensDefaultReply;
|
|
2195
|
+
if (!reply) {
|
|
2196
|
+
return starlensFailure(
|
|
2197
|
+
"[stardeck-testing] No Starlens reply registered. Call app.starlens.mock({ output, issues }) or app.starlens.reject(reason) before the request.",
|
|
2198
|
+
502,
|
|
2199
|
+
"model_failed"
|
|
2200
|
+
);
|
|
2201
|
+
}
|
|
2202
|
+
if ("reject" in reply) {
|
|
2203
|
+
return starlensFailure(reply.reject, 422, "unsupported_document");
|
|
2204
|
+
}
|
|
2205
|
+
const issues = reply.issues ?? [];
|
|
2206
|
+
return success({
|
|
2207
|
+
version: call.version === "latest" ? "v1" : call.version,
|
|
2208
|
+
output: nullFlagged(reply.output, issues),
|
|
2209
|
+
issues,
|
|
2210
|
+
usage: { inputTokens: 0, outputTokens: 0, cost: 0 }
|
|
2211
|
+
});
|
|
2212
|
+
}
|
|
2213
|
+
|
|
2003
2214
|
// src/simulator/router.ts
|
|
2004
2215
|
var fetchHolder = globalSingleton("fetch-holder", () => ({
|
|
2005
2216
|
originalFetch: null
|
|
@@ -2021,7 +2232,8 @@ function requiresDeploymentHmac(request, url) {
|
|
|
2021
2232
|
/^\/api\/deployments\/[^/]+\/integrations\/(slack|line|facebook)(\/.*)?$/
|
|
2022
2233
|
);
|
|
2023
2234
|
const edgeMatch = url.pathname.match(/^\/api\/deployments\/[^/]+\/edge(\/.*)?$/);
|
|
2024
|
-
|
|
2235
|
+
const isStarlens = url.pathname === "/api/starlens/extract";
|
|
2236
|
+
return !!(isStarlens || dataStoreMatch || isEmail || identitiesMatch || storeMatch || messagingMatch || edgeMatch);
|
|
2025
2237
|
}
|
|
2026
2238
|
function deploymentIdFromPath(url) {
|
|
2027
2239
|
const match = url.pathname.match(/\/api\/deployments\/([^/]+)(?:\/|$)/);
|
|
@@ -2051,6 +2263,7 @@ async function handleSimulatedRequest(request, url) {
|
|
|
2051
2263
|
);
|
|
2052
2264
|
const edgeMatch = url.pathname.match(/^\/api\/deployments\/[^/]+\/edge(\/.*)?$/);
|
|
2053
2265
|
const isStorageHost = url.hostname === STORAGE_TEST_HOST;
|
|
2266
|
+
let signedDeploymentId = null;
|
|
2054
2267
|
if (requiresDeploymentHmac(request, url)) {
|
|
2055
2268
|
const authHeader = request.headers.get("X-Stardeck-Auth");
|
|
2056
2269
|
if (!authHeader) {
|
|
@@ -2062,6 +2275,7 @@ async function handleSimulatedRequest(request, url) {
|
|
|
2062
2275
|
if (!authPayload || pathDeploymentId !== null && authPayload.deploymentId !== pathDeploymentId) {
|
|
2063
2276
|
return failure("Invalid authentication", 401);
|
|
2064
2277
|
}
|
|
2278
|
+
signedDeploymentId = authPayload.deploymentId;
|
|
2065
2279
|
}
|
|
2066
2280
|
if (isStorageHost) {
|
|
2067
2281
|
return handleStorageRequest(request, url);
|
|
@@ -2082,6 +2296,9 @@ async function handleSimulatedRequest(request, url) {
|
|
|
2082
2296
|
if (edgeMatch) {
|
|
2083
2297
|
return handleEdgeRequest(request, edgeMatch[1] ?? "");
|
|
2084
2298
|
}
|
|
2299
|
+
if (url.pathname === "/api/starlens/extract" && request.method === "POST") {
|
|
2300
|
+
return handleStarlensExtract(request, signedDeploymentId);
|
|
2301
|
+
}
|
|
2085
2302
|
if (dataStoreMatch) {
|
|
2086
2303
|
const subPath = dataStoreMatch[1] ?? "";
|
|
2087
2304
|
const db = requireDb();
|
|
@@ -2103,7 +2320,7 @@ async function handleSimulatedRequest(request, url) {
|
|
|
2103
2320
|
}
|
|
2104
2321
|
}
|
|
2105
2322
|
return failure(
|
|
2106
|
-
`[stardeck-testing] No simulator for ${request.method} ${url.pathname}. Supported: data-store query/mutate/schema, email send, identities CRUD, payments store/checkout, storage upload/files, messaging send, edge print/display/bindings, auth verify/refresh, Neon /sql.`,
|
|
2323
|
+
`[stardeck-testing] No simulator for ${request.method} ${url.pathname}. Supported: data-store query/mutate/schema, email send, identities CRUD, payments store/checkout, storage upload/files, messaging send, edge print/display/bindings, starlens extract, auth verify/refresh, Neon /sql.`,
|
|
2107
2324
|
404
|
|
2108
2325
|
);
|
|
2109
2326
|
}
|