@stardeck-customer-apps/testing 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/SKILL.md +8 -1
- package/dist/index.d.mts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +13 -2
- package/dist/index.mjs +13 -2
- package/dist/setup.js +6 -0
- package/dist/setup.mjs +6 -0
- package/package.json +3 -5
package/SKILL.md
CHANGED
|
@@ -350,6 +350,9 @@ externalId })` helper models a trusted platform/channel link, and
|
|
|
350
350
|
platform turning one down. `completeClaim` flips the claimed contact to
|
|
351
351
|
`verified`, or records `invalidated` when the contact is no longer on that
|
|
352
352
|
profile and `refused` / `contact_owned` when someone else already proved it.
|
|
353
|
+
A contact the claimant already proved on their own login profile is not
|
|
354
|
+
"someone else": the claim completes and the one verified copy ends up on the
|
|
355
|
+
claimed profile.
|
|
353
356
|
Completing a finished claim replays the recorded result. Completion attaches
|
|
354
357
|
the claimant's login to the profile (moving it off any profile it was on), but
|
|
355
358
|
does not move history — the platform's merge core is not modelled.
|
|
@@ -368,7 +371,11 @@ externalId })` helper models a trusted platform/channel link, and
|
|
|
368
371
|
back `refused` / `owned_elsewhere`. That refusal is the one a leaked bearer
|
|
369
372
|
link runs into, so test it. A profile that holds an email or phone is refused
|
|
370
373
|
at `create` with a 409 (send an `invite`); one that gains a contact before
|
|
371
|
-
the link is used completes as `refused` / `protected
|
|
374
|
+
the link is used completes as `refused` / `protected`. The same holds for a
|
|
375
|
+
profile another one was merged into (`app.identities.merge(other, target)`):
|
|
376
|
+
`create` is a 409 and an outstanding link completes as `refused` /
|
|
377
|
+
`protected`, because a bearer link hands over one visit's profile, never
|
|
378
|
+
history staff folded into it. A link still
|
|
372
379
|
outstanding when `.enableBearerLinks(false)` runs completes as `refused` /
|
|
373
380
|
`expired`. People seeded with only a `dashboard_user` link (team members) are
|
|
374
381
|
left out of `identities.list()` and `search()`, as on the platform.
|
package/dist/index.d.mts
CHANGED
|
@@ -185,7 +185,9 @@ interface TestDirectory {
|
|
|
185
185
|
* Test setup only: model the customer finishing the platform-hosted claim —
|
|
186
186
|
* Clerk proof plus their confirmation. Flips the claimed contact to verified,
|
|
187
187
|
* or records `invalidated` (the contact was removed from that profile) or
|
|
188
|
-
* `refused` with `contact_owned` (someone else already proved it
|
|
188
|
+
* `refused` with `contact_owned` (someone else already proved it — the
|
|
189
|
+
* claimant's own login profile does not count; its verified copy is dropped
|
|
190
|
+
* so the claimed profile holds the only one). Replaying a
|
|
189
191
|
* finished claim returns the recorded result unchanged.
|
|
190
192
|
*
|
|
191
193
|
* It does NOT attach a login link or move history: the platform's merge core
|
package/dist/index.d.ts
CHANGED
|
@@ -185,7 +185,9 @@ interface TestDirectory {
|
|
|
185
185
|
* Test setup only: model the customer finishing the platform-hosted claim —
|
|
186
186
|
* Clerk proof plus their confirmation. Flips the claimed contact to verified,
|
|
187
187
|
* or records `invalidated` (the contact was removed from that profile) or
|
|
188
|
-
* `refused` with `contact_owned` (someone else already proved it
|
|
188
|
+
* `refused` with `contact_owned` (someone else already proved it — the
|
|
189
|
+
* claimant's own login profile does not count; its verified copy is dropped
|
|
190
|
+
* so the claimed profile holds the only one). Replaying a
|
|
189
191
|
* finished claim returns the recorded result unchanged.
|
|
190
192
|
*
|
|
191
193
|
* It does NOT attach a login link or move history: the platform's merge core
|
package/dist/index.js
CHANGED
|
@@ -867,6 +867,9 @@ function isCurrentTeamMember(identityId) {
|
|
|
867
867
|
function hasRecordedContact(identityId) {
|
|
868
868
|
return linksFor(identityId).some((link) => link.kind === "email" || link.kind === "phone");
|
|
869
869
|
}
|
|
870
|
+
function hasAbsorbedIdentity(identityId) {
|
|
871
|
+
return [...state.identities.values()].some((identity) => identity.mergedIntoId === identityId);
|
|
872
|
+
}
|
|
870
873
|
function handleList(request) {
|
|
871
874
|
const typeParam = new URL(request.url).searchParams.get("type");
|
|
872
875
|
const type = typeParam === "person" || typeParam === "account" ? typeParam : void 0;
|
|
@@ -1217,6 +1220,9 @@ function createBearerClaim(body, returnTo) {
|
|
|
1217
1220
|
if (hasRecordedContact(identity.id)) {
|
|
1218
1221
|
return failure("identity has an email or phone on record; send an invite claim instead", 409);
|
|
1219
1222
|
}
|
|
1223
|
+
if (hasAbsorbedIdentity(identity.id)) {
|
|
1224
|
+
return failure("another profile was merged into this identity; reception must connect it", 409);
|
|
1225
|
+
}
|
|
1220
1226
|
return mintClaim({
|
|
1221
1227
|
flow: "bearer",
|
|
1222
1228
|
identityId: identity.id,
|
|
@@ -1415,8 +1421,12 @@ function completeClaim(claimId, params) {
|
|
|
1415
1421
|
claim.state = "invalidated";
|
|
1416
1422
|
return claim;
|
|
1417
1423
|
}
|
|
1424
|
+
const claimantIdentityId = state.identityLinks.find(
|
|
1425
|
+
(link) => link.kind === "project_auth_user" && link.externalId === params.clerkUserId
|
|
1426
|
+
)?.identityId;
|
|
1418
1427
|
const otherVerified = rows.find((link) => link.verified && link.identityId !== claim.identityId);
|
|
1419
|
-
|
|
1428
|
+
const ownProof = otherVerified !== void 0 && otherVerified.identityId === claimantIdentityId;
|
|
1429
|
+
if (otherVerified && !ownProof) {
|
|
1420
1430
|
claim.state = "refused";
|
|
1421
1431
|
claim.refusalReason = "contact_owned";
|
|
1422
1432
|
return claim;
|
|
@@ -1426,6 +1436,7 @@ function completeClaim(claimId, params) {
|
|
|
1426
1436
|
claim.refusalReason = "owned_elsewhere";
|
|
1427
1437
|
return claim;
|
|
1428
1438
|
}
|
|
1439
|
+
if (ownProof) state.identityLinks = state.identityLinks.filter((link) => link !== otherVerified);
|
|
1429
1440
|
target.verified = true;
|
|
1430
1441
|
connectLogin(claim, params.clerkUserId);
|
|
1431
1442
|
claim.state = "completed";
|
|
@@ -1458,7 +1469,7 @@ function completeBearerClaim(claim, clerkUserId) {
|
|
|
1458
1469
|
claim.state = "invalidated";
|
|
1459
1470
|
return claim;
|
|
1460
1471
|
}
|
|
1461
|
-
const refusal = !state.identityBearerLinksEnabled ? "expired" : hasRecordedContact(claim.identityId) ? "protected" : ownedByAnotherLogin(claim.identityId, clerkUserId) ? "owned_elsewhere" : null;
|
|
1472
|
+
const refusal = !state.identityBearerLinksEnabled ? "expired" : hasRecordedContact(claim.identityId) || hasAbsorbedIdentity(claim.identityId) ? "protected" : ownedByAnotherLogin(claim.identityId, clerkUserId) ? "owned_elsewhere" : null;
|
|
1462
1473
|
if (refusal) {
|
|
1463
1474
|
claim.state = "refused";
|
|
1464
1475
|
claim.refusalReason = refusal;
|
package/dist/index.mjs
CHANGED
|
@@ -818,6 +818,9 @@ function isCurrentTeamMember(identityId) {
|
|
|
818
818
|
function hasRecordedContact(identityId) {
|
|
819
819
|
return linksFor(identityId).some((link) => link.kind === "email" || link.kind === "phone");
|
|
820
820
|
}
|
|
821
|
+
function hasAbsorbedIdentity(identityId) {
|
|
822
|
+
return [...state.identities.values()].some((identity) => identity.mergedIntoId === identityId);
|
|
823
|
+
}
|
|
821
824
|
function handleList(request) {
|
|
822
825
|
const typeParam = new URL(request.url).searchParams.get("type");
|
|
823
826
|
const type = typeParam === "person" || typeParam === "account" ? typeParam : void 0;
|
|
@@ -1168,6 +1171,9 @@ function createBearerClaim(body, returnTo) {
|
|
|
1168
1171
|
if (hasRecordedContact(identity.id)) {
|
|
1169
1172
|
return failure("identity has an email or phone on record; send an invite claim instead", 409);
|
|
1170
1173
|
}
|
|
1174
|
+
if (hasAbsorbedIdentity(identity.id)) {
|
|
1175
|
+
return failure("another profile was merged into this identity; reception must connect it", 409);
|
|
1176
|
+
}
|
|
1171
1177
|
return mintClaim({
|
|
1172
1178
|
flow: "bearer",
|
|
1173
1179
|
identityId: identity.id,
|
|
@@ -1366,8 +1372,12 @@ function completeClaim(claimId, params) {
|
|
|
1366
1372
|
claim.state = "invalidated";
|
|
1367
1373
|
return claim;
|
|
1368
1374
|
}
|
|
1375
|
+
const claimantIdentityId = state.identityLinks.find(
|
|
1376
|
+
(link) => link.kind === "project_auth_user" && link.externalId === params.clerkUserId
|
|
1377
|
+
)?.identityId;
|
|
1369
1378
|
const otherVerified = rows.find((link) => link.verified && link.identityId !== claim.identityId);
|
|
1370
|
-
|
|
1379
|
+
const ownProof = otherVerified !== void 0 && otherVerified.identityId === claimantIdentityId;
|
|
1380
|
+
if (otherVerified && !ownProof) {
|
|
1371
1381
|
claim.state = "refused";
|
|
1372
1382
|
claim.refusalReason = "contact_owned";
|
|
1373
1383
|
return claim;
|
|
@@ -1377,6 +1387,7 @@ function completeClaim(claimId, params) {
|
|
|
1377
1387
|
claim.refusalReason = "owned_elsewhere";
|
|
1378
1388
|
return claim;
|
|
1379
1389
|
}
|
|
1390
|
+
if (ownProof) state.identityLinks = state.identityLinks.filter((link) => link !== otherVerified);
|
|
1380
1391
|
target.verified = true;
|
|
1381
1392
|
connectLogin(claim, params.clerkUserId);
|
|
1382
1393
|
claim.state = "completed";
|
|
@@ -1409,7 +1420,7 @@ function completeBearerClaim(claim, clerkUserId) {
|
|
|
1409
1420
|
claim.state = "invalidated";
|
|
1410
1421
|
return claim;
|
|
1411
1422
|
}
|
|
1412
|
-
const refusal = !state.identityBearerLinksEnabled ? "expired" : hasRecordedContact(claim.identityId) ? "protected" : ownedByAnotherLogin(claim.identityId, clerkUserId) ? "owned_elsewhere" : null;
|
|
1423
|
+
const refusal = !state.identityBearerLinksEnabled ? "expired" : hasRecordedContact(claim.identityId) || hasAbsorbedIdentity(claim.identityId) ? "protected" : ownedByAnotherLogin(claim.identityId, clerkUserId) ? "owned_elsewhere" : null;
|
|
1413
1424
|
if (refusal) {
|
|
1414
1425
|
claim.state = "refused";
|
|
1415
1426
|
claim.refusalReason = refusal;
|
package/dist/setup.js
CHANGED
|
@@ -769,6 +769,9 @@ function isCurrentTeamMember(identityId) {
|
|
|
769
769
|
function hasRecordedContact(identityId) {
|
|
770
770
|
return linksFor(identityId).some((link) => link.kind === "email" || link.kind === "phone");
|
|
771
771
|
}
|
|
772
|
+
function hasAbsorbedIdentity(identityId) {
|
|
773
|
+
return [...state.identities.values()].some((identity) => identity.mergedIntoId === identityId);
|
|
774
|
+
}
|
|
772
775
|
function handleList(request) {
|
|
773
776
|
const typeParam = new URL(request.url).searchParams.get("type");
|
|
774
777
|
const type = typeParam === "person" || typeParam === "account" ? typeParam : void 0;
|
|
@@ -1119,6 +1122,9 @@ function createBearerClaim(body, returnTo) {
|
|
|
1119
1122
|
if (hasRecordedContact(identity.id)) {
|
|
1120
1123
|
return failure("identity has an email or phone on record; send an invite claim instead", 409);
|
|
1121
1124
|
}
|
|
1125
|
+
if (hasAbsorbedIdentity(identity.id)) {
|
|
1126
|
+
return failure("another profile was merged into this identity; reception must connect it", 409);
|
|
1127
|
+
}
|
|
1122
1128
|
return mintClaim({
|
|
1123
1129
|
flow: "bearer",
|
|
1124
1130
|
identityId: identity.id,
|
package/dist/setup.mjs
CHANGED
|
@@ -745,6 +745,9 @@ function isCurrentTeamMember(identityId) {
|
|
|
745
745
|
function hasRecordedContact(identityId) {
|
|
746
746
|
return linksFor(identityId).some((link) => link.kind === "email" || link.kind === "phone");
|
|
747
747
|
}
|
|
748
|
+
function hasAbsorbedIdentity(identityId) {
|
|
749
|
+
return [...state.identities.values()].some((identity) => identity.mergedIntoId === identityId);
|
|
750
|
+
}
|
|
748
751
|
function handleList(request) {
|
|
749
752
|
const typeParam = new URL(request.url).searchParams.get("type");
|
|
750
753
|
const type = typeParam === "person" || typeParam === "account" ? typeParam : void 0;
|
|
@@ -1095,6 +1098,9 @@ function createBearerClaim(body, returnTo) {
|
|
|
1095
1098
|
if (hasRecordedContact(identity.id)) {
|
|
1096
1099
|
return failure("identity has an email or phone on record; send an invite claim instead", 409);
|
|
1097
1100
|
}
|
|
1101
|
+
if (hasAbsorbedIdentity(identity.id)) {
|
|
1102
|
+
return failure("another profile was merged into this identity; reception must connect it", 409);
|
|
1103
|
+
}
|
|
1098
1104
|
return mintClaim({
|
|
1099
1105
|
flow: "bearer",
|
|
1100
1106
|
identityId: identity.id,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stardeck-customer-apps/testing",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "Vitest test harness for Stardeck customer apps — in-process Postgres (PGlite) plus a control-plane simulator so the real Stardeck SDKs run unmodified in tests",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -47,9 +47,9 @@
|
|
|
47
47
|
"scripts": {
|
|
48
48
|
"build": "tsup src/index.ts src/config.ts src/setup.ts src/next/headers-shim.ts src/server-only-shim.ts --format cjs,esm --dts --no-splitting",
|
|
49
49
|
"dev": "tsup src/index.ts src/config.ts src/setup.ts src/next/headers-shim.ts src/server-only-shim.ts --format cjs,esm --dts --no-splitting --watch",
|
|
50
|
-
"format": "prettier --write . &&
|
|
50
|
+
"format": "prettier --write . && oxlint . --fix",
|
|
51
51
|
"typecheck": "tsc --noEmit && tsc -p tsconfig.test.json",
|
|
52
|
-
"lint": "
|
|
52
|
+
"lint": "oxlint src/",
|
|
53
53
|
"test": "vitest run",
|
|
54
54
|
"test:watch": "vitest"
|
|
55
55
|
},
|
|
@@ -94,7 +94,6 @@
|
|
|
94
94
|
}
|
|
95
95
|
},
|
|
96
96
|
"devDependencies": {
|
|
97
|
-
"@eslint/js": "^9.0.0",
|
|
98
97
|
"@neondatabase/serverless": "^1.0.0",
|
|
99
98
|
"@stardeck-customer-apps/data-store-sdk": "*",
|
|
100
99
|
"@stardeck-customer-apps/edge-sdk": "*",
|
|
@@ -109,7 +108,6 @@
|
|
|
109
108
|
"next": "^16.3.0",
|
|
110
109
|
"tsup": "^8.0.0",
|
|
111
110
|
"typescript": "^5.0.0",
|
|
112
|
-
"typescript-eslint": "^8.0.0",
|
|
113
111
|
"vitest": "^3.2.4",
|
|
114
112
|
"@stardeck-customer-apps/starlens": "*",
|
|
115
113
|
"zod": "^4.3.5"
|