@odla-ai/chapter 0.47.2 → 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.
- package/dist/worker/index.cjs +87 -35
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.js +87 -35
- package/dist/worker/index.js.map +1 -1
- package/package.json +1 -1
package/dist/worker/index.js
CHANGED
|
@@ -6190,40 +6190,80 @@ function restrictedRecord(record4, target) {
|
|
|
6190
6190
|
|
|
6191
6191
|
// src/worker-routes-network-records-search.ts
|
|
6192
6192
|
import { CRM_NS, rowToRecord } from "@odla-ai/crm";
|
|
6193
|
-
var
|
|
6194
|
-
|
|
6195
|
-
|
|
6196
|
-
|
|
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));
|
|
6197
6241
|
}
|
|
6198
6242
|
async function listReaderRecords(db, type, allowed2, emailField, window) {
|
|
6199
|
-
const
|
|
6200
|
-
|
|
6201
|
-
|
|
6202
|
-
|
|
6203
|
-
];
|
|
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;
|
|
6204
6247
|
if (window.search) {
|
|
6205
|
-
const
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6209
|
-
|
|
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
|
+
};
|
|
6210
6255
|
}
|
|
6211
|
-
const
|
|
6212
|
-
|
|
6213
|
-
|
|
6214
|
-
|
|
6215
|
-
|
|
6216
|
-
|
|
6217
|
-
|
|
6218
|
-
|
|
6219
|
-
offset: window.offset
|
|
6220
|
-
} }
|
|
6221
|
-
}),
|
|
6222
|
-
crmDb.aggregate(CRM_NS.record, { count: true }, { where })
|
|
6223
|
-
]);
|
|
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
|
+
});
|
|
6224
6264
|
return {
|
|
6225
6265
|
records: (result[CRM_NS.record] ?? []).map(rowToRecord),
|
|
6226
|
-
total:
|
|
6266
|
+
total: candidateCount
|
|
6227
6267
|
};
|
|
6228
6268
|
}
|
|
6229
6269
|
|
|
@@ -6382,13 +6422,25 @@ var handleNetworkRecords = async (req, url, env, ctx) => {
|
|
|
6382
6422
|
if (window.stage && !stages.some((stage) => stage.id === window.stage)) {
|
|
6383
6423
|
return json({ error: "stage is not defined for this record type" }, 400);
|
|
6384
6424
|
}
|
|
6385
|
-
|
|
6386
|
-
|
|
6387
|
-
|
|
6388
|
-
|
|
6389
|
-
|
|
6390
|
-
|
|
6391
|
-
|
|
6425
|
+
let page;
|
|
6426
|
+
try {
|
|
6427
|
+
page = await listReaderRecords(
|
|
6428
|
+
db,
|
|
6429
|
+
type,
|
|
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);
|
|
6441
|
+
}
|
|
6442
|
+
throw error;
|
|
6443
|
+
}
|
|
6392
6444
|
const response = {
|
|
6393
6445
|
version: 1,
|
|
6394
6446
|
site: { id: ctx.chapter.id, name: ctx.chapter.name },
|