@odla-ai/chapter 0.42.1 → 0.42.2

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.
@@ -607,6 +607,11 @@ function validateAdmissionGrantDocument(input) {
607
607
  }
608
608
  };
609
609
  }
610
+ async function createAdmissionGrantEnvelope(document) {
611
+ const checked = validateAdmissionGrantDocument(document);
612
+ if (!checked.ok) throw new TypeError(`Invalid admission grant document: ${checked.errors.join("; ")}`);
613
+ return { document: checked.value, digest: await signupControlDigest(checked.value) };
614
+ }
610
615
  var rows = (value) => Array.isArray(value) ? value : [];
611
616
  function asGrant(row) {
612
617
  if (typeof row.id !== "string" || typeof row.emailNormalized !== "string") return null;
@@ -3110,12 +3115,17 @@ import { verifyFederatedRequest as verifyFederatedRequest4 } from "@odla-ai/db";
3110
3115
  var reject3 = (reason, status, errors) => json({ ok: false, reason, ...errors ? { errors } : {} }, status);
3111
3116
  var handleAdmissionGrants = async (req, url, env, ctx) => {
3112
3117
  if (req.method !== "POST" || url.pathname !== ADMISSION_GRANTS_PATH) return null;
3113
- const authority = ctx.chapter.signupControl;
3114
- if (!authority) return json({ error: "not found" }, 404);
3118
+ const claimedSender = req.headers.get("x-odla-federation-sender") ?? "";
3119
+ const networkReader = ctx.chapter.network.readers.find((reader) => reader.id === claimedSender && reader.admissionGrants);
3120
+ const signupAuthority = ctx.chapter.signupControl?.sourceId === claimedSender ? ctx.chapter.signupControl : void 0;
3121
+ if (!networkReader && !signupAuthority) return json({ error: "not found" }, 404);
3115
3122
  const db = ctx.makeDb(env);
3116
- const secret = await getVaultSecret(db, authority.secretName);
3123
+ const secret = await getVaultSecret(
3124
+ db,
3125
+ networkReader ? "network_share_secret" : signupAuthority.secretName
3126
+ );
3117
3127
  if (!secret) return json({ error: "unauthorized" }, 401);
3118
- const verified = await verifyFederatedRequest4(req, { secret, senders: [authority.sourceId] });
3128
+ const verified = await verifyFederatedRequest4(req, { secret, senders: [claimedSender] });
3119
3129
  if (!verified.ok) return json({ error: "unauthorized", reason: verified.reason }, 401);
3120
3130
  let input;
3121
3131
  try {
@@ -3127,11 +3137,11 @@ var handleAdmissionGrants = async (req, url, env, ctx) => {
3127
3137
  const checked = validateAdmissionGrantDocument(input.document);
3128
3138
  if (!checked.ok) return reject3("invalid_document", 400, checked.errors);
3129
3139
  const document = checked.value;
3130
- if (document.source.appId !== authority.sourceId) return reject3("wrong_source", 403);
3140
+ if (document.source.appId !== claimedSender) return reject3("wrong_source", 403);
3131
3141
  if (document.target.chapterId !== ctx.chapter.id) return reject3("wrong_target", 409);
3132
3142
  if (document.target.environment !== env.ODLA_ENV) return reject3("wrong_environment", 409);
3133
3143
  if (!digest || await signupControlDigest(document) !== digest) return reject3("invalid_digest", 400);
3134
- const latest = await loadLatestAdmissionRevision(db, authority.sourceId, ctx.chapter.id, env.ODLA_ENV);
3144
+ const latest = await loadLatestAdmissionRevision(db, claimedSender, ctx.chapter.id, env.ODLA_ENV);
3135
3145
  if (latest && document.revision < latest.revision) return reject3("stale_revision", 409);
3136
3146
  if (latest && document.revision === latest.revision) {
3137
3147
  if (latest.digest !== digest) return reject3("conflicting_revision", 409);
@@ -3256,6 +3266,365 @@ var handleAdminAdmissionGrants = async (req, url, env, ctx) => {
3256
3266
  return null;
3257
3267
  };
3258
3268
 
3269
+ // src/worker-routes-network-admission.ts
3270
+ import { getRecord as getRecord2 } from "@odla-ai/crm";
3271
+
3272
+ // src/admission-grants-delivery.ts
3273
+ import { signFederatedRequest } from "@odla-ai/db";
3274
+ var AdmissionGrantDeliveryError = class extends Error {
3275
+ constructor(status, reason) {
3276
+ super(`admission grants rejected (${status}): ${reason}`);
3277
+ this.status = status;
3278
+ this.reason = reason;
3279
+ this.name = "AdmissionGrantDeliveryError";
3280
+ }
3281
+ status;
3282
+ reason;
3283
+ };
3284
+ async function deliverAdmissionGrants(options) {
3285
+ const destination = new URL(ADMISSION_GRANTS_PATH, options.targetUrl);
3286
+ const body = JSON.stringify(options.envelope);
3287
+ const signed = await signFederatedRequest({
3288
+ secret: options.secret,
3289
+ sender: options.sender,
3290
+ method: "POST",
3291
+ url: destination,
3292
+ body
3293
+ });
3294
+ const response = await (options.fetch ?? globalThis.fetch)(destination, {
3295
+ method: "POST",
3296
+ headers: { ...signed, "content-type": "application/json" },
3297
+ body
3298
+ });
3299
+ const result = await response.json();
3300
+ if (!response.ok || !("ok" in result) || result.ok !== true) {
3301
+ const failure = result;
3302
+ throw new AdmissionGrantDeliveryError(
3303
+ response.status,
3304
+ failure.reason ?? failure.error ?? "unknown"
3305
+ );
3306
+ }
3307
+ return result;
3308
+ }
3309
+
3310
+ // src/worker-network-fetch.ts
3311
+ function targetFetcher(env, target) {
3312
+ if (!target.binding) return null;
3313
+ const candidate = env[target.binding];
3314
+ if (typeof candidate !== "object" || candidate === null || !("fetch" in candidate) || typeof candidate.fetch !== "function") {
3315
+ throw new Error(`service binding "${target.binding}" is unavailable`);
3316
+ }
3317
+ return candidate;
3318
+ }
3319
+ function fetchNetworkTarget(env, target, destination, init) {
3320
+ const binding = targetFetcher(env, target);
3321
+ return binding ? binding.fetch(new Request(destination, init)) : globalThis.fetch(destination, init);
3322
+ }
3323
+
3324
+ // src/worker-network-admission-state.ts
3325
+ import { getRecord } from "@odla-ai/crm";
3326
+ var MAX_ASSIGNMENTS = 2e3;
3327
+ var admissionRows = (value) => Array.isArray(value) ? value : [];
3328
+ function assignmentId(targetId, personId) {
3329
+ return `leader-admission:${targetId}:${personId}`;
3330
+ }
3331
+ function deliveryId(targetId, environment) {
3332
+ return `leader-admission-delivery:${targetId}:${environment}`;
3333
+ }
3334
+ async function loadLeaderAdmissionAssignments(db, targetId) {
3335
+ const result = await db.query({ leaderAdmissionAssignments: { $: {
3336
+ where: { targetId },
3337
+ order: { updatedAt: "desc" },
3338
+ limit: MAX_ASSIGNMENTS
3339
+ } } });
3340
+ return admissionRows(result.leaderAdmissionAssignments);
3341
+ }
3342
+ async function loadLeaderAdmissionDelivery(db, targetId, environment) {
3343
+ const id = deliveryId(targetId, environment);
3344
+ const result = await db.query({ leaderAdmissionDeliveries: { $: { where: { id }, limit: 1 } } });
3345
+ return admissionRows(result.leaderAdmissionDeliveries)[0] ?? null;
3346
+ }
3347
+ function personAdmissionEmail(ctx, record4) {
3348
+ if (record4.type !== "person") return null;
3349
+ const field = ctx.chapter.crm.config.types.person?.emailField;
3350
+ if (!field) return null;
3351
+ const raw = record4.fields[field] ?? record4.primaryEmail;
3352
+ if (typeof raw !== "string") return null;
3353
+ const normalized = normalizeAdmissionEmail(raw);
3354
+ return isValidEmail(normalized) ? normalized : null;
3355
+ }
3356
+ async function grantInput(db, ctx, assignment) {
3357
+ const person = await getRecord({ crm: ctx.chapter.crm, db }, assignment.personId);
3358
+ const email = person ? personAdmissionEmail(ctx, person) : null;
3359
+ if (!email) throw new Error(`person ${assignment.personId} has no valid authoritative email`);
3360
+ return {
3361
+ id: assignment.grantId,
3362
+ email,
3363
+ decision: assignment.decision,
3364
+ ...assignment.note ? { note: assignment.note } : {},
3365
+ ...assignment.expiresAt !== void 0 ? { expiresAt: assignment.expiresAt } : {}
3366
+ };
3367
+ }
3368
+ async function persistLeaderAdmissionDesiredSet(opts) {
3369
+ const now = Date.now();
3370
+ const current = await loadLeaderAdmissionAssignments(opts.db, opts.target.id);
3371
+ const id = assignmentId(opts.target.id, opts.personId);
3372
+ const existing = current.find((row) => row.id === id);
3373
+ const digest = await signupControlDigest({
3374
+ targetId: opts.target.id,
3375
+ personId: opts.personId,
3376
+ decision: opts.decision,
3377
+ note: opts.note ?? null,
3378
+ expiresAt: opts.expiresAt ?? null,
3379
+ active: opts.active
3380
+ });
3381
+ const mutationReceiptId = `leader-admission-mutation:${opts.target.id}:${opts.mutationId}`;
3382
+ const priorMutation2 = await opts.db.query({ leaderAdmissionMutations: { $: {
3383
+ where: { id: mutationReceiptId },
3384
+ limit: 1
3385
+ } } });
3386
+ const receipt = admissionRows(priorMutation2.leaderAdmissionMutations)[0];
3387
+ if (receipt) {
3388
+ if (receipt.requestDigest !== digest || receipt.personId !== opts.personId) {
3389
+ throw new Error("mutation_conflict");
3390
+ }
3391
+ const saved = await loadLeaderAdmissionDelivery(opts.db, opts.target.id, opts.env.ODLA_ENV);
3392
+ if (!saved) throw new Error("delivery_missing");
3393
+ return { delivery: saved, duplicate: true };
3394
+ }
3395
+ if (existing?.lastMutationId === opts.mutationId) {
3396
+ if (existing.lastRequestDigest !== digest) throw new Error("mutation_conflict");
3397
+ const saved = await loadLeaderAdmissionDelivery(opts.db, opts.target.id, opts.env.ODLA_ENV);
3398
+ if (!saved) throw new Error("delivery_missing");
3399
+ return { delivery: saved, duplicate: true };
3400
+ }
3401
+ const assignment = {
3402
+ id,
3403
+ targetId: opts.target.id,
3404
+ personId: opts.personId,
3405
+ grantId: existing?.grantId ?? crypto.randomUUID().replaceAll("-", ""),
3406
+ decision: opts.decision,
3407
+ ...opts.note ? { note: opts.note } : {},
3408
+ ...opts.expiresAt !== void 0 ? { expiresAt: opts.expiresAt } : {},
3409
+ active: opts.active,
3410
+ lastMutationId: opts.mutationId,
3411
+ lastRequestDigest: digest,
3412
+ createdAt: existing?.createdAt ?? now,
3413
+ createdBy: existing?.createdBy ?? opts.actor,
3414
+ updatedAt: now,
3415
+ updatedBy: opts.actor
3416
+ };
3417
+ const desired = current.filter((row) => row.id !== id && row.active).concat(assignment.active ? [assignment] : []);
3418
+ const grants = await Promise.all(desired.map((row) => grantInput(opts.db, opts.ctx, row)));
3419
+ const previous = await loadLeaderAdmissionDelivery(opts.db, opts.target.id, opts.env.ODLA_ENV);
3420
+ const revision = (previous?.revision ?? 0) + 1;
3421
+ const envelope = await createAdmissionGrantEnvelope({
3422
+ schemaVersion: 1,
3423
+ revision,
3424
+ generatedAt: now,
3425
+ source: { appId: opts.ctx.chapter.id, actor: opts.actor },
3426
+ target: { chapterId: opts.target.id, environment: opts.env.ODLA_ENV },
3427
+ grants
3428
+ });
3429
+ const delivery = {
3430
+ id: deliveryId(opts.target.id, opts.env.ODLA_ENV),
3431
+ targetId: opts.target.id,
3432
+ environment: opts.env.ODLA_ENV,
3433
+ revision,
3434
+ digest: envelope.digest,
3435
+ envelope,
3436
+ status: "saved",
3437
+ attempts: 0,
3438
+ updatedAt: now
3439
+ };
3440
+ const ops = [
3441
+ { t: "update", ns: "leaderAdmissionAssignments", id, attrs: assignment },
3442
+ { t: "update", ns: "leaderAdmissionDeliveries", id: delivery.id, attrs: delivery },
3443
+ { t: "update", ns: "leaderAdmissionMutations", id: mutationReceiptId, attrs: {
3444
+ id: mutationReceiptId,
3445
+ targetId: opts.target.id,
3446
+ personId: opts.personId,
3447
+ requestDigest: digest,
3448
+ revision,
3449
+ createdAt: now
3450
+ } }
3451
+ ];
3452
+ const retract = [
3453
+ ...!opts.note && existing?.note !== void 0 ? ["note"] : [],
3454
+ ...opts.expiresAt === void 0 && existing?.expiresAt !== void 0 ? ["expiresAt"] : []
3455
+ ];
3456
+ if (retract.length) ops.push({ t: "retract", ns: "leaderAdmissionAssignments", id, attrs: retract });
3457
+ const tx = await opts.db.transact(ops, {
3458
+ mutationId: `leader-admission:${opts.target.id}:${opts.mutationId}`,
3459
+ guards: previous ? [{ ns: "leaderAdmissionDeliveries", id: previous.id, exists: true, equals: { revision: previous.revision } }] : [{ ns: "leaderAdmissionDeliveries", id: delivery.id, exists: false }]
3460
+ });
3461
+ return { delivery, duplicate: tx.duplicate };
3462
+ }
3463
+
3464
+ // src/worker-routes-network-admission.ts
3465
+ var PATH = "/api/admin/network/admissions";
3466
+ var RETRY_PATH = "/api/admin/network/admissions/retry";
3467
+ var REVOKE_PATH = "/api/admin/network/admissions/revoke";
3468
+ var MUTATION_ID = /^[A-Za-z0-9_-]{16,128}$/;
3469
+ var MAX_NOTE2 = 2e3;
3470
+ var reject4 = (status, code, error) => json({ ok: false, code, error }, status);
3471
+ async function gate2(req, env, ctx) {
3472
+ const db = ctx.makeDb(env);
3473
+ const user = await ctx.verifyUser(req, env);
3474
+ if (!user) return { response: reject4(401, "unauthorized", "unauthorized") };
3475
+ if (!await ctx.isAdmin(db, user)) return { response: reject4(403, "forbidden", "forbidden") };
3476
+ return { db, user };
3477
+ }
3478
+ function targetFor(ctx, id) {
3479
+ return ctx.chapter.network.targets.find((target) => target.id === id && target.admissionGrants);
3480
+ }
3481
+ async function deliverSaved(db, env, ctx, target, delivery) {
3482
+ if (delivery.status === "acknowledged") return delivery;
3483
+ const secret = await getVaultSecret(db, target.secretName);
3484
+ const attemptedAt = Date.now();
3485
+ const attempts = delivery.attempts + 1;
3486
+ let patch;
3487
+ if (!secret) {
3488
+ patch = {
3489
+ status: delivery.attempts > 0 ? "retrying" : "pending",
3490
+ attempts,
3491
+ lastAttemptAt: attemptedAt,
3492
+ lastError: "edge secret is missing",
3493
+ updatedAt: attemptedAt
3494
+ };
3495
+ } else {
3496
+ try {
3497
+ const receipt = await deliverAdmissionGrants({
3498
+ secret,
3499
+ sender: ctx.chapter.id,
3500
+ targetUrl: target.url,
3501
+ envelope: delivery.envelope,
3502
+ fetch: ((input, init) => fetchNetworkTarget(env, target, new URL(String(input)), init ?? {}))
3503
+ });
3504
+ patch = {
3505
+ status: "acknowledged",
3506
+ attempts,
3507
+ lastAttemptAt: attemptedAt,
3508
+ acknowledgedAt: receipt.acknowledgedAt,
3509
+ updatedAt: attemptedAt
3510
+ };
3511
+ } catch (error) {
3512
+ const permanent = error instanceof AdmissionGrantDeliveryError && error.status >= 400 && error.status < 500;
3513
+ patch = {
3514
+ status: permanent ? "rejected" : delivery.attempts > 0 ? "retrying" : "pending",
3515
+ attempts,
3516
+ lastAttemptAt: attemptedAt,
3517
+ lastError: error instanceof Error ? error.message.slice(0, 2e3) : "delivery failed",
3518
+ updatedAt: attemptedAt
3519
+ };
3520
+ }
3521
+ }
3522
+ const ops = [{
3523
+ t: "update",
3524
+ ns: "leaderAdmissionDeliveries",
3525
+ id: delivery.id,
3526
+ attrs: patch
3527
+ }];
3528
+ if (patch.status === "acknowledged" && delivery.lastError) {
3529
+ ops.push({ t: "retract", ns: "leaderAdmissionDeliveries", id: delivery.id, attrs: ["lastError"] });
3530
+ }
3531
+ await db.transact(ops, {
3532
+ mutationId: `leader-admission-attempt:${delivery.targetId}:${delivery.revision}:${attempts}:${patch.status}`
3533
+ });
3534
+ return { ...delivery, ...patch };
3535
+ }
3536
+ async function parseBody(req) {
3537
+ try {
3538
+ return await req.json();
3539
+ } catch {
3540
+ return null;
3541
+ }
3542
+ }
3543
+ var handleAdminNetworkAdmissions = async (req, url, env, ctx) => {
3544
+ if (![PATH, RETRY_PATH, REVOKE_PATH].includes(url.pathname)) return null;
3545
+ const auth = await gate2(req, env, ctx);
3546
+ if ("response" in auth) return auth.response;
3547
+ const db = auth.db;
3548
+ if (url.pathname === PATH && req.method === "GET") {
3549
+ const targetId2 = url.searchParams.get("targetId") ?? "";
3550
+ const target2 = targetFor(ctx, targetId2);
3551
+ if (!target2) return reject4(404, "unknown_target", "admission target is not configured");
3552
+ return json({
3553
+ ok: true,
3554
+ target: { id: target2.id, name: target2.name },
3555
+ assignments: await loadLeaderAdmissionAssignments(db, target2.id),
3556
+ delivery: await loadLeaderAdmissionDelivery(db, target2.id, env.ODLA_ENV)
3557
+ });
3558
+ }
3559
+ if (req.method !== "POST") return null;
3560
+ const body = await parseBody(req);
3561
+ if (!body) return reject4(422, "invalid_request", "request body must be JSON");
3562
+ const targetId = typeof body.targetId === "string" ? body.targetId : "";
3563
+ const target = targetFor(ctx, targetId);
3564
+ if (!target) return reject4(404, "unknown_target", "admission target is not configured");
3565
+ if (url.pathname === RETRY_PATH) {
3566
+ const delivery = await loadLeaderAdmissionDelivery(db, target.id, env.ODLA_ENV);
3567
+ if (!delivery) return reject4(404, "not_found", "no saved admission delivery exists");
3568
+ return json({ ok: true, delivery: await deliverSaved(db, env, ctx, target, delivery) });
3569
+ }
3570
+ const personId = typeof body.personId === "string" ? body.personId : "";
3571
+ const mutationId = typeof body.mutationId === "string" ? body.mutationId : "";
3572
+ if (!personId || !MUTATION_ID.test(mutationId)) {
3573
+ return reject4(422, "invalid_request", "personId and a stable mutationId are required");
3574
+ }
3575
+ const person = await getRecord2({ crm: ctx.chapter.crm, db: auth.db }, personId);
3576
+ if (!person) return reject4(404, "person_not_found", "person not found");
3577
+ if (!personAdmissionEmail(ctx, person)) {
3578
+ return reject4(422, "invalid_email", "person has no valid authoritative email");
3579
+ }
3580
+ const decision = body.decision ?? "waive_interview";
3581
+ if (decision !== "waive_interview" && decision !== "require_interview") {
3582
+ return reject4(422, "invalid_decision", "decision must be waive_interview or require_interview");
3583
+ }
3584
+ const note = typeof body.note === "string" ? body.note.trim() : "";
3585
+ if (note.length > MAX_NOTE2) return reject4(422, "invalid_note", "note is too long");
3586
+ const expiresAt = body.expiresAt == null ? void 0 : Number(body.expiresAt);
3587
+ if (expiresAt !== void 0 && (!Number.isFinite(expiresAt) || expiresAt <= Date.now())) {
3588
+ return reject4(422, "invalid_expiry", "expiresAt must be a future epoch timestamp");
3589
+ }
3590
+ try {
3591
+ const saved = await persistLeaderAdmissionDesiredSet({
3592
+ db,
3593
+ ctx,
3594
+ env,
3595
+ target,
3596
+ personId,
3597
+ decision,
3598
+ ...note ? { note } : {},
3599
+ ...expiresAt !== void 0 ? { expiresAt } : {},
3600
+ active: url.pathname !== REVOKE_PATH,
3601
+ mutationId,
3602
+ actor: auth.user.email ?? auth.user.userId
3603
+ });
3604
+ const delivery = await deliverSaved(db, env, ctx, target, saved.delivery);
3605
+ return json({ ok: true, duplicate: saved.duplicate, delivery }, delivery.status === "acknowledged" ? 200 : 202);
3606
+ } catch (error) {
3607
+ if (error instanceof Error && error.message === "mutation_conflict") {
3608
+ return reject4(409, "mutation_conflict", "mutationId was already used for another request");
3609
+ }
3610
+ if (error instanceof Error && /valid authoritative email/.test(error.message)) {
3611
+ return reject4(422, "invalid_email", error.message);
3612
+ }
3613
+ return reject4(409, "revision_conflict", "admission assignments changed; reload and retry");
3614
+ }
3615
+ };
3616
+ async function retryLeaderAdmissionDeliveries(env, ctx, limit = 100) {
3617
+ const db = ctx.makeDb(env);
3618
+ const result = await db.query({ leaderAdmissionDeliveries: { $: { limit } } });
3619
+ const pending = admissionRows(result.leaderAdmissionDeliveries).filter((delivery) => delivery.status !== "acknowledged" && delivery.status !== "rejected").slice(0, Math.max(1, Math.min(limit, 500)));
3620
+ const delivered = [];
3621
+ for (const item of pending) {
3622
+ const target = targetFor(ctx, item.targetId);
3623
+ if (target) delivered.push(await deliverSaved(db, env, ctx, target, item));
3624
+ }
3625
+ return delivered;
3626
+ }
3627
+
3259
3628
  // src/worker-routes-schedule.ts
3260
3629
  import { computeBookableSlots, initCalendar } from "@odla-ai/calendar";
3261
3630
  function errCode(err) {
@@ -4517,16 +4886,16 @@ var crmDeps2 = (db, ctx) => ({
4517
4886
  });
4518
4887
  var handleAdminCrmSync = async (req, url, env, ctx) => {
4519
4888
  if (req.method !== "POST" || url.pathname !== "/api/admin/crm/sync") return null;
4520
- const gate9 = await adminGate(req, env, ctx);
4521
- if (gate9 instanceof Response) return gate9;
4522
- const result = await backfillCrm(crmDeps2(gate9.db, ctx));
4889
+ const gate10 = await adminGate(req, env, ctx);
4890
+ if (gate10 instanceof Response) return gate10;
4891
+ const result = await backfillCrm(crmDeps2(gate10.db, ctx));
4523
4892
  return json({ ok: true, ...result });
4524
4893
  };
4525
4894
  var handleAdminPeople = async (req, url, env, ctx) => {
4526
4895
  if (req.method !== "GET" || url.pathname !== "/api/admin/people") return null;
4527
- const gate9 = await adminGate(req, env, ctx);
4528
- if (gate9 instanceof Response) return gate9;
4529
- const { db } = gate9;
4896
+ const gate10 = await adminGate(req, env, ctx);
4897
+ if (gate10 instanceof Response) return gate10;
4898
+ const { db } = gate10;
4530
4899
  const sk = await getVaultSecret(db, "clerk_secret_key");
4531
4900
  const [data, roleList] = await Promise.all([
4532
4901
  db.query({
@@ -4579,9 +4948,9 @@ var handleAdminPeople = async (req, url, env, ctx) => {
4579
4948
  };
4580
4949
  var handleAdminPeopleAccess = async (req, url, env, ctx) => {
4581
4950
  if (req.method !== "GET" || url.pathname !== "/api/admin/people/access") return null;
4582
- const gate9 = await adminGate(req, env, ctx);
4583
- if (gate9 instanceof Response) return gate9;
4584
- const { db } = gate9;
4951
+ const gate10 = await adminGate(req, env, ctx);
4952
+ if (gate10 instanceof Response) return gate10;
4953
+ const { db } = gate10;
4585
4954
  const targetId = url.searchParams.get("userId") ?? "";
4586
4955
  if (!targetId.startsWith("user_")) return json({ error: "invalid userId" }, 400);
4587
4956
  const sk = await getVaultSecret(db, "clerk_secret_key");
@@ -4592,9 +4961,9 @@ var handleAdminPeopleAccess = async (req, url, env, ctx) => {
4592
4961
  };
4593
4962
  var handleAdminPeopleRole = async (req, url, env, ctx) => {
4594
4963
  if (req.method !== "POST" || url.pathname !== "/api/admin/people/role") return null;
4595
- const gate9 = await adminGate(req, env, ctx);
4596
- if (gate9 instanceof Response) return gate9;
4597
- const { db, actor } = gate9;
4964
+ const gate10 = await adminGate(req, env, ctx);
4965
+ if (gate10 instanceof Response) return gate10;
4966
+ const { db, actor } = gate10;
4598
4967
  let body;
4599
4968
  try {
4600
4969
  body = await req.json();
@@ -4741,7 +5110,7 @@ function subAnnualCents(sub) {
4741
5110
  }
4742
5111
 
4743
5112
  // src/worker-routes-admin-dashboard.ts
4744
- async function gate2(req, env, ctx) {
5113
+ async function gate3(req, env, ctx) {
4745
5114
  const rawDb = ctx.makeDb(env);
4746
5115
  const u = await ctx.verifyUser(req, env);
4747
5116
  if (!u) return json({ error: "unauthorized" }, 401);
@@ -4751,7 +5120,7 @@ async function gate2(req, env, ctx) {
4751
5120
  var rows3 = (v) => Array.isArray(v) ? v : [];
4752
5121
  var handleAdminDashboard = async (req, url, env, ctx) => {
4753
5122
  if (req.method !== "GET" || url.pathname !== "/api/admin/dashboard") return null;
4754
- const got = await gate2(req, env, ctx);
5123
+ const got = await gate3(req, env, ctx);
4755
5124
  if (got instanceof Response) return got;
4756
5125
  const db = got;
4757
5126
  const now = Date.now();
@@ -4834,7 +5203,7 @@ var handleAdminDashboard = async (req, url, env, ctx) => {
4834
5203
  };
4835
5204
  var handleAdminBilling = async (req, url, env, ctx) => {
4836
5205
  if (req.method !== "GET" || url.pathname !== "/api/admin/billing") return null;
4837
- const got = await gate2(req, env, ctx);
5206
+ const got = await gate3(req, env, ctx);
4838
5207
  if (got instanceof Response) return got;
4839
5208
  const db = got;
4840
5209
  const sk = await getVaultSecret(db, "stripe_secret_key");
@@ -4894,7 +5263,7 @@ var handleAdminBilling = async (req, url, env, ctx) => {
4894
5263
 
4895
5264
  // src/worker-routes-admin-lifecycle.ts
4896
5265
  import { computeBookableSlots as computeBookableSlots2, initCalendar as initCalendar3 } from "@odla-ai/calendar";
4897
- async function gate3(req, env, ctx) {
5266
+ async function gate4(req, env, ctx) {
4898
5267
  const rawDb = ctx.makeDb(env);
4899
5268
  const u = await ctx.verifyUser(req, env);
4900
5269
  if (!u) return json({ error: "unauthorized" }, 401);
@@ -4920,7 +5289,7 @@ async function loadGroup(db, id) {
4920
5289
  var handleAdminMeetingReschedule = async (req, url, env, ctx) => {
4921
5290
  const m = url.pathname.match(/^\/api\/admin\/meetings\/([0-9a-f-]+)\/reschedule$/);
4922
5291
  if (req.method !== "POST" || !m) return null;
4923
- const got = await gate3(req, env, ctx);
5292
+ const got = await gate4(req, env, ctx);
4924
5293
  if (got instanceof Response) return got;
4925
5294
  const db = got;
4926
5295
  const body = await readJson(req);
@@ -4961,7 +5330,7 @@ var handleAdminMeetingReschedule = async (req, url, env, ctx) => {
4961
5330
  var handleAdminMeetingCancel = async (req, url, env, ctx) => {
4962
5331
  const m = url.pathname.match(/^\/api\/admin\/meetings\/([0-9a-f-]+)\/cancel$/);
4963
5332
  if (req.method !== "POST" || !m) return null;
4964
- const got = await gate3(req, env, ctx);
5333
+ const got = await gate4(req, env, ctx);
4965
5334
  if (got instanceof Response) return got;
4966
5335
  const db = got;
4967
5336
  const { meetings } = await db.query({ meetings: { $: { where: { id: m[1] }, limit: 1 } } });
@@ -4984,7 +5353,7 @@ var handleAdminMeetingCancel = async (req, url, env, ctx) => {
4984
5353
  var handleAdminApprove = async (req, url, env, ctx) => {
4985
5354
  const m = url.pathname.match(/^\/api\/admin\/applications\/([0-9a-f-]+)\/approve$/);
4986
5355
  if (req.method !== "POST" || !m) return null;
4987
- const got = await gate3(req, env, ctx);
5356
+ const got = await gate4(req, env, ctx);
4988
5357
  if (got instanceof Response) return got;
4989
5358
  const db = got;
4990
5359
  const id = m[1];
@@ -4997,7 +5366,7 @@ var handleAdminApprove = async (req, url, env, ctx) => {
4997
5366
  var handleAdminRefund = async (req, url, env, ctx) => {
4998
5367
  const m = url.pathname.match(/^\/api\/admin\/applications\/([0-9a-f-]+)\/refund$/);
4999
5368
  if (req.method !== "POST" || !m) return null;
5000
- const got = await gate3(req, env, ctx);
5369
+ const got = await gate4(req, env, ctx);
5001
5370
  if (got instanceof Response) return got;
5002
5371
  const db = got;
5003
5372
  const app = await loadApp(db, m[1]);
@@ -5029,7 +5398,7 @@ var handleAdminRefund = async (req, url, env, ctx) => {
5029
5398
  var handleAdminApplicationPatch = async (req, url, env, ctx) => {
5030
5399
  const m = url.pathname.match(/^\/api\/admin\/applications\/([0-9a-f-]+)$/);
5031
5400
  if (req.method !== "PATCH" || !m) return null;
5032
- const got = await gate3(req, env, ctx);
5401
+ const got = await gate4(req, env, ctx);
5033
5402
  if (got instanceof Response) return got;
5034
5403
  const db = got;
5035
5404
  const id = m[1];
@@ -5057,7 +5426,7 @@ var handleAdminApplicationPatch = async (req, url, env, ctx) => {
5057
5426
  };
5058
5427
 
5059
5428
  // src/worker-routes-admin-comms.ts
5060
- async function gate4(req, env, ctx) {
5429
+ async function gate5(req, env, ctx) {
5061
5430
  const rawDb = ctx.makeDb(env);
5062
5431
  const u = await ctx.verifyUser(req, env);
5063
5432
  if (!u) return json({ error: "unauthorized" }, 401);
@@ -5079,7 +5448,7 @@ var notifyDeps = (db, env) => ({
5079
5448
  });
5080
5449
  var handleAdminGroupEmail = async (req, url, env, ctx) => {
5081
5450
  if (url.pathname !== "/api/admin/group/email" || req.method !== "GET" && req.method !== "PUT") return null;
5082
- const got = await gate4(req, env, ctx);
5451
+ const got = await gate5(req, env, ctx);
5083
5452
  if (got instanceof Response) return got;
5084
5453
  const db = got;
5085
5454
  const group = await loadGroup2(db, ctx.chapter.id);
@@ -5141,7 +5510,7 @@ var handleAdminGroupEmail = async (req, url, env, ctx) => {
5141
5510
  };
5142
5511
  var handleAdminEmailLog = async (req, url, env, ctx) => {
5143
5512
  if (req.method !== "GET" || url.pathname !== "/api/admin/email/log") return null;
5144
- const got = await gate4(req, env, ctx);
5513
+ const got = await gate5(req, env, ctx);
5145
5514
  if (got instanceof Response) return got;
5146
5515
  const { emailLog } = await got.query({ emailLog: { $: { order: { sentAt: "desc" }, limit: 50 } } });
5147
5516
  const sends = (emailLog ?? []).map((r) => ({
@@ -5158,7 +5527,7 @@ var handleAdminEmailLog = async (req, url, env, ctx) => {
5158
5527
  };
5159
5528
  var handleAdminEmailTest = async (req, url, env, ctx) => {
5160
5529
  if (req.method !== "POST" || url.pathname !== "/api/admin/email/test") return null;
5161
- const got = await gate4(req, env, ctx);
5530
+ const got = await gate5(req, env, ctx);
5162
5531
  if (got instanceof Response) return got;
5163
5532
  const db = got;
5164
5533
  let body;
@@ -5186,7 +5555,7 @@ var handleAdminEmailTest = async (req, url, env, ctx) => {
5186
5555
  var handleAdminComms = async (req, url, env, ctx) => {
5187
5556
  const m = url.pathname.match(/^\/api\/admin\/people\/([0-9a-fA-F-]+)\/comms$/);
5188
5557
  if (req.method !== "GET" || !m) return null;
5189
- const got = await gate4(req, env, ctx);
5558
+ const got = await gate5(req, env, ctx);
5190
5559
  if (got instanceof Response) return got;
5191
5560
  const db = got;
5192
5561
  const appId = m[1];
@@ -5224,26 +5593,12 @@ var handleAdminComms = async (req, url, env, ctx) => {
5224
5593
  };
5225
5594
 
5226
5595
  // src/worker-routes-network.ts
5227
- import { getRecord, typeSummary } from "@odla-ai/crm";
5228
- import { signFederatedRequest as signFederatedRequest2, verifyFederatedRequest as verifyFederatedRequest5 } from "@odla-ai/db";
5229
-
5230
- // src/worker-network-fetch.ts
5231
- function targetFetcher(env, target) {
5232
- if (!target.binding) return null;
5233
- const candidate = env[target.binding];
5234
- if (typeof candidate !== "object" || candidate === null || !("fetch" in candidate) || typeof candidate.fetch !== "function") {
5235
- throw new Error(`service binding "${target.binding}" is unavailable`);
5236
- }
5237
- return candidate;
5238
- }
5239
- function fetchNetworkTarget(env, target, destination, init) {
5240
- const binding = targetFetcher(env, target);
5241
- return binding ? binding.fetch(new Request(destination, init)) : globalThis.fetch(destination, init);
5242
- }
5596
+ import { getRecord as getRecord3, typeSummary } from "@odla-ai/crm";
5597
+ import { signFederatedRequest as signFederatedRequest3, verifyFederatedRequest as verifyFederatedRequest5 } from "@odla-ai/db";
5243
5598
 
5244
5599
  // src/worker-network-push.ts
5245
5600
  import { recordDeliveryAttempt } from "@odla-ai/crm";
5246
- import { signFederatedRequest } from "@odla-ai/db";
5601
+ import { signFederatedRequest as signFederatedRequest2 } from "@odla-ai/db";
5247
5602
  async function pushNetworkRecord(db, env, ctx, target, record4) {
5248
5603
  const secret = await getVaultSecret(db, target.secretName);
5249
5604
  const crmDeps3 = {
@@ -5277,7 +5632,7 @@ async function pushNetworkRecord(db, env, ctx, target, record4) {
5277
5632
  const payloadBody = JSON.stringify(payload);
5278
5633
  const destination = new URL("/api/network/shared", target.url);
5279
5634
  try {
5280
- const signed = await signFederatedRequest({
5635
+ const signed = await signFederatedRequest2({
5281
5636
  secret,
5282
5637
  sender: ctx.chapter.id,
5283
5638
  method: "POST",
@@ -5336,7 +5691,7 @@ async function pushNetworkRecord(db, env, ctx, target, record4) {
5336
5691
  }
5337
5692
 
5338
5693
  // src/worker-routes-network.ts
5339
- async function gate5(req, env, ctx) {
5694
+ async function gate6(req, env, ctx) {
5340
5695
  const db = ctx.makeDb(env);
5341
5696
  const user = await ctx.verifyUser(req, env);
5342
5697
  if (!user) return { response: json({ error: "unauthorized" }, 401) };
@@ -5345,7 +5700,7 @@ async function gate5(req, env, ctx) {
5345
5700
  }
5346
5701
  var handleAdminNetworkTargets = async (req, url, env, ctx) => {
5347
5702
  if (req.method !== "GET" || url.pathname !== "/api/admin/network/targets") return null;
5348
- const got = await gate5(req, env, ctx);
5703
+ const got = await gate6(req, env, ctx);
5349
5704
  if ("response" in got) return got.response;
5350
5705
  return json({
5351
5706
  targets: ctx.chapter.network.targets.map(({
@@ -5390,7 +5745,7 @@ async function snapshotOne(db, env, ctx, target) {
5390
5745
  if (!secret) return { id: target.id, name: target.name, url: target.url, available: false, error: "edge secret is missing" };
5391
5746
  const destination = new URL("/api/network/snapshot", target.url);
5392
5747
  try {
5393
- const headers = await signFederatedRequest2({
5748
+ const headers = await signFederatedRequest3({
5394
5749
  secret,
5395
5750
  sender: ctx.chapter.id,
5396
5751
  method: "GET",
@@ -5431,7 +5786,7 @@ async function snapshotOne(db, env, ctx, target) {
5431
5786
  }
5432
5787
  var handleAdminNetworkRollup = async (req, url, env, ctx) => {
5433
5788
  if (req.method !== "GET" || url.pathname !== "/api/admin/network/rollup") return null;
5434
- const got = await gate5(req, env, ctx);
5789
+ const got = await gate6(req, env, ctx);
5435
5790
  if ("response" in got) return got.response;
5436
5791
  const targets = await Promise.all(
5437
5792
  ctx.chapter.network.targets.map((target) => snapshotOne(got.db, env, ctx, target))
@@ -5458,7 +5813,7 @@ var handleAdminNetworkRollup = async (req, url, env, ctx) => {
5458
5813
  };
5459
5814
  var handleAdminNetworkPush = async (req, url, env, ctx) => {
5460
5815
  if (req.method !== "POST" || url.pathname !== "/api/admin/network/push") return null;
5461
- const got = await gate5(req, env, ctx);
5816
+ const got = await gate6(req, env, ctx);
5462
5817
  if ("response" in got) return got.response;
5463
5818
  let body;
5464
5819
  try {
@@ -5473,7 +5828,7 @@ var handleAdminNetworkPush = async (req, url, env, ctx) => {
5473
5828
  if (requested.size !== body.targetIds.length) return json({ error: "targetIds must contain unique strings" }, 400);
5474
5829
  const targets = ctx.chapter.network.targets.filter((target) => requested.has(target.id));
5475
5830
  if (targets.length !== requested.size) return json({ error: "one or more targetIds are not configured" }, 400);
5476
- const record4 = await getRecord({ crm: ctx.chapter.crm, db: got.db }, body.recordId);
5831
+ const record4 = await getRecord3({ crm: ctx.chapter.crm, db: got.db }, body.recordId);
5477
5832
  if (!record4) return json({ error: "record not found" }, 404);
5478
5833
  const results = await Promise.all(
5479
5834
  targets.map((target) => pushNetworkRecord(got.db, env, ctx, target, record4))
@@ -5482,7 +5837,7 @@ var handleAdminNetworkPush = async (req, url, env, ctx) => {
5482
5837
  };
5483
5838
 
5484
5839
  // src/worker-routes-network-records-follower.ts
5485
- import { CRM_NS, CrmInputError, getRecord as getRecord2, listRecords, setStage as setStage2, updateRecord as updateRecord3 } from "@odla-ai/crm";
5840
+ import { CRM_NS, CrmInputError, getRecord as getRecord4, listRecords, setStage as setStage2, updateRecord as updateRecord3 } from "@odla-ai/crm";
5486
5841
  import { verifyFederatedRequest as verifyFederatedRequest6 } from "@odla-ai/db";
5487
5842
 
5488
5843
  // src/worker-routes-network-records-common.ts
@@ -5492,7 +5847,7 @@ var MAX_OFFSET = 1e4;
5492
5847
  var MAX_SEARCH = 200;
5493
5848
  var MAX_BODY = 32768;
5494
5849
  var RECORD_ID = /^[a-zA-Z0-9][a-zA-Z0-9:_-]{0,191}$/;
5495
- var MUTATION_ID = /^[a-zA-Z0-9][a-zA-Z0-9:._-]{7,127}$/;
5850
+ var MUTATION_ID2 = /^[a-zA-Z0-9][a-zA-Z0-9:._-]{7,127}$/;
5496
5851
  function bodyTooLarge(body) {
5497
5852
  return new TextEncoder().encode(body).byteLength > MAX_BODY;
5498
5853
  }
@@ -5610,7 +5965,7 @@ var handleNetworkRecords = async (req, url, env, ctx) => {
5610
5965
  const hasFields = Boolean(fields && typeof fields === "object" && !Array.isArray(fields));
5611
5966
  const hasStage = typeof input?.stage === "string";
5612
5967
  const actor = input?.actor;
5613
- 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) {
5968
+ if (!input || !IDENTIFIER.test(input.type) || !RECORD_ID.test(input.recordId) || !Number.isSafeInteger(input.expectedRevision) || input.expectedRevision < 0 || !MUTATION_ID2.test(input.mutationId) || !actor || typeof actor.id !== "string" || !RECORD_ID.test(actor.id) || hasFields === hasStage) {
5614
5969
  return json({ error: "invalid network mutation", code: "invalid_mutation" }, 422);
5615
5970
  }
5616
5971
  const editable = reader.editableFields[input.type];
@@ -5629,7 +5984,7 @@ var handleNetworkRecords = async (req, url, env, ctx) => {
5629
5984
  if (prior.fingerprint !== fingerprint) {
5630
5985
  return json({ error: "mutationId was already used for different input", code: "mutation_conflict" }, 409);
5631
5986
  }
5632
- const replayed = await getRecord2({ crm: ctx.chapter.crm, db }, input.recordId);
5987
+ const replayed = await getRecord4({ crm: ctx.chapter.crm, db }, input.recordId);
5633
5988
  if (!replayed || replayed.type !== input.type) return json({ error: "record not found", code: "not_found" }, 404);
5634
5989
  const result = {
5635
5990
  version: 1,
@@ -5639,7 +5994,7 @@ var handleNetworkRecords = async (req, url, env, ctx) => {
5639
5994
  };
5640
5995
  return json(result);
5641
5996
  }
5642
- const record4 = await getRecord2({ crm: ctx.chapter.crm, db }, input.recordId);
5997
+ const record4 = await getRecord4({ crm: ctx.chapter.crm, db }, input.recordId);
5643
5998
  if (!record4 || record4.type !== input.type) return json({ error: "record not found", code: "not_found" }, 404);
5644
5999
  if (record4.updatedAt !== input.expectedRevision) {
5645
6000
  return json({ error: "record changed since it was reviewed", code: "stale_revision" }, 409);
@@ -5724,15 +6079,15 @@ var handleNetworkRecords = async (req, url, env, ctx) => {
5724
6079
  };
5725
6080
 
5726
6081
  // src/worker-routes-network-records-admin.ts
5727
- import { signFederatedRequest as signFederatedRequest3 } from "@odla-ai/db";
5728
- async function gate6(req, env, ctx) {
6082
+ import { signFederatedRequest as signFederatedRequest4 } from "@odla-ai/db";
6083
+ async function gate7(req, env, ctx) {
5729
6084
  const db = ctx.makeDb(env);
5730
6085
  const user = await ctx.verifyUser(req, env);
5731
6086
  if (!user) return { response: json({ error: "unauthorized" }, 401) };
5732
6087
  if (!await ctx.isAdmin(db, user)) return { response: json({ error: "forbidden" }, 403) };
5733
6088
  return { db, user };
5734
6089
  }
5735
- function targetFor(ctx, id) {
6090
+ function targetFor2(ctx, id) {
5736
6091
  return ctx.chapter.network.targets.find((target) => target.id === id);
5737
6092
  }
5738
6093
  async function readTargetRecords(db, env, ctx, target, query) {
@@ -5741,7 +6096,7 @@ async function readTargetRecords(db, env, ctx, target, query) {
5741
6096
  const destination = new URL("/api/network/records", target.url);
5742
6097
  destination.search = query.toString();
5743
6098
  try {
5744
- const headers = await signFederatedRequest3({ secret, sender: ctx.chapter.id, method: "GET", url: destination });
6099
+ const headers = await signFederatedRequest4({ secret, sender: ctx.chapter.id, method: "GET", url: destination });
5745
6100
  const response = await fetchNetworkTarget(env, target, destination, {
5746
6101
  headers,
5747
6102
  signal: AbortSignal.timeout(1e4)
@@ -5777,7 +6132,7 @@ async function mutateTargetRecord(db, env, ctx, target, input) {
5777
6132
  const destination = new URL("/api/network/records", target.url);
5778
6133
  const body = JSON.stringify(input);
5779
6134
  try {
5780
- const headers = await signFederatedRequest3({
6135
+ const headers = await signFederatedRequest4({
5781
6136
  secret,
5782
6137
  sender: ctx.chapter.id,
5783
6138
  method: "PATCH",
@@ -5809,7 +6164,7 @@ var handleAdminNetworkRecords = async (req, url, env, ctx) => {
5809
6164
  if (req.method === "PATCH" && Number(req.headers.get("content-length") ?? 0) > MAX_BODY) {
5810
6165
  return json({ error: "mutation body is too large", code: "body_too_large" }, 413);
5811
6166
  }
5812
- const got = await gate6(req, env, ctx);
6167
+ const got = await gate7(req, env, ctx);
5813
6168
  if ("response" in got) return got.response;
5814
6169
  if (req.method === "PATCH") {
5815
6170
  let input;
@@ -5822,10 +6177,10 @@ var handleAdminNetworkRecords = async (req, url, env, ctx) => {
5822
6177
  } catch {
5823
6178
  return json({ error: "invalid JSON body", code: "invalid_json" }, 422);
5824
6179
  }
5825
- const target2 = targetFor(ctx, input?.targetId);
6180
+ const target2 = targetFor2(ctx, input?.targetId);
5826
6181
  const hasFields = Boolean(input?.fields && typeof input.fields === "object" && !Array.isArray(input.fields));
5827
6182
  const hasStage = typeof input?.stage === "string";
5828
- 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) {
6183
+ if (!target2 || !IDENTIFIER.test(input.type) || !RECORD_ID.test(input.recordId) || !Number.isSafeInteger(input.expectedRevision) || input.expectedRevision < 0 || !MUTATION_ID2.test(input.mutationId) || hasFields === hasStage) {
5829
6184
  return json({ error: "invalid network mutation", code: "invalid_mutation" }, 422);
5830
6185
  }
5831
6186
  return mutateTargetRecord(got.db, env, ctx, target2, {
@@ -5840,7 +6195,7 @@ var handleAdminNetworkRecords = async (req, url, env, ctx) => {
5840
6195
  }
5841
6196
  const targetId = url.searchParams.get("targetId") ?? "";
5842
6197
  const type = url.searchParams.get("type") ?? "";
5843
- const target = targetFor(ctx, targetId);
6198
+ const target = targetFor2(ctx, targetId);
5844
6199
  if (!target || !IDENTIFIER.test(type)) return json({ error: "configured targetId and record type are required" }, 400);
5845
6200
  const limit = integer(url.searchParams.get("limit"), 25, 1, MAX_PAGE);
5846
6201
  const offset = integer(url.searchParams.get("offset"), 0, 0, MAX_OFFSET);
@@ -5857,8 +6212,8 @@ var handleAdminNetworkRecords = async (req, url, env, ctx) => {
5857
6212
  // src/worker-routes-network-notes.ts
5858
6213
  var IDENTIFIER2 = /^[a-z][a-zA-Z0-9_]*$/;
5859
6214
  var RECORD_ID2 = /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,255}$/;
5860
- var MAX_NOTE2 = 4e3;
5861
- async function gate7(req, env, ctx) {
6215
+ var MAX_NOTE3 = 4e3;
6216
+ async function gate8(req, env, ctx) {
5862
6217
  const db = ctx.makeDb(env);
5863
6218
  const user = await ctx.verifyUser(req, env);
5864
6219
  if (!user) return { response: json({ error: "unauthorized" }, 401) };
@@ -5892,7 +6247,7 @@ function asNote(row) {
5892
6247
  }
5893
6248
  var handleAdminNetworkNotes = async (req, url, env, ctx) => {
5894
6249
  if (url.pathname !== "/api/admin/network/notes" || req.method !== "GET" && req.method !== "POST") return null;
5895
- const got = await gate7(req, env, ctx);
6250
+ const got = await gate8(req, env, ctx);
5896
6251
  if ("response" in got) return got.response;
5897
6252
  const identity = noteIdentity(url, ctx);
5898
6253
  if (!identity) return json({ error: "valid targetId, type, and recordId are required" }, 400);
@@ -5909,7 +6264,7 @@ var handleAdminNetworkNotes = async (req, url, env, ctx) => {
5909
6264
  return json({ notes: (result.networkNotes ?? []).map((row) => asNote(row)) });
5910
6265
  }
5911
6266
  const contentLength = Number(req.headers.get("content-length") ?? 0);
5912
- if (contentLength > MAX_NOTE2 * 2) return json({ error: "note is too large" }, 413);
6267
+ if (contentLength > MAX_NOTE3 * 2) return json({ error: "note is too large" }, 413);
5913
6268
  let input;
5914
6269
  try {
5915
6270
  input = await req.json();
@@ -5917,7 +6272,7 @@ var handleAdminNetworkNotes = async (req, url, env, ctx) => {
5917
6272
  return json({ error: "invalid JSON body" }, 400);
5918
6273
  }
5919
6274
  const body = typeof input.body === "string" ? input.body.trim() : "";
5920
- if (!body || body.length > MAX_NOTE2) return json({ error: `note must be 1\u2013${MAX_NOTE2} characters` }, 400);
6275
+ if (!body || body.length > MAX_NOTE3) return json({ error: `note must be 1\u2013${MAX_NOTE3} characters` }, 400);
5921
6276
  const id = crypto.randomUUID();
5922
6277
  const note = {
5923
6278
  id,
@@ -5942,15 +6297,15 @@ var handleAdminNetworkNotes = async (req, url, env, ctx) => {
5942
6297
  import {
5943
6298
  addActivity,
5944
6299
  CRM_NS as CRM_NS2,
5945
- getRecord as getRecord3
6300
+ getRecord as getRecord5
5946
6301
  } from "@odla-ai/crm";
5947
- import { signFederatedRequest as signFederatedRequest4, verifyFederatedRequest as verifyFederatedRequest7 } from "@odla-ai/db";
6302
+ import { signFederatedRequest as signFederatedRequest5, verifyFederatedRequest as verifyFederatedRequest7 } from "@odla-ai/db";
5948
6303
  var IDENTIFIER3 = /^[a-z][a-zA-Z0-9_]*$/;
5949
6304
  var RECORD_ID3 = /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,255}$/;
5950
- var MUTATION_ID2 = /^[A-Za-z0-9_-]{16,128}$/;
5951
- var MAX_NOTE3 = 4e3;
6305
+ var MUTATION_ID3 = /^[A-Za-z0-9_-]{16,128}$/;
6306
+ var MAX_NOTE4 = 4e3;
5952
6307
  var NETWORK_META = "chapterNetwork";
5953
- async function gate8(req, env, ctx) {
6308
+ async function gate9(req, env, ctx) {
5954
6309
  const db = ctx.makeDb(env);
5955
6310
  const user = await ctx.verifyUser(req, env);
5956
6311
  if (!user) return { response: json({ error: "unauthorized" }, 401) };
@@ -5996,7 +6351,7 @@ function sourceLabel(sourceId) {
5996
6351
  }
5997
6352
  var handleNetworkSharedNotes = async (req, url, env, ctx) => {
5998
6353
  if (url.pathname !== "/api/network/shared-notes" || req.method !== "GET" && req.method !== "POST") return null;
5999
- if (req.method === "POST" && Number(req.headers.get("content-length") ?? 0) > MAX_NOTE3 * 2) {
6354
+ if (req.method === "POST" && Number(req.headers.get("content-length") ?? 0) > MAX_NOTE4 * 2) {
6000
6355
  return json({ error: "note is too large" }, 413);
6001
6356
  }
6002
6357
  const db = ctx.makeDb(env);
@@ -6014,7 +6369,7 @@ var handleNetworkSharedNotes = async (req, url, env, ctx) => {
6014
6369
  if (!IDENTIFIER3.test(recordType) || !RECORD_ID3.test(recordId) || !reader?.sharedNotes.includes(recordType)) {
6015
6370
  return json({ error: "shared notes are not enabled for this reader and record type" }, 403);
6016
6371
  }
6017
- const record4 = await getRecord3({ crm: ctx.chapter.crm, db }, recordId);
6372
+ const record4 = await getRecord5({ crm: ctx.chapter.crm, db }, recordId);
6018
6373
  if (!record4 || record4.type !== recordType) return json({ error: "record not found" }, 404);
6019
6374
  if (req.method === "GET") {
6020
6375
  return json({
@@ -6031,8 +6386,8 @@ var handleNetworkSharedNotes = async (req, url, env, ctx) => {
6031
6386
  }
6032
6387
  const body = typeof input.body === "string" ? input.body.trim() : "";
6033
6388
  const mutationId = typeof input.mutationId === "string" ? input.mutationId : "";
6034
- if (!body || body.length > MAX_NOTE3 || !MUTATION_ID2.test(mutationId)) {
6035
- return json({ error: `body must be 1\u2013${MAX_NOTE3} characters and mutationId must be valid` }, 400);
6389
+ if (!body || body.length > MAX_NOTE4 || !MUTATION_ID3.test(mutationId)) {
6390
+ return json({ error: `body must be 1\u2013${MAX_NOTE4} characters and mutationId must be valid` }, 400);
6036
6391
  }
6037
6392
  const createdAt = Date.now();
6038
6393
  const id = `network:${verified.sender}:${mutationId}`;
@@ -6069,17 +6424,17 @@ var handleNetworkSharedNotes = async (req, url, env, ctx) => {
6069
6424
  };
6070
6425
  return json({ version: 1, site: { id: ctx.chapter.id, name: ctx.chapter.name }, note, duplicate: result.duplicate }, 201);
6071
6426
  };
6072
- function targetFor2(ctx, id) {
6427
+ function targetFor3(ctx, id) {
6073
6428
  return ctx.chapter.network.targets.find((target) => target.id === id);
6074
6429
  }
6075
6430
  var handleAdminNetworkSharedNotes = async (req, url, env, ctx) => {
6076
6431
  if (url.pathname !== "/api/admin/network/shared-notes" || req.method !== "GET" && req.method !== "POST") return null;
6077
- const got = await gate8(req, env, ctx);
6432
+ const got = await gate9(req, env, ctx);
6078
6433
  if ("response" in got) return got.response;
6079
6434
  const targetId = url.searchParams.get("targetId") ?? "";
6080
6435
  const recordType = url.searchParams.get("type") ?? "";
6081
6436
  const recordId = url.searchParams.get("recordId") ?? "";
6082
- const target = targetFor2(ctx, targetId);
6437
+ const target = targetFor3(ctx, targetId);
6083
6438
  if (!target || !target.sharedNotes.includes(recordType) || !IDENTIFIER3.test(recordType) || !RECORD_ID3.test(recordId)) {
6084
6439
  return json({ error: "configured targetId, type, and recordId are required" }, 400);
6085
6440
  }
@@ -6092,7 +6447,7 @@ var handleAdminNetworkSharedNotes = async (req, url, env, ctx) => {
6092
6447
  return json({ error: "invalid JSON body" }, 400);
6093
6448
  }
6094
6449
  const note = typeof input.body === "string" ? input.body.trim() : "";
6095
- if (!note || note.length > MAX_NOTE3) return json({ error: `note must be 1\u2013${MAX_NOTE3} characters` }, 400);
6450
+ if (!note || note.length > MAX_NOTE4) return json({ error: `note must be 1\u2013${MAX_NOTE4} characters` }, 400);
6096
6451
  body = JSON.stringify({ body: note, mutationId: crypto.randomUUID() });
6097
6452
  }
6098
6453
  const secret = await getVaultSecret(got.db, target.secretName);
@@ -6100,7 +6455,7 @@ var handleAdminNetworkSharedNotes = async (req, url, env, ctx) => {
6100
6455
  const destination = new URL("/api/network/shared-notes", target.url);
6101
6456
  destination.search = new URLSearchParams({ type: recordType, recordId }).toString();
6102
6457
  try {
6103
- const headers = await signFederatedRequest4({
6458
+ const headers = await signFederatedRequest5({
6104
6459
  secret,
6105
6460
  sender: ctx.chapter.id,
6106
6461
  method: req.method,
@@ -6692,11 +7047,11 @@ var handleAdminBuilderLibrary = async (req, url, env, ctx) => {
6692
7047
  };
6693
7048
 
6694
7049
  // src/signup-control-delivery.ts
6695
- import { signFederatedRequest as signFederatedRequest5 } from "@odla-ai/db";
7050
+ import { signFederatedRequest as signFederatedRequest6 } from "@odla-ai/db";
6696
7051
  async function deliverSignupControl(options) {
6697
7052
  const destination = new URL(SIGNUP_CONTROL_PATH, options.targetUrl);
6698
7053
  const body = JSON.stringify(options.envelope);
6699
- const signed = await signFederatedRequest5({
7054
+ const signed = await signFederatedRequest6({
6700
7055
  secret: options.secret,
6701
7056
  sender: options.sender,
6702
7057
  method: "POST",
@@ -6717,11 +7072,11 @@ async function deliverSignupControl(options) {
6717
7072
  }
6718
7073
 
6719
7074
  // src/member-content-delivery.ts
6720
- import { signFederatedRequest as signFederatedRequest6 } from "@odla-ai/db";
7075
+ import { signFederatedRequest as signFederatedRequest7 } from "@odla-ai/db";
6721
7076
  async function deliverMemberContent(options) {
6722
7077
  const destination = new URL(MEMBER_CONTENT_CONTROL_PATH, options.targetUrl);
6723
7078
  const body = JSON.stringify(options.envelope);
6724
- const signed = await signFederatedRequest6({
7079
+ const signed = await signFederatedRequest7({
6725
7080
  secret: options.secret,
6726
7081
  sender: options.sender,
6727
7082
  method: "POST",
@@ -6742,11 +7097,11 @@ async function deliverMemberContent(options) {
6742
7097
  }
6743
7098
 
6744
7099
  // src/builder-library-delivery.ts
6745
- import { signFederatedRequest as signFederatedRequest7 } from "@odla-ai/db";
7100
+ import { signFederatedRequest as signFederatedRequest8 } from "@odla-ai/db";
6746
7101
  async function deliverBuilderLibrary(options) {
6747
7102
  const destination = new URL(BUILDER_LIBRARY_CONTROL_PATH, options.targetUrl);
6748
7103
  const body = JSON.stringify(options.envelope);
6749
- const signed = await signFederatedRequest7({ secret: options.secret, sender: options.sender, method: "POST", url: destination, body });
7104
+ const signed = await signFederatedRequest8({ secret: options.secret, sender: options.sender, method: "POST", url: destination, body });
6750
7105
  const response = await (options.fetch ?? globalThis.fetch)(destination, { method: "POST", headers: { ...signed, "content-type": "application/json" }, body });
6751
7106
  const result = await response.json();
6752
7107
  if (!response.ok || !("ok" in result) || result.ok !== true) {
@@ -6756,31 +7111,6 @@ async function deliverBuilderLibrary(options) {
6756
7111
  return result;
6757
7112
  }
6758
7113
 
6759
- // src/admission-grants-delivery.ts
6760
- import { signFederatedRequest as signFederatedRequest8 } from "@odla-ai/db";
6761
- async function deliverAdmissionGrants(options) {
6762
- const destination = new URL(ADMISSION_GRANTS_PATH, options.targetUrl);
6763
- const body = JSON.stringify(options.envelope);
6764
- const signed = await signFederatedRequest8({
6765
- secret: options.secret,
6766
- sender: options.sender,
6767
- method: "POST",
6768
- url: destination,
6769
- body
6770
- });
6771
- const response = await (options.fetch ?? globalThis.fetch)(destination, {
6772
- method: "POST",
6773
- headers: { ...signed, "content-type": "application/json" },
6774
- body
6775
- });
6776
- const result = await response.json();
6777
- if (!response.ok || !("ok" in result) || result.ok !== true) {
6778
- const failure = result;
6779
- throw new Error(`admission grants rejected (${response.status}): ${failure.reason ?? failure.error ?? "unknown"}`);
6780
- }
6781
- return result;
6782
- }
6783
-
6784
7114
  // src/signup-control-theme.ts
6785
7115
  var SIGNUP_CONTROL_THEME_MARKER = "<!--odla:signup-control-theme-->";
6786
7116
  var SIGNUP_CONTROL_THEME_STYLE_ID = "odla-signup-control-theme";
@@ -6940,6 +7270,7 @@ var BUILTIN_ROUTES = [
6940
7270
  handleAdminPeopleRole,
6941
7271
  handleAdminCrmSync,
6942
7272
  handleAdminAdmissionGrants,
7273
+ handleAdminNetworkAdmissions,
6943
7274
  handleAdminClerkPrivateProfileSync,
6944
7275
  // Aggregation
6945
7276
  handleAdminDashboard,
@@ -6984,6 +7315,7 @@ function chapterWorker(options) {
6984
7315
  };
6985
7316
  }
6986
7317
  export {
7318
+ AdmissionGrantDeliveryError,
6987
7319
  chapterWorker,
6988
7320
  createWorkerContext,
6989
7321
  deliverAdmissionGrants,
@@ -6991,6 +7323,7 @@ export {
6991
7323
  deliverMemberContent,
6992
7324
  deliverSignupControl,
6993
7325
  renderActiveSignupControlHtml,
7326
+ retryLeaderAdmissionDeliveries,
6994
7327
  withActiveSignupControlTheme
6995
7328
  };
6996
7329
  //# sourceMappingURL=index.js.map