Preuve · code, extrait

backend/flexylead-admin-mcp.mjs

Dépôt jeremy-flexylead/flexylead-crm · commit ccbbaa07870e0f786729003dd6f8862e4bcc8256 · dossier de production synchronisé le 14 septembre 2026 à 08 h 46

Extrait tel qu'à ce commit : lignes 1439–1498, sur 1 498 (numérotation d'origine, 30 lignes de contexte autour de chaque citation) · fichier intégral sur GitHub (dépôt privé, accès requis)

Lignes citées par l'audit : 1469

60 lignes reproduites · ← retour à l'audit · catalogue des preuves

1439    if (page.length < 500) break;
1440  }
1441  return rows;
1442}
1443
1444function unwrapInvocation(value) {
1445  let body = value;
1446  for (let index = 0; index < 6 && body != null; index += 1) {
1447    if (typeof body === "string") { try { body = JSON.parse(body); } catch { return body; } continue; }
1448    if (typeof body === "object" && body.response !== undefined) { body = body.response; continue; }
1449    if (typeof body === "object" && body.body !== undefined) { body = body.body; continue; }
1450    break;
1451  }
1452  return body;
1453}
1454
1455function requireCentre(args) {
1456  const centreId = clean(args?.centre_id);
1457  if (!centreId) throw new Error("centre_id est requis.");
1458  return centreId;
1459}
1460
1461function requireDate(value, label) {
1462  const text = clean(value);
1463  if (!/^\d{4}-\d{2}-\d{2}$/.test(text) || Number.isNaN(new Date(`${text}T00:00:00Z`).getTime())) {
1464    throw new Error(`${label} doit être une date AAAA-MM-JJ valide.`);
1465  }
1466  return text;
1467}
1468
1469function todayISO() { return new Date().toISOString().slice(0, 10); }
1470function dateOnly(value) { const text = clean(value); return /^\d{4}-\d{2}-\d{2}/.test(text) ? text.slice(0, 10) : ""; }
1471function effectiveToday(rule) {
1472  const today = todayISO();
1473  const starts = dateOnly(rule.starts_at);
1474  const ends = dateOnly(rule.ends_at);
1475  return (!starts || starts <= today) && (!ends || ends >= today);
1476}
1477function activeRow(row) { return row?.active !== false && row?.active !== 0 && clean(row?.active).toLowerCase() !== "false" && clean(row?.active) !== "0"; }
1478function truthy(value) { return value === true || value === 1 || clean(value).toLowerCase() === "true" || clean(value) === "1"; }
1479function jsonObject(value) {
1480  let parsed = value;
1481  for (let index = 0; index < 2 && typeof parsed === "string"; index += 1) { try { parsed = JSON.parse(parsed); } catch { return {}; } }
1482  return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
1483}
1484function values(input) { return Array.isArray(input) ? input.map(clean).filter(Boolean) : []; }
1485function jsonArray(value) {
1486  let parsed = value;
1487  for (let index = 0; index < 2 && typeof parsed === "string"; index += 1) { try { parsed = JSON.parse(parsed); } catch { return []; } }
1488  return Array.isArray(parsed) ? parsed.map(clean).filter(Boolean) : [];
1489}
1490function clean(value) { return String(value ?? "").trim(); }
1491function response(statusCode, body) { return { statusCode, headers: { "Content-Type": "application/json" }, body }; }
1492function rpcResult(id, result) { return { jsonrpc: "2.0", id, result }; }
1493function rpcError(id, code, message) { return { jsonrpc: "2.0", id, error: { code, message } }; }
1494function toolResult(payload) { return { content: [{ type: "text", text: JSON.stringify(payload) }] }; }
1495function toolError(message) { return { content: [{ type: "text", text: JSON.stringify({ error: message }) }], isError: true }; }
1496
1497export const __testHooks = { normalizeFormula, evaluateFormula, unwrapInvocation, effectiveToday, requireInvocationSuccess, slugify, requireLocalId, generatePassword, normalizeMembers, forwardableActor };
1498