@odla-ai/chapter 0.41.1 → 0.42.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 +18 -8
- package/dist/{chunk-IAWBE5PX.js → chunk-JXFYOUDL.js} +8 -2
- package/dist/chunk-JXFYOUDL.js.map +1 -0
- package/dist/{chunk-O22RRPBT.js → chunk-MMDWXTJU.js} +2 -2
- package/dist/{chunk-PCRYLPMQ.js → chunk-S7CND3R5.js} +373 -327
- package/dist/chunk-S7CND3R5.js.map +1 -0
- package/dist/{copy-context-B14OOiYq.d.ts → copy-context-DuJkH4U9.d.ts} +1 -1
- package/dist/index.cjs +7 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/ui/admin/index.d.ts +2 -2
- package/dist/ui/admin/index.js +2 -2
- package/dist/ui/index.d.ts +2 -2
- package/dist/ui/index.js +5 -3
- package/dist/ui/member/index.d.ts +37 -9
- package/dist/ui/member/index.js +4 -2
- package/dist/worker/index.cjs +69 -12
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.d.cts +1 -1
- package/dist/worker/index.d.ts +1 -1
- package/dist/worker/index.js +69 -12
- package/dist/worker/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-IAWBE5PX.js.map +0 -1
- package/dist/chunk-PCRYLPMQ.js.map +0 -1
- /package/dist/{chunk-O22RRPBT.js.map → chunk-MMDWXTJU.js.map} +0 -0
package/dist/worker/index.d.cts
CHANGED
|
@@ -44,7 +44,7 @@ interface ChapterCopy {
|
|
|
44
44
|
payment: TextFields<"setupFailed" | "preparing" | "pending" | "processing" | "payAndContinue" | "incomplete" | "consent">;
|
|
45
45
|
done: TextFields<"label" | "calendarInvite" | "memberArea">;
|
|
46
46
|
accepted: TextFields<"label" | "body">;
|
|
47
|
-
gift: TextFields<"chooseSelf" | "optionsLabel" | "label" | "body" | "save" | "rotate" | "rotateFailed">;
|
|
47
|
+
gift: TextFields<"chooseSelf" | "optionsLabel" | "label" | "body" | "save" | "rotate" | "rotateFailed" | "claimTitle" | "claimBody" | "code" | "redeem" | "redeemed" | "redeemFailed">;
|
|
48
48
|
};
|
|
49
49
|
members: {
|
|
50
50
|
loadFailed: string;
|
package/dist/worker/index.d.ts
CHANGED
|
@@ -44,7 +44,7 @@ interface ChapterCopy {
|
|
|
44
44
|
payment: TextFields<"setupFailed" | "preparing" | "pending" | "processing" | "payAndContinue" | "incomplete" | "consent">;
|
|
45
45
|
done: TextFields<"label" | "calendarInvite" | "memberArea">;
|
|
46
46
|
accepted: TextFields<"label" | "body">;
|
|
47
|
-
gift: TextFields<"chooseSelf" | "optionsLabel" | "label" | "body" | "save" | "rotate" | "rotateFailed">;
|
|
47
|
+
gift: TextFields<"chooseSelf" | "optionsLabel" | "label" | "body" | "save" | "rotate" | "rotateFailed" | "claimTitle" | "claimBody" | "code" | "redeem" | "redeemed" | "redeemFailed">;
|
|
48
48
|
};
|
|
49
49
|
members: {
|
|
50
50
|
loadFailed: string;
|
package/dist/worker/index.js
CHANGED
|
@@ -1981,7 +1981,12 @@ var handleMember = async (req, url, env, ctx) => {
|
|
|
1981
1981
|
const group = Array.isArray(groups) ? groups[0] : void 0;
|
|
1982
1982
|
if (!group) return json({ error: "not found" }, 404);
|
|
1983
1983
|
const state = await continueJoin(db, ctx, env, url.origin, app, group);
|
|
1984
|
-
if (state) return json({
|
|
1984
|
+
if (state) return json({
|
|
1985
|
+
state,
|
|
1986
|
+
applicationId,
|
|
1987
|
+
...state === "gift" ? { claimId: app.giftClaimId } : {},
|
|
1988
|
+
...state === "payment" && typeof app.giftOfferId === "string" ? { giftPurchase: true } : {}
|
|
1989
|
+
});
|
|
1985
1990
|
return json({ error: `application cannot resume from status "${String(app.status ?? "")}"` }, 409);
|
|
1986
1991
|
}
|
|
1987
1992
|
if (req.method === "POST" && url.pathname === "/api/applications") {
|
|
@@ -2030,6 +2035,7 @@ var handleMember = async (req, url, env, ctx) => {
|
|
|
2030
2035
|
duplicate: result.duplicate,
|
|
2031
2036
|
status: result.status,
|
|
2032
2037
|
...state ? { state } : {},
|
|
2038
|
+
...state === "payment" && requestedGiftId ? { giftPurchase: true } : {},
|
|
2033
2039
|
// Echoed so a site can see (and assert in an integration test) whether its
|
|
2034
2040
|
// join page actually posted the ack. Absent consent is otherwise invisible.
|
|
2035
2041
|
disclaimerAckAt: result.disclaimerAckAt
|
|
@@ -3728,9 +3734,15 @@ async function redeemGiftClaim(db, claim, beneficiary, now) {
|
|
|
3728
3734
|
const target = claim.target;
|
|
3729
3735
|
const ops = [];
|
|
3730
3736
|
let result;
|
|
3737
|
+
let applicationGuard;
|
|
3731
3738
|
if (target.type === "tier") {
|
|
3732
|
-
const apps = (await db.query({ applications: { $: { where: { emailNormalized }, limit: 20 } } })).applications;
|
|
3733
|
-
const existing = apps?.filter((row) => row.groupId === chapterId).sort((a, b) => Number(b.createdAt ?? 0) - Number(a.createdAt ?? 0))[0];
|
|
3739
|
+
const apps = beneficiary.applicationId ? (await db.query({ applications: { $: { where: { id: beneficiary.applicationId }, limit: 1 } } })).applications : (await db.query({ applications: { $: { where: { emailNormalized }, limit: 20 } } })).applications;
|
|
3740
|
+
const existing = beneficiary.applicationId ? apps?.[0] : apps?.filter((row) => row.groupId === chapterId).sort((a, b) => Number(b.createdAt ?? 0) - Number(a.createdAt ?? 0))[0];
|
|
3741
|
+
if (beneficiary.applicationId && (!existing || String(existing.groupId ?? "") !== chapterId || String(existing.emailNormalized ?? "") !== emailNormalized || String(existing.tierId ?? "") !== target.tierId || String(existing.status ?? "") !== "submitted" || typeof existing.stripeSubscriptionId === "string" || typeof existing.giftClaimId === "string" || typeof existing.clerkUserId === "string" && existing.clerkUserId !== beneficiary.userId)) return null;
|
|
3742
|
+
if (beneficiary.applicationId) applicationGuard = {
|
|
3743
|
+
tierId: target.tierId,
|
|
3744
|
+
...typeof existing?.clerkUserId === "string" ? { clerkUserId: existing.clerkUserId } : {}
|
|
3745
|
+
};
|
|
3734
3746
|
const id = existing ? String(existing.id) : `gift-application:${String(claim.id)}`;
|
|
3735
3747
|
const status = existing && existing.status !== "submitted" ? String(existing.status) : "paid_pending_vetting";
|
|
3736
3748
|
ops.push({ t: "update", ns: "applications", id, attrs: {
|
|
@@ -3747,6 +3759,7 @@ async function redeemGiftClaim(db, claim, beneficiary, now) {
|
|
|
3747
3759
|
} });
|
|
3748
3760
|
result = { kind: "application", id };
|
|
3749
3761
|
} else {
|
|
3762
|
+
if (beneficiary.applicationId) return null;
|
|
3750
3763
|
const id = `gift-entitlement:${String(claim.id)}`;
|
|
3751
3764
|
ops.push({ t: "update", ns: "giftEntitlements", id, attrs: {
|
|
3752
3765
|
id,
|
|
@@ -3770,13 +3783,27 @@ async function redeemGiftClaim(db, claim, beneficiary, now) {
|
|
|
3770
3783
|
attemptCount: Number(claim.attemptCount ?? 0) + 1
|
|
3771
3784
|
} });
|
|
3772
3785
|
try {
|
|
3786
|
+
const guards = [{ ns: "giftClaims", id: String(claim.id), exists: true, equals: {
|
|
3787
|
+
status: "pending",
|
|
3788
|
+
codeVersion: Number(claim.codeVersion),
|
|
3789
|
+
codeHash: String(claim.codeHash)
|
|
3790
|
+
} }];
|
|
3791
|
+
if (beneficiary.applicationId) guards.push({
|
|
3792
|
+
ns: "applications",
|
|
3793
|
+
id: beneficiary.applicationId,
|
|
3794
|
+
exists: true,
|
|
3795
|
+
equals: {
|
|
3796
|
+
status: "submitted",
|
|
3797
|
+
groupId: chapterId,
|
|
3798
|
+
emailNormalized,
|
|
3799
|
+
tierId: applicationGuard.tierId,
|
|
3800
|
+
...applicationGuard?.clerkUserId ? { clerkUserId: applicationGuard.clerkUserId } : {}
|
|
3801
|
+
},
|
|
3802
|
+
absent: ["stripeSubscriptionId", "giftClaimId"]
|
|
3803
|
+
});
|
|
3773
3804
|
await db.transact(ops, {
|
|
3774
3805
|
mutationId: `gift-redeem:${String(claim.id)}:${beneficiary.userId}`,
|
|
3775
|
-
guards
|
|
3776
|
-
status: "pending",
|
|
3777
|
-
codeVersion: Number(claim.codeVersion),
|
|
3778
|
-
codeHash: String(claim.codeHash)
|
|
3779
|
-
} }]
|
|
3806
|
+
guards
|
|
3780
3807
|
});
|
|
3781
3808
|
return result;
|
|
3782
3809
|
} catch {
|
|
@@ -4291,15 +4318,35 @@ var handleGifts = async (req, url, env, ctx) => {
|
|
|
4291
4318
|
return json({ error: "too many attempts" }, 429);
|
|
4292
4319
|
}
|
|
4293
4320
|
let code = "";
|
|
4321
|
+
let applicationId = "";
|
|
4294
4322
|
try {
|
|
4295
|
-
|
|
4323
|
+
const body = JSON.parse(await req.text());
|
|
4324
|
+
code = String(body.code ?? "").trim();
|
|
4325
|
+
applicationId = typeof body.applicationId === "string" ? body.applicationId : "";
|
|
4296
4326
|
} catch {
|
|
4297
4327
|
return invalid();
|
|
4298
4328
|
}
|
|
4299
|
-
if (!/^gift1_[A-Za-z0-9_-]{43}$/.test(code)) return invalid();
|
|
4329
|
+
if (!/^gift1_[A-Za-z0-9_-]{43}$/.test(code) || !applicationId) return invalid();
|
|
4330
|
+
const app = first3((await db.query({ applications: { $: { where: { id: applicationId }, limit: 1 } } })).applications);
|
|
4331
|
+
const emailNormalized = normalizeChapterEmail(user.email);
|
|
4332
|
+
if (!app || String(app.groupId ?? "") !== ctx.chapter.id || String(app.emailNormalized ?? "") !== emailNormalized || typeof app.clerkUserId === "string" && app.clerkUserId !== user.userId) return invalid();
|
|
4300
4333
|
const codeHash = await hashGiftCode(ctx.chapter.id, code);
|
|
4301
4334
|
const claim = first3((await db.query({ giftClaims: { $: { where: { codeHash }, limit: 1 } } })).giftClaims);
|
|
4302
4335
|
if (!claim || claim.chapterId !== ctx.chapter.id) return invalid();
|
|
4336
|
+
const sameRedemption = (candidate) => candidate.status === "redeemed" && candidate.applicationId === applicationId && candidate.beneficiaryEmailNormalized === emailNormalized && candidate.beneficiaryUserId === user.userId;
|
|
4337
|
+
const success = async () => {
|
|
4338
|
+
const current = first3((await db.query({ applications: { $: { where: { id: applicationId }, limit: 1 } } })).applications);
|
|
4339
|
+
const group2 = first3((await db.query({ groups: { $: { where: { id: ctx.chapter.id }, limit: 1 } } })).groups);
|
|
4340
|
+
const state = current && group2 ? await continueJoin(db, ctx, env, url.origin, current, group2) : null;
|
|
4341
|
+
return json({
|
|
4342
|
+
ok: true,
|
|
4343
|
+
kind: "application",
|
|
4344
|
+
id: applicationId,
|
|
4345
|
+
applicationId,
|
|
4346
|
+
...state ? { state } : {}
|
|
4347
|
+
});
|
|
4348
|
+
};
|
|
4349
|
+
if (sameRedemption(claim)) return success();
|
|
4303
4350
|
if (claim.status !== "pending" || Number(claim.codeExpiresAt) <= now) {
|
|
4304
4351
|
await db.transact([{ t: "update", ns: "giftClaims", id: String(claim.id), attrs: {
|
|
4305
4352
|
attemptCount: Number(claim.attemptCount ?? 0) + 1,
|
|
@@ -4307,8 +4354,18 @@ var handleGifts = async (req, url, env, ctx) => {
|
|
|
4307
4354
|
} }]).catch(() => void 0);
|
|
4308
4355
|
return invalid();
|
|
4309
4356
|
}
|
|
4310
|
-
const
|
|
4311
|
-
|
|
4357
|
+
const target = claim.target;
|
|
4358
|
+
const group = first3((await db.query({ groups: { $: { where: { id: ctx.chapter.id }, limit: 1 } } })).groups);
|
|
4359
|
+
const tier = group ? findTier(await loadTiers(db, group), typeof app.tierId === "string" ? app.tierId : null) : void 0;
|
|
4360
|
+
if (String(app.status ?? "") !== "submitted" || typeof app.stripeSubscriptionId === "string" || typeof app.giftClaimId === "string" || !tier || tierIsFree(tier) || target?.type !== "tier" || target.tierId !== app.tierId) return invalid();
|
|
4361
|
+
const redeemed = await redeemGiftClaim(db, claim, {
|
|
4362
|
+
email: user.email,
|
|
4363
|
+
userId: user.userId,
|
|
4364
|
+
applicationId
|
|
4365
|
+
}, now);
|
|
4366
|
+
if (redeemed) return success();
|
|
4367
|
+
const raced = await loadClaim(db, String(claim.id));
|
|
4368
|
+
return raced && sameRedemption(raced) ? success() : invalid();
|
|
4312
4369
|
}
|
|
4313
4370
|
return null;
|
|
4314
4371
|
};
|