@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.
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/worker.ts
21
21
  var worker_exports = {};
22
22
  __export(worker_exports, {
23
+ AdmissionGrantDeliveryError: () => AdmissionGrantDeliveryError,
23
24
  chapterWorker: () => chapterWorker,
24
25
  createWorkerContext: () => createWorkerContext,
25
26
  deliverAdmissionGrants: () => deliverAdmissionGrants,
@@ -27,6 +28,7 @@ __export(worker_exports, {
27
28
  deliverMemberContent: () => deliverMemberContent,
28
29
  deliverSignupControl: () => deliverSignupControl,
29
30
  renderActiveSignupControlHtml: () => renderActiveSignupControlHtml,
31
+ retryLeaderAdmissionDeliveries: () => retryLeaderAdmissionDeliveries,
30
32
  withActiveSignupControlTheme: () => withActiveSignupControlTheme
31
33
  });
32
34
  module.exports = __toCommonJS(worker_exports);
@@ -640,6 +642,11 @@ function validateAdmissionGrantDocument(input) {
640
642
  }
641
643
  };
642
644
  }
645
+ async function createAdmissionGrantEnvelope(document) {
646
+ const checked = validateAdmissionGrantDocument(document);
647
+ if (!checked.ok) throw new TypeError(`Invalid admission grant document: ${checked.errors.join("; ")}`);
648
+ return { document: checked.value, digest: await signupControlDigest(checked.value) };
649
+ }
643
650
  var rows = (value) => Array.isArray(value) ? value : [];
644
651
  function asGrant(row) {
645
652
  if (typeof row.id !== "string" || typeof row.emailNormalized !== "string") return null;
@@ -3138,12 +3145,17 @@ var import_db5 = require("@odla-ai/db");
3138
3145
  var reject3 = (reason, status, errors) => json({ ok: false, reason, ...errors ? { errors } : {} }, status);
3139
3146
  var handleAdmissionGrants = async (req, url, env, ctx) => {
3140
3147
  if (req.method !== "POST" || url.pathname !== ADMISSION_GRANTS_PATH) return null;
3141
- const authority = ctx.chapter.signupControl;
3142
- if (!authority) return json({ error: "not found" }, 404);
3148
+ const claimedSender = req.headers.get("x-odla-federation-sender") ?? "";
3149
+ const networkReader = ctx.chapter.network.readers.find((reader) => reader.id === claimedSender && reader.admissionGrants);
3150
+ const signupAuthority = ctx.chapter.signupControl?.sourceId === claimedSender ? ctx.chapter.signupControl : void 0;
3151
+ if (!networkReader && !signupAuthority) return json({ error: "not found" }, 404);
3143
3152
  const db = ctx.makeDb(env);
3144
- const secret = await getVaultSecret(db, authority.secretName);
3153
+ const secret = await getVaultSecret(
3154
+ db,
3155
+ networkReader ? "network_share_secret" : signupAuthority.secretName
3156
+ );
3145
3157
  if (!secret) return json({ error: "unauthorized" }, 401);
3146
- const verified = await (0, import_db5.verifyFederatedRequest)(req, { secret, senders: [authority.sourceId] });
3158
+ const verified = await (0, import_db5.verifyFederatedRequest)(req, { secret, senders: [claimedSender] });
3147
3159
  if (!verified.ok) return json({ error: "unauthorized", reason: verified.reason }, 401);
3148
3160
  let input;
3149
3161
  try {
@@ -3155,11 +3167,11 @@ var handleAdmissionGrants = async (req, url, env, ctx) => {
3155
3167
  const checked = validateAdmissionGrantDocument(input.document);
3156
3168
  if (!checked.ok) return reject3("invalid_document", 400, checked.errors);
3157
3169
  const document = checked.value;
3158
- if (document.source.appId !== authority.sourceId) return reject3("wrong_source", 403);
3170
+ if (document.source.appId !== claimedSender) return reject3("wrong_source", 403);
3159
3171
  if (document.target.chapterId !== ctx.chapter.id) return reject3("wrong_target", 409);
3160
3172
  if (document.target.environment !== env.ODLA_ENV) return reject3("wrong_environment", 409);
3161
3173
  if (!digest || await signupControlDigest(document) !== digest) return reject3("invalid_digest", 400);
3162
- const latest = await loadLatestAdmissionRevision(db, authority.sourceId, ctx.chapter.id, env.ODLA_ENV);
3174
+ const latest = await loadLatestAdmissionRevision(db, claimedSender, ctx.chapter.id, env.ODLA_ENV);
3163
3175
  if (latest && document.revision < latest.revision) return reject3("stale_revision", 409);
3164
3176
  if (latest && document.revision === latest.revision) {
3165
3177
  if (latest.digest !== digest) return reject3("conflicting_revision", 409);
@@ -3284,6 +3296,365 @@ var handleAdminAdmissionGrants = async (req, url, env, ctx) => {
3284
3296
  return null;
3285
3297
  };
3286
3298
 
3299
+ // src/worker-routes-network-admission.ts
3300
+ var import_crm6 = require("@odla-ai/crm");
3301
+
3302
+ // src/admission-grants-delivery.ts
3303
+ var import_db6 = require("@odla-ai/db");
3304
+ var AdmissionGrantDeliveryError = class extends Error {
3305
+ constructor(status, reason) {
3306
+ super(`admission grants rejected (${status}): ${reason}`);
3307
+ this.status = status;
3308
+ this.reason = reason;
3309
+ this.name = "AdmissionGrantDeliveryError";
3310
+ }
3311
+ status;
3312
+ reason;
3313
+ };
3314
+ async function deliverAdmissionGrants(options) {
3315
+ const destination = new URL(ADMISSION_GRANTS_PATH, options.targetUrl);
3316
+ const body = JSON.stringify(options.envelope);
3317
+ const signed = await (0, import_db6.signFederatedRequest)({
3318
+ secret: options.secret,
3319
+ sender: options.sender,
3320
+ method: "POST",
3321
+ url: destination,
3322
+ body
3323
+ });
3324
+ const response = await (options.fetch ?? globalThis.fetch)(destination, {
3325
+ method: "POST",
3326
+ headers: { ...signed, "content-type": "application/json" },
3327
+ body
3328
+ });
3329
+ const result = await response.json();
3330
+ if (!response.ok || !("ok" in result) || result.ok !== true) {
3331
+ const failure = result;
3332
+ throw new AdmissionGrantDeliveryError(
3333
+ response.status,
3334
+ failure.reason ?? failure.error ?? "unknown"
3335
+ );
3336
+ }
3337
+ return result;
3338
+ }
3339
+
3340
+ // src/worker-network-fetch.ts
3341
+ function targetFetcher(env, target) {
3342
+ if (!target.binding) return null;
3343
+ const candidate = env[target.binding];
3344
+ if (typeof candidate !== "object" || candidate === null || !("fetch" in candidate) || typeof candidate.fetch !== "function") {
3345
+ throw new Error(`service binding "${target.binding}" is unavailable`);
3346
+ }
3347
+ return candidate;
3348
+ }
3349
+ function fetchNetworkTarget(env, target, destination, init) {
3350
+ const binding = targetFetcher(env, target);
3351
+ return binding ? binding.fetch(new Request(destination, init)) : globalThis.fetch(destination, init);
3352
+ }
3353
+
3354
+ // src/worker-network-admission-state.ts
3355
+ var import_crm5 = require("@odla-ai/crm");
3356
+ var MAX_ASSIGNMENTS = 2e3;
3357
+ var admissionRows = (value) => Array.isArray(value) ? value : [];
3358
+ function assignmentId(targetId, personId) {
3359
+ return `leader-admission:${targetId}:${personId}`;
3360
+ }
3361
+ function deliveryId(targetId, environment) {
3362
+ return `leader-admission-delivery:${targetId}:${environment}`;
3363
+ }
3364
+ async function loadLeaderAdmissionAssignments(db, targetId) {
3365
+ const result = await db.query({ leaderAdmissionAssignments: { $: {
3366
+ where: { targetId },
3367
+ order: { updatedAt: "desc" },
3368
+ limit: MAX_ASSIGNMENTS
3369
+ } } });
3370
+ return admissionRows(result.leaderAdmissionAssignments);
3371
+ }
3372
+ async function loadLeaderAdmissionDelivery(db, targetId, environment) {
3373
+ const id = deliveryId(targetId, environment);
3374
+ const result = await db.query({ leaderAdmissionDeliveries: { $: { where: { id }, limit: 1 } } });
3375
+ return admissionRows(result.leaderAdmissionDeliveries)[0] ?? null;
3376
+ }
3377
+ function personAdmissionEmail(ctx, record4) {
3378
+ if (record4.type !== "person") return null;
3379
+ const field = ctx.chapter.crm.config.types.person?.emailField;
3380
+ if (!field) return null;
3381
+ const raw = record4.fields[field] ?? record4.primaryEmail;
3382
+ if (typeof raw !== "string") return null;
3383
+ const normalized = normalizeAdmissionEmail(raw);
3384
+ return isValidEmail(normalized) ? normalized : null;
3385
+ }
3386
+ async function grantInput(db, ctx, assignment) {
3387
+ const person = await (0, import_crm5.getRecord)({ crm: ctx.chapter.crm, db }, assignment.personId);
3388
+ const email = person ? personAdmissionEmail(ctx, person) : null;
3389
+ if (!email) throw new Error(`person ${assignment.personId} has no valid authoritative email`);
3390
+ return {
3391
+ id: assignment.grantId,
3392
+ email,
3393
+ decision: assignment.decision,
3394
+ ...assignment.note ? { note: assignment.note } : {},
3395
+ ...assignment.expiresAt !== void 0 ? { expiresAt: assignment.expiresAt } : {}
3396
+ };
3397
+ }
3398
+ async function persistLeaderAdmissionDesiredSet(opts) {
3399
+ const now = Date.now();
3400
+ const current = await loadLeaderAdmissionAssignments(opts.db, opts.target.id);
3401
+ const id = assignmentId(opts.target.id, opts.personId);
3402
+ const existing = current.find((row) => row.id === id);
3403
+ const digest = await signupControlDigest({
3404
+ targetId: opts.target.id,
3405
+ personId: opts.personId,
3406
+ decision: opts.decision,
3407
+ note: opts.note ?? null,
3408
+ expiresAt: opts.expiresAt ?? null,
3409
+ active: opts.active
3410
+ });
3411
+ const mutationReceiptId = `leader-admission-mutation:${opts.target.id}:${opts.mutationId}`;
3412
+ const priorMutation2 = await opts.db.query({ leaderAdmissionMutations: { $: {
3413
+ where: { id: mutationReceiptId },
3414
+ limit: 1
3415
+ } } });
3416
+ const receipt = admissionRows(priorMutation2.leaderAdmissionMutations)[0];
3417
+ if (receipt) {
3418
+ if (receipt.requestDigest !== digest || receipt.personId !== opts.personId) {
3419
+ throw new Error("mutation_conflict");
3420
+ }
3421
+ const saved = await loadLeaderAdmissionDelivery(opts.db, opts.target.id, opts.env.ODLA_ENV);
3422
+ if (!saved) throw new Error("delivery_missing");
3423
+ return { delivery: saved, duplicate: true };
3424
+ }
3425
+ if (existing?.lastMutationId === opts.mutationId) {
3426
+ if (existing.lastRequestDigest !== digest) throw new Error("mutation_conflict");
3427
+ const saved = await loadLeaderAdmissionDelivery(opts.db, opts.target.id, opts.env.ODLA_ENV);
3428
+ if (!saved) throw new Error("delivery_missing");
3429
+ return { delivery: saved, duplicate: true };
3430
+ }
3431
+ const assignment = {
3432
+ id,
3433
+ targetId: opts.target.id,
3434
+ personId: opts.personId,
3435
+ grantId: existing?.grantId ?? crypto.randomUUID().replaceAll("-", ""),
3436
+ decision: opts.decision,
3437
+ ...opts.note ? { note: opts.note } : {},
3438
+ ...opts.expiresAt !== void 0 ? { expiresAt: opts.expiresAt } : {},
3439
+ active: opts.active,
3440
+ lastMutationId: opts.mutationId,
3441
+ lastRequestDigest: digest,
3442
+ createdAt: existing?.createdAt ?? now,
3443
+ createdBy: existing?.createdBy ?? opts.actor,
3444
+ updatedAt: now,
3445
+ updatedBy: opts.actor
3446
+ };
3447
+ const desired = current.filter((row) => row.id !== id && row.active).concat(assignment.active ? [assignment] : []);
3448
+ const grants = await Promise.all(desired.map((row) => grantInput(opts.db, opts.ctx, row)));
3449
+ const previous = await loadLeaderAdmissionDelivery(opts.db, opts.target.id, opts.env.ODLA_ENV);
3450
+ const revision = (previous?.revision ?? 0) + 1;
3451
+ const envelope = await createAdmissionGrantEnvelope({
3452
+ schemaVersion: 1,
3453
+ revision,
3454
+ generatedAt: now,
3455
+ source: { appId: opts.ctx.chapter.id, actor: opts.actor },
3456
+ target: { chapterId: opts.target.id, environment: opts.env.ODLA_ENV },
3457
+ grants
3458
+ });
3459
+ const delivery = {
3460
+ id: deliveryId(opts.target.id, opts.env.ODLA_ENV),
3461
+ targetId: opts.target.id,
3462
+ environment: opts.env.ODLA_ENV,
3463
+ revision,
3464
+ digest: envelope.digest,
3465
+ envelope,
3466
+ status: "saved",
3467
+ attempts: 0,
3468
+ updatedAt: now
3469
+ };
3470
+ const ops = [
3471
+ { t: "update", ns: "leaderAdmissionAssignments", id, attrs: assignment },
3472
+ { t: "update", ns: "leaderAdmissionDeliveries", id: delivery.id, attrs: delivery },
3473
+ { t: "update", ns: "leaderAdmissionMutations", id: mutationReceiptId, attrs: {
3474
+ id: mutationReceiptId,
3475
+ targetId: opts.target.id,
3476
+ personId: opts.personId,
3477
+ requestDigest: digest,
3478
+ revision,
3479
+ createdAt: now
3480
+ } }
3481
+ ];
3482
+ const retract = [
3483
+ ...!opts.note && existing?.note !== void 0 ? ["note"] : [],
3484
+ ...opts.expiresAt === void 0 && existing?.expiresAt !== void 0 ? ["expiresAt"] : []
3485
+ ];
3486
+ if (retract.length) ops.push({ t: "retract", ns: "leaderAdmissionAssignments", id, attrs: retract });
3487
+ const tx = await opts.db.transact(ops, {
3488
+ mutationId: `leader-admission:${opts.target.id}:${opts.mutationId}`,
3489
+ guards: previous ? [{ ns: "leaderAdmissionDeliveries", id: previous.id, exists: true, equals: { revision: previous.revision } }] : [{ ns: "leaderAdmissionDeliveries", id: delivery.id, exists: false }]
3490
+ });
3491
+ return { delivery, duplicate: tx.duplicate };
3492
+ }
3493
+
3494
+ // src/worker-routes-network-admission.ts
3495
+ var PATH = "/api/admin/network/admissions";
3496
+ var RETRY_PATH = "/api/admin/network/admissions/retry";
3497
+ var REVOKE_PATH = "/api/admin/network/admissions/revoke";
3498
+ var MUTATION_ID = /^[A-Za-z0-9_-]{16,128}$/;
3499
+ var MAX_NOTE2 = 2e3;
3500
+ var reject4 = (status, code, error) => json({ ok: false, code, error }, status);
3501
+ async function gate2(req, env, ctx) {
3502
+ const db = ctx.makeDb(env);
3503
+ const user = await ctx.verifyUser(req, env);
3504
+ if (!user) return { response: reject4(401, "unauthorized", "unauthorized") };
3505
+ if (!await ctx.isAdmin(db, user)) return { response: reject4(403, "forbidden", "forbidden") };
3506
+ return { db, user };
3507
+ }
3508
+ function targetFor(ctx, id) {
3509
+ return ctx.chapter.network.targets.find((target) => target.id === id && target.admissionGrants);
3510
+ }
3511
+ async function deliverSaved(db, env, ctx, target, delivery) {
3512
+ if (delivery.status === "acknowledged") return delivery;
3513
+ const secret = await getVaultSecret(db, target.secretName);
3514
+ const attemptedAt = Date.now();
3515
+ const attempts = delivery.attempts + 1;
3516
+ let patch;
3517
+ if (!secret) {
3518
+ patch = {
3519
+ status: delivery.attempts > 0 ? "retrying" : "pending",
3520
+ attempts,
3521
+ lastAttemptAt: attemptedAt,
3522
+ lastError: "edge secret is missing",
3523
+ updatedAt: attemptedAt
3524
+ };
3525
+ } else {
3526
+ try {
3527
+ const receipt = await deliverAdmissionGrants({
3528
+ secret,
3529
+ sender: ctx.chapter.id,
3530
+ targetUrl: target.url,
3531
+ envelope: delivery.envelope,
3532
+ fetch: ((input, init) => fetchNetworkTarget(env, target, new URL(String(input)), init ?? {}))
3533
+ });
3534
+ patch = {
3535
+ status: "acknowledged",
3536
+ attempts,
3537
+ lastAttemptAt: attemptedAt,
3538
+ acknowledgedAt: receipt.acknowledgedAt,
3539
+ updatedAt: attemptedAt
3540
+ };
3541
+ } catch (error) {
3542
+ const permanent = error instanceof AdmissionGrantDeliveryError && error.status >= 400 && error.status < 500;
3543
+ patch = {
3544
+ status: permanent ? "rejected" : delivery.attempts > 0 ? "retrying" : "pending",
3545
+ attempts,
3546
+ lastAttemptAt: attemptedAt,
3547
+ lastError: error instanceof Error ? error.message.slice(0, 2e3) : "delivery failed",
3548
+ updatedAt: attemptedAt
3549
+ };
3550
+ }
3551
+ }
3552
+ const ops = [{
3553
+ t: "update",
3554
+ ns: "leaderAdmissionDeliveries",
3555
+ id: delivery.id,
3556
+ attrs: patch
3557
+ }];
3558
+ if (patch.status === "acknowledged" && delivery.lastError) {
3559
+ ops.push({ t: "retract", ns: "leaderAdmissionDeliveries", id: delivery.id, attrs: ["lastError"] });
3560
+ }
3561
+ await db.transact(ops, {
3562
+ mutationId: `leader-admission-attempt:${delivery.targetId}:${delivery.revision}:${attempts}:${patch.status}`
3563
+ });
3564
+ return { ...delivery, ...patch };
3565
+ }
3566
+ async function parseBody(req) {
3567
+ try {
3568
+ return await req.json();
3569
+ } catch {
3570
+ return null;
3571
+ }
3572
+ }
3573
+ var handleAdminNetworkAdmissions = async (req, url, env, ctx) => {
3574
+ if (![PATH, RETRY_PATH, REVOKE_PATH].includes(url.pathname)) return null;
3575
+ const auth = await gate2(req, env, ctx);
3576
+ if ("response" in auth) return auth.response;
3577
+ const db = auth.db;
3578
+ if (url.pathname === PATH && req.method === "GET") {
3579
+ const targetId2 = url.searchParams.get("targetId") ?? "";
3580
+ const target2 = targetFor(ctx, targetId2);
3581
+ if (!target2) return reject4(404, "unknown_target", "admission target is not configured");
3582
+ return json({
3583
+ ok: true,
3584
+ target: { id: target2.id, name: target2.name },
3585
+ assignments: await loadLeaderAdmissionAssignments(db, target2.id),
3586
+ delivery: await loadLeaderAdmissionDelivery(db, target2.id, env.ODLA_ENV)
3587
+ });
3588
+ }
3589
+ if (req.method !== "POST") return null;
3590
+ const body = await parseBody(req);
3591
+ if (!body) return reject4(422, "invalid_request", "request body must be JSON");
3592
+ const targetId = typeof body.targetId === "string" ? body.targetId : "";
3593
+ const target = targetFor(ctx, targetId);
3594
+ if (!target) return reject4(404, "unknown_target", "admission target is not configured");
3595
+ if (url.pathname === RETRY_PATH) {
3596
+ const delivery = await loadLeaderAdmissionDelivery(db, target.id, env.ODLA_ENV);
3597
+ if (!delivery) return reject4(404, "not_found", "no saved admission delivery exists");
3598
+ return json({ ok: true, delivery: await deliverSaved(db, env, ctx, target, delivery) });
3599
+ }
3600
+ const personId = typeof body.personId === "string" ? body.personId : "";
3601
+ const mutationId = typeof body.mutationId === "string" ? body.mutationId : "";
3602
+ if (!personId || !MUTATION_ID.test(mutationId)) {
3603
+ return reject4(422, "invalid_request", "personId and a stable mutationId are required");
3604
+ }
3605
+ const person = await (0, import_crm6.getRecord)({ crm: ctx.chapter.crm, db: auth.db }, personId);
3606
+ if (!person) return reject4(404, "person_not_found", "person not found");
3607
+ if (!personAdmissionEmail(ctx, person)) {
3608
+ return reject4(422, "invalid_email", "person has no valid authoritative email");
3609
+ }
3610
+ const decision = body.decision ?? "waive_interview";
3611
+ if (decision !== "waive_interview" && decision !== "require_interview") {
3612
+ return reject4(422, "invalid_decision", "decision must be waive_interview or require_interview");
3613
+ }
3614
+ const note = typeof body.note === "string" ? body.note.trim() : "";
3615
+ if (note.length > MAX_NOTE2) return reject4(422, "invalid_note", "note is too long");
3616
+ const expiresAt = body.expiresAt == null ? void 0 : Number(body.expiresAt);
3617
+ if (expiresAt !== void 0 && (!Number.isFinite(expiresAt) || expiresAt <= Date.now())) {
3618
+ return reject4(422, "invalid_expiry", "expiresAt must be a future epoch timestamp");
3619
+ }
3620
+ try {
3621
+ const saved = await persistLeaderAdmissionDesiredSet({
3622
+ db,
3623
+ ctx,
3624
+ env,
3625
+ target,
3626
+ personId,
3627
+ decision,
3628
+ ...note ? { note } : {},
3629
+ ...expiresAt !== void 0 ? { expiresAt } : {},
3630
+ active: url.pathname !== REVOKE_PATH,
3631
+ mutationId,
3632
+ actor: auth.user.email ?? auth.user.userId
3633
+ });
3634
+ const delivery = await deliverSaved(db, env, ctx, target, saved.delivery);
3635
+ return json({ ok: true, duplicate: saved.duplicate, delivery }, delivery.status === "acknowledged" ? 200 : 202);
3636
+ } catch (error) {
3637
+ if (error instanceof Error && error.message === "mutation_conflict") {
3638
+ return reject4(409, "mutation_conflict", "mutationId was already used for another request");
3639
+ }
3640
+ if (error instanceof Error && /valid authoritative email/.test(error.message)) {
3641
+ return reject4(422, "invalid_email", error.message);
3642
+ }
3643
+ return reject4(409, "revision_conflict", "admission assignments changed; reload and retry");
3644
+ }
3645
+ };
3646
+ async function retryLeaderAdmissionDeliveries(env, ctx, limit = 100) {
3647
+ const db = ctx.makeDb(env);
3648
+ const result = await db.query({ leaderAdmissionDeliveries: { $: { limit } } });
3649
+ const pending = admissionRows(result.leaderAdmissionDeliveries).filter((delivery) => delivery.status !== "acknowledged" && delivery.status !== "rejected").slice(0, Math.max(1, Math.min(limit, 500)));
3650
+ const delivered = [];
3651
+ for (const item of pending) {
3652
+ const target = targetFor(ctx, item.targetId);
3653
+ if (target) delivered.push(await deliverSaved(db, env, ctx, target, item));
3654
+ }
3655
+ return delivered;
3656
+ }
3657
+
3287
3658
  // src/worker-routes-schedule.ts
3288
3659
  var import_calendar = require("@odla-ai/calendar");
3289
3660
  function errCode(err) {
@@ -4545,16 +4916,16 @@ var crmDeps2 = (db, ctx) => ({
4545
4916
  });
4546
4917
  var handleAdminCrmSync = async (req, url, env, ctx) => {
4547
4918
  if (req.method !== "POST" || url.pathname !== "/api/admin/crm/sync") return null;
4548
- const gate9 = await adminGate(req, env, ctx);
4549
- if (gate9 instanceof Response) return gate9;
4550
- const result = await backfillCrm(crmDeps2(gate9.db, ctx));
4919
+ const gate10 = await adminGate(req, env, ctx);
4920
+ if (gate10 instanceof Response) return gate10;
4921
+ const result = await backfillCrm(crmDeps2(gate10.db, ctx));
4551
4922
  return json({ ok: true, ...result });
4552
4923
  };
4553
4924
  var handleAdminPeople = async (req, url, env, ctx) => {
4554
4925
  if (req.method !== "GET" || url.pathname !== "/api/admin/people") return null;
4555
- const gate9 = await adminGate(req, env, ctx);
4556
- if (gate9 instanceof Response) return gate9;
4557
- const { db } = gate9;
4926
+ const gate10 = await adminGate(req, env, ctx);
4927
+ if (gate10 instanceof Response) return gate10;
4928
+ const { db } = gate10;
4558
4929
  const sk = await getVaultSecret(db, "clerk_secret_key");
4559
4930
  const [data, roleList] = await Promise.all([
4560
4931
  db.query({
@@ -4607,9 +4978,9 @@ var handleAdminPeople = async (req, url, env, ctx) => {
4607
4978
  };
4608
4979
  var handleAdminPeopleAccess = async (req, url, env, ctx) => {
4609
4980
  if (req.method !== "GET" || url.pathname !== "/api/admin/people/access") return null;
4610
- const gate9 = await adminGate(req, env, ctx);
4611
- if (gate9 instanceof Response) return gate9;
4612
- const { db } = gate9;
4981
+ const gate10 = await adminGate(req, env, ctx);
4982
+ if (gate10 instanceof Response) return gate10;
4983
+ const { db } = gate10;
4613
4984
  const targetId = url.searchParams.get("userId") ?? "";
4614
4985
  if (!targetId.startsWith("user_")) return json({ error: "invalid userId" }, 400);
4615
4986
  const sk = await getVaultSecret(db, "clerk_secret_key");
@@ -4620,9 +4991,9 @@ var handleAdminPeopleAccess = async (req, url, env, ctx) => {
4620
4991
  };
4621
4992
  var handleAdminPeopleRole = async (req, url, env, ctx) => {
4622
4993
  if (req.method !== "POST" || url.pathname !== "/api/admin/people/role") return null;
4623
- const gate9 = await adminGate(req, env, ctx);
4624
- if (gate9 instanceof Response) return gate9;
4625
- const { db, actor } = gate9;
4994
+ const gate10 = await adminGate(req, env, ctx);
4995
+ if (gate10 instanceof Response) return gate10;
4996
+ const { db, actor } = gate10;
4626
4997
  let body;
4627
4998
  try {
4628
4999
  body = await req.json();
@@ -4769,7 +5140,7 @@ function subAnnualCents(sub) {
4769
5140
  }
4770
5141
 
4771
5142
  // src/worker-routes-admin-dashboard.ts
4772
- async function gate2(req, env, ctx) {
5143
+ async function gate3(req, env, ctx) {
4773
5144
  const rawDb = ctx.makeDb(env);
4774
5145
  const u = await ctx.verifyUser(req, env);
4775
5146
  if (!u) return json({ error: "unauthorized" }, 401);
@@ -4779,7 +5150,7 @@ async function gate2(req, env, ctx) {
4779
5150
  var rows3 = (v) => Array.isArray(v) ? v : [];
4780
5151
  var handleAdminDashboard = async (req, url, env, ctx) => {
4781
5152
  if (req.method !== "GET" || url.pathname !== "/api/admin/dashboard") return null;
4782
- const got = await gate2(req, env, ctx);
5153
+ const got = await gate3(req, env, ctx);
4783
5154
  if (got instanceof Response) return got;
4784
5155
  const db = got;
4785
5156
  const now = Date.now();
@@ -4862,7 +5233,7 @@ var handleAdminDashboard = async (req, url, env, ctx) => {
4862
5233
  };
4863
5234
  var handleAdminBilling = async (req, url, env, ctx) => {
4864
5235
  if (req.method !== "GET" || url.pathname !== "/api/admin/billing") return null;
4865
- const got = await gate2(req, env, ctx);
5236
+ const got = await gate3(req, env, ctx);
4866
5237
  if (got instanceof Response) return got;
4867
5238
  const db = got;
4868
5239
  const sk = await getVaultSecret(db, "stripe_secret_key");
@@ -4922,7 +5293,7 @@ var handleAdminBilling = async (req, url, env, ctx) => {
4922
5293
 
4923
5294
  // src/worker-routes-admin-lifecycle.ts
4924
5295
  var import_calendar3 = require("@odla-ai/calendar");
4925
- async function gate3(req, env, ctx) {
5296
+ async function gate4(req, env, ctx) {
4926
5297
  const rawDb = ctx.makeDb(env);
4927
5298
  const u = await ctx.verifyUser(req, env);
4928
5299
  if (!u) return json({ error: "unauthorized" }, 401);
@@ -4948,7 +5319,7 @@ async function loadGroup(db, id) {
4948
5319
  var handleAdminMeetingReschedule = async (req, url, env, ctx) => {
4949
5320
  const m = url.pathname.match(/^\/api\/admin\/meetings\/([0-9a-f-]+)\/reschedule$/);
4950
5321
  if (req.method !== "POST" || !m) return null;
4951
- const got = await gate3(req, env, ctx);
5322
+ const got = await gate4(req, env, ctx);
4952
5323
  if (got instanceof Response) return got;
4953
5324
  const db = got;
4954
5325
  const body = await readJson(req);
@@ -4989,7 +5360,7 @@ var handleAdminMeetingReschedule = async (req, url, env, ctx) => {
4989
5360
  var handleAdminMeetingCancel = async (req, url, env, ctx) => {
4990
5361
  const m = url.pathname.match(/^\/api\/admin\/meetings\/([0-9a-f-]+)\/cancel$/);
4991
5362
  if (req.method !== "POST" || !m) return null;
4992
- const got = await gate3(req, env, ctx);
5363
+ const got = await gate4(req, env, ctx);
4993
5364
  if (got instanceof Response) return got;
4994
5365
  const db = got;
4995
5366
  const { meetings } = await db.query({ meetings: { $: { where: { id: m[1] }, limit: 1 } } });
@@ -5012,7 +5383,7 @@ var handleAdminMeetingCancel = async (req, url, env, ctx) => {
5012
5383
  var handleAdminApprove = async (req, url, env, ctx) => {
5013
5384
  const m = url.pathname.match(/^\/api\/admin\/applications\/([0-9a-f-]+)\/approve$/);
5014
5385
  if (req.method !== "POST" || !m) return null;
5015
- const got = await gate3(req, env, ctx);
5386
+ const got = await gate4(req, env, ctx);
5016
5387
  if (got instanceof Response) return got;
5017
5388
  const db = got;
5018
5389
  const id = m[1];
@@ -5025,7 +5396,7 @@ var handleAdminApprove = async (req, url, env, ctx) => {
5025
5396
  var handleAdminRefund = async (req, url, env, ctx) => {
5026
5397
  const m = url.pathname.match(/^\/api\/admin\/applications\/([0-9a-f-]+)\/refund$/);
5027
5398
  if (req.method !== "POST" || !m) return null;
5028
- const got = await gate3(req, env, ctx);
5399
+ const got = await gate4(req, env, ctx);
5029
5400
  if (got instanceof Response) return got;
5030
5401
  const db = got;
5031
5402
  const app = await loadApp(db, m[1]);
@@ -5057,7 +5428,7 @@ var handleAdminRefund = async (req, url, env, ctx) => {
5057
5428
  var handleAdminApplicationPatch = async (req, url, env, ctx) => {
5058
5429
  const m = url.pathname.match(/^\/api\/admin\/applications\/([0-9a-f-]+)$/);
5059
5430
  if (req.method !== "PATCH" || !m) return null;
5060
- const got = await gate3(req, env, ctx);
5431
+ const got = await gate4(req, env, ctx);
5061
5432
  if (got instanceof Response) return got;
5062
5433
  const db = got;
5063
5434
  const id = m[1];
@@ -5085,7 +5456,7 @@ var handleAdminApplicationPatch = async (req, url, env, ctx) => {
5085
5456
  };
5086
5457
 
5087
5458
  // src/worker-routes-admin-comms.ts
5088
- async function gate4(req, env, ctx) {
5459
+ async function gate5(req, env, ctx) {
5089
5460
  const rawDb = ctx.makeDb(env);
5090
5461
  const u = await ctx.verifyUser(req, env);
5091
5462
  if (!u) return json({ error: "unauthorized" }, 401);
@@ -5107,7 +5478,7 @@ var notifyDeps = (db, env) => ({
5107
5478
  });
5108
5479
  var handleAdminGroupEmail = async (req, url, env, ctx) => {
5109
5480
  if (url.pathname !== "/api/admin/group/email" || req.method !== "GET" && req.method !== "PUT") return null;
5110
- const got = await gate4(req, env, ctx);
5481
+ const got = await gate5(req, env, ctx);
5111
5482
  if (got instanceof Response) return got;
5112
5483
  const db = got;
5113
5484
  const group = await loadGroup2(db, ctx.chapter.id);
@@ -5169,7 +5540,7 @@ var handleAdminGroupEmail = async (req, url, env, ctx) => {
5169
5540
  };
5170
5541
  var handleAdminEmailLog = async (req, url, env, ctx) => {
5171
5542
  if (req.method !== "GET" || url.pathname !== "/api/admin/email/log") return null;
5172
- const got = await gate4(req, env, ctx);
5543
+ const got = await gate5(req, env, ctx);
5173
5544
  if (got instanceof Response) return got;
5174
5545
  const { emailLog } = await got.query({ emailLog: { $: { order: { sentAt: "desc" }, limit: 50 } } });
5175
5546
  const sends = (emailLog ?? []).map((r) => ({
@@ -5186,7 +5557,7 @@ var handleAdminEmailLog = async (req, url, env, ctx) => {
5186
5557
  };
5187
5558
  var handleAdminEmailTest = async (req, url, env, ctx) => {
5188
5559
  if (req.method !== "POST" || url.pathname !== "/api/admin/email/test") return null;
5189
- const got = await gate4(req, env, ctx);
5560
+ const got = await gate5(req, env, ctx);
5190
5561
  if (got instanceof Response) return got;
5191
5562
  const db = got;
5192
5563
  let body;
@@ -5214,7 +5585,7 @@ var handleAdminEmailTest = async (req, url, env, ctx) => {
5214
5585
  var handleAdminComms = async (req, url, env, ctx) => {
5215
5586
  const m = url.pathname.match(/^\/api\/admin\/people\/([0-9a-fA-F-]+)\/comms$/);
5216
5587
  if (req.method !== "GET" || !m) return null;
5217
- const got = await gate4(req, env, ctx);
5588
+ const got = await gate5(req, env, ctx);
5218
5589
  if (got instanceof Response) return got;
5219
5590
  const db = got;
5220
5591
  const appId = m[1];
@@ -5252,26 +5623,12 @@ var handleAdminComms = async (req, url, env, ctx) => {
5252
5623
  };
5253
5624
 
5254
5625
  // src/worker-routes-network.ts
5255
- var import_crm6 = require("@odla-ai/crm");
5256
- var import_db7 = require("@odla-ai/db");
5257
-
5258
- // src/worker-network-fetch.ts
5259
- function targetFetcher(env, target) {
5260
- if (!target.binding) return null;
5261
- const candidate = env[target.binding];
5262
- if (typeof candidate !== "object" || candidate === null || !("fetch" in candidate) || typeof candidate.fetch !== "function") {
5263
- throw new Error(`service binding "${target.binding}" is unavailable`);
5264
- }
5265
- return candidate;
5266
- }
5267
- function fetchNetworkTarget(env, target, destination, init) {
5268
- const binding = targetFetcher(env, target);
5269
- return binding ? binding.fetch(new Request(destination, init)) : globalThis.fetch(destination, init);
5270
- }
5626
+ var import_crm8 = require("@odla-ai/crm");
5627
+ var import_db8 = require("@odla-ai/db");
5271
5628
 
5272
5629
  // src/worker-network-push.ts
5273
- var import_crm5 = require("@odla-ai/crm");
5274
- var import_db6 = require("@odla-ai/db");
5630
+ var import_crm7 = require("@odla-ai/crm");
5631
+ var import_db7 = require("@odla-ai/db");
5275
5632
  async function pushNetworkRecord(db, env, ctx, target, record4) {
5276
5633
  const secret = await getVaultSecret(db, target.secretName);
5277
5634
  const crmDeps3 = {
@@ -5282,7 +5639,7 @@ async function pushNetworkRecord(db, env, ctx, target, record4) {
5282
5639
  };
5283
5640
  if (!secret) {
5284
5641
  const error = `vault secret "${target.secretName}" is missing`;
5285
- await (0, import_crm5.recordDeliveryAttempt)(crmDeps3, {
5642
+ await (0, import_crm7.recordDeliveryAttempt)(crmDeps3, {
5286
5643
  recordId: record4.id,
5287
5644
  targetId: target.id,
5288
5645
  status: "failed",
@@ -5305,7 +5662,7 @@ async function pushNetworkRecord(db, env, ctx, target, record4) {
5305
5662
  const payloadBody = JSON.stringify(payload);
5306
5663
  const destination = new URL("/api/network/shared", target.url);
5307
5664
  try {
5308
- const signed = await (0, import_db6.signFederatedRequest)({
5665
+ const signed = await (0, import_db7.signFederatedRequest)({
5309
5666
  secret,
5310
5667
  sender: ctx.chapter.id,
5311
5668
  method: "POST",
@@ -5321,7 +5678,7 @@ async function pushNetworkRecord(db, env, ctx, target, record4) {
5321
5678
  const responseBody = await res.json().catch(() => ({}));
5322
5679
  if (!res.ok) {
5323
5680
  const error = responseBody.error ?? "follower rejected the record";
5324
- await (0, import_crm5.recordDeliveryAttempt)(crmDeps3, {
5681
+ await (0, import_crm7.recordDeliveryAttempt)(crmDeps3, {
5325
5682
  recordId: record4.id,
5326
5683
  targetId: target.id,
5327
5684
  status: "failed",
@@ -5336,7 +5693,7 @@ async function pushNetworkRecord(db, env, ctx, target, record4) {
5336
5693
  error
5337
5694
  };
5338
5695
  }
5339
- await (0, import_crm5.recordDeliveryAttempt)(crmDeps3, {
5696
+ await (0, import_crm7.recordDeliveryAttempt)(crmDeps3, {
5340
5697
  recordId: record4.id,
5341
5698
  targetId: target.id,
5342
5699
  status: "delivered",
@@ -5352,7 +5709,7 @@ async function pushNetworkRecord(db, env, ctx, target, record4) {
5352
5709
  };
5353
5710
  } catch (err) {
5354
5711
  const error = err instanceof Error ? err.message : "delivery failed";
5355
- await (0, import_crm5.recordDeliveryAttempt)(crmDeps3, {
5712
+ await (0, import_crm7.recordDeliveryAttempt)(crmDeps3, {
5356
5713
  recordId: record4.id,
5357
5714
  targetId: target.id,
5358
5715
  status: "failed",
@@ -5364,7 +5721,7 @@ async function pushNetworkRecord(db, env, ctx, target, record4) {
5364
5721
  }
5365
5722
 
5366
5723
  // src/worker-routes-network.ts
5367
- async function gate5(req, env, ctx) {
5724
+ async function gate6(req, env, ctx) {
5368
5725
  const db = ctx.makeDb(env);
5369
5726
  const user = await ctx.verifyUser(req, env);
5370
5727
  if (!user) return { response: json({ error: "unauthorized" }, 401) };
@@ -5373,7 +5730,7 @@ async function gate5(req, env, ctx) {
5373
5730
  }
5374
5731
  var handleAdminNetworkTargets = async (req, url, env, ctx) => {
5375
5732
  if (req.method !== "GET" || url.pathname !== "/api/admin/network/targets") return null;
5376
- const got = await gate5(req, env, ctx);
5733
+ const got = await gate6(req, env, ctx);
5377
5734
  if ("response" in got) return got.response;
5378
5735
  return json({
5379
5736
  targets: ctx.chapter.network.targets.map(({
@@ -5400,10 +5757,10 @@ var handleNetworkSnapshot = async (req, url, env, ctx) => {
5400
5757
  const db = ctx.makeDb(env);
5401
5758
  const secret = await getVaultSecret(db, "network_share_secret");
5402
5759
  if (!secret) return json({ error: "unauthorized" }, 401);
5403
- const verified = await (0, import_db7.verifyFederatedRequest)(req, { secret });
5760
+ const verified = await (0, import_db8.verifyFederatedRequest)(req, { secret });
5404
5761
  if (!verified.ok) return json({ error: "unauthorized", reason: verified.reason }, 401);
5405
5762
  const types = await Promise.all(
5406
- Object.keys(ctx.chapter.crm.config.types).map((type) => (0, import_crm6.typeSummary)({ crm: ctx.chapter.crm, db }, type))
5763
+ Object.keys(ctx.chapter.crm.config.types).map((type) => (0, import_crm8.typeSummary)({ crm: ctx.chapter.crm, db }, type))
5407
5764
  );
5408
5765
  const snapshot = {
5409
5766
  version: 1,
@@ -5418,7 +5775,7 @@ async function snapshotOne(db, env, ctx, target) {
5418
5775
  if (!secret) return { id: target.id, name: target.name, url: target.url, available: false, error: "edge secret is missing" };
5419
5776
  const destination = new URL("/api/network/snapshot", target.url);
5420
5777
  try {
5421
- const headers = await (0, import_db7.signFederatedRequest)({
5778
+ const headers = await (0, import_db8.signFederatedRequest)({
5422
5779
  secret,
5423
5780
  sender: ctx.chapter.id,
5424
5781
  method: "GET",
@@ -5459,7 +5816,7 @@ async function snapshotOne(db, env, ctx, target) {
5459
5816
  }
5460
5817
  var handleAdminNetworkRollup = async (req, url, env, ctx) => {
5461
5818
  if (req.method !== "GET" || url.pathname !== "/api/admin/network/rollup") return null;
5462
- const got = await gate5(req, env, ctx);
5819
+ const got = await gate6(req, env, ctx);
5463
5820
  if ("response" in got) return got.response;
5464
5821
  const targets = await Promise.all(
5465
5822
  ctx.chapter.network.targets.map((target) => snapshotOne(got.db, env, ctx, target))
@@ -5486,7 +5843,7 @@ var handleAdminNetworkRollup = async (req, url, env, ctx) => {
5486
5843
  };
5487
5844
  var handleAdminNetworkPush = async (req, url, env, ctx) => {
5488
5845
  if (req.method !== "POST" || url.pathname !== "/api/admin/network/push") return null;
5489
- const got = await gate5(req, env, ctx);
5846
+ const got = await gate6(req, env, ctx);
5490
5847
  if ("response" in got) return got.response;
5491
5848
  let body;
5492
5849
  try {
@@ -5501,7 +5858,7 @@ var handleAdminNetworkPush = async (req, url, env, ctx) => {
5501
5858
  if (requested.size !== body.targetIds.length) return json({ error: "targetIds must contain unique strings" }, 400);
5502
5859
  const targets = ctx.chapter.network.targets.filter((target) => requested.has(target.id));
5503
5860
  if (targets.length !== requested.size) return json({ error: "one or more targetIds are not configured" }, 400);
5504
- const record4 = await (0, import_crm6.getRecord)({ crm: ctx.chapter.crm, db: got.db }, body.recordId);
5861
+ const record4 = await (0, import_crm8.getRecord)({ crm: ctx.chapter.crm, db: got.db }, body.recordId);
5505
5862
  if (!record4) return json({ error: "record not found" }, 404);
5506
5863
  const results = await Promise.all(
5507
5864
  targets.map((target) => pushNetworkRecord(got.db, env, ctx, target, record4))
@@ -5510,8 +5867,8 @@ var handleAdminNetworkPush = async (req, url, env, ctx) => {
5510
5867
  };
5511
5868
 
5512
5869
  // src/worker-routes-network-records-follower.ts
5513
- var import_crm7 = require("@odla-ai/crm");
5514
- var import_db8 = require("@odla-ai/db");
5870
+ var import_crm9 = require("@odla-ai/crm");
5871
+ var import_db9 = require("@odla-ai/db");
5515
5872
 
5516
5873
  // src/worker-routes-network-records-common.ts
5517
5874
  var IDENTIFIER = /^[a-z][a-zA-Z0-9_]*$/;
@@ -5520,7 +5877,7 @@ var MAX_OFFSET = 1e4;
5520
5877
  var MAX_SEARCH = 200;
5521
5878
  var MAX_BODY = 32768;
5522
5879
  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}$/;
5880
+ var MUTATION_ID2 = /^[a-zA-Z0-9][a-zA-Z0-9:._-]{7,127}$/;
5524
5881
  function bodyTooLarge(body) {
5525
5882
  return new TextEncoder().encode(body).byteLength > MAX_BODY;
5526
5883
  }
@@ -5594,16 +5951,16 @@ function mutationError(error) {
5594
5951
  if (error?.code === "transact_guard_failed") {
5595
5952
  return json({ error: "record changed since it was reviewed", code: "stale_revision" }, 409);
5596
5953
  }
5597
- if (error instanceof import_crm7.CrmInputError) {
5954
+ if (error instanceof import_crm9.CrmInputError) {
5598
5955
  return json({ error: error.message, code: "invalid_transition_or_fields", ...error.fields ? { fields: error.fields } : {} }, 422);
5599
5956
  }
5600
5957
  return json({ error: "follower mutation failed", code: "mutation_failed" }, 500);
5601
5958
  }
5602
5959
  async function priorMutation(db, id) {
5603
5960
  const result = await db.query({
5604
- [import_crm7.CRM_NS.activity]: { $: { where: { id }, limit: 1 } }
5961
+ [import_crm9.CRM_NS.activity]: { $: { where: { id }, limit: 1 } }
5605
5962
  });
5606
- const row = (result[import_crm7.CRM_NS.activity] ?? [])[0];
5963
+ const row = (result[import_crm9.CRM_NS.activity] ?? [])[0];
5607
5964
  const meta = row?.meta;
5608
5965
  const receipt = meta?.[NETWORK_MUTATION_META];
5609
5966
  return typeof receipt?.fingerprint === "string" ? { fingerprint: receipt.fingerprint } : null;
@@ -5617,7 +5974,7 @@ var handleNetworkRecords = async (req, url, env, ctx) => {
5617
5974
  const secret = await getVaultSecret(db, "network_share_secret");
5618
5975
  const readers = ctx.chapter.network.readers;
5619
5976
  if (!secret || readers.length === 0) return json({ error: "unauthorized" }, 401);
5620
- const verified = await (0, import_db8.verifyFederatedRequest)(req, {
5977
+ const verified = await (0, import_db9.verifyFederatedRequest)(req, {
5621
5978
  secret,
5622
5979
  senders: readers.map((reader2) => reader2.id)
5623
5980
  });
@@ -5638,7 +5995,7 @@ var handleNetworkRecords = async (req, url, env, ctx) => {
5638
5995
  const hasFields = Boolean(fields && typeof fields === "object" && !Array.isArray(fields));
5639
5996
  const hasStage = typeof input?.stage === "string";
5640
5997
  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) {
5998
+ 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) {
5642
5999
  return json({ error: "invalid network mutation", code: "invalid_mutation" }, 422);
5643
6000
  }
5644
6001
  const editable = reader.editableFields[input.type];
@@ -5657,7 +6014,7 @@ var handleNetworkRecords = async (req, url, env, ctx) => {
5657
6014
  if (prior.fingerprint !== fingerprint) {
5658
6015
  return json({ error: "mutationId was already used for different input", code: "mutation_conflict" }, 409);
5659
6016
  }
5660
- const replayed = await (0, import_crm7.getRecord)({ crm: ctx.chapter.crm, db }, input.recordId);
6017
+ const replayed = await (0, import_crm9.getRecord)({ crm: ctx.chapter.crm, db }, input.recordId);
5661
6018
  if (!replayed || replayed.type !== input.type) return json({ error: "record not found", code: "not_found" }, 404);
5662
6019
  const result = {
5663
6020
  version: 1,
@@ -5667,7 +6024,7 @@ var handleNetworkRecords = async (req, url, env, ctx) => {
5667
6024
  };
5668
6025
  return json(result);
5669
6026
  }
5670
- const record4 = await (0, import_crm7.getRecord)({ crm: ctx.chapter.crm, db }, input.recordId);
6027
+ const record4 = await (0, import_crm9.getRecord)({ crm: ctx.chapter.crm, db }, input.recordId);
5671
6028
  if (!record4 || record4.type !== input.type) return json({ error: "record not found", code: "not_found" }, 404);
5672
6029
  if (record4.updatedAt !== input.expectedRevision) {
5673
6030
  return json({ error: "record changed since it was reviewed", code: "stale_revision" }, 409);
@@ -5688,7 +6045,7 @@ var handleNetworkRecords = async (req, url, env, ctx) => {
5688
6045
  };
5689
6046
  try {
5690
6047
  const deps = { crm: ctx.chapter.crm, db };
5691
- const updated = hasFields ? await (0, import_crm7.updateRecord)(deps, {
6048
+ const updated = hasFields ? await (0, import_crm9.updateRecord)(deps, {
5692
6049
  id: record4.id,
5693
6050
  input: fields,
5694
6051
  mutationId: activityId,
@@ -5699,7 +6056,7 @@ var handleNetworkRecords = async (req, url, env, ctx) => {
5699
6056
  authorId: `network:${verified.sender}`,
5700
6057
  meta: auditMeta
5701
6058
  }
5702
- }) : await (0, import_crm7.setStage)(deps, {
6059
+ }) : await (0, import_crm9.setStage)(deps, {
5703
6060
  id: record4.id,
5704
6061
  to: input.stage,
5705
6062
  authorId: `network:${verified.sender}`,
@@ -5729,7 +6086,7 @@ var handleNetworkRecords = async (req, url, env, ctx) => {
5729
6086
  if (limit == null || offset == null || search.length > MAX_SEARCH) {
5730
6087
  return json({ error: "invalid pagination or search parameters" }, 400);
5731
6088
  }
5732
- const page = await (0, import_crm7.listRecords)(
6089
+ const page = await (0, import_crm9.listRecords)(
5733
6090
  { crm: ctx.chapter.crm, db },
5734
6091
  {
5735
6092
  type,
@@ -5752,15 +6109,15 @@ var handleNetworkRecords = async (req, url, env, ctx) => {
5752
6109
  };
5753
6110
 
5754
6111
  // src/worker-routes-network-records-admin.ts
5755
- var import_db9 = require("@odla-ai/db");
5756
- async function gate6(req, env, ctx) {
6112
+ var import_db10 = require("@odla-ai/db");
6113
+ async function gate7(req, env, ctx) {
5757
6114
  const db = ctx.makeDb(env);
5758
6115
  const user = await ctx.verifyUser(req, env);
5759
6116
  if (!user) return { response: json({ error: "unauthorized" }, 401) };
5760
6117
  if (!await ctx.isAdmin(db, user)) return { response: json({ error: "forbidden" }, 403) };
5761
6118
  return { db, user };
5762
6119
  }
5763
- function targetFor(ctx, id) {
6120
+ function targetFor2(ctx, id) {
5764
6121
  return ctx.chapter.network.targets.find((target) => target.id === id);
5765
6122
  }
5766
6123
  async function readTargetRecords(db, env, ctx, target, query) {
@@ -5769,7 +6126,7 @@ async function readTargetRecords(db, env, ctx, target, query) {
5769
6126
  const destination = new URL("/api/network/records", target.url);
5770
6127
  destination.search = query.toString();
5771
6128
  try {
5772
- const headers = await (0, import_db9.signFederatedRequest)({ secret, sender: ctx.chapter.id, method: "GET", url: destination });
6129
+ const headers = await (0, import_db10.signFederatedRequest)({ secret, sender: ctx.chapter.id, method: "GET", url: destination });
5773
6130
  const response = await fetchNetworkTarget(env, target, destination, {
5774
6131
  headers,
5775
6132
  signal: AbortSignal.timeout(1e4)
@@ -5805,7 +6162,7 @@ async function mutateTargetRecord(db, env, ctx, target, input) {
5805
6162
  const destination = new URL("/api/network/records", target.url);
5806
6163
  const body = JSON.stringify(input);
5807
6164
  try {
5808
- const headers = await (0, import_db9.signFederatedRequest)({
6165
+ const headers = await (0, import_db10.signFederatedRequest)({
5809
6166
  secret,
5810
6167
  sender: ctx.chapter.id,
5811
6168
  method: "PATCH",
@@ -5837,7 +6194,7 @@ var handleAdminNetworkRecords = async (req, url, env, ctx) => {
5837
6194
  if (req.method === "PATCH" && Number(req.headers.get("content-length") ?? 0) > MAX_BODY) {
5838
6195
  return json({ error: "mutation body is too large", code: "body_too_large" }, 413);
5839
6196
  }
5840
- const got = await gate6(req, env, ctx);
6197
+ const got = await gate7(req, env, ctx);
5841
6198
  if ("response" in got) return got.response;
5842
6199
  if (req.method === "PATCH") {
5843
6200
  let input;
@@ -5850,10 +6207,10 @@ var handleAdminNetworkRecords = async (req, url, env, ctx) => {
5850
6207
  } catch {
5851
6208
  return json({ error: "invalid JSON body", code: "invalid_json" }, 422);
5852
6209
  }
5853
- const target2 = targetFor(ctx, input?.targetId);
6210
+ const target2 = targetFor2(ctx, input?.targetId);
5854
6211
  const hasFields = Boolean(input?.fields && typeof input.fields === "object" && !Array.isArray(input.fields));
5855
6212
  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) {
6213
+ 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) {
5857
6214
  return json({ error: "invalid network mutation", code: "invalid_mutation" }, 422);
5858
6215
  }
5859
6216
  return mutateTargetRecord(got.db, env, ctx, target2, {
@@ -5868,7 +6225,7 @@ var handleAdminNetworkRecords = async (req, url, env, ctx) => {
5868
6225
  }
5869
6226
  const targetId = url.searchParams.get("targetId") ?? "";
5870
6227
  const type = url.searchParams.get("type") ?? "";
5871
- const target = targetFor(ctx, targetId);
6228
+ const target = targetFor2(ctx, targetId);
5872
6229
  if (!target || !IDENTIFIER.test(type)) return json({ error: "configured targetId and record type are required" }, 400);
5873
6230
  const limit = integer(url.searchParams.get("limit"), 25, 1, MAX_PAGE);
5874
6231
  const offset = integer(url.searchParams.get("offset"), 0, 0, MAX_OFFSET);
@@ -5885,8 +6242,8 @@ var handleAdminNetworkRecords = async (req, url, env, ctx) => {
5885
6242
  // src/worker-routes-network-notes.ts
5886
6243
  var IDENTIFIER2 = /^[a-z][a-zA-Z0-9_]*$/;
5887
6244
  var RECORD_ID2 = /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,255}$/;
5888
- var MAX_NOTE2 = 4e3;
5889
- async function gate7(req, env, ctx) {
6245
+ var MAX_NOTE3 = 4e3;
6246
+ async function gate8(req, env, ctx) {
5890
6247
  const db = ctx.makeDb(env);
5891
6248
  const user = await ctx.verifyUser(req, env);
5892
6249
  if (!user) return { response: json({ error: "unauthorized" }, 401) };
@@ -5920,7 +6277,7 @@ function asNote(row) {
5920
6277
  }
5921
6278
  var handleAdminNetworkNotes = async (req, url, env, ctx) => {
5922
6279
  if (url.pathname !== "/api/admin/network/notes" || req.method !== "GET" && req.method !== "POST") return null;
5923
- const got = await gate7(req, env, ctx);
6280
+ const got = await gate8(req, env, ctx);
5924
6281
  if ("response" in got) return got.response;
5925
6282
  const identity = noteIdentity(url, ctx);
5926
6283
  if (!identity) return json({ error: "valid targetId, type, and recordId are required" }, 400);
@@ -5937,7 +6294,7 @@ var handleAdminNetworkNotes = async (req, url, env, ctx) => {
5937
6294
  return json({ notes: (result.networkNotes ?? []).map((row) => asNote(row)) });
5938
6295
  }
5939
6296
  const contentLength = Number(req.headers.get("content-length") ?? 0);
5940
- if (contentLength > MAX_NOTE2 * 2) return json({ error: "note is too large" }, 413);
6297
+ if (contentLength > MAX_NOTE3 * 2) return json({ error: "note is too large" }, 413);
5941
6298
  let input;
5942
6299
  try {
5943
6300
  input = await req.json();
@@ -5945,7 +6302,7 @@ var handleAdminNetworkNotes = async (req, url, env, ctx) => {
5945
6302
  return json({ error: "invalid JSON body" }, 400);
5946
6303
  }
5947
6304
  const body = typeof input.body === "string" ? input.body.trim() : "";
5948
- if (!body || body.length > MAX_NOTE2) return json({ error: `note must be 1\u2013${MAX_NOTE2} characters` }, 400);
6305
+ if (!body || body.length > MAX_NOTE3) return json({ error: `note must be 1\u2013${MAX_NOTE3} characters` }, 400);
5949
6306
  const id = crypto.randomUUID();
5950
6307
  const note = {
5951
6308
  id,
@@ -5967,14 +6324,14 @@ var handleAdminNetworkNotes = async (req, url, env, ctx) => {
5967
6324
  };
5968
6325
 
5969
6326
  // src/worker-routes-network-shared-notes.ts
5970
- var import_crm8 = require("@odla-ai/crm");
5971
- var import_db10 = require("@odla-ai/db");
6327
+ var import_crm10 = require("@odla-ai/crm");
6328
+ var import_db11 = require("@odla-ai/db");
5972
6329
  var IDENTIFIER3 = /^[a-z][a-zA-Z0-9_]*$/;
5973
6330
  var RECORD_ID3 = /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,255}$/;
5974
- var MUTATION_ID2 = /^[A-Za-z0-9_-]{16,128}$/;
5975
- var MAX_NOTE3 = 4e3;
6331
+ var MUTATION_ID3 = /^[A-Za-z0-9_-]{16,128}$/;
6332
+ var MAX_NOTE4 = 4e3;
5976
6333
  var NETWORK_META = "chapterNetwork";
5977
- async function gate8(req, env, ctx) {
6334
+ async function gate9(req, env, ctx) {
5978
6335
  const db = ctx.makeDb(env);
5979
6336
  const user = await ctx.verifyUser(req, env);
5980
6337
  if (!user) return { response: json({ error: "unauthorized" }, 401) };
@@ -5997,7 +6354,7 @@ function sharedNote(activity, sourceId) {
5997
6354
  }
5998
6355
  async function listSharedNotes(db, sourceId, recordId) {
5999
6356
  const result = await db.query({
6000
- [import_crm8.CRM_NS.activity]: {
6357
+ [import_crm10.CRM_NS.activity]: {
6001
6358
  $: {
6002
6359
  where: {
6003
6360
  and: [
@@ -6010,7 +6367,7 @@ async function listSharedNotes(db, sourceId, recordId) {
6010
6367
  }
6011
6368
  }
6012
6369
  });
6013
- return (result[import_crm8.CRM_NS.activity] ?? []).flatMap((activity) => {
6370
+ return (result[import_crm10.CRM_NS.activity] ?? []).flatMap((activity) => {
6014
6371
  const note = sharedNote(activity, sourceId);
6015
6372
  return note ? [note] : [];
6016
6373
  });
@@ -6020,14 +6377,14 @@ function sourceLabel(sourceId) {
6020
6377
  }
6021
6378
  var handleNetworkSharedNotes = async (req, url, env, ctx) => {
6022
6379
  if (url.pathname !== "/api/network/shared-notes" || req.method !== "GET" && req.method !== "POST") return null;
6023
- if (req.method === "POST" && Number(req.headers.get("content-length") ?? 0) > MAX_NOTE3 * 2) {
6380
+ if (req.method === "POST" && Number(req.headers.get("content-length") ?? 0) > MAX_NOTE4 * 2) {
6024
6381
  return json({ error: "note is too large" }, 413);
6025
6382
  }
6026
6383
  const db = ctx.makeDb(env);
6027
6384
  const secret = await getVaultSecret(db, "network_share_secret");
6028
6385
  const readers = ctx.chapter.network.readers;
6029
6386
  if (!secret || readers.length === 0) return json({ error: "unauthorized" }, 401);
6030
- const verified = await (0, import_db10.verifyFederatedRequest)(req, {
6387
+ const verified = await (0, import_db11.verifyFederatedRequest)(req, {
6031
6388
  secret,
6032
6389
  senders: readers.map((reader2) => reader2.id)
6033
6390
  });
@@ -6038,7 +6395,7 @@ var handleNetworkSharedNotes = async (req, url, env, ctx) => {
6038
6395
  if (!IDENTIFIER3.test(recordType) || !RECORD_ID3.test(recordId) || !reader?.sharedNotes.includes(recordType)) {
6039
6396
  return json({ error: "shared notes are not enabled for this reader and record type" }, 403);
6040
6397
  }
6041
- const record4 = await (0, import_crm8.getRecord)({ crm: ctx.chapter.crm, db }, recordId);
6398
+ const record4 = await (0, import_crm10.getRecord)({ crm: ctx.chapter.crm, db }, recordId);
6042
6399
  if (!record4 || record4.type !== recordType) return json({ error: "record not found" }, 404);
6043
6400
  if (req.method === "GET") {
6044
6401
  return json({
@@ -6055,12 +6412,12 @@ var handleNetworkSharedNotes = async (req, url, env, ctx) => {
6055
6412
  }
6056
6413
  const body = typeof input.body === "string" ? input.body.trim() : "";
6057
6414
  const mutationId = typeof input.mutationId === "string" ? input.mutationId : "";
6058
- if (!body || body.length > MAX_NOTE3 || !MUTATION_ID2.test(mutationId)) {
6059
- return json({ error: `body must be 1\u2013${MAX_NOTE3} characters and mutationId must be valid` }, 400);
6415
+ if (!body || body.length > MAX_NOTE4 || !MUTATION_ID3.test(mutationId)) {
6416
+ return json({ error: `body must be 1\u2013${MAX_NOTE4} characters and mutationId must be valid` }, 400);
6060
6417
  }
6061
6418
  const createdAt = Date.now();
6062
6419
  const id = `network:${verified.sender}:${mutationId}`;
6063
- const result = await (0, import_crm8.addActivity)(
6420
+ const result = await (0, import_crm10.addActivity)(
6064
6421
  {
6065
6422
  crm: ctx.chapter.crm,
6066
6423
  db,
@@ -6093,17 +6450,17 @@ var handleNetworkSharedNotes = async (req, url, env, ctx) => {
6093
6450
  };
6094
6451
  return json({ version: 1, site: { id: ctx.chapter.id, name: ctx.chapter.name }, note, duplicate: result.duplicate }, 201);
6095
6452
  };
6096
- function targetFor2(ctx, id) {
6453
+ function targetFor3(ctx, id) {
6097
6454
  return ctx.chapter.network.targets.find((target) => target.id === id);
6098
6455
  }
6099
6456
  var handleAdminNetworkSharedNotes = async (req, url, env, ctx) => {
6100
6457
  if (url.pathname !== "/api/admin/network/shared-notes" || req.method !== "GET" && req.method !== "POST") return null;
6101
- const got = await gate8(req, env, ctx);
6458
+ const got = await gate9(req, env, ctx);
6102
6459
  if ("response" in got) return got.response;
6103
6460
  const targetId = url.searchParams.get("targetId") ?? "";
6104
6461
  const recordType = url.searchParams.get("type") ?? "";
6105
6462
  const recordId = url.searchParams.get("recordId") ?? "";
6106
- const target = targetFor2(ctx, targetId);
6463
+ const target = targetFor3(ctx, targetId);
6107
6464
  if (!target || !target.sharedNotes.includes(recordType) || !IDENTIFIER3.test(recordType) || !RECORD_ID3.test(recordId)) {
6108
6465
  return json({ error: "configured targetId, type, and recordId are required" }, 400);
6109
6466
  }
@@ -6116,7 +6473,7 @@ var handleAdminNetworkSharedNotes = async (req, url, env, ctx) => {
6116
6473
  return json({ error: "invalid JSON body" }, 400);
6117
6474
  }
6118
6475
  const note = typeof input.body === "string" ? input.body.trim() : "";
6119
- if (!note || note.length > MAX_NOTE3) return json({ error: `note must be 1\u2013${MAX_NOTE3} characters` }, 400);
6476
+ if (!note || note.length > MAX_NOTE4) return json({ error: `note must be 1\u2013${MAX_NOTE4} characters` }, 400);
6120
6477
  body = JSON.stringify({ body: note, mutationId: crypto.randomUUID() });
6121
6478
  }
6122
6479
  const secret = await getVaultSecret(got.db, target.secretName);
@@ -6124,7 +6481,7 @@ var handleAdminNetworkSharedNotes = async (req, url, env, ctx) => {
6124
6481
  const destination = new URL("/api/network/shared-notes", target.url);
6125
6482
  destination.search = new URLSearchParams({ type: recordType, recordId }).toString();
6126
6483
  try {
6127
- const headers = await (0, import_db10.signFederatedRequest)({
6484
+ const headers = await (0, import_db11.signFederatedRequest)({
6128
6485
  secret,
6129
6486
  sender: ctx.chapter.id,
6130
6487
  method: req.method,
@@ -6151,7 +6508,7 @@ var handleAdminNetworkSharedNotes = async (req, url, env, ctx) => {
6151
6508
  };
6152
6509
 
6153
6510
  // src/worker-routes-formation.ts
6154
- var import_crm9 = require("@odla-ai/crm");
6511
+ var import_crm11 = require("@odla-ai/crm");
6155
6512
  var SUBMISSION = /^[A-Za-z0-9_-]{8,128}$/;
6156
6513
  var handleFormation = async (req, url, env, ctx) => {
6157
6514
  const formation = ctx.chapter.formation;
@@ -6203,7 +6560,7 @@ var handleFormation = async (req, url, env, ctx) => {
6203
6560
  const db = ctx.makeDb(env);
6204
6561
  const id = submissionId ? await applicationIdForSubmission(`formation:${ctx.chapter.id}:${submissionId}`) : crypto.randomUUID();
6205
6562
  try {
6206
- const created = await (0, import_crm9.createRecord)(
6563
+ const created = await (0, import_crm11.createRecord)(
6207
6564
  {
6208
6565
  crm: ctx.chapter.crm,
6209
6566
  db,
@@ -6231,7 +6588,7 @@ var handleFormation = async (req, url, env, ctx) => {
6231
6588
  };
6232
6589
 
6233
6590
  // src/worker-routes-builder-library.ts
6234
- var import_db11 = require("@odla-ai/db");
6591
+ var import_db12 = require("@odla-ai/db");
6235
6592
 
6236
6593
  // src/builder-library-validation.ts
6237
6594
  var ID4 = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
@@ -6514,7 +6871,7 @@ async function verifySource(req, db, ctx) {
6514
6871
  if (ctx.chapter.mode === "chapter") {
6515
6872
  const secret = await getVaultSecret(db, "network_share_secret");
6516
6873
  if (!secret) return null;
6517
- const verified = await (0, import_db11.verifyFederatedRequest)(req, {
6874
+ const verified = await (0, import_db12.verifyFederatedRequest)(req, {
6518
6875
  secret,
6519
6876
  senders: sources.map((source2) => source2.sourceId)
6520
6877
  });
@@ -6527,7 +6884,7 @@ async function verifySource(req, db, ctx) {
6527
6884
  if (!target) continue;
6528
6885
  const secret = await getVaultSecret(db, target.secretName);
6529
6886
  if (!secret) continue;
6530
- const verified = await (0, import_db11.verifyFederatedRequest)(req.clone(), { secret, senders: [source.sourceId] });
6887
+ const verified = await (0, import_db12.verifyFederatedRequest)(req.clone(), { secret, senders: [source.sourceId] });
6531
6888
  if (verified.ok) return { source, body: verified.body };
6532
6889
  }
6533
6890
  return null;
@@ -6716,11 +7073,11 @@ var handleAdminBuilderLibrary = async (req, url, env, ctx) => {
6716
7073
  };
6717
7074
 
6718
7075
  // src/signup-control-delivery.ts
6719
- var import_db12 = require("@odla-ai/db");
7076
+ var import_db13 = require("@odla-ai/db");
6720
7077
  async function deliverSignupControl(options) {
6721
7078
  const destination = new URL(SIGNUP_CONTROL_PATH, options.targetUrl);
6722
7079
  const body = JSON.stringify(options.envelope);
6723
- const signed = await (0, import_db12.signFederatedRequest)({
7080
+ const signed = await (0, import_db13.signFederatedRequest)({
6724
7081
  secret: options.secret,
6725
7082
  sender: options.sender,
6726
7083
  method: "POST",
@@ -6741,11 +7098,11 @@ async function deliverSignupControl(options) {
6741
7098
  }
6742
7099
 
6743
7100
  // src/member-content-delivery.ts
6744
- var import_db13 = require("@odla-ai/db");
7101
+ var import_db14 = require("@odla-ai/db");
6745
7102
  async function deliverMemberContent(options) {
6746
7103
  const destination = new URL(MEMBER_CONTENT_CONTROL_PATH, options.targetUrl);
6747
7104
  const body = JSON.stringify(options.envelope);
6748
- const signed = await (0, import_db13.signFederatedRequest)({
7105
+ const signed = await (0, import_db14.signFederatedRequest)({
6749
7106
  secret: options.secret,
6750
7107
  sender: options.sender,
6751
7108
  method: "POST",
@@ -6766,11 +7123,11 @@ async function deliverMemberContent(options) {
6766
7123
  }
6767
7124
 
6768
7125
  // src/builder-library-delivery.ts
6769
- var import_db14 = require("@odla-ai/db");
7126
+ var import_db15 = require("@odla-ai/db");
6770
7127
  async function deliverBuilderLibrary(options) {
6771
7128
  const destination = new URL(BUILDER_LIBRARY_CONTROL_PATH, options.targetUrl);
6772
7129
  const body = JSON.stringify(options.envelope);
6773
- const signed = await (0, import_db14.signFederatedRequest)({ secret: options.secret, sender: options.sender, method: "POST", url: destination, body });
7130
+ const signed = await (0, import_db15.signFederatedRequest)({ secret: options.secret, sender: options.sender, method: "POST", url: destination, body });
6774
7131
  const response = await (options.fetch ?? globalThis.fetch)(destination, { method: "POST", headers: { ...signed, "content-type": "application/json" }, body });
6775
7132
  const result = await response.json();
6776
7133
  if (!response.ok || !("ok" in result) || result.ok !== true) {
@@ -6780,31 +7137,6 @@ async function deliverBuilderLibrary(options) {
6780
7137
  return result;
6781
7138
  }
6782
7139
 
6783
- // src/admission-grants-delivery.ts
6784
- var import_db15 = require("@odla-ai/db");
6785
- async function deliverAdmissionGrants(options) {
6786
- const destination = new URL(ADMISSION_GRANTS_PATH, options.targetUrl);
6787
- const body = JSON.stringify(options.envelope);
6788
- const signed = await (0, import_db15.signFederatedRequest)({
6789
- secret: options.secret,
6790
- sender: options.sender,
6791
- method: "POST",
6792
- url: destination,
6793
- body
6794
- });
6795
- const response = await (options.fetch ?? globalThis.fetch)(destination, {
6796
- method: "POST",
6797
- headers: { ...signed, "content-type": "application/json" },
6798
- body
6799
- });
6800
- const result = await response.json();
6801
- if (!response.ok || !("ok" in result) || result.ok !== true) {
6802
- const failure = result;
6803
- throw new Error(`admission grants rejected (${response.status}): ${failure.reason ?? failure.error ?? "unknown"}`);
6804
- }
6805
- return result;
6806
- }
6807
-
6808
7140
  // src/signup-control-theme.ts
6809
7141
  var SIGNUP_CONTROL_THEME_MARKER = "<!--odla:signup-control-theme-->";
6810
7142
  var SIGNUP_CONTROL_THEME_STYLE_ID = "odla-signup-control-theme";
@@ -6964,6 +7296,7 @@ var BUILTIN_ROUTES = [
6964
7296
  handleAdminPeopleRole,
6965
7297
  handleAdminCrmSync,
6966
7298
  handleAdminAdmissionGrants,
7299
+ handleAdminNetworkAdmissions,
6967
7300
  handleAdminClerkPrivateProfileSync,
6968
7301
  // Aggregation
6969
7302
  handleAdminDashboard,