@odla-ai/chapter 0.41.1 → 0.42.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 +38 -12
- 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-BNUzbL5g.d.ts} +19 -1
- package/dist/index.cjs +67 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +66 -2
- package/dist/index.d.ts +66 -2
- package/dist/index.js +67 -11
- 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 +368 -62
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.d.cts +19 -1
- package/dist/worker/index.d.ts +19 -1
- package/dist/worker/index.js +361 -55
- package/dist/worker/index.js.map +1 -1
- package/package.json +2 -2
- package/runbooks/greenfield.md +3 -0
- 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.cjs
CHANGED
|
@@ -2009,7 +2009,12 @@ var handleMember = async (req, url, env, ctx) => {
|
|
|
2009
2009
|
const group = Array.isArray(groups) ? groups[0] : void 0;
|
|
2010
2010
|
if (!group) return json({ error: "not found" }, 404);
|
|
2011
2011
|
const state = await continueJoin(db, ctx, env, url.origin, app, group);
|
|
2012
|
-
if (state) return json({
|
|
2012
|
+
if (state) return json({
|
|
2013
|
+
state,
|
|
2014
|
+
applicationId,
|
|
2015
|
+
...state === "gift" ? { claimId: app.giftClaimId } : {},
|
|
2016
|
+
...state === "payment" && typeof app.giftOfferId === "string" ? { giftPurchase: true } : {}
|
|
2017
|
+
});
|
|
2013
2018
|
return json({ error: `application cannot resume from status "${String(app.status ?? "")}"` }, 409);
|
|
2014
2019
|
}
|
|
2015
2020
|
if (req.method === "POST" && url.pathname === "/api/applications") {
|
|
@@ -2058,6 +2063,7 @@ var handleMember = async (req, url, env, ctx) => {
|
|
|
2058
2063
|
duplicate: result.duplicate,
|
|
2059
2064
|
status: result.status,
|
|
2060
2065
|
...state ? { state } : {},
|
|
2066
|
+
...state === "payment" && requestedGiftId ? { giftPurchase: true } : {},
|
|
2061
2067
|
// Echoed so a site can see (and assert in an integration test) whether its
|
|
2062
2068
|
// join page actually posted the ack. Absent consent is otherwise invisible.
|
|
2063
2069
|
disclaimerAckAt: result.disclaimerAckAt
|
|
@@ -3756,9 +3762,15 @@ async function redeemGiftClaim(db, claim, beneficiary, now) {
|
|
|
3756
3762
|
const target = claim.target;
|
|
3757
3763
|
const ops = [];
|
|
3758
3764
|
let result;
|
|
3765
|
+
let applicationGuard;
|
|
3759
3766
|
if (target.type === "tier") {
|
|
3760
|
-
const apps = (await db.query({ applications: { $: { where: { emailNormalized }, limit: 20 } } })).applications;
|
|
3761
|
-
const existing = apps?.filter((row) => row.groupId === chapterId).sort((a, b) => Number(b.createdAt ?? 0) - Number(a.createdAt ?? 0))[0];
|
|
3767
|
+
const apps = beneficiary.applicationId ? (await db.query({ applications: { $: { where: { id: beneficiary.applicationId }, limit: 1 } } })).applications : (await db.query({ applications: { $: { where: { emailNormalized }, limit: 20 } } })).applications;
|
|
3768
|
+
const existing = beneficiary.applicationId ? apps?.[0] : apps?.filter((row) => row.groupId === chapterId).sort((a, b) => Number(b.createdAt ?? 0) - Number(a.createdAt ?? 0))[0];
|
|
3769
|
+
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;
|
|
3770
|
+
if (beneficiary.applicationId) applicationGuard = {
|
|
3771
|
+
tierId: target.tierId,
|
|
3772
|
+
...typeof existing?.clerkUserId === "string" ? { clerkUserId: existing.clerkUserId } : {}
|
|
3773
|
+
};
|
|
3762
3774
|
const id = existing ? String(existing.id) : `gift-application:${String(claim.id)}`;
|
|
3763
3775
|
const status = existing && existing.status !== "submitted" ? String(existing.status) : "paid_pending_vetting";
|
|
3764
3776
|
ops.push({ t: "update", ns: "applications", id, attrs: {
|
|
@@ -3775,6 +3787,7 @@ async function redeemGiftClaim(db, claim, beneficiary, now) {
|
|
|
3775
3787
|
} });
|
|
3776
3788
|
result = { kind: "application", id };
|
|
3777
3789
|
} else {
|
|
3790
|
+
if (beneficiary.applicationId) return null;
|
|
3778
3791
|
const id = `gift-entitlement:${String(claim.id)}`;
|
|
3779
3792
|
ops.push({ t: "update", ns: "giftEntitlements", id, attrs: {
|
|
3780
3793
|
id,
|
|
@@ -3798,13 +3811,27 @@ async function redeemGiftClaim(db, claim, beneficiary, now) {
|
|
|
3798
3811
|
attemptCount: Number(claim.attemptCount ?? 0) + 1
|
|
3799
3812
|
} });
|
|
3800
3813
|
try {
|
|
3814
|
+
const guards = [{ ns: "giftClaims", id: String(claim.id), exists: true, equals: {
|
|
3815
|
+
status: "pending",
|
|
3816
|
+
codeVersion: Number(claim.codeVersion),
|
|
3817
|
+
codeHash: String(claim.codeHash)
|
|
3818
|
+
} }];
|
|
3819
|
+
if (beneficiary.applicationId) guards.push({
|
|
3820
|
+
ns: "applications",
|
|
3821
|
+
id: beneficiary.applicationId,
|
|
3822
|
+
exists: true,
|
|
3823
|
+
equals: {
|
|
3824
|
+
status: "submitted",
|
|
3825
|
+
groupId: chapterId,
|
|
3826
|
+
emailNormalized,
|
|
3827
|
+
tierId: applicationGuard.tierId,
|
|
3828
|
+
...applicationGuard?.clerkUserId ? { clerkUserId: applicationGuard.clerkUserId } : {}
|
|
3829
|
+
},
|
|
3830
|
+
absent: ["stripeSubscriptionId", "giftClaimId"]
|
|
3831
|
+
});
|
|
3801
3832
|
await db.transact(ops, {
|
|
3802
3833
|
mutationId: `gift-redeem:${String(claim.id)}:${beneficiary.userId}`,
|
|
3803
|
-
guards
|
|
3804
|
-
status: "pending",
|
|
3805
|
-
codeVersion: Number(claim.codeVersion),
|
|
3806
|
-
codeHash: String(claim.codeHash)
|
|
3807
|
-
} }]
|
|
3834
|
+
guards
|
|
3808
3835
|
});
|
|
3809
3836
|
return result;
|
|
3810
3837
|
} catch {
|
|
@@ -4319,15 +4346,35 @@ var handleGifts = async (req, url, env, ctx) => {
|
|
|
4319
4346
|
return json({ error: "too many attempts" }, 429);
|
|
4320
4347
|
}
|
|
4321
4348
|
let code = "";
|
|
4349
|
+
let applicationId = "";
|
|
4322
4350
|
try {
|
|
4323
|
-
|
|
4351
|
+
const body = JSON.parse(await req.text());
|
|
4352
|
+
code = String(body.code ?? "").trim();
|
|
4353
|
+
applicationId = typeof body.applicationId === "string" ? body.applicationId : "";
|
|
4324
4354
|
} catch {
|
|
4325
4355
|
return invalid();
|
|
4326
4356
|
}
|
|
4327
|
-
if (!/^gift1_[A-Za-z0-9_-]{43}$/.test(code)) return invalid();
|
|
4357
|
+
if (!/^gift1_[A-Za-z0-9_-]{43}$/.test(code) || !applicationId) return invalid();
|
|
4358
|
+
const app = first3((await db.query({ applications: { $: { where: { id: applicationId }, limit: 1 } } })).applications);
|
|
4359
|
+
const emailNormalized = normalizeChapterEmail(user.email);
|
|
4360
|
+
if (!app || String(app.groupId ?? "") !== ctx.chapter.id || String(app.emailNormalized ?? "") !== emailNormalized || typeof app.clerkUserId === "string" && app.clerkUserId !== user.userId) return invalid();
|
|
4328
4361
|
const codeHash = await hashGiftCode(ctx.chapter.id, code);
|
|
4329
4362
|
const claim = first3((await db.query({ giftClaims: { $: { where: { codeHash }, limit: 1 } } })).giftClaims);
|
|
4330
4363
|
if (!claim || claim.chapterId !== ctx.chapter.id) return invalid();
|
|
4364
|
+
const sameRedemption = (candidate) => candidate.status === "redeemed" && candidate.applicationId === applicationId && candidate.beneficiaryEmailNormalized === emailNormalized && candidate.beneficiaryUserId === user.userId;
|
|
4365
|
+
const success = async () => {
|
|
4366
|
+
const current = first3((await db.query({ applications: { $: { where: { id: applicationId }, limit: 1 } } })).applications);
|
|
4367
|
+
const group2 = first3((await db.query({ groups: { $: { where: { id: ctx.chapter.id }, limit: 1 } } })).groups);
|
|
4368
|
+
const state = current && group2 ? await continueJoin(db, ctx, env, url.origin, current, group2) : null;
|
|
4369
|
+
return json({
|
|
4370
|
+
ok: true,
|
|
4371
|
+
kind: "application",
|
|
4372
|
+
id: applicationId,
|
|
4373
|
+
applicationId,
|
|
4374
|
+
...state ? { state } : {}
|
|
4375
|
+
});
|
|
4376
|
+
};
|
|
4377
|
+
if (sameRedemption(claim)) return success();
|
|
4331
4378
|
if (claim.status !== "pending" || Number(claim.codeExpiresAt) <= now) {
|
|
4332
4379
|
await db.transact([{ t: "update", ns: "giftClaims", id: String(claim.id), attrs: {
|
|
4333
4380
|
attemptCount: Number(claim.attemptCount ?? 0) + 1,
|
|
@@ -4335,8 +4382,18 @@ var handleGifts = async (req, url, env, ctx) => {
|
|
|
4335
4382
|
} }]).catch(() => void 0);
|
|
4336
4383
|
return invalid();
|
|
4337
4384
|
}
|
|
4338
|
-
const
|
|
4339
|
-
|
|
4385
|
+
const target = claim.target;
|
|
4386
|
+
const group = first3((await db.query({ groups: { $: { where: { id: ctx.chapter.id }, limit: 1 } } })).groups);
|
|
4387
|
+
const tier = group ? findTier(await loadTiers(db, group), typeof app.tierId === "string" ? app.tierId : null) : void 0;
|
|
4388
|
+
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();
|
|
4389
|
+
const redeemed = await redeemGiftClaim(db, claim, {
|
|
4390
|
+
email: user.email,
|
|
4391
|
+
userId: user.userId,
|
|
4392
|
+
applicationId
|
|
4393
|
+
}, now);
|
|
4394
|
+
if (redeemed) return success();
|
|
4395
|
+
const raced = await loadClaim(db, String(claim.id));
|
|
4396
|
+
return raced && sameRedemption(raced) ? success() : invalid();
|
|
4340
4397
|
}
|
|
4341
4398
|
return null;
|
|
4342
4399
|
};
|
|
@@ -5319,12 +5376,22 @@ var handleAdminNetworkTargets = async (req, url, env, ctx) => {
|
|
|
5319
5376
|
const got = await gate5(req, env, ctx);
|
|
5320
5377
|
if ("response" in got) return got.response;
|
|
5321
5378
|
return json({
|
|
5322
|
-
targets: ctx.chapter.network.targets.map(({
|
|
5379
|
+
targets: ctx.chapter.network.targets.map(({
|
|
5380
|
+
id,
|
|
5381
|
+
name,
|
|
5382
|
+
url: targetUrl,
|
|
5383
|
+
fields,
|
|
5384
|
+
sharedNotes,
|
|
5385
|
+
editableFields,
|
|
5386
|
+
stageTransitions
|
|
5387
|
+
}) => ({
|
|
5323
5388
|
id,
|
|
5324
5389
|
name,
|
|
5325
5390
|
url: targetUrl,
|
|
5326
5391
|
types: Object.keys(fields ?? DEFAULT_SHARE_FIELDS),
|
|
5327
|
-
sharedNotes
|
|
5392
|
+
sharedNotes,
|
|
5393
|
+
editableFields,
|
|
5394
|
+
stageTransitions
|
|
5328
5395
|
}))
|
|
5329
5396
|
});
|
|
5330
5397
|
};
|
|
@@ -5442,19 +5509,20 @@ var handleAdminNetworkPush = async (req, url, env, ctx) => {
|
|
|
5442
5509
|
return json({ ok: results.every((result) => result.ok), results });
|
|
5443
5510
|
};
|
|
5444
5511
|
|
|
5445
|
-
// src/worker-routes-network-records.ts
|
|
5512
|
+
// src/worker-routes-network-records-follower.ts
|
|
5446
5513
|
var import_crm7 = require("@odla-ai/crm");
|
|
5447
5514
|
var import_db8 = require("@odla-ai/db");
|
|
5515
|
+
|
|
5516
|
+
// src/worker-routes-network-records-common.ts
|
|
5448
5517
|
var IDENTIFIER = /^[a-z][a-zA-Z0-9_]*$/;
|
|
5449
5518
|
var MAX_PAGE = 50;
|
|
5450
5519
|
var MAX_OFFSET = 1e4;
|
|
5451
5520
|
var MAX_SEARCH = 200;
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
return { db, user };
|
|
5521
|
+
var MAX_BODY = 32768;
|
|
5522
|
+
var RECORD_ID = /^[a-zA-Z0-9][a-zA-Z0-9:_-]{0,191}$/;
|
|
5523
|
+
var MUTATION_ID = /^[a-zA-Z0-9][a-zA-Z0-9:._-]{7,127}$/;
|
|
5524
|
+
function bodyTooLarge(body) {
|
|
5525
|
+
return new TextEncoder().encode(body).byteLength > MAX_BODY;
|
|
5458
5526
|
}
|
|
5459
5527
|
function integer(value, fallback, min, max) {
|
|
5460
5528
|
if (value == null || value === "") return fallback;
|
|
@@ -5462,10 +5530,22 @@ function integer(value, fallback, min, max) {
|
|
|
5462
5530
|
const parsed = Number(value);
|
|
5463
5531
|
return Number.isSafeInteger(parsed) && parsed >= min && parsed <= max ? parsed : null;
|
|
5464
5532
|
}
|
|
5465
|
-
function projectRecord(record4, allowed2) {
|
|
5533
|
+
function projectRecord(record4, allowed2, editable, stagesEnabled, crm) {
|
|
5466
5534
|
const fields = Object.fromEntries(
|
|
5467
5535
|
allowed2.flatMap((name) => Object.prototype.hasOwnProperty.call(record4.fields, name) ? [[name, record4.fields[name]]] : [])
|
|
5468
5536
|
);
|
|
5537
|
+
const def = crm.type(record4.type);
|
|
5538
|
+
const editFields = editable.map((id) => {
|
|
5539
|
+
const field = def.fields[id];
|
|
5540
|
+
return {
|
|
5541
|
+
id,
|
|
5542
|
+
label: field.label ?? id,
|
|
5543
|
+
type: field.type,
|
|
5544
|
+
required: field.required ?? false,
|
|
5545
|
+
...field.options ? { options: field.options } : {}
|
|
5546
|
+
};
|
|
5547
|
+
});
|
|
5548
|
+
const allowedStages = stagesEnabled ? (def.pipeline?.stages ?? []).flatMap((stage) => crm.canTransition(record4.type, record4, stage.id) ? [] : [{ id: stage.id, label: stage.label ?? stage.id }]) : [];
|
|
5469
5549
|
return {
|
|
5470
5550
|
id: record4.id,
|
|
5471
5551
|
type: record4.type,
|
|
@@ -5473,11 +5553,66 @@ function projectRecord(record4, allowed2) {
|
|
|
5473
5553
|
...record4.stage ? { stage: record4.stage } : {},
|
|
5474
5554
|
fields,
|
|
5475
5555
|
createdAt: record4.createdAt,
|
|
5476
|
-
updatedAt: record4.updatedAt
|
|
5556
|
+
updatedAt: record4.updatedAt,
|
|
5557
|
+
revision: record4.updatedAt,
|
|
5558
|
+
...editFields.length || stagesEnabled ? { edit: { fields: editFields, allowedStages } } : {}
|
|
5477
5559
|
};
|
|
5478
5560
|
}
|
|
5561
|
+
function readerRecord(record4, reader, crm) {
|
|
5562
|
+
return projectRecord(
|
|
5563
|
+
record4,
|
|
5564
|
+
reader.fields[record4.type] ?? [],
|
|
5565
|
+
reader.editableFields[record4.type] ?? [],
|
|
5566
|
+
reader.stageTransitions.includes(record4.type),
|
|
5567
|
+
crm
|
|
5568
|
+
);
|
|
5569
|
+
}
|
|
5570
|
+
function restrictedRecord(record4, target) {
|
|
5571
|
+
if (!record4.edit) return record4;
|
|
5572
|
+
const fields = record4.edit.fields.filter((field) => target.editableFields[record4.type]?.includes(field.id));
|
|
5573
|
+
const allowedStages = target.stageTransitions.includes(record4.type) ? record4.edit.allowedStages : [];
|
|
5574
|
+
const edit = fields.length || target.stageTransitions.includes(record4.type) ? { fields, allowedStages } : void 0;
|
|
5575
|
+
return { ...record4, ...edit ? { edit } : { edit: void 0 } };
|
|
5576
|
+
}
|
|
5577
|
+
|
|
5578
|
+
// src/worker-routes-network-records-follower.ts
|
|
5579
|
+
var NETWORK_MUTATION_META = "chapterNetworkMutation";
|
|
5580
|
+
function mutationFingerprint(input) {
|
|
5581
|
+
const fields = input.fields ? Object.fromEntries(Object.entries(input.fields).sort(([a], [b]) => a.localeCompare(b))) : void 0;
|
|
5582
|
+
return JSON.stringify({
|
|
5583
|
+
recordId: input.recordId,
|
|
5584
|
+
type: input.type,
|
|
5585
|
+
expectedRevision: input.expectedRevision,
|
|
5586
|
+
expectedStage: input.expectedStage,
|
|
5587
|
+
mutationId: input.mutationId,
|
|
5588
|
+
fields,
|
|
5589
|
+
stage: input.stage,
|
|
5590
|
+
actor: input.actor
|
|
5591
|
+
});
|
|
5592
|
+
}
|
|
5593
|
+
function mutationError(error) {
|
|
5594
|
+
if (error?.code === "transact_guard_failed") {
|
|
5595
|
+
return json({ error: "record changed since it was reviewed", code: "stale_revision" }, 409);
|
|
5596
|
+
}
|
|
5597
|
+
if (error instanceof import_crm7.CrmInputError) {
|
|
5598
|
+
return json({ error: error.message, code: "invalid_transition_or_fields", ...error.fields ? { fields: error.fields } : {} }, 422);
|
|
5599
|
+
}
|
|
5600
|
+
return json({ error: "follower mutation failed", code: "mutation_failed" }, 500);
|
|
5601
|
+
}
|
|
5602
|
+
async function priorMutation(db, id) {
|
|
5603
|
+
const result = await db.query({
|
|
5604
|
+
[import_crm7.CRM_NS.activity]: { $: { where: { id }, limit: 1 } }
|
|
5605
|
+
});
|
|
5606
|
+
const row = (result[import_crm7.CRM_NS.activity] ?? [])[0];
|
|
5607
|
+
const meta = row?.meta;
|
|
5608
|
+
const receipt = meta?.[NETWORK_MUTATION_META];
|
|
5609
|
+
return typeof receipt?.fingerprint === "string" ? { fingerprint: receipt.fingerprint } : null;
|
|
5610
|
+
}
|
|
5479
5611
|
var handleNetworkRecords = async (req, url, env, ctx) => {
|
|
5480
|
-
if (req.method !== "GET"
|
|
5612
|
+
if (url.pathname !== "/api/network/records" || req.method !== "GET" && req.method !== "PATCH") return null;
|
|
5613
|
+
if (req.method === "PATCH" && Number(req.headers.get("content-length") ?? 0) > MAX_BODY) {
|
|
5614
|
+
return json({ error: "mutation body is too large", code: "body_too_large" }, 413);
|
|
5615
|
+
}
|
|
5481
5616
|
const db = ctx.makeDb(env);
|
|
5482
5617
|
const secret = await getVaultSecret(db, "network_share_secret");
|
|
5483
5618
|
const readers = ctx.chapter.network.readers;
|
|
@@ -5488,8 +5623,105 @@ var handleNetworkRecords = async (req, url, env, ctx) => {
|
|
|
5488
5623
|
});
|
|
5489
5624
|
if (!verified.ok) return json({ error: "unauthorized", reason: verified.reason }, 401);
|
|
5490
5625
|
const reader = readers.find((candidate) => candidate.id === verified.sender);
|
|
5626
|
+
if (!reader) return json({ error: "unauthorized" }, 401);
|
|
5627
|
+
if (req.method === "PATCH") {
|
|
5628
|
+
if (bodyTooLarge(verified.body)) {
|
|
5629
|
+
return json({ error: "mutation body is too large", code: "body_too_large" }, 413);
|
|
5630
|
+
}
|
|
5631
|
+
let input;
|
|
5632
|
+
try {
|
|
5633
|
+
input = JSON.parse(verified.body);
|
|
5634
|
+
} catch {
|
|
5635
|
+
return json({ error: "invalid JSON body", code: "invalid_json" }, 422);
|
|
5636
|
+
}
|
|
5637
|
+
const fields = input?.fields;
|
|
5638
|
+
const hasFields = Boolean(fields && typeof fields === "object" && !Array.isArray(fields));
|
|
5639
|
+
const hasStage = typeof input?.stage === "string";
|
|
5640
|
+
const actor = input?.actor;
|
|
5641
|
+
if (!input || !IDENTIFIER.test(input.type) || !RECORD_ID.test(input.recordId) || !Number.isSafeInteger(input.expectedRevision) || input.expectedRevision < 0 || !MUTATION_ID.test(input.mutationId) || !actor || typeof actor.id !== "string" || !RECORD_ID.test(actor.id) || hasFields === hasStage) {
|
|
5642
|
+
return json({ error: "invalid network mutation", code: "invalid_mutation" }, 422);
|
|
5643
|
+
}
|
|
5644
|
+
const editable = reader.editableFields[input.type];
|
|
5645
|
+
if (hasFields) {
|
|
5646
|
+
const names = Object.keys(fields);
|
|
5647
|
+
if (!editable || names.length === 0 || names.length > 32 || names.some((name) => !editable.includes(name))) {
|
|
5648
|
+
return json({ error: "profile fields are not delegated for this reader", code: "field_forbidden" }, 403);
|
|
5649
|
+
}
|
|
5650
|
+
} else if (!reader.stageTransitions.includes(input.type)) {
|
|
5651
|
+
return json({ error: "stage transitions are not delegated for this reader", code: "stage_forbidden" }, 403);
|
|
5652
|
+
}
|
|
5653
|
+
const activityId = `network-edit:${verified.sender}:${input.mutationId}`;
|
|
5654
|
+
const fingerprint = mutationFingerprint(input);
|
|
5655
|
+
const prior = await priorMutation(db, activityId);
|
|
5656
|
+
if (prior) {
|
|
5657
|
+
if (prior.fingerprint !== fingerprint) {
|
|
5658
|
+
return json({ error: "mutationId was already used for different input", code: "mutation_conflict" }, 409);
|
|
5659
|
+
}
|
|
5660
|
+
const replayed = await (0, import_crm7.getRecord)({ crm: ctx.chapter.crm, db }, input.recordId);
|
|
5661
|
+
if (!replayed || replayed.type !== input.type) return json({ error: "record not found", code: "not_found" }, 404);
|
|
5662
|
+
const result = {
|
|
5663
|
+
version: 1,
|
|
5664
|
+
site: { id: ctx.chapter.id, name: ctx.chapter.name },
|
|
5665
|
+
record: readerRecord(replayed, reader, ctx.chapter.crm),
|
|
5666
|
+
duplicate: true
|
|
5667
|
+
};
|
|
5668
|
+
return json(result);
|
|
5669
|
+
}
|
|
5670
|
+
const record4 = await (0, import_crm7.getRecord)({ crm: ctx.chapter.crm, db }, input.recordId);
|
|
5671
|
+
if (!record4 || record4.type !== input.type) return json({ error: "record not found", code: "not_found" }, 404);
|
|
5672
|
+
if (record4.updatedAt !== input.expectedRevision) {
|
|
5673
|
+
return json({ error: "record changed since it was reviewed", code: "stale_revision" }, 409);
|
|
5674
|
+
}
|
|
5675
|
+
if (hasStage && (input.expectedStage ?? null) !== (record4.stage ?? null)) {
|
|
5676
|
+
return json({ error: "stage changed since it was reviewed", code: "stale_stage" }, 409);
|
|
5677
|
+
}
|
|
5678
|
+
const actorEmail = typeof actor.email === "string" && actor.email.length <= 320 ? actor.email : void 0;
|
|
5679
|
+
const auditMeta = {
|
|
5680
|
+
[NETWORK_MUTATION_META]: {
|
|
5681
|
+
version: 1,
|
|
5682
|
+
sourceId: verified.sender,
|
|
5683
|
+
actorId: actor.id,
|
|
5684
|
+
...actorEmail ? { actorEmail } : {},
|
|
5685
|
+
mutationId: input.mutationId,
|
|
5686
|
+
fingerprint
|
|
5687
|
+
}
|
|
5688
|
+
};
|
|
5689
|
+
try {
|
|
5690
|
+
const deps = { crm: ctx.chapter.crm, db };
|
|
5691
|
+
const updated = hasFields ? await (0, import_crm7.updateRecord)(deps, {
|
|
5692
|
+
id: record4.id,
|
|
5693
|
+
input: fields,
|
|
5694
|
+
mutationId: activityId,
|
|
5695
|
+
expectedUpdatedAt: input.expectedRevision,
|
|
5696
|
+
audit: {
|
|
5697
|
+
id: activityId,
|
|
5698
|
+
body: `Profile edited remotely by ${verified.sender}`,
|
|
5699
|
+
authorId: `network:${verified.sender}`,
|
|
5700
|
+
meta: auditMeta
|
|
5701
|
+
}
|
|
5702
|
+
}) : await (0, import_crm7.setStage)(deps, {
|
|
5703
|
+
id: record4.id,
|
|
5704
|
+
to: input.stage,
|
|
5705
|
+
authorId: `network:${verified.sender}`,
|
|
5706
|
+
mutationId: activityId,
|
|
5707
|
+
expectedUpdatedAt: input.expectedRevision,
|
|
5708
|
+
expectedStage: input.expectedStage ?? null,
|
|
5709
|
+
activityId,
|
|
5710
|
+
activityMeta: auditMeta
|
|
5711
|
+
});
|
|
5712
|
+
const result = {
|
|
5713
|
+
version: 1,
|
|
5714
|
+
site: { id: ctx.chapter.id, name: ctx.chapter.name },
|
|
5715
|
+
record: readerRecord(updated.record, reader, ctx.chapter.crm),
|
|
5716
|
+
duplicate: updated.duplicate
|
|
5717
|
+
};
|
|
5718
|
+
return json(result);
|
|
5719
|
+
} catch (error) {
|
|
5720
|
+
return mutationError(error);
|
|
5721
|
+
}
|
|
5722
|
+
}
|
|
5491
5723
|
const type = url.searchParams.get("type") ?? "";
|
|
5492
|
-
const allowed2 = reader
|
|
5724
|
+
const allowed2 = reader.fields[type];
|
|
5493
5725
|
if (!IDENTIFIER.test(type) || !allowed2) return json({ error: "record type is not shared with this reader" }, 403);
|
|
5494
5726
|
const limit = integer(url.searchParams.get("limit"), 25, 1, MAX_PAGE);
|
|
5495
5727
|
const offset = integer(url.searchParams.get("offset"), 0, 0, MAX_OFFSET);
|
|
@@ -5511,13 +5743,23 @@ var handleNetworkRecords = async (req, url, env, ctx) => {
|
|
|
5511
5743
|
version: 1,
|
|
5512
5744
|
site: { id: ctx.chapter.id, name: ctx.chapter.name },
|
|
5513
5745
|
type,
|
|
5514
|
-
records: page.records.map((record4) =>
|
|
5746
|
+
records: page.records.map((record4) => readerRecord(record4, reader, ctx.chapter.crm)),
|
|
5515
5747
|
total: page.total,
|
|
5516
5748
|
limit: page.limit,
|
|
5517
5749
|
offset: page.offset
|
|
5518
5750
|
};
|
|
5519
5751
|
return json(response);
|
|
5520
5752
|
};
|
|
5753
|
+
|
|
5754
|
+
// src/worker-routes-network-records-admin.ts
|
|
5755
|
+
var import_db9 = require("@odla-ai/db");
|
|
5756
|
+
async function gate6(req, env, ctx) {
|
|
5757
|
+
const db = ctx.makeDb(env);
|
|
5758
|
+
const user = await ctx.verifyUser(req, env);
|
|
5759
|
+
if (!user) return { response: json({ error: "unauthorized" }, 401) };
|
|
5760
|
+
if (!await ctx.isAdmin(db, user)) return { response: json({ error: "forbidden" }, 403) };
|
|
5761
|
+
return { db, user };
|
|
5762
|
+
}
|
|
5521
5763
|
function targetFor(ctx, id) {
|
|
5522
5764
|
return ctx.chapter.network.targets.find((target) => target.id === id);
|
|
5523
5765
|
}
|
|
@@ -5527,12 +5769,7 @@ async function readTargetRecords(db, env, ctx, target, query) {
|
|
|
5527
5769
|
const destination = new URL("/api/network/records", target.url);
|
|
5528
5770
|
destination.search = query.toString();
|
|
5529
5771
|
try {
|
|
5530
|
-
const headers = await (0,
|
|
5531
|
-
secret,
|
|
5532
|
-
sender: ctx.chapter.id,
|
|
5533
|
-
method: "GET",
|
|
5534
|
-
url: destination
|
|
5535
|
-
});
|
|
5772
|
+
const headers = await (0, import_db9.signFederatedRequest)({ secret, sender: ctx.chapter.id, method: "GET", url: destination });
|
|
5536
5773
|
const response = await fetchNetworkTarget(env, target, destination, {
|
|
5537
5774
|
headers,
|
|
5538
5775
|
signal: AbortSignal.timeout(1e4)
|
|
@@ -5545,15 +5782,90 @@ async function readTargetRecords(db, env, ctx, target, query) {
|
|
|
5545
5782
|
error: upstream ?? (response.ok ? "invalid follower record response" : `follower returned ${response.status}`)
|
|
5546
5783
|
};
|
|
5547
5784
|
}
|
|
5548
|
-
return {
|
|
5785
|
+
return {
|
|
5786
|
+
ok: true,
|
|
5787
|
+
page: { ...body, records: body.records.map((record4) => restrictedRecord(record4, target)) }
|
|
5788
|
+
};
|
|
5549
5789
|
} catch (error) {
|
|
5550
5790
|
return { ok: false, error: error instanceof Error ? error.message : "record read failed" };
|
|
5551
5791
|
}
|
|
5552
5792
|
}
|
|
5793
|
+
async function mutateTargetRecord(db, env, ctx, target, input) {
|
|
5794
|
+
const fields = input.fields;
|
|
5795
|
+
if (fields) {
|
|
5796
|
+
const allowed2 = target.editableFields[input.type];
|
|
5797
|
+
if (!allowed2 || Object.keys(fields).some((name) => !allowed2.includes(name))) {
|
|
5798
|
+
return json({ error: "profile fields are not enabled for this target", code: "field_forbidden" }, 403);
|
|
5799
|
+
}
|
|
5800
|
+
} else if (!target.stageTransitions.includes(input.type)) {
|
|
5801
|
+
return json({ error: "stage transitions are not enabled for this target", code: "stage_forbidden" }, 403);
|
|
5802
|
+
}
|
|
5803
|
+
const secret = await getVaultSecret(db, target.secretName);
|
|
5804
|
+
if (!secret) return json({ error: "edge secret is missing", code: "edge_unavailable" }, 502);
|
|
5805
|
+
const destination = new URL("/api/network/records", target.url);
|
|
5806
|
+
const body = JSON.stringify(input);
|
|
5807
|
+
try {
|
|
5808
|
+
const headers = await (0, import_db9.signFederatedRequest)({
|
|
5809
|
+
secret,
|
|
5810
|
+
sender: ctx.chapter.id,
|
|
5811
|
+
method: "PATCH",
|
|
5812
|
+
url: destination,
|
|
5813
|
+
body
|
|
5814
|
+
});
|
|
5815
|
+
const response = await fetchNetworkTarget(env, target, destination, {
|
|
5816
|
+
method: "PATCH",
|
|
5817
|
+
headers: { ...headers, "content-type": "application/json" },
|
|
5818
|
+
body,
|
|
5819
|
+
signal: AbortSignal.timeout(1e4)
|
|
5820
|
+
});
|
|
5821
|
+
const payload = await response.json().catch(() => null);
|
|
5822
|
+
const site = payload && "site" in payload ? payload.site : void 0;
|
|
5823
|
+
if (!response.ok || !payload || !("record" in payload) || site && site.id !== target.id) {
|
|
5824
|
+
const status = [401, 403, 404, 409, 422].includes(response.status) ? response.status : 502;
|
|
5825
|
+
return json({
|
|
5826
|
+
error: payload && "error" in payload && typeof payload.error === "string" ? payload.error : `follower returned ${response.status}`,
|
|
5827
|
+
...payload && "code" in payload && typeof payload.code === "string" ? { code: payload.code } : {}
|
|
5828
|
+
}, status);
|
|
5829
|
+
}
|
|
5830
|
+
return json({ ...payload, record: restrictedRecord(payload.record, target) });
|
|
5831
|
+
} catch {
|
|
5832
|
+
return json({ error: "follower mutation request failed", code: "follower_unavailable" }, 502);
|
|
5833
|
+
}
|
|
5834
|
+
}
|
|
5553
5835
|
var handleAdminNetworkRecords = async (req, url, env, ctx) => {
|
|
5554
|
-
if (req.method !== "GET"
|
|
5836
|
+
if (url.pathname !== "/api/admin/network/records" || req.method !== "GET" && req.method !== "PATCH") return null;
|
|
5837
|
+
if (req.method === "PATCH" && Number(req.headers.get("content-length") ?? 0) > MAX_BODY) {
|
|
5838
|
+
return json({ error: "mutation body is too large", code: "body_too_large" }, 413);
|
|
5839
|
+
}
|
|
5555
5840
|
const got = await gate6(req, env, ctx);
|
|
5556
5841
|
if ("response" in got) return got.response;
|
|
5842
|
+
if (req.method === "PATCH") {
|
|
5843
|
+
let input;
|
|
5844
|
+
try {
|
|
5845
|
+
const body = await req.text();
|
|
5846
|
+
if (bodyTooLarge(body)) {
|
|
5847
|
+
return json({ error: "mutation body is too large", code: "body_too_large" }, 413);
|
|
5848
|
+
}
|
|
5849
|
+
input = JSON.parse(body);
|
|
5850
|
+
} catch {
|
|
5851
|
+
return json({ error: "invalid JSON body", code: "invalid_json" }, 422);
|
|
5852
|
+
}
|
|
5853
|
+
const target2 = targetFor(ctx, input?.targetId);
|
|
5854
|
+
const hasFields = Boolean(input?.fields && typeof input.fields === "object" && !Array.isArray(input.fields));
|
|
5855
|
+
const hasStage = typeof input?.stage === "string";
|
|
5856
|
+
if (!target2 || !IDENTIFIER.test(input.type) || !RECORD_ID.test(input.recordId) || !Number.isSafeInteger(input.expectedRevision) || input.expectedRevision < 0 || !MUTATION_ID.test(input.mutationId) || hasFields === hasStage) {
|
|
5857
|
+
return json({ error: "invalid network mutation", code: "invalid_mutation" }, 422);
|
|
5858
|
+
}
|
|
5859
|
+
return mutateTargetRecord(got.db, env, ctx, target2, {
|
|
5860
|
+
type: input.type,
|
|
5861
|
+
recordId: input.recordId,
|
|
5862
|
+
expectedRevision: input.expectedRevision,
|
|
5863
|
+
mutationId: input.mutationId,
|
|
5864
|
+
...input.expectedStage !== void 0 ? { expectedStage: input.expectedStage } : {},
|
|
5865
|
+
...hasFields ? { fields: input.fields } : { stage: input.stage },
|
|
5866
|
+
actor: { id: got.user.userId, ...got.user.email ? { email: got.user.email } : {} }
|
|
5867
|
+
});
|
|
5868
|
+
}
|
|
5557
5869
|
const targetId = url.searchParams.get("targetId") ?? "";
|
|
5558
5870
|
const type = url.searchParams.get("type") ?? "";
|
|
5559
5871
|
const target = targetFor(ctx, targetId);
|
|
@@ -5566,19 +5878,13 @@ var handleAdminNetworkRecords = async (req, url, env, ctx) => {
|
|
|
5566
5878
|
}
|
|
5567
5879
|
const query = new URLSearchParams({ type, limit: String(limit), offset: String(offset) });
|
|
5568
5880
|
if (search) query.set("search", search);
|
|
5569
|
-
const result = await readTargetRecords(
|
|
5570
|
-
got.db,
|
|
5571
|
-
env,
|
|
5572
|
-
ctx,
|
|
5573
|
-
target,
|
|
5574
|
-
query
|
|
5575
|
-
);
|
|
5881
|
+
const result = await readTargetRecords(got.db, env, ctx, target, query);
|
|
5576
5882
|
return result.ok ? json(result.page) : json({ error: result.error }, 502);
|
|
5577
5883
|
};
|
|
5578
5884
|
|
|
5579
5885
|
// src/worker-routes-network-notes.ts
|
|
5580
5886
|
var IDENTIFIER2 = /^[a-z][a-zA-Z0-9_]*$/;
|
|
5581
|
-
var
|
|
5887
|
+
var RECORD_ID2 = /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,255}$/;
|
|
5582
5888
|
var MAX_NOTE2 = 4e3;
|
|
5583
5889
|
async function gate7(req, env, ctx) {
|
|
5584
5890
|
const db = ctx.makeDb(env);
|
|
@@ -5592,7 +5898,7 @@ function noteIdentity(url, ctx) {
|
|
|
5592
5898
|
const recordType = url.searchParams.get("type") ?? "";
|
|
5593
5899
|
const recordId = url.searchParams.get("recordId") ?? "";
|
|
5594
5900
|
const configured = ctx.chapter.network.targets.some((target) => target.id === targetId);
|
|
5595
|
-
if (!configured || !IDENTIFIER2.test(recordType) || !
|
|
5901
|
+
if (!configured || !IDENTIFIER2.test(recordType) || !RECORD_ID2.test(recordId)) return null;
|
|
5596
5902
|
return {
|
|
5597
5903
|
targetId,
|
|
5598
5904
|
recordType,
|
|
@@ -5662,10 +5968,10 @@ var handleAdminNetworkNotes = async (req, url, env, ctx) => {
|
|
|
5662
5968
|
|
|
5663
5969
|
// src/worker-routes-network-shared-notes.ts
|
|
5664
5970
|
var import_crm8 = require("@odla-ai/crm");
|
|
5665
|
-
var
|
|
5971
|
+
var import_db10 = require("@odla-ai/db");
|
|
5666
5972
|
var IDENTIFIER3 = /^[a-z][a-zA-Z0-9_]*$/;
|
|
5667
|
-
var
|
|
5668
|
-
var
|
|
5973
|
+
var RECORD_ID3 = /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,255}$/;
|
|
5974
|
+
var MUTATION_ID2 = /^[A-Za-z0-9_-]{16,128}$/;
|
|
5669
5975
|
var MAX_NOTE3 = 4e3;
|
|
5670
5976
|
var NETWORK_META = "chapterNetwork";
|
|
5671
5977
|
async function gate8(req, env, ctx) {
|
|
@@ -5721,7 +6027,7 @@ var handleNetworkSharedNotes = async (req, url, env, ctx) => {
|
|
|
5721
6027
|
const secret = await getVaultSecret(db, "network_share_secret");
|
|
5722
6028
|
const readers = ctx.chapter.network.readers;
|
|
5723
6029
|
if (!secret || readers.length === 0) return json({ error: "unauthorized" }, 401);
|
|
5724
|
-
const verified = await (0,
|
|
6030
|
+
const verified = await (0, import_db10.verifyFederatedRequest)(req, {
|
|
5725
6031
|
secret,
|
|
5726
6032
|
senders: readers.map((reader2) => reader2.id)
|
|
5727
6033
|
});
|
|
@@ -5729,7 +6035,7 @@ var handleNetworkSharedNotes = async (req, url, env, ctx) => {
|
|
|
5729
6035
|
const reader = readers.find((candidate) => candidate.id === verified.sender);
|
|
5730
6036
|
const recordType = url.searchParams.get("type") ?? "";
|
|
5731
6037
|
const recordId = url.searchParams.get("recordId") ?? "";
|
|
5732
|
-
if (!IDENTIFIER3.test(recordType) || !
|
|
6038
|
+
if (!IDENTIFIER3.test(recordType) || !RECORD_ID3.test(recordId) || !reader?.sharedNotes.includes(recordType)) {
|
|
5733
6039
|
return json({ error: "shared notes are not enabled for this reader and record type" }, 403);
|
|
5734
6040
|
}
|
|
5735
6041
|
const record4 = await (0, import_crm8.getRecord)({ crm: ctx.chapter.crm, db }, recordId);
|
|
@@ -5749,7 +6055,7 @@ var handleNetworkSharedNotes = async (req, url, env, ctx) => {
|
|
|
5749
6055
|
}
|
|
5750
6056
|
const body = typeof input.body === "string" ? input.body.trim() : "";
|
|
5751
6057
|
const mutationId = typeof input.mutationId === "string" ? input.mutationId : "";
|
|
5752
|
-
if (!body || body.length > MAX_NOTE3 || !
|
|
6058
|
+
if (!body || body.length > MAX_NOTE3 || !MUTATION_ID2.test(mutationId)) {
|
|
5753
6059
|
return json({ error: `body must be 1\u2013${MAX_NOTE3} characters and mutationId must be valid` }, 400);
|
|
5754
6060
|
}
|
|
5755
6061
|
const createdAt = Date.now();
|
|
@@ -5798,7 +6104,7 @@ var handleAdminNetworkSharedNotes = async (req, url, env, ctx) => {
|
|
|
5798
6104
|
const recordType = url.searchParams.get("type") ?? "";
|
|
5799
6105
|
const recordId = url.searchParams.get("recordId") ?? "";
|
|
5800
6106
|
const target = targetFor2(ctx, targetId);
|
|
5801
|
-
if (!target || !target.sharedNotes.includes(recordType) || !IDENTIFIER3.test(recordType) || !
|
|
6107
|
+
if (!target || !target.sharedNotes.includes(recordType) || !IDENTIFIER3.test(recordType) || !RECORD_ID3.test(recordId)) {
|
|
5802
6108
|
return json({ error: "configured targetId, type, and recordId are required" }, 400);
|
|
5803
6109
|
}
|
|
5804
6110
|
let body = "";
|
|
@@ -5818,7 +6124,7 @@ var handleAdminNetworkSharedNotes = async (req, url, env, ctx) => {
|
|
|
5818
6124
|
const destination = new URL("/api/network/shared-notes", target.url);
|
|
5819
6125
|
destination.search = new URLSearchParams({ type: recordType, recordId }).toString();
|
|
5820
6126
|
try {
|
|
5821
|
-
const headers = await (0,
|
|
6127
|
+
const headers = await (0, import_db10.signFederatedRequest)({
|
|
5822
6128
|
secret,
|
|
5823
6129
|
sender: ctx.chapter.id,
|
|
5824
6130
|
method: req.method,
|
|
@@ -5925,7 +6231,7 @@ var handleFormation = async (req, url, env, ctx) => {
|
|
|
5925
6231
|
};
|
|
5926
6232
|
|
|
5927
6233
|
// src/worker-routes-builder-library.ts
|
|
5928
|
-
var
|
|
6234
|
+
var import_db11 = require("@odla-ai/db");
|
|
5929
6235
|
|
|
5930
6236
|
// src/builder-library-validation.ts
|
|
5931
6237
|
var ID4 = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
@@ -6208,7 +6514,7 @@ async function verifySource(req, db, ctx) {
|
|
|
6208
6514
|
if (ctx.chapter.mode === "chapter") {
|
|
6209
6515
|
const secret = await getVaultSecret(db, "network_share_secret");
|
|
6210
6516
|
if (!secret) return null;
|
|
6211
|
-
const verified = await (0,
|
|
6517
|
+
const verified = await (0, import_db11.verifyFederatedRequest)(req, {
|
|
6212
6518
|
secret,
|
|
6213
6519
|
senders: sources.map((source2) => source2.sourceId)
|
|
6214
6520
|
});
|
|
@@ -6221,7 +6527,7 @@ async function verifySource(req, db, ctx) {
|
|
|
6221
6527
|
if (!target) continue;
|
|
6222
6528
|
const secret = await getVaultSecret(db, target.secretName);
|
|
6223
6529
|
if (!secret) continue;
|
|
6224
|
-
const verified = await (0,
|
|
6530
|
+
const verified = await (0, import_db11.verifyFederatedRequest)(req.clone(), { secret, senders: [source.sourceId] });
|
|
6225
6531
|
if (verified.ok) return { source, body: verified.body };
|
|
6226
6532
|
}
|
|
6227
6533
|
return null;
|
|
@@ -6410,11 +6716,11 @@ var handleAdminBuilderLibrary = async (req, url, env, ctx) => {
|
|
|
6410
6716
|
};
|
|
6411
6717
|
|
|
6412
6718
|
// src/signup-control-delivery.ts
|
|
6413
|
-
var
|
|
6719
|
+
var import_db12 = require("@odla-ai/db");
|
|
6414
6720
|
async function deliverSignupControl(options) {
|
|
6415
6721
|
const destination = new URL(SIGNUP_CONTROL_PATH, options.targetUrl);
|
|
6416
6722
|
const body = JSON.stringify(options.envelope);
|
|
6417
|
-
const signed = await (0,
|
|
6723
|
+
const signed = await (0, import_db12.signFederatedRequest)({
|
|
6418
6724
|
secret: options.secret,
|
|
6419
6725
|
sender: options.sender,
|
|
6420
6726
|
method: "POST",
|
|
@@ -6435,11 +6741,11 @@ async function deliverSignupControl(options) {
|
|
|
6435
6741
|
}
|
|
6436
6742
|
|
|
6437
6743
|
// src/member-content-delivery.ts
|
|
6438
|
-
var
|
|
6744
|
+
var import_db13 = require("@odla-ai/db");
|
|
6439
6745
|
async function deliverMemberContent(options) {
|
|
6440
6746
|
const destination = new URL(MEMBER_CONTENT_CONTROL_PATH, options.targetUrl);
|
|
6441
6747
|
const body = JSON.stringify(options.envelope);
|
|
6442
|
-
const signed = await (0,
|
|
6748
|
+
const signed = await (0, import_db13.signFederatedRequest)({
|
|
6443
6749
|
secret: options.secret,
|
|
6444
6750
|
sender: options.sender,
|
|
6445
6751
|
method: "POST",
|
|
@@ -6460,11 +6766,11 @@ async function deliverMemberContent(options) {
|
|
|
6460
6766
|
}
|
|
6461
6767
|
|
|
6462
6768
|
// src/builder-library-delivery.ts
|
|
6463
|
-
var
|
|
6769
|
+
var import_db14 = require("@odla-ai/db");
|
|
6464
6770
|
async function deliverBuilderLibrary(options) {
|
|
6465
6771
|
const destination = new URL(BUILDER_LIBRARY_CONTROL_PATH, options.targetUrl);
|
|
6466
6772
|
const body = JSON.stringify(options.envelope);
|
|
6467
|
-
const signed = await (0,
|
|
6773
|
+
const signed = await (0, import_db14.signFederatedRequest)({ secret: options.secret, sender: options.sender, method: "POST", url: destination, body });
|
|
6468
6774
|
const response = await (options.fetch ?? globalThis.fetch)(destination, { method: "POST", headers: { ...signed, "content-type": "application/json" }, body });
|
|
6469
6775
|
const result = await response.json();
|
|
6470
6776
|
if (!response.ok || !("ok" in result) || result.ok !== true) {
|
|
@@ -6475,11 +6781,11 @@ async function deliverBuilderLibrary(options) {
|
|
|
6475
6781
|
}
|
|
6476
6782
|
|
|
6477
6783
|
// src/admission-grants-delivery.ts
|
|
6478
|
-
var
|
|
6784
|
+
var import_db15 = require("@odla-ai/db");
|
|
6479
6785
|
async function deliverAdmissionGrants(options) {
|
|
6480
6786
|
const destination = new URL(ADMISSION_GRANTS_PATH, options.targetUrl);
|
|
6481
6787
|
const body = JSON.stringify(options.envelope);
|
|
6482
|
-
const signed = await (0,
|
|
6788
|
+
const signed = await (0, import_db15.signFederatedRequest)({
|
|
6483
6789
|
secret: options.secret,
|
|
6484
6790
|
sender: options.sender,
|
|
6485
6791
|
method: "POST",
|