@odla-ai/chapter 0.47.0 → 0.47.1

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.
@@ -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
  }
@@ -6330,20 +6360,22 @@ var handleNetworkRecords = async (req, url, env, ctx) => {
6330
6360
  const type = url.searchParams.get("type") ?? "";
6331
6361
  const allowed2 = reader.fields[type];
6332
6362
  if (!IDENTIFIER.test(type) || !allowed2) return json({ error: "record type is not shared with this reader" }, 403);
6333
- const limit = integer(url.searchParams.get("limit"), 25, 1, MAX_PAGE);
6334
- const offset = integer(url.searchParams.get("offset"), 0, 0, MAX_OFFSET);
6335
- const search = (url.searchParams.get("search") ?? "").trim();
6336
- if (limit == null || offset == null || search.length > MAX_SEARCH) {
6337
- return json({ error: "invalid pagination or search parameters" }, 400);
6363
+ const window = networkRecordWindow(url.searchParams);
6364
+ if (!window) return json({ error: "invalid record query parameters" }, 400);
6365
+ const typeDef = ctx.chapter.crm.type(type);
6366
+ const stages = typeDef.pipeline?.stages ?? [];
6367
+ if (window.stage && !stages.some((stage) => stage.id === window.stage)) {
6368
+ return json({ error: "stage is not defined for this record type" }, 400);
6338
6369
  }
6339
6370
  const page = await (0, import_crm9.listRecords)(
6340
6371
  { crm: ctx.chapter.crm, db },
6341
6372
  {
6342
6373
  type,
6343
- limit,
6344
- offset,
6345
- ...search ? { search } : {},
6346
- sort: { field: "updatedAt", dir: "desc" }
6374
+ limit: window.limit,
6375
+ offset: window.offset,
6376
+ ...window.search ? { search: window.search } : {},
6377
+ ...window.stage ? { stage: [window.stage] } : {},
6378
+ sort: window.sort
6347
6379
  }
6348
6380
  );
6349
6381
  const response = {
@@ -6353,7 +6385,15 @@ var handleNetworkRecords = async (req, url, env, ctx) => {
6353
6385
  records: page.records.map((record4) => readerRecord(record4, reader, ctx.chapter.crm)),
6354
6386
  total: page.total,
6355
6387
  limit: page.limit,
6356
- offset: page.offset
6388
+ offset: page.offset,
6389
+ filters: {
6390
+ stages: stages.map((stage) => ({ id: stage.id, label: stage.label ?? stage.id })),
6391
+ sorts: [...NETWORK_RECORD_SORTS]
6392
+ },
6393
+ appliedQuery: {
6394
+ ...window.stage ? { stage: window.stage } : {},
6395
+ sort: window.sortId
6396
+ }
6357
6397
  };
6358
6398
  return json(response);
6359
6399
  };
@@ -6386,7 +6426,8 @@ async function readTargetRecords(db, env, ctx, target, query) {
6386
6426
  const upstream = body && "error" in body && typeof body.error === "string" ? body.error : void 0;
6387
6427
  return {
6388
6428
  ok: false,
6389
- error: upstream ?? (response.ok ? "invalid follower record response" : `follower returned ${response.status}`)
6429
+ error: upstream ?? (response.ok ? "invalid follower record response" : `follower returned ${response.status}`),
6430
+ ...response.status === 400 ? { status: 400 } : {}
6390
6431
  };
6391
6432
  }
6392
6433
  return {
@@ -6477,16 +6518,22 @@ var handleAdminNetworkRecords = async (req, url, env, ctx) => {
6477
6518
  const type = url.searchParams.get("type") ?? "";
6478
6519
  const target = targetFor2(ctx, targetId);
6479
6520
  if (!target || !IDENTIFIER.test(type)) return json({ error: "configured targetId and record type are required" }, 400);
6480
- const limit = integer(url.searchParams.get("limit"), 25, 1, MAX_PAGE);
6481
- const offset = integer(url.searchParams.get("offset"), 0, 0, MAX_OFFSET);
6482
- const search = (url.searchParams.get("search") ?? "").trim();
6483
- if (limit == null || offset == null || search.length > MAX_SEARCH) {
6484
- return json({ error: "invalid pagination or search parameters" }, 400);
6485
- }
6486
- const query = new URLSearchParams({ type, limit: String(limit), offset: String(offset) });
6487
- if (search) query.set("search", search);
6521
+ const window = networkRecordWindow(url.searchParams);
6522
+ if (!window) return json({ error: "invalid record query parameters" }, 400);
6523
+ const query = new URLSearchParams({
6524
+ type,
6525
+ limit: String(window.limit),
6526
+ offset: String(window.offset),
6527
+ sort: window.sortId
6528
+ });
6529
+ if (window.search) query.set("search", window.search);
6530
+ if (window.stage) query.set("stage", window.stage);
6488
6531
  const result = await readTargetRecords(got.db, env, ctx, target, query);
6489
- return result.ok ? json(result.page) : json({ error: result.error }, 502);
6532
+ if (!result.ok) return json({ error: result.error }, result.status ?? 502);
6533
+ if ((window.stage || url.searchParams.has("sort")) && (!result.page.appliedQuery || result.page.appliedQuery.sort !== window.sortId || result.page.appliedQuery.stage !== window.stage)) {
6534
+ return json({ error: "follower did not apply the requested record query" }, 502);
6535
+ }
6536
+ return json(result.page);
6490
6537
  };
6491
6538
 
6492
6539
  // src/worker-routes-network-notes.ts