@odla-ai/chapter 0.47.1 → 0.47.3

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.
@@ -6087,7 +6087,13 @@ var handleAdminNetworkPush = async (req, url, env, ctx) => {
6087
6087
  };
6088
6088
 
6089
6089
  // src/worker-routes-network-records-follower.ts
6090
- import { CRM_NS, CrmInputError, getRecord as getRecord4, listRecords, setStage as setStage2, updateRecord as updateRecord3 } from "@odla-ai/crm";
6090
+ import {
6091
+ CRM_NS as CRM_NS2,
6092
+ CrmInputError,
6093
+ getRecord as getRecord4,
6094
+ setStage as setStage2,
6095
+ updateRecord as updateRecord3
6096
+ } from "@odla-ai/crm";
6091
6097
  import { verifyFederatedRequest as verifyFederatedRequest6 } from "@odla-ai/db";
6092
6098
 
6093
6099
  // src/worker-routes-network-records-common.ts
@@ -6182,6 +6188,85 @@ function restrictedRecord(record4, target) {
6182
6188
  return { ...record4, ...edit ? { edit } : { edit: void 0 } };
6183
6189
  }
6184
6190
 
6191
+ // src/worker-routes-network-records-search.ts
6192
+ import { CRM_NS, rowToRecord } from "@odla-ai/crm";
6193
+ var SEARCH_BATCH_SIZE = 1e3;
6194
+ var MAX_READER_SEARCH_POPULATION = 1e4;
6195
+ var ReaderSearchPopulationError = class extends Error {
6196
+ count;
6197
+ constructor(count) {
6198
+ super(`reader-visible search population exceeds ${MAX_READER_SEARCH_POPULATION} records`);
6199
+ this.name = "ReaderSearchPopulationError";
6200
+ this.count = count;
6201
+ }
6202
+ };
6203
+ function folded(value) {
6204
+ return value.replace(/[øØ]/gu, "o").replace(/[łŁ]/gu, "l").replace(/[đĐðÐ]/gu, "d").replace(/[æÆ]/gu, "ae").replace(/[œŒ]/gu, "oe").replace(/[þÞ]/gu, "th").replace(/ß/gu, "ss").normalize("NFKD").replace(/\p{M}/gu, "").toLocaleLowerCase("en-US").replace(/[^\p{L}\p{N}@._+-]+/gu, " ").trim();
6205
+ }
6206
+ function terms(value) {
6207
+ return folded(value).split(/\s+/u).filter(Boolean);
6208
+ }
6209
+ function matchesReaderIdentity(record4, search, includeEmail) {
6210
+ const query = terms(search);
6211
+ if (!query.length) return true;
6212
+ const name = folded(record4.name);
6213
+ const visible = [name, ...name.split(/\s+/u).filter(Boolean)];
6214
+ if (includeEmail && record4.primaryEmail) visible.push(folded(record4.primaryEmail));
6215
+ return query.every((term) => visible.some((candidate) => candidate.includes(term)));
6216
+ }
6217
+ function baseWhere(type, stage) {
6218
+ return {
6219
+ and: [
6220
+ { type },
6221
+ { archived: false },
6222
+ ...stage ? [{ stage }] : []
6223
+ ]
6224
+ };
6225
+ }
6226
+ async function listVisibleSearchPopulation(db, where, window, count) {
6227
+ if (count > MAX_READER_SEARCH_POPULATION) throw new ReaderSearchPopulationError(count);
6228
+ const offsets = Array.from(
6229
+ { length: Math.ceil(count / SEARCH_BATCH_SIZE) },
6230
+ (_, index2) => index2 * SEARCH_BATCH_SIZE
6231
+ );
6232
+ const pages = await Promise.all(offsets.map((offset) => db.query({
6233
+ [CRM_NS.record]: { $: {
6234
+ where,
6235
+ order: { [window.sort.field]: window.sort.dir },
6236
+ limit: Math.min(SEARCH_BATCH_SIZE, count - offset),
6237
+ offset
6238
+ } }
6239
+ })));
6240
+ return pages.flatMap((page) => (page[CRM_NS.record] ?? []).map(rowToRecord));
6241
+ }
6242
+ async function listReaderRecords(db, type, allowed2, emailField, window) {
6243
+ const where = baseWhere(type, window.stage);
6244
+ const crmDb = db;
6245
+ const aggregate = await crmDb.aggregate(CRM_NS.record, { count: true }, { where });
6246
+ const candidateCount = aggregate.count ?? 0;
6247
+ if (window.search) {
6248
+ const population = await listVisibleSearchPopulation(crmDb, where, window, candidateCount);
6249
+ const includeEmail = Boolean(emailField && allowed2.includes(emailField));
6250
+ const matches = population.filter((record4) => matchesReaderIdentity(record4, window.search, includeEmail));
6251
+ return {
6252
+ records: matches.slice(window.offset, window.offset + window.limit),
6253
+ total: matches.length
6254
+ };
6255
+ }
6256
+ const result = await crmDb.query({
6257
+ [CRM_NS.record]: { $: {
6258
+ where,
6259
+ order: { [window.sort.field]: window.sort.dir },
6260
+ limit: window.limit,
6261
+ offset: window.offset
6262
+ } }
6263
+ });
6264
+ return {
6265
+ records: (result[CRM_NS.record] ?? []).map(rowToRecord),
6266
+ total: candidateCount
6267
+ };
6268
+ }
6269
+
6185
6270
  // src/worker-routes-network-records-follower.ts
6186
6271
  var NETWORK_MUTATION_META = "chapterNetworkMutation";
6187
6272
  function mutationFingerprint(input) {
@@ -6208,9 +6293,9 @@ function mutationError(error) {
6208
6293
  }
6209
6294
  async function priorMutation(db, id) {
6210
6295
  const result = await db.query({
6211
- [CRM_NS.activity]: { $: { where: { id }, limit: 1 } }
6296
+ [CRM_NS2.activity]: { $: { where: { id }, limit: 1 } }
6212
6297
  });
6213
- const row = (result[CRM_NS.activity] ?? [])[0];
6298
+ const row = (result[CRM_NS2.activity] ?? [])[0];
6214
6299
  const meta = row?.meta;
6215
6300
  const receipt = meta?.[NETWORK_MUTATION_META];
6216
6301
  return typeof receipt?.fingerprint === "string" ? { fingerprint: receipt.fingerprint } : null;
@@ -6337,25 +6422,33 @@ var handleNetworkRecords = async (req, url, env, ctx) => {
6337
6422
  if (window.stage && !stages.some((stage) => stage.id === window.stage)) {
6338
6423
  return json({ error: "stage is not defined for this record type" }, 400);
6339
6424
  }
6340
- const page = await listRecords(
6341
- { crm: ctx.chapter.crm, db },
6342
- {
6425
+ let page;
6426
+ try {
6427
+ page = await listReaderRecords(
6428
+ db,
6343
6429
  type,
6344
- limit: window.limit,
6345
- offset: window.offset,
6346
- ...window.search ? { search: window.search } : {},
6347
- ...window.stage ? { stage: [window.stage] } : {},
6348
- sort: window.sort
6430
+ allowed2,
6431
+ typeDef.emailField,
6432
+ window
6433
+ );
6434
+ } catch (error) {
6435
+ if (error instanceof ReaderSearchPopulationError) {
6436
+ return json({
6437
+ error: "directory search requires a narrower stage filter",
6438
+ code: "search_population_too_large",
6439
+ maximum: 1e4
6440
+ }, 422);
6349
6441
  }
6350
- );
6442
+ throw error;
6443
+ }
6351
6444
  const response = {
6352
6445
  version: 1,
6353
6446
  site: { id: ctx.chapter.id, name: ctx.chapter.name },
6354
6447
  type,
6355
6448
  records: page.records.map((record4) => readerRecord(record4, reader, ctx.chapter.crm)),
6356
6449
  total: page.total,
6357
- limit: page.limit,
6358
- offset: page.offset,
6450
+ limit: window.limit,
6451
+ offset: window.offset,
6359
6452
  filters: {
6360
6453
  stages: stages.map((stage) => ({ id: stage.id, label: stage.label ?? stage.id })),
6361
6454
  sorts: [...NETWORK_RECORD_SORTS]
@@ -6593,7 +6686,7 @@ var handleAdminNetworkNotes = async (req, url, env, ctx) => {
6593
6686
  // src/worker-routes-network-shared-notes.ts
6594
6687
  import {
6595
6688
  addActivity,
6596
- CRM_NS as CRM_NS2,
6689
+ CRM_NS as CRM_NS3,
6597
6690
  getRecord as getRecord5
6598
6691
  } from "@odla-ai/crm";
6599
6692
  import { signFederatedRequest as signFederatedRequest5, verifyFederatedRequest as verifyFederatedRequest7 } from "@odla-ai/db";
@@ -6625,7 +6718,7 @@ function sharedNote(activity, sourceId) {
6625
6718
  }
6626
6719
  async function listSharedNotes(db, sourceId, recordId) {
6627
6720
  const result = await db.query({
6628
- [CRM_NS2.activity]: {
6721
+ [CRM_NS3.activity]: {
6629
6722
  $: {
6630
6723
  where: {
6631
6724
  and: [
@@ -6638,7 +6731,7 @@ async function listSharedNotes(db, sourceId, recordId) {
6638
6731
  }
6639
6732
  }
6640
6733
  });
6641
- return (result[CRM_NS2.activity] ?? []).flatMap((activity) => {
6734
+ return (result[CRM_NS3.activity] ?? []).flatMap((activity) => {
6642
6735
  const note = sharedNote(activity, sourceId);
6643
6736
  return note ? [note] : [];
6644
6737
  });