@odla-ai/chapter 0.47.0 → 0.47.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.
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -1
- package/dist/index.d.ts +23 -1
- package/dist/index.js.map +1 -1
- package/dist/worker/index.cjs +124 -42
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.js +121 -33
- package/dist/worker/index.js.map +1 -1
- package/package.json +1 -1
package/dist/worker/index.cjs
CHANGED
|
@@ -6117,7 +6117,7 @@ var handleAdminNetworkPush = async (req, url, env, ctx) => {
|
|
|
6117
6117
|
};
|
|
6118
6118
|
|
|
6119
6119
|
// src/worker-routes-network-records-follower.ts
|
|
6120
|
-
var
|
|
6120
|
+
var import_crm10 = require("@odla-ai/crm");
|
|
6121
6121
|
var import_db9 = require("@odla-ai/db");
|
|
6122
6122
|
|
|
6123
6123
|
// src/worker-routes-network-records-common.ts
|
|
@@ -6125,9 +6125,39 @@ var IDENTIFIER = /^[a-z][a-zA-Z0-9_]*$/;
|
|
|
6125
6125
|
var MAX_PAGE = 50;
|
|
6126
6126
|
var MAX_OFFSET = 1e4;
|
|
6127
6127
|
var MAX_SEARCH = 200;
|
|
6128
|
+
var MAX_STAGE = 100;
|
|
6128
6129
|
var MAX_BODY = 32768;
|
|
6129
6130
|
var RECORD_ID = /^[a-zA-Z0-9][a-zA-Z0-9:_-]{0,191}$/;
|
|
6130
6131
|
var MUTATION_ID2 = /^[a-zA-Z0-9][a-zA-Z0-9:._-]{7,127}$/;
|
|
6132
|
+
var NETWORK_RECORD_SORTS = [
|
|
6133
|
+
{ id: "updated_desc", label: "Recently updated" },
|
|
6134
|
+
{ id: "updated_asc", label: "Least recently updated" },
|
|
6135
|
+
{ id: "name_asc", label: "Name A\u2013Z" },
|
|
6136
|
+
{ id: "name_desc", label: "Name Z\u2013A" }
|
|
6137
|
+
];
|
|
6138
|
+
var NETWORK_RECORD_SORT_COLUMNS = {
|
|
6139
|
+
updated_desc: { field: "updatedAt", dir: "desc" },
|
|
6140
|
+
updated_asc: { field: "updatedAt", dir: "asc" },
|
|
6141
|
+
name_asc: { field: "name", dir: "asc" },
|
|
6142
|
+
name_desc: { field: "name", dir: "desc" }
|
|
6143
|
+
};
|
|
6144
|
+
function networkRecordWindow(params) {
|
|
6145
|
+
const limit = integer(params.get("limit"), 25, 1, MAX_PAGE);
|
|
6146
|
+
const offset = integer(params.get("offset"), 0, 0, MAX_OFFSET);
|
|
6147
|
+
const search = (params.get("search") ?? "").trim();
|
|
6148
|
+
const stage = (params.get("stage") ?? "").trim();
|
|
6149
|
+
const requestedSort = (params.get("sort") ?? "updated_desc").trim();
|
|
6150
|
+
const sortId = NETWORK_RECORD_SORTS.some((candidate) => candidate.id === requestedSort) ? requestedSort : null;
|
|
6151
|
+
if (limit == null || offset == null || search.length > MAX_SEARCH || stage.length > 0 && (stage.length > MAX_STAGE || /[\u0000-\u001f\u007f]/.test(stage)) || !sortId) return null;
|
|
6152
|
+
return {
|
|
6153
|
+
limit,
|
|
6154
|
+
offset,
|
|
6155
|
+
...search ? { search } : {},
|
|
6156
|
+
...stage ? { stage } : {},
|
|
6157
|
+
sortId,
|
|
6158
|
+
sort: NETWORK_RECORD_SORT_COLUMNS[sortId]
|
|
6159
|
+
};
|
|
6160
|
+
}
|
|
6131
6161
|
function bodyTooLarge(body) {
|
|
6132
6162
|
return new TextEncoder().encode(body).byteLength > MAX_BODY;
|
|
6133
6163
|
}
|
|
@@ -6182,6 +6212,45 @@ function restrictedRecord(record4, target) {
|
|
|
6182
6212
|
return { ...record4, ...edit ? { edit } : { edit: void 0 } };
|
|
6183
6213
|
}
|
|
6184
6214
|
|
|
6215
|
+
// src/worker-routes-network-records-search.ts
|
|
6216
|
+
var import_crm9 = require("@odla-ai/crm");
|
|
6217
|
+
var MAX_SAFE_LIKE_BYTES = 50;
|
|
6218
|
+
function safePrefix(value) {
|
|
6219
|
+
const prefixed = `${value}%`;
|
|
6220
|
+
return value.includes("%") || value.includes("_") || new TextEncoder().encode(prefixed).byteLength > MAX_SAFE_LIKE_BYTES ? value : { $like: prefixed };
|
|
6221
|
+
}
|
|
6222
|
+
async function listReaderRecords(db, type, allowed2, emailField, window) {
|
|
6223
|
+
const clauses = [
|
|
6224
|
+
{ type },
|
|
6225
|
+
{ archived: false },
|
|
6226
|
+
...window.stage ? [{ stage: window.stage }] : []
|
|
6227
|
+
];
|
|
6228
|
+
if (window.search) {
|
|
6229
|
+
const visible = [{ name: safePrefix(window.search) }];
|
|
6230
|
+
if (emailField && allowed2.includes(emailField)) {
|
|
6231
|
+
visible.push({ primaryEmail: safePrefix(window.search.toLowerCase()) });
|
|
6232
|
+
}
|
|
6233
|
+
clauses.push({ or: visible });
|
|
6234
|
+
}
|
|
6235
|
+
const where = { and: clauses };
|
|
6236
|
+
const crmDb = db;
|
|
6237
|
+
const [result, aggregate] = await Promise.all([
|
|
6238
|
+
crmDb.query({
|
|
6239
|
+
[import_crm9.CRM_NS.record]: { $: {
|
|
6240
|
+
where,
|
|
6241
|
+
order: { [window.sort.field]: window.sort.dir },
|
|
6242
|
+
limit: window.limit,
|
|
6243
|
+
offset: window.offset
|
|
6244
|
+
} }
|
|
6245
|
+
}),
|
|
6246
|
+
crmDb.aggregate(import_crm9.CRM_NS.record, { count: true }, { where })
|
|
6247
|
+
]);
|
|
6248
|
+
return {
|
|
6249
|
+
records: (result[import_crm9.CRM_NS.record] ?? []).map(import_crm9.rowToRecord),
|
|
6250
|
+
total: aggregate.count ?? 0
|
|
6251
|
+
};
|
|
6252
|
+
}
|
|
6253
|
+
|
|
6185
6254
|
// src/worker-routes-network-records-follower.ts
|
|
6186
6255
|
var NETWORK_MUTATION_META = "chapterNetworkMutation";
|
|
6187
6256
|
function mutationFingerprint(input) {
|
|
@@ -6201,16 +6270,16 @@ function mutationError(error) {
|
|
|
6201
6270
|
if (error?.code === "transact_guard_failed") {
|
|
6202
6271
|
return json({ error: "record changed since it was reviewed", code: "stale_revision" }, 409);
|
|
6203
6272
|
}
|
|
6204
|
-
if (error instanceof
|
|
6273
|
+
if (error instanceof import_crm10.CrmInputError) {
|
|
6205
6274
|
return json({ error: error.message, code: "invalid_transition_or_fields", ...error.fields ? { fields: error.fields } : {} }, 422);
|
|
6206
6275
|
}
|
|
6207
6276
|
return json({ error: "follower mutation failed", code: "mutation_failed" }, 500);
|
|
6208
6277
|
}
|
|
6209
6278
|
async function priorMutation(db, id) {
|
|
6210
6279
|
const result = await db.query({
|
|
6211
|
-
[
|
|
6280
|
+
[import_crm10.CRM_NS.activity]: { $: { where: { id }, limit: 1 } }
|
|
6212
6281
|
});
|
|
6213
|
-
const row = (result[
|
|
6282
|
+
const row = (result[import_crm10.CRM_NS.activity] ?? [])[0];
|
|
6214
6283
|
const meta = row?.meta;
|
|
6215
6284
|
const receipt = meta?.[NETWORK_MUTATION_META];
|
|
6216
6285
|
return typeof receipt?.fingerprint === "string" ? { fingerprint: receipt.fingerprint } : null;
|
|
@@ -6264,7 +6333,7 @@ var handleNetworkRecords = async (req, url, env, ctx) => {
|
|
|
6264
6333
|
if (prior.fingerprint !== fingerprint) {
|
|
6265
6334
|
return json({ error: "mutationId was already used for different input", code: "mutation_conflict" }, 409);
|
|
6266
6335
|
}
|
|
6267
|
-
const replayed = await (0,
|
|
6336
|
+
const replayed = await (0, import_crm10.getRecord)({ crm: ctx.chapter.crm, db }, input.recordId);
|
|
6268
6337
|
if (!replayed || replayed.type !== input.type) return json({ error: "record not found", code: "not_found" }, 404);
|
|
6269
6338
|
const result = {
|
|
6270
6339
|
version: 1,
|
|
@@ -6274,7 +6343,7 @@ var handleNetworkRecords = async (req, url, env, ctx) => {
|
|
|
6274
6343
|
};
|
|
6275
6344
|
return json(result);
|
|
6276
6345
|
}
|
|
6277
|
-
const record4 = await (0,
|
|
6346
|
+
const record4 = await (0, import_crm10.getRecord)({ crm: ctx.chapter.crm, db }, input.recordId);
|
|
6278
6347
|
if (!record4 || record4.type !== input.type) return json({ error: "record not found", code: "not_found" }, 404);
|
|
6279
6348
|
if (record4.updatedAt !== input.expectedRevision) {
|
|
6280
6349
|
return json({ error: "record changed since it was reviewed", code: "stale_revision" }, 409);
|
|
@@ -6295,7 +6364,7 @@ var handleNetworkRecords = async (req, url, env, ctx) => {
|
|
|
6295
6364
|
};
|
|
6296
6365
|
try {
|
|
6297
6366
|
const deps2 = { crm: ctx.chapter.crm, db };
|
|
6298
|
-
const updated = hasFields ? await (0,
|
|
6367
|
+
const updated = hasFields ? await (0, import_crm10.updateRecord)(deps2, {
|
|
6299
6368
|
id: record4.id,
|
|
6300
6369
|
input: fields,
|
|
6301
6370
|
mutationId: activityId,
|
|
@@ -6306,7 +6375,7 @@ var handleNetworkRecords = async (req, url, env, ctx) => {
|
|
|
6306
6375
|
authorId: `network:${verified.sender}`,
|
|
6307
6376
|
meta: auditMeta
|
|
6308
6377
|
}
|
|
6309
|
-
}) : await (0,
|
|
6378
|
+
}) : await (0, import_crm10.setStage)(deps2, {
|
|
6310
6379
|
id: record4.id,
|
|
6311
6380
|
to: input.stage,
|
|
6312
6381
|
authorId: `network:${verified.sender}`,
|
|
@@ -6330,21 +6399,19 @@ var handleNetworkRecords = async (req, url, env, ctx) => {
|
|
|
6330
6399
|
const type = url.searchParams.get("type") ?? "";
|
|
6331
6400
|
const allowed2 = reader.fields[type];
|
|
6332
6401
|
if (!IDENTIFIER.test(type) || !allowed2) return json({ error: "record type is not shared with this reader" }, 403);
|
|
6333
|
-
const
|
|
6334
|
-
|
|
6335
|
-
const
|
|
6336
|
-
|
|
6337
|
-
|
|
6338
|
-
|
|
6339
|
-
|
|
6340
|
-
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
|
|
6344
|
-
|
|
6345
|
-
|
|
6346
|
-
sort: { field: "updatedAt", dir: "desc" }
|
|
6347
|
-
}
|
|
6402
|
+
const window = networkRecordWindow(url.searchParams);
|
|
6403
|
+
if (!window) return json({ error: "invalid record query parameters" }, 400);
|
|
6404
|
+
const typeDef = ctx.chapter.crm.type(type);
|
|
6405
|
+
const stages = typeDef.pipeline?.stages ?? [];
|
|
6406
|
+
if (window.stage && !stages.some((stage) => stage.id === window.stage)) {
|
|
6407
|
+
return json({ error: "stage is not defined for this record type" }, 400);
|
|
6408
|
+
}
|
|
6409
|
+
const page = await listReaderRecords(
|
|
6410
|
+
db,
|
|
6411
|
+
type,
|
|
6412
|
+
allowed2,
|
|
6413
|
+
typeDef.emailField,
|
|
6414
|
+
window
|
|
6348
6415
|
);
|
|
6349
6416
|
const response = {
|
|
6350
6417
|
version: 1,
|
|
@@ -6352,8 +6419,16 @@ var handleNetworkRecords = async (req, url, env, ctx) => {
|
|
|
6352
6419
|
type,
|
|
6353
6420
|
records: page.records.map((record4) => readerRecord(record4, reader, ctx.chapter.crm)),
|
|
6354
6421
|
total: page.total,
|
|
6355
|
-
limit:
|
|
6356
|
-
offset:
|
|
6422
|
+
limit: window.limit,
|
|
6423
|
+
offset: window.offset,
|
|
6424
|
+
filters: {
|
|
6425
|
+
stages: stages.map((stage) => ({ id: stage.id, label: stage.label ?? stage.id })),
|
|
6426
|
+
sorts: [...NETWORK_RECORD_SORTS]
|
|
6427
|
+
},
|
|
6428
|
+
appliedQuery: {
|
|
6429
|
+
...window.stage ? { stage: window.stage } : {},
|
|
6430
|
+
sort: window.sortId
|
|
6431
|
+
}
|
|
6357
6432
|
};
|
|
6358
6433
|
return json(response);
|
|
6359
6434
|
};
|
|
@@ -6386,7 +6461,8 @@ async function readTargetRecords(db, env, ctx, target, query) {
|
|
|
6386
6461
|
const upstream = body && "error" in body && typeof body.error === "string" ? body.error : void 0;
|
|
6387
6462
|
return {
|
|
6388
6463
|
ok: false,
|
|
6389
|
-
error: upstream ?? (response.ok ? "invalid follower record response" : `follower returned ${response.status}`)
|
|
6464
|
+
error: upstream ?? (response.ok ? "invalid follower record response" : `follower returned ${response.status}`),
|
|
6465
|
+
...response.status === 400 ? { status: 400 } : {}
|
|
6390
6466
|
};
|
|
6391
6467
|
}
|
|
6392
6468
|
return {
|
|
@@ -6477,16 +6553,22 @@ var handleAdminNetworkRecords = async (req, url, env, ctx) => {
|
|
|
6477
6553
|
const type = url.searchParams.get("type") ?? "";
|
|
6478
6554
|
const target = targetFor2(ctx, targetId);
|
|
6479
6555
|
if (!target || !IDENTIFIER.test(type)) return json({ error: "configured targetId and record type are required" }, 400);
|
|
6480
|
-
const
|
|
6481
|
-
|
|
6482
|
-
const
|
|
6483
|
-
|
|
6484
|
-
|
|
6485
|
-
|
|
6486
|
-
|
|
6487
|
-
|
|
6556
|
+
const window = networkRecordWindow(url.searchParams);
|
|
6557
|
+
if (!window) return json({ error: "invalid record query parameters" }, 400);
|
|
6558
|
+
const query = new URLSearchParams({
|
|
6559
|
+
type,
|
|
6560
|
+
limit: String(window.limit),
|
|
6561
|
+
offset: String(window.offset),
|
|
6562
|
+
sort: window.sortId
|
|
6563
|
+
});
|
|
6564
|
+
if (window.search) query.set("search", window.search);
|
|
6565
|
+
if (window.stage) query.set("stage", window.stage);
|
|
6488
6566
|
const result = await readTargetRecords(got.db, env, ctx, target, query);
|
|
6489
|
-
|
|
6567
|
+
if (!result.ok) return json({ error: result.error }, result.status ?? 502);
|
|
6568
|
+
if ((window.stage || url.searchParams.has("sort")) && (!result.page.appliedQuery || result.page.appliedQuery.sort !== window.sortId || result.page.appliedQuery.stage !== window.stage)) {
|
|
6569
|
+
return json({ error: "follower did not apply the requested record query" }, 502);
|
|
6570
|
+
}
|
|
6571
|
+
return json(result.page);
|
|
6490
6572
|
};
|
|
6491
6573
|
|
|
6492
6574
|
// src/worker-routes-network-notes.ts
|
|
@@ -6574,7 +6656,7 @@ var handleAdminNetworkNotes = async (req, url, env, ctx) => {
|
|
|
6574
6656
|
};
|
|
6575
6657
|
|
|
6576
6658
|
// src/worker-routes-network-shared-notes.ts
|
|
6577
|
-
var
|
|
6659
|
+
var import_crm11 = require("@odla-ai/crm");
|
|
6578
6660
|
var import_db11 = require("@odla-ai/db");
|
|
6579
6661
|
var IDENTIFIER3 = /^[a-z][a-zA-Z0-9_]*$/;
|
|
6580
6662
|
var RECORD_ID3 = /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,255}$/;
|
|
@@ -6604,7 +6686,7 @@ function sharedNote(activity, sourceId) {
|
|
|
6604
6686
|
}
|
|
6605
6687
|
async function listSharedNotes(db, sourceId, recordId) {
|
|
6606
6688
|
const result = await db.query({
|
|
6607
|
-
[
|
|
6689
|
+
[import_crm11.CRM_NS.activity]: {
|
|
6608
6690
|
$: {
|
|
6609
6691
|
where: {
|
|
6610
6692
|
and: [
|
|
@@ -6617,7 +6699,7 @@ async function listSharedNotes(db, sourceId, recordId) {
|
|
|
6617
6699
|
}
|
|
6618
6700
|
}
|
|
6619
6701
|
});
|
|
6620
|
-
return (result[
|
|
6702
|
+
return (result[import_crm11.CRM_NS.activity] ?? []).flatMap((activity) => {
|
|
6621
6703
|
const note = sharedNote(activity, sourceId);
|
|
6622
6704
|
return note ? [note] : [];
|
|
6623
6705
|
});
|
|
@@ -6645,7 +6727,7 @@ var handleNetworkSharedNotes = async (req, url, env, ctx) => {
|
|
|
6645
6727
|
if (!IDENTIFIER3.test(recordType) || !RECORD_ID3.test(recordId) || !reader?.sharedNotes.includes(recordType)) {
|
|
6646
6728
|
return json({ error: "shared notes are not enabled for this reader and record type" }, 403);
|
|
6647
6729
|
}
|
|
6648
|
-
const record4 = await (0,
|
|
6730
|
+
const record4 = await (0, import_crm11.getRecord)({ crm: ctx.chapter.crm, db }, recordId);
|
|
6649
6731
|
if (!record4 || record4.type !== recordType) return json({ error: "record not found" }, 404);
|
|
6650
6732
|
if (req.method === "GET") {
|
|
6651
6733
|
return json({
|
|
@@ -6667,7 +6749,7 @@ var handleNetworkSharedNotes = async (req, url, env, ctx) => {
|
|
|
6667
6749
|
}
|
|
6668
6750
|
const createdAt = Date.now();
|
|
6669
6751
|
const id = `network:${verified.sender}:${mutationId}`;
|
|
6670
|
-
const result = await (0,
|
|
6752
|
+
const result = await (0, import_crm11.addActivity)(
|
|
6671
6753
|
{
|
|
6672
6754
|
crm: ctx.chapter.crm,
|
|
6673
6755
|
db,
|
|
@@ -8240,7 +8322,7 @@ var handleAdminNetworkCommercialReadModel = async (req, url, env, ctx) => {
|
|
|
8240
8322
|
};
|
|
8241
8323
|
|
|
8242
8324
|
// src/worker-routes-formation.ts
|
|
8243
|
-
var
|
|
8325
|
+
var import_crm12 = require("@odla-ai/crm");
|
|
8244
8326
|
var SUBMISSION = /^[A-Za-z0-9_-]{8,128}$/;
|
|
8245
8327
|
var handleFormation = async (req, url, env, ctx) => {
|
|
8246
8328
|
const formation = ctx.chapter.formation;
|
|
@@ -8292,7 +8374,7 @@ var handleFormation = async (req, url, env, ctx) => {
|
|
|
8292
8374
|
const db = ctx.makeDb(env);
|
|
8293
8375
|
const id = submissionId ? await applicationIdForSubmission(`formation:${ctx.chapter.id}:${submissionId}`) : crypto.randomUUID();
|
|
8294
8376
|
try {
|
|
8295
|
-
const created = await (0,
|
|
8377
|
+
const created = await (0, import_crm12.createRecord)(
|
|
8296
8378
|
{
|
|
8297
8379
|
crm: ctx.chapter.crm,
|
|
8298
8380
|
db,
|