clivly 0.10.8 → 0.11.0-next.0

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.
Files changed (106) hide show
  1. package/README.md +31 -1
  2. package/{adapter-1TN0zohk.d.cts → adapter-BQl13eS3.d.mts} +1 -1
  3. package/{adapter-uxirB88i.d.mts → adapter-CA8bA6o-.d.cts} +1 -1
  4. package/{auth-adapter-CXTiW3Mb.d.cts → auth-adapter-B4vVyUiX.d.mts} +1 -1
  5. package/{auth-adapter-DSuYwv1c.d.mts → auth-adapter-Cvc_sOpY.d.cts} +1 -1
  6. package/auth-better-auth.d.cts +1 -1
  7. package/auth-better-auth.d.mts +1 -1
  8. package/auth-clerk.d.cts +1 -1
  9. package/auth-clerk.d.mts +1 -1
  10. package/auth-workos.d.cts +1 -1
  11. package/auth-workos.d.mts +1 -1
  12. package/core-adapter.d.cts +1 -1
  13. package/core-adapter.d.mts +1 -1
  14. package/core-auth-adapter.d.cts +1 -1
  15. package/core-auth-adapter.d.mts +1 -1
  16. package/core-drizzle.d.cts +1 -1
  17. package/core-drizzle.d.mts +1 -1
  18. package/core-entity-config.cjs +1 -1
  19. package/core-entity-config.d.cts +1 -1
  20. package/core-entity-config.d.mts +1 -1
  21. package/core-entity-config.mjs +1 -1
  22. package/core-entity-heuristics.d.cts +1 -1
  23. package/core-entity-heuristics.d.mts +1 -1
  24. package/core-mapping-form.d.cts +1 -1
  25. package/core-mapping-form.d.mts +1 -1
  26. package/core-mappings-config.cjs +1 -1
  27. package/core-mappings-config.d.cts +1 -1
  28. package/core-mappings-config.d.mts +1 -1
  29. package/core-mappings-config.mjs +1 -1
  30. package/core-sync-engine.d.cts +1 -1
  31. package/core-sync-engine.d.mts +1 -1
  32. package/core-types.d.cts +1 -1
  33. package/core-types.d.mts +1 -1
  34. package/core-view-compiler.d.cts +1 -1
  35. package/core-view-compiler.d.mts +1 -1
  36. package/core.cjs +2 -2
  37. package/core.d.cts +9 -9
  38. package/core.d.mts +9 -9
  39. package/core.mjs +2 -2
  40. package/{drizzle-SD8WCvYj.d.cts → drizzle-C_0pIDpx.d.mts} +1 -1
  41. package/{drizzle-axugRJ0u.d.mts → drizzle-D3DcaC2d.d.cts} +1 -1
  42. package/drizzle.d.cts +3 -3
  43. package/drizzle.d.mts +3 -3
  44. package/{entity-config-bC3DX9f1.mjs → entity-config-CO0TEwZC.mjs} +1 -0
  45. package/{entity-config-w8oMBWf8.d.cts → entity-config-CbS2DGmA.d.cts} +1 -1
  46. package/{entity-config-w8oMBWf8.d.mts → entity-config-CbS2DGmA.d.mts} +1 -1
  47. package/{entity-config-C2MfIDL2.cjs → entity-config-EZg9BtqD.cjs} +1 -0
  48. package/index.cjs +207 -14
  49. package/index.d.cts +2 -0
  50. package/index.d.mts +2 -0
  51. package/index.mjs +207 -14
  52. package/{internal-CHvZMvxC.d.cts → internal-B8cq-xP-.d.cts} +1 -1
  53. package/{internal-WximMGmH.d.mts → internal-DEV1NCQc.d.mts} +1 -1
  54. package/internal-adapter.d.cts +1 -1
  55. package/internal-adapter.d.mts +1 -1
  56. package/internal-auth-adapter.d.cts +1 -1
  57. package/internal-auth-adapter.d.mts +1 -1
  58. package/internal-drizzle.d.cts +1 -1
  59. package/internal-drizzle.d.mts +1 -1
  60. package/internal-entity-config.cjs +1 -1
  61. package/internal-entity-config.d.cts +1 -1
  62. package/internal-entity-config.d.mts +1 -1
  63. package/internal-entity-config.mjs +1 -1
  64. package/internal-entity-heuristics.cjs +35 -18
  65. package/internal-entity-heuristics.d.cts +1 -1
  66. package/internal-entity-heuristics.d.mts +1 -1
  67. package/internal-entity-heuristics.mjs +35 -18
  68. package/internal-mapping-form.d.cts +1 -1
  69. package/internal-mapping-form.d.mts +1 -1
  70. package/internal-mappings-config.cjs +1 -1
  71. package/internal-mappings-config.d.cts +1 -1
  72. package/internal-mappings-config.d.mts +1 -1
  73. package/internal-mappings-config.mjs +1 -1
  74. package/internal-sync-engine.d.cts +1 -1
  75. package/internal-sync-engine.d.mts +1 -1
  76. package/internal-types.d.cts +1 -1
  77. package/internal-types.d.mts +1 -1
  78. package/internal-view-compiler.d.cts +1 -1
  79. package/internal-view-compiler.d.mts +1 -1
  80. package/internal.cjs +2 -2
  81. package/internal.d.cts +9 -9
  82. package/internal.d.mts +9 -9
  83. package/internal.mjs +2 -2
  84. package/{mapping-form-BsTxv8w3.d.cts → mapping-form-BBgP1iEf.d.cts} +1 -1
  85. package/{mapping-form-OTFwr1sN.d.mts → mapping-form-B_vX_kZJ.d.mts} +1 -1
  86. package/{mappings-config-Dpv08-x4.cjs → mappings-config-BRP_Dev5.cjs} +1 -1
  87. package/{mappings-config-DbznSjTX.d.cts → mappings-config-BXdSUl-K.d.mts} +1 -1
  88. package/{mappings-config-SXu_zlmp.d.mts → mappings-config-Cvw7RDSb.d.cts} +1 -1
  89. package/{mappings-config-CRSL9dzw.mjs → mappings-config-fAfUoVuD.mjs} +1 -1
  90. package/{namespace-probe-BmxtnOfc.d.mts → namespace-probe-BWZ7f1fg.d.mts} +1 -1
  91. package/{namespace-probe-C3xMOqWE.d.cts → namespace-probe-DC4YsI6m.d.cts} +1 -1
  92. package/package.json +1 -1
  93. package/{sdk-C0DU5KDS.mjs → sdk-CsR4W9QQ.mjs} +71 -14
  94. package/{sdk-BAHcnhm7.cjs → sdk-UCBezLoT.cjs} +71 -14
  95. package/sdk.cjs +2 -2
  96. package/sdk.d.cts +2 -2
  97. package/sdk.d.mts +2 -2
  98. package/sdk.mjs +2 -2
  99. package/{sync-engine-CUKWXUv7.d.mts → sync-engine-BQRApDAG.d.mts} +2 -2
  100. package/{sync-engine-D2YlIHZB.d.cts → sync-engine-qxbrOEJX.d.cts} +2 -2
  101. package/{types-Cq1uHUKp.d.cts → types-S0N0hqOV.d.cts} +3 -0
  102. package/{types-Cq1uHUKp.d.mts → types-S0N0hqOV.d.mts} +3 -0
  103. package/{view-compiler-BO4CKamn.d.cts → view-compiler-CdnLqPQx.d.mts} +1 -1
  104. package/{view-compiler-iEwUOuba.d.mts → view-compiler-P4GHpOHt.d.cts} +1 -1
  105. package/vite.d.cts +1 -1
  106. package/vite.d.mts +1 -1
@@ -1,6 +1,6 @@
1
1
  import { i as projectionSchema } from "./projection-CSGJbZlZ.mjs";
2
- import { _ as validateProjection, s as customObjectDeclarations, w as primaryKeyColumns } from "./entity-config-bC3DX9f1.mjs";
3
- import { t as entitiesToMappings } from "./mappings-config-CRSL9dzw.mjs";
2
+ import { _ as validateProjection, s as customObjectDeclarations, w as primaryKeyColumns } from "./entity-config-CO0TEwZC.mjs";
3
+ import { t as entitiesToMappings } from "./mappings-config-fAfUoVuD.mjs";
4
4
  import { t as DRIZZLE_META } from "./drizzle-meta-D-5k3n0i.mjs";
5
5
  //#region ../sdk/src/deployment-identity.ts
6
6
  const MAX_DEPLOYMENT_ID_LENGTH = 128;
@@ -392,11 +392,15 @@ function errorMessage(error) {
392
392
  /**
393
393
  * The entities to ask about, in the exact terms the sync pushes them.
394
394
  *
395
- * `source` is the resolved `SyncSource.entity` — the value that arrives at
395
+ * `source` is whatever the push will actually send — the value that arrives at
396
396
  * `ingestPush` as `input.source` and is compared against `mapping.sourceTable`.
397
+ * Usually that is `SyncSource.entity`, but a projection pushes under its base
398
+ * table, so this asks `customPushSource` rather than reading `entity` directly.
399
+ *
397
400
  * It is NOT the entity config's own `source` field, which names a host table;
398
- * the two are routinely conflated, and using the wrong one would report every
399
- * correctly mapped workspace as unmapped.
401
+ * the two are routinely conflated, and using the wrong one reports a correctly
402
+ * mapped workspace as unmapped — which is exactly what happened to every
403
+ * projection until this used the push label.
400
404
  */
401
405
  function readinessEntitiesFromConfig(config) {
402
406
  const entities = [];
@@ -405,7 +409,7 @@ function readinessEntitiesFromConfig(config) {
405
409
  if (!resolved) continue;
406
410
  const objectTypeSlug = resolved.source.objectType;
407
411
  entities.push({
408
- source: resolved.source.entity,
412
+ source: customPushSource(config, resolved.source),
409
413
  concept: resolved.concept,
410
414
  ...resolved.concept === "custom" && objectTypeSlug ? { objectTypeSlug } : {}
411
415
  });
@@ -1468,8 +1472,18 @@ function jsonResponse$1(body, status) {
1468
1472
  function normalizeApiUrl(apiUrl) {
1469
1473
  return apiUrl.replace(TRAILING_SLASH$1, "");
1470
1474
  }
1471
- function resolveOrigin(req) {
1472
- const headerOrigin = req.headers.get("origin");
1475
+ /**
1476
+ * The origin *claimed* by the request, for reporting upstream.
1477
+ *
1478
+ * Falls back to the request URL so Clivly still receives an origin for a
1479
+ * non-browser caller. This value is reported, never trusted: only a real
1480
+ * `Origin` header is evidence of where a request came from, so the access
1481
+ * decision uses {@link trustedOrigin} instead. Keeping the two apart is the
1482
+ * point — conflating them let a caller that simply omitted the header be
1483
+ * judged against the server's own origin, which is usually in the allowlist.
1484
+ */
1485
+ function reportedOrigin(req) {
1486
+ const headerOrigin = trustedOrigin(req);
1473
1487
  if (headerOrigin) return headerOrigin;
1474
1488
  try {
1475
1489
  return new URL(req.url).origin;
@@ -1477,6 +1491,15 @@ function resolveOrigin(req) {
1477
1491
  return null;
1478
1492
  }
1479
1493
  }
1494
+ /**
1495
+ * The only origin worth an access decision: one the browser set itself.
1496
+ *
1497
+ * A missing header means "not a browser request we can vouch for", which is
1498
+ * untrusted — never the server's own origin.
1499
+ */
1500
+ function trustedOrigin(req) {
1501
+ return req.headers.get("origin");
1502
+ }
1480
1503
  function isChatSessionRequestBody(value) {
1481
1504
  if (typeof value !== "object" || value === null) return false;
1482
1505
  const body = value;
@@ -1487,6 +1510,17 @@ function isChatSessionRequestBody(value) {
1487
1510
  if (typeof visitor.email !== "string" || visitor.email.trim().length === 0) return false;
1488
1511
  return body.visitorToken === void 0 || body.visitorToken === null || typeof body.visitorToken === "string";
1489
1512
  }
1513
+ /**
1514
+ * Judge a request against the configured allowlist.
1515
+ *
1516
+ * `origin` must be a *trusted* origin (see {@link trustedOrigin}) — passing
1517
+ * the reported one would reinstate the bypass this guards against.
1518
+ *
1519
+ * An unset allowlist still admits every origin, because requiring it would
1520
+ * break every existing deployment on upgrade. The unset case is instead
1521
+ * warned about once at construction, so the permissive default is loud
1522
+ * rather than silent.
1523
+ */
1490
1524
  function originAllowed(origin, allowedOrigins) {
1491
1525
  if (!allowedOrigins || allowedOrigins.length === 0) return true;
1492
1526
  if (!origin) return false;
@@ -1494,6 +1528,7 @@ function originAllowed(origin, allowedOrigins) {
1494
1528
  }
1495
1529
  function createChatSessionHandler({ allowedOrigins, apiKey, apiUrl = DEFAULT_API_URL$1, fetchImpl = fetch, resolveUser }) {
1496
1530
  const sessionUrl = `${normalizeApiUrl(apiUrl)}/clivly/widget/session`;
1531
+ if (!allowedOrigins || allowedOrigins.length === 0) console.warn("[clivly] createChatSessionHandler has no allowedOrigins; every origin can open a chat session against this workspace. Pass allowedOrigins to restrict it.");
1497
1532
  return async (request) => {
1498
1533
  if (request.method !== "POST") return new Response(JSON.stringify({ error: "method_not_allowed" }), {
1499
1534
  status: 405,
@@ -1502,8 +1537,8 @@ function createChatSessionHandler({ allowedOrigins, apiKey, apiUrl = DEFAULT_API
1502
1537
  allow: "POST"
1503
1538
  }
1504
1539
  });
1505
- const origin = resolveOrigin(request);
1506
- if (!originAllowed(origin, allowedOrigins)) return jsonResponse$1({ error: "origin_not_allowed" }, 403);
1540
+ if (!originAllowed(trustedOrigin(request), allowedOrigins)) return jsonResponse$1({ error: "origin_not_allowed" }, 403);
1541
+ const origin = reportedOrigin(request);
1507
1542
  let body;
1508
1543
  try {
1509
1544
  body = await request.json();
@@ -1701,6 +1736,18 @@ var TriggerRequestError = class extends Error {
1701
1736
  function triggerErrorStatus(error) {
1702
1737
  return error instanceof TriggerRequestError ? error.status : 500;
1703
1738
  }
1739
+ const SENTENCE_END_RE = /[.!?]$/;
1740
+ async function readPushFailureMessage(response) {
1741
+ if (!response || typeof response.json !== "function") return null;
1742
+ try {
1743
+ const body = await response.json();
1744
+ if (typeof body !== "object" || body === null) return null;
1745
+ const { message } = body;
1746
+ return typeof message === "string" && message.trim().length > 0 ? message : null;
1747
+ } catch {
1748
+ return null;
1749
+ }
1750
+ }
1704
1751
  const SIGNATURE_TOLERANCE_SECONDS = 300;
1705
1752
  const SIGNATURE_HEADER_RE = /^t=(\d+),v1=([0-9a-f]+)$/;
1706
1753
  const SECONDS_PER_MS = 1e3;
@@ -1923,17 +1970,27 @@ function createClivlySDK(config) {
1923
1970
  }, response);
1924
1971
  return true;
1925
1972
  }
1926
- lastPushFailure ??= { status: response?.status ?? null };
1973
+ lastPushFailure ??= {
1974
+ status: response?.status ?? null,
1975
+ message: await readPushFailureMessage(response)
1976
+ };
1927
1977
  return false;
1928
1978
  };
1929
1979
  const describePushFailure = (entities) => {
1930
1980
  const base = `Clivly sync: push failed for ${entities.join(", ")}`;
1931
1981
  const status = lastPushFailure?.status ?? null;
1982
+ const serverMessage = lastPushFailure?.message ?? null;
1932
1983
  let cause;
1933
1984
  if (status === 401 || status === 403) cause = "the API key was rejected — check CLIVLY_API_KEY is set to a valid key for this org";
1934
1985
  else if (status === null) cause = `couldn't reach Clivly at ${apiUrl} — check network access and CLIVLY_API_URL`;
1986
+ else if (serverMessage) cause = `Clivly returned HTTP ${status}: ${serverMessage}`;
1935
1987
  else cause = `Clivly returned HTTP ${status}`;
1936
- return `${base}: ${cause}. Run \`clivly doctor\` to diagnose.`;
1988
+ const separator = SENTENCE_END_RE.test(cause) ? "" : ".";
1989
+ return `${base}: ${cause}${separator} Run \`clivly doctor\` to diagnose.`;
1990
+ };
1991
+ const pushFailureError = (message) => {
1992
+ const status = lastPushFailure?.status ?? null;
1993
+ return status !== null && status >= CLIENT_ERROR_FLOOR && status < RETRYABLE_STATUS_FLOOR ? new TriggerRequestError(status, message) : new Error(message);
1937
1994
  };
1938
1995
  const syncOne = async (source, path, mode, objectType, runId, batchSizeOverride, maxRowsOverride, persistCursor = true) => {
1939
1996
  const since = mode === "full" ? null : await cursorStore.get(source.entity) ?? null;
@@ -2023,7 +2080,7 @@ function createClivlySDK(config) {
2023
2080
  failed: true,
2024
2081
  error: message
2025
2082
  };
2026
- throw new Error(message);
2083
+ throw pushFailureError(message);
2027
2084
  }
2028
2085
  lastPush = { failed: false };
2029
2086
  };
@@ -2031,7 +2088,7 @@ function createClivlySDK(config) {
2031
2088
  const resolved = resolveSampleSource(config, entityKey);
2032
2089
  if (!resolved) throw new TriggerRequestError(400, `Clivly sync: no source is configured for entity "${entityKey}". The dashboard maps this table, but clivly.config declares no matching entity — add it to \`entities\`, or remove the mapping in Integrations → Mapping.`);
2033
2090
  lastPushFailure = null;
2034
- if (!await syncOne(resolved.source, resolved.path, "delta", resolved.objectType, runId, SAMPLE_BATCH_SIZE, SAMPLE_BATCH_SIZE, false)) throw new Error(describePushFailure([resolved.source.entity]));
2091
+ if (!await syncOne(resolved.source, resolved.path, "delta", resolved.objectType, runId, SAMPLE_BATCH_SIZE, SAMPLE_BATCH_SIZE, false)) throw pushFailureError(describePushFailure([resolved.source.entity]));
2035
2092
  };
2036
2093
  const describeSyncTrigger = () => ({
2037
2094
  configured: config.syncTrigger?.url,
@@ -1,6 +1,6 @@
1
1
  const require_projection = require("./projection-Dz6_i1-w.cjs");
2
- const require_entity_config = require("./entity-config-C2MfIDL2.cjs");
3
- const require_mappings_config = require("./mappings-config-Dpv08-x4.cjs");
2
+ const require_entity_config = require("./entity-config-EZg9BtqD.cjs");
3
+ const require_mappings_config = require("./mappings-config-BRP_Dev5.cjs");
4
4
  const require_drizzle_meta = require("./drizzle-meta-Bb1JWYqf.cjs");
5
5
  //#region ../sdk/src/deployment-identity.ts
6
6
  const MAX_DEPLOYMENT_ID_LENGTH = 128;
@@ -392,11 +392,15 @@ function errorMessage(error) {
392
392
  /**
393
393
  * The entities to ask about, in the exact terms the sync pushes them.
394
394
  *
395
- * `source` is the resolved `SyncSource.entity` — the value that arrives at
395
+ * `source` is whatever the push will actually send — the value that arrives at
396
396
  * `ingestPush` as `input.source` and is compared against `mapping.sourceTable`.
397
+ * Usually that is `SyncSource.entity`, but a projection pushes under its base
398
+ * table, so this asks `customPushSource` rather than reading `entity` directly.
399
+ *
397
400
  * It is NOT the entity config's own `source` field, which names a host table;
398
- * the two are routinely conflated, and using the wrong one would report every
399
- * correctly mapped workspace as unmapped.
401
+ * the two are routinely conflated, and using the wrong one reports a correctly
402
+ * mapped workspace as unmapped — which is exactly what happened to every
403
+ * projection until this used the push label.
400
404
  */
401
405
  function readinessEntitiesFromConfig(config) {
402
406
  const entities = [];
@@ -405,7 +409,7 @@ function readinessEntitiesFromConfig(config) {
405
409
  if (!resolved) continue;
406
410
  const objectTypeSlug = resolved.source.objectType;
407
411
  entities.push({
408
- source: resolved.source.entity,
412
+ source: customPushSource(config, resolved.source),
409
413
  concept: resolved.concept,
410
414
  ...resolved.concept === "custom" && objectTypeSlug ? { objectTypeSlug } : {}
411
415
  });
@@ -1468,8 +1472,18 @@ function jsonResponse$1(body, status) {
1468
1472
  function normalizeApiUrl(apiUrl) {
1469
1473
  return apiUrl.replace(TRAILING_SLASH$1, "");
1470
1474
  }
1471
- function resolveOrigin(req) {
1472
- const headerOrigin = req.headers.get("origin");
1475
+ /**
1476
+ * The origin *claimed* by the request, for reporting upstream.
1477
+ *
1478
+ * Falls back to the request URL so Clivly still receives an origin for a
1479
+ * non-browser caller. This value is reported, never trusted: only a real
1480
+ * `Origin` header is evidence of where a request came from, so the access
1481
+ * decision uses {@link trustedOrigin} instead. Keeping the two apart is the
1482
+ * point — conflating them let a caller that simply omitted the header be
1483
+ * judged against the server's own origin, which is usually in the allowlist.
1484
+ */
1485
+ function reportedOrigin(req) {
1486
+ const headerOrigin = trustedOrigin(req);
1473
1487
  if (headerOrigin) return headerOrigin;
1474
1488
  try {
1475
1489
  return new URL(req.url).origin;
@@ -1477,6 +1491,15 @@ function resolveOrigin(req) {
1477
1491
  return null;
1478
1492
  }
1479
1493
  }
1494
+ /**
1495
+ * The only origin worth an access decision: one the browser set itself.
1496
+ *
1497
+ * A missing header means "not a browser request we can vouch for", which is
1498
+ * untrusted — never the server's own origin.
1499
+ */
1500
+ function trustedOrigin(req) {
1501
+ return req.headers.get("origin");
1502
+ }
1480
1503
  function isChatSessionRequestBody(value) {
1481
1504
  if (typeof value !== "object" || value === null) return false;
1482
1505
  const body = value;
@@ -1487,6 +1510,17 @@ function isChatSessionRequestBody(value) {
1487
1510
  if (typeof visitor.email !== "string" || visitor.email.trim().length === 0) return false;
1488
1511
  return body.visitorToken === void 0 || body.visitorToken === null || typeof body.visitorToken === "string";
1489
1512
  }
1513
+ /**
1514
+ * Judge a request against the configured allowlist.
1515
+ *
1516
+ * `origin` must be a *trusted* origin (see {@link trustedOrigin}) — passing
1517
+ * the reported one would reinstate the bypass this guards against.
1518
+ *
1519
+ * An unset allowlist still admits every origin, because requiring it would
1520
+ * break every existing deployment on upgrade. The unset case is instead
1521
+ * warned about once at construction, so the permissive default is loud
1522
+ * rather than silent.
1523
+ */
1490
1524
  function originAllowed(origin, allowedOrigins) {
1491
1525
  if (!allowedOrigins || allowedOrigins.length === 0) return true;
1492
1526
  if (!origin) return false;
@@ -1494,6 +1528,7 @@ function originAllowed(origin, allowedOrigins) {
1494
1528
  }
1495
1529
  function createChatSessionHandler({ allowedOrigins, apiKey, apiUrl = DEFAULT_API_URL$1, fetchImpl = fetch, resolveUser }) {
1496
1530
  const sessionUrl = `${normalizeApiUrl(apiUrl)}/clivly/widget/session`;
1531
+ if (!allowedOrigins || allowedOrigins.length === 0) console.warn("[clivly] createChatSessionHandler has no allowedOrigins; every origin can open a chat session against this workspace. Pass allowedOrigins to restrict it.");
1497
1532
  return async (request) => {
1498
1533
  if (request.method !== "POST") return new Response(JSON.stringify({ error: "method_not_allowed" }), {
1499
1534
  status: 405,
@@ -1502,8 +1537,8 @@ function createChatSessionHandler({ allowedOrigins, apiKey, apiUrl = DEFAULT_API
1502
1537
  allow: "POST"
1503
1538
  }
1504
1539
  });
1505
- const origin = resolveOrigin(request);
1506
- if (!originAllowed(origin, allowedOrigins)) return jsonResponse$1({ error: "origin_not_allowed" }, 403);
1540
+ if (!originAllowed(trustedOrigin(request), allowedOrigins)) return jsonResponse$1({ error: "origin_not_allowed" }, 403);
1541
+ const origin = reportedOrigin(request);
1507
1542
  let body;
1508
1543
  try {
1509
1544
  body = await request.json();
@@ -1701,6 +1736,18 @@ var TriggerRequestError = class extends Error {
1701
1736
  function triggerErrorStatus(error) {
1702
1737
  return error instanceof TriggerRequestError ? error.status : 500;
1703
1738
  }
1739
+ const SENTENCE_END_RE = /[.!?]$/;
1740
+ async function readPushFailureMessage(response) {
1741
+ if (!response || typeof response.json !== "function") return null;
1742
+ try {
1743
+ const body = await response.json();
1744
+ if (typeof body !== "object" || body === null) return null;
1745
+ const { message } = body;
1746
+ return typeof message === "string" && message.trim().length > 0 ? message : null;
1747
+ } catch {
1748
+ return null;
1749
+ }
1750
+ }
1704
1751
  const SIGNATURE_TOLERANCE_SECONDS = 300;
1705
1752
  const SIGNATURE_HEADER_RE = /^t=(\d+),v1=([0-9a-f]+)$/;
1706
1753
  const SECONDS_PER_MS = 1e3;
@@ -1923,17 +1970,27 @@ function createClivlySDK(config) {
1923
1970
  }, response);
1924
1971
  return true;
1925
1972
  }
1926
- lastPushFailure ??= { status: response?.status ?? null };
1973
+ lastPushFailure ??= {
1974
+ status: response?.status ?? null,
1975
+ message: await readPushFailureMessage(response)
1976
+ };
1927
1977
  return false;
1928
1978
  };
1929
1979
  const describePushFailure = (entities) => {
1930
1980
  const base = `Clivly sync: push failed for ${entities.join(", ")}`;
1931
1981
  const status = lastPushFailure?.status ?? null;
1982
+ const serverMessage = lastPushFailure?.message ?? null;
1932
1983
  let cause;
1933
1984
  if (status === 401 || status === 403) cause = "the API key was rejected — check CLIVLY_API_KEY is set to a valid key for this org";
1934
1985
  else if (status === null) cause = `couldn't reach Clivly at ${apiUrl} — check network access and CLIVLY_API_URL`;
1986
+ else if (serverMessage) cause = `Clivly returned HTTP ${status}: ${serverMessage}`;
1935
1987
  else cause = `Clivly returned HTTP ${status}`;
1936
- return `${base}: ${cause}. Run \`clivly doctor\` to diagnose.`;
1988
+ const separator = SENTENCE_END_RE.test(cause) ? "" : ".";
1989
+ return `${base}: ${cause}${separator} Run \`clivly doctor\` to diagnose.`;
1990
+ };
1991
+ const pushFailureError = (message) => {
1992
+ const status = lastPushFailure?.status ?? null;
1993
+ return status !== null && status >= CLIENT_ERROR_FLOOR && status < RETRYABLE_STATUS_FLOOR ? new TriggerRequestError(status, message) : new Error(message);
1937
1994
  };
1938
1995
  const syncOne = async (source, path, mode, objectType, runId, batchSizeOverride, maxRowsOverride, persistCursor = true) => {
1939
1996
  const since = mode === "full" ? null : await cursorStore.get(source.entity) ?? null;
@@ -2023,7 +2080,7 @@ function createClivlySDK(config) {
2023
2080
  failed: true,
2024
2081
  error: message
2025
2082
  };
2026
- throw new Error(message);
2083
+ throw pushFailureError(message);
2027
2084
  }
2028
2085
  lastPush = { failed: false };
2029
2086
  };
@@ -2031,7 +2088,7 @@ function createClivlySDK(config) {
2031
2088
  const resolved = resolveSampleSource(config, entityKey);
2032
2089
  if (!resolved) throw new TriggerRequestError(400, `Clivly sync: no source is configured for entity "${entityKey}". The dashboard maps this table, but clivly.config declares no matching entity — add it to \`entities\`, or remove the mapping in Integrations → Mapping.`);
2033
2090
  lastPushFailure = null;
2034
- if (!await syncOne(resolved.source, resolved.path, "delta", resolved.objectType, runId, SAMPLE_BATCH_SIZE, SAMPLE_BATCH_SIZE, false)) throw new Error(describePushFailure([resolved.source.entity]));
2091
+ if (!await syncOne(resolved.source, resolved.path, "delta", resolved.objectType, runId, SAMPLE_BATCH_SIZE, SAMPLE_BATCH_SIZE, false)) throw pushFailureError(describePushFailure([resolved.source.entity]));
2035
2092
  };
2036
2093
  const describeSyncTrigger = () => ({
2037
2094
  configured: config.syncTrigger?.url,
package/sdk.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_entity_config = require("./entity-config-C2MfIDL2.cjs");
3
- const require_sdk = require("./sdk-BAHcnhm7.cjs");
2
+ const require_entity_config = require("./entity-config-EZg9BtqD.cjs");
3
+ const require_sdk = require("./sdk-UCBezLoT.cjs");
4
4
  exports.ClivlyAuthError = require_sdk.ClivlyAuthError;
5
5
  exports.ClivlyError = require_sdk.ClivlyError;
6
6
  exports.ClivlyNetworkError = require_sdk.ClivlyNetworkError;
package/sdk.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { C as customObjectDeclarations, M as validateEntitiesConfig, a as ClivlyEntitiesConfig, f as CustomObjectDeclaration, l as ConfigValidationError, m as DiscoveredSchemaTable, u as ConfigValidationResult, w as defineClivlyConfig } from "./entity-config-w8oMBWf8.cjs";
2
- import { _ as RemoteProjectionsDescription, a as ClivlyConnectResult, c as ClivlyUser, d as DiscoveredTable, f as DoctorCheck, g as EnsuredObjectTypeResult, h as EnsuredMappingResult, i as ClivlyConnectReason, l as CreateChatSessionHandlerOptions, m as EnsureSchemaResult, n as ChatSessionRequestBody, o as ClivlySDK, p as DoctorReport, r as ChatWidgetVisitor, s as ClivlySDKConfig, u as CursorStore, y as SyncTriggerDescription } from "./namespace-probe-C3xMOqWE.cjs";
1
+ import { C as customObjectDeclarations, M as validateEntitiesConfig, a as ClivlyEntitiesConfig, f as CustomObjectDeclaration, l as ConfigValidationError, m as DiscoveredSchemaTable, u as ConfigValidationResult, w as defineClivlyConfig } from "./entity-config-CbS2DGmA.cjs";
2
+ import { _ as RemoteProjectionsDescription, a as ClivlyConnectResult, c as ClivlyUser, d as DiscoveredTable, f as DoctorCheck, g as EnsuredObjectTypeResult, h as EnsuredMappingResult, i as ClivlyConnectReason, l as CreateChatSessionHandlerOptions, m as EnsureSchemaResult, n as ChatSessionRequestBody, o as ClivlySDK, p as DoctorReport, r as ChatWidgetVisitor, s as ClivlySDKConfig, u as CursorStore, y as SyncTriggerDescription } from "./namespace-probe-DC4YsI6m.cjs";
3
3
  //#region ../sdk/src/trigger-url.d.ts
4
4
  declare function isPlaceholderUrl(value: string): boolean;
5
5
  //#endregion
package/sdk.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { C as customObjectDeclarations, M as validateEntitiesConfig, a as ClivlyEntitiesConfig, f as CustomObjectDeclaration, l as ConfigValidationError, m as DiscoveredSchemaTable, u as ConfigValidationResult, w as defineClivlyConfig } from "./entity-config-w8oMBWf8.mjs";
2
- import { _ as RemoteProjectionsDescription, a as ClivlyConnectResult, c as ClivlyUser, d as DiscoveredTable, f as DoctorCheck, g as EnsuredObjectTypeResult, h as EnsuredMappingResult, i as ClivlyConnectReason, l as CreateChatSessionHandlerOptions, m as EnsureSchemaResult, n as ChatSessionRequestBody, o as ClivlySDK, p as DoctorReport, r as ChatWidgetVisitor, s as ClivlySDKConfig, u as CursorStore, y as SyncTriggerDescription } from "./namespace-probe-BmxtnOfc.mjs";
1
+ import { C as customObjectDeclarations, M as validateEntitiesConfig, a as ClivlyEntitiesConfig, f as CustomObjectDeclaration, l as ConfigValidationError, m as DiscoveredSchemaTable, u as ConfigValidationResult, w as defineClivlyConfig } from "./entity-config-CbS2DGmA.mjs";
2
+ import { _ as RemoteProjectionsDescription, a as ClivlyConnectResult, c as ClivlyUser, d as DiscoveredTable, f as DoctorCheck, g as EnsuredObjectTypeResult, h as EnsuredMappingResult, i as ClivlyConnectReason, l as CreateChatSessionHandlerOptions, m as EnsureSchemaResult, n as ChatSessionRequestBody, o as ClivlySDK, p as DoctorReport, r as ChatWidgetVisitor, s as ClivlySDKConfig, u as CursorStore, y as SyncTriggerDescription } from "./namespace-probe-BWZ7f1fg.mjs";
3
3
  //#region ../sdk/src/trigger-url.d.ts
4
4
  declare function isPlaceholderUrl(value: string): boolean;
5
5
  //#endregion
package/sdk.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import { c as defineClivlyConfig, g as validateEntitiesConfig, s as customObjectDeclarations } from "./entity-config-bC3DX9f1.mjs";
2
- import { a as ClivlyRateLimitError, i as ClivlyNetworkError, n as ClivlyAuthError, o as createChatSessionHandler, r as ClivlyError, s as isPlaceholderUrl, t as createClivlySDK } from "./sdk-C0DU5KDS.mjs";
1
+ import { c as defineClivlyConfig, g as validateEntitiesConfig, s as customObjectDeclarations } from "./entity-config-CO0TEwZC.mjs";
2
+ import { a as ClivlyRateLimitError, i as ClivlyNetworkError, n as ClivlyAuthError, o as createChatSessionHandler, r as ClivlyError, s as isPlaceholderUrl, t as createClivlySDK } from "./sdk-CsR4W9QQ.mjs";
3
3
  export { ClivlyAuthError, ClivlyError, ClivlyNetworkError, ClivlyRateLimitError, createChatSessionHandler, createClivlySDK, customObjectDeclarations, defineClivlyConfig, isPlaceholderUrl, validateEntitiesConfig };
@@ -1,5 +1,5 @@
1
- import { a as ClivlyEntitiesConfig } from "./entity-config-w8oMBWf8.mjs";
2
- import { n as CompiledView, t as CompileOptions } from "./view-compiler-iEwUOuba.mjs";
1
+ import { a as ClivlyEntitiesConfig } from "./entity-config-CbS2DGmA.mjs";
2
+ import { n as CompiledView, t as CompileOptions } from "./view-compiler-CdnLqPQx.mjs";
3
3
  //#region ../core/src/sync-engine.d.ts
4
4
  /**
5
5
  * Sync engine (Phase 3) — materializes the read-only entity views into Clivly's
@@ -1,5 +1,5 @@
1
- import { a as ClivlyEntitiesConfig } from "./entity-config-w8oMBWf8.cjs";
2
- import { n as CompiledView, t as CompileOptions } from "./view-compiler-BO4CKamn.cjs";
1
+ import { a as ClivlyEntitiesConfig } from "./entity-config-CbS2DGmA.cjs";
2
+ import { n as CompiledView, t as CompileOptions } from "./view-compiler-P4GHpOHt.cjs";
3
3
  //#region ../core/src/sync-engine.d.ts
4
4
  /**
5
5
  * Sync engine (Phase 3) — materializes the read-only entity views into Clivly's
@@ -39,6 +39,7 @@ interface Contact {
39
39
  lastActivityAt: Date | null;
40
40
  name: string;
41
41
  orgId: string;
42
+ phone: string | null;
42
43
  status: ContactStatus;
43
44
  title: string | null;
44
45
  }
@@ -108,6 +109,7 @@ interface CreateContactInput {
108
109
  companyId?: string;
109
110
  email: string;
110
111
  name: string;
112
+ phone?: string;
111
113
  status?: ContactStatus;
112
114
  title?: string;
113
115
  }
@@ -115,6 +117,7 @@ interface UpdateContactInput {
115
117
  companyId?: string | null;
116
118
  email?: string;
117
119
  name?: string;
120
+ phone?: string | null;
118
121
  status?: ContactStatus;
119
122
  title?: string | null;
120
123
  }
@@ -39,6 +39,7 @@ interface Contact {
39
39
  lastActivityAt: Date | null;
40
40
  name: string;
41
41
  orgId: string;
42
+ phone: string | null;
42
43
  status: ContactStatus;
43
44
  title: string | null;
44
45
  }
@@ -108,6 +109,7 @@ interface CreateContactInput {
108
109
  companyId?: string;
109
110
  email: string;
110
111
  name: string;
112
+ phone?: string;
111
113
  status?: ContactStatus;
112
114
  title?: string;
113
115
  }
@@ -115,6 +117,7 @@ interface UpdateContactInput {
115
117
  companyId?: string | null;
116
118
  email?: string;
117
119
  name?: string;
120
+ phone?: string | null;
118
121
  status?: ContactStatus;
119
122
  title?: string | null;
120
123
  }
@@ -1,4 +1,4 @@
1
- import { a as ClivlyEntitiesConfig } from "./entity-config-w8oMBWf8.cjs";
1
+ import { a as ClivlyEntitiesConfig } from "./entity-config-CbS2DGmA.mjs";
2
2
  //#region ../core/src/view-compiler.d.ts
3
3
  declare const DEFAULT_VIEW_PREFIX = "crm_src_";
4
4
  interface CompileOptions {
@@ -1,4 +1,4 @@
1
- import { a as ClivlyEntitiesConfig } from "./entity-config-w8oMBWf8.mjs";
1
+ import { a as ClivlyEntitiesConfig } from "./entity-config-CbS2DGmA.cjs";
2
2
  //#region ../core/src/view-compiler.d.ts
3
3
  declare const DEFAULT_VIEW_PREFIX = "crm_src_";
4
4
  interface CompileOptions {
package/vite.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { o as ClivlySDK } from "./namespace-probe-C3xMOqWE.cjs";
1
+ import { o as ClivlySDK } from "./namespace-probe-DC4YsI6m.cjs";
2
2
  import { Plugin } from "vite";
3
3
  //#region ../sdk/src/vite.d.ts
4
4
  interface ClivlyViteOptions {
package/vite.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { o as ClivlySDK } from "./namespace-probe-BmxtnOfc.mjs";
1
+ import { o as ClivlySDK } from "./namespace-probe-BWZ7f1fg.mjs";
2
2
  import { Plugin } from "vite";
3
3
  //#region ../sdk/src/vite.d.ts
4
4
  interface ClivlyViteOptions {