@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.
@@ -6095,9 +6095,39 @@ var IDENTIFIER = /^[a-z][a-zA-Z0-9_]*$/;
6095
6095
  var MAX_PAGE = 50;
6096
6096
  var MAX_OFFSET = 1e4;
6097
6097
  var MAX_SEARCH = 200;
6098
+ var MAX_STAGE = 100;
6098
6099
  var MAX_BODY = 32768;
6099
6100
  var RECORD_ID = /^[a-zA-Z0-9][a-zA-Z0-9:_-]{0,191}$/;
6100
6101
  var MUTATION_ID2 = /^[a-zA-Z0-9][a-zA-Z0-9:._-]{7,127}$/;
6102
+ var NETWORK_RECORD_SORTS = [
6103
+ { id: "updated_desc", label: "Recently updated" },
6104
+ { id: "updated_asc", label: "Least recently updated" },
6105
+ { id: "name_asc", label: "Name A\u2013Z" },
6106
+ { id: "name_desc", label: "Name Z\u2013A" }
6107
+ ];
6108
+ var NETWORK_RECORD_SORT_COLUMNS = {
6109
+ updated_desc: { field: "updatedAt", dir: "desc" },
6110
+ updated_asc: { field: "updatedAt", dir: "asc" },
6111
+ name_asc: { field: "name", dir: "asc" },
6112
+ name_desc: { field: "name", dir: "desc" }
6113
+ };
6114
+ function networkRecordWindow(params) {
6115
+ const limit = integer(params.get("limit"), 25, 1, MAX_PAGE);
6116
+ const offset = integer(params.get("offset"), 0, 0, MAX_OFFSET);
6117
+ const search = (params.get("search") ?? "").trim();
6118
+ const stage = (params.get("stage") ?? "").trim();
6119
+ const requestedSort = (params.get("sort") ?? "updated_desc").trim();
6120
+ const sortId = NETWORK_RECORD_SORTS.some((candidate) => candidate.id === requestedSort) ? requestedSort : null;
6121
+ if (limit == null || offset == null || search.length > MAX_SEARCH || stage.length > 0 && (stage.length > MAX_STAGE || /[\u0000-\u001f\u007f]/.test(stage)) || !sortId) return null;
6122
+ return {
6123
+ limit,
6124
+ offset,
6125
+ ...search ? { search } : {},
6126
+ ...stage ? { stage } : {},
6127
+ sortId,
6128
+ sort: NETWORK_RECORD_SORT_COLUMNS[sortId]
6129
+ };
6130
+ }
6101
6131
  function bodyTooLarge(body) {
6102
6132
  return new TextEncoder().encode(body).byteLength > MAX_BODY;
6103
6133
  }
@@ -6300,20 +6330,22 @@ var handleNetworkRecords = async (req, url, env, ctx) => {
6300
6330
  const type = url.searchParams.get("type") ?? "";
6301
6331
  const allowed2 = reader.fields[type];
6302
6332
  if (!IDENTIFIER.test(type) || !allowed2) return json({ error: "record type is not shared with this reader" }, 403);
6303
- const limit = integer(url.searchParams.get("limit"), 25, 1, MAX_PAGE);
6304
- const offset = integer(url.searchParams.get("offset"), 0, 0, MAX_OFFSET);
6305
- const search = (url.searchParams.get("search") ?? "").trim();
6306
- if (limit == null || offset == null || search.length > MAX_SEARCH) {
6307
- return json({ error: "invalid pagination or search parameters" }, 400);
6333
+ const window = networkRecordWindow(url.searchParams);
6334
+ if (!window) return json({ error: "invalid record query parameters" }, 400);
6335
+ const typeDef = ctx.chapter.crm.type(type);
6336
+ const stages = typeDef.pipeline?.stages ?? [];
6337
+ if (window.stage && !stages.some((stage) => stage.id === window.stage)) {
6338
+ return json({ error: "stage is not defined for this record type" }, 400);
6308
6339
  }
6309
6340
  const page = await listRecords(
6310
6341
  { crm: ctx.chapter.crm, db },
6311
6342
  {
6312
6343
  type,
6313
- limit,
6314
- offset,
6315
- ...search ? { search } : {},
6316
- sort: { field: "updatedAt", dir: "desc" }
6344
+ limit: window.limit,
6345
+ offset: window.offset,
6346
+ ...window.search ? { search: window.search } : {},
6347
+ ...window.stage ? { stage: [window.stage] } : {},
6348
+ sort: window.sort
6317
6349
  }
6318
6350
  );
6319
6351
  const response = {
@@ -6323,7 +6355,15 @@ var handleNetworkRecords = async (req, url, env, ctx) => {
6323
6355
  records: page.records.map((record4) => readerRecord(record4, reader, ctx.chapter.crm)),
6324
6356
  total: page.total,
6325
6357
  limit: page.limit,
6326
- offset: page.offset
6358
+ offset: page.offset,
6359
+ filters: {
6360
+ stages: stages.map((stage) => ({ id: stage.id, label: stage.label ?? stage.id })),
6361
+ sorts: [...NETWORK_RECORD_SORTS]
6362
+ },
6363
+ appliedQuery: {
6364
+ ...window.stage ? { stage: window.stage } : {},
6365
+ sort: window.sortId
6366
+ }
6327
6367
  };
6328
6368
  return json(response);
6329
6369
  };
@@ -6356,7 +6396,8 @@ async function readTargetRecords(db, env, ctx, target, query) {
6356
6396
  const upstream = body && "error" in body && typeof body.error === "string" ? body.error : void 0;
6357
6397
  return {
6358
6398
  ok: false,
6359
- error: upstream ?? (response.ok ? "invalid follower record response" : `follower returned ${response.status}`)
6399
+ error: upstream ?? (response.ok ? "invalid follower record response" : `follower returned ${response.status}`),
6400
+ ...response.status === 400 ? { status: 400 } : {}
6360
6401
  };
6361
6402
  }
6362
6403
  return {
@@ -6447,16 +6488,22 @@ var handleAdminNetworkRecords = async (req, url, env, ctx) => {
6447
6488
  const type = url.searchParams.get("type") ?? "";
6448
6489
  const target = targetFor2(ctx, targetId);
6449
6490
  if (!target || !IDENTIFIER.test(type)) return json({ error: "configured targetId and record type are required" }, 400);
6450
- const limit = integer(url.searchParams.get("limit"), 25, 1, MAX_PAGE);
6451
- const offset = integer(url.searchParams.get("offset"), 0, 0, MAX_OFFSET);
6452
- const search = (url.searchParams.get("search") ?? "").trim();
6453
- if (limit == null || offset == null || search.length > MAX_SEARCH) {
6454
- return json({ error: "invalid pagination or search parameters" }, 400);
6455
- }
6456
- const query = new URLSearchParams({ type, limit: String(limit), offset: String(offset) });
6457
- if (search) query.set("search", search);
6491
+ const window = networkRecordWindow(url.searchParams);
6492
+ if (!window) return json({ error: "invalid record query parameters" }, 400);
6493
+ const query = new URLSearchParams({
6494
+ type,
6495
+ limit: String(window.limit),
6496
+ offset: String(window.offset),
6497
+ sort: window.sortId
6498
+ });
6499
+ if (window.search) query.set("search", window.search);
6500
+ if (window.stage) query.set("stage", window.stage);
6458
6501
  const result = await readTargetRecords(got.db, env, ctx, target, query);
6459
- return result.ok ? json(result.page) : json({ error: result.error }, 502);
6502
+ if (!result.ok) return json({ error: result.error }, result.status ?? 502);
6503
+ if ((window.stage || url.searchParams.has("sort")) && (!result.page.appliedQuery || result.page.appliedQuery.sort !== window.sortId || result.page.appliedQuery.stage !== window.stage)) {
6504
+ return json({ error: "follower did not apply the requested record query" }, 502);
6505
+ }
6506
+ return json(result.page);
6460
6507
  };
6461
6508
 
6462
6509
  // src/worker-routes-network-notes.ts