@clivly/sdk 0.9.5 → 0.9.7

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 CHANGED
@@ -3,6 +3,7 @@ const require_drizzle_meta = require("./drizzle-meta-CdCs30q3.cjs");
3
3
  const require_chat_session = require("./chat-session.cjs");
4
4
  let _clivly_core_entity_config = require("@clivly/core/entity-config");
5
5
  let _clivly_core_mappings_config = require("@clivly/core/mappings-config");
6
+ let _clivly_core = require("@clivly/core");
6
7
  //#region src/deployment-identity.ts
7
8
  const MAX_DEPLOYMENT_ID_LENGTH = 128;
8
9
  const PLATFORM_DEPLOYMENT_KEYS = [
@@ -574,12 +575,21 @@ async function runDoctor(args) {
574
575
  ok: entityCount > 0,
575
576
  detail: entityCount > 0 ? `${entityCount} entit${entityCount === 1 ? "y" : "ies"} configured.` : "No `entities` config — pass defineClivlyConfig(...) as `entities`."
576
577
  });
578
+ const sourcesDetail = (count, deferred) => {
579
+ if (deferred) return "Deferred — the coverage check below names the entity and the source key it expected.";
580
+ return count === 0 ? "No `source.contacts` — nothing to sync." : `${count} source(s) to mirror.`;
581
+ };
582
+ const coverage = checkConceptCoverage(config);
583
+ const coverageOwnsIt = !(coverage.ok || coverage.skipped);
584
+ const noSources = sources.length === 0;
585
+ const deferred = noSources && coverageOwnsIt;
577
586
  checks.push({
578
587
  name: "Sources configured",
579
- ok: sources.length > 0,
580
- detail: sources.length > 0 ? `${sources.length} source(s) to mirror.` : "No `source.contacts` — nothing to sync."
588
+ ok: !noSources,
589
+ skipped: deferred,
590
+ detail: sourcesDetail(sources.length, deferred)
581
591
  });
582
- checks.push(checkConceptCoverage(config));
592
+ checks.push(coverage);
583
593
  checks.push(...cursorColumnChecks(sources));
584
594
  for (const source of sources) {
585
595
  const check = cursorMonotonicityCheck(source);
@@ -982,6 +992,240 @@ function describeSilentPush(target, body) {
982
992
  return `Clivly sync: pushed ${received} row(s) for ${objectType ? `"${source}" → "${objectType}"` : `"${source}"`} and none were stored — no active mapping in this workspace targets it, so the rows were dropped. Map it in Integrations → Mapping, then sync again.`;
983
993
  }
984
994
  //#endregion
995
+ //#region src/remote-projections.ts
996
+ const SYNC_CUSTOM_PATH$1 = "/clivly/sdk/sync/custom";
997
+ const MISSING_INTROSPECTOR = "this app cannot run dashboard-authored derived objects yet — add `introspector: drizzleIntrospector(db)` to clivly.config.ts";
998
+ /**
999
+ * Turn definitions fetched from the cloud into runnable sync sources.
1000
+ *
1001
+ * Revalidates every projection locally against the schema THIS app discovered,
1002
+ * never against the cloud's stored copy. Two reasons, and both matter:
1003
+ *
1004
+ * 1. Security. `checkTablesExist`/`checkRefs` bound every table and column ref
1005
+ * to the schema passed in, so validating against the locally-discovered
1006
+ * schema makes the blast radius exactly the namespace the developer handed
1007
+ * to `discoverFromDrizzle`. Trusting the cloud's verdict instead would let a
1008
+ * compromised cloud read any table in the database.
1009
+ * 2. Truth. The stored copy can be stale. A column dropped in the host app
1010
+ * becomes a readable quarantine here instead of a raw Postgres error out of
1011
+ * `sql.raw` at execution time.
1012
+ */
1013
+ function resolveRemoteProjections(args) {
1014
+ const { entries, introspector, schema } = args;
1015
+ return entries.map((entry) => resolveOne(entry, schema, introspector));
1016
+ }
1017
+ /**
1018
+ * The base table's PK, resolved from the schema THIS app discovered — never
1019
+ * from the cloud's copy.
1020
+ *
1021
+ * `validateProjection` proved its uniqueness case against this exact source
1022
+ * (`primaryKeyColumns` over the locally-discovered schema, see
1023
+ * `projection-validate.ts`). Compiling a *different* column — the cloud's
1024
+ * `basePkColumn`, which can go stale the moment a host renames a PK and the
1025
+ * `updatedAt`-only ETag never invalidates — would defeat that proof and
1026
+ * revive the raw-Postgres-error failure mode local revalidation exists to
1027
+ * prevent. Only relevant when the identity is `base_pk` with no explicit
1028
+ * column; every other identity kind ignores `basePkColumn` entirely.
1029
+ */
1030
+ function resolveLocalBasePk(projection, schema) {
1031
+ if (projection.identity.kind !== "base_pk" || projection.identity.column) return;
1032
+ const baseTable = schema.find((t) => t.name === projection.baseTable);
1033
+ if (!baseTable) return;
1034
+ return (0, _clivly_core.primaryKeyColumns)(baseTable)[0];
1035
+ }
1036
+ function resolveOne(entry, schema, introspector) {
1037
+ const parsed = _clivly_core.projectionSchema.safeParse(entry.projection);
1038
+ if (!parsed.success) return {
1039
+ id: entry.id,
1040
+ status: "invalid",
1041
+ reason: `projection did not parse: ${parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")}`
1042
+ };
1043
+ const projection = parsed.data;
1044
+ const errors = (0, _clivly_core.validateProjection)(projection, schema, entry.entityKey);
1045
+ if (errors.length > 0) return {
1046
+ id: entry.id,
1047
+ status: "invalid",
1048
+ reason: errors.map((e) => `${e.path}: ${e.message}`).join("; ")
1049
+ };
1050
+ if (!introspector?.projectionSource) return {
1051
+ id: entry.id,
1052
+ status: "skipped",
1053
+ reason: MISSING_INTROSPECTOR
1054
+ };
1055
+ try {
1056
+ const basePkColumn = resolveLocalBasePk(projection, schema) ?? entry.basePkColumn;
1057
+ const source = introspector.projectionSource(projection, {
1058
+ objectType: entry.objectType,
1059
+ ...basePkColumn ? { basePkColumn } : {}
1060
+ });
1061
+ return {
1062
+ id: entry.id,
1063
+ status: "ok",
1064
+ source
1065
+ };
1066
+ } catch (error) {
1067
+ return {
1068
+ id: entry.id,
1069
+ status: "failed",
1070
+ reason: error instanceof Error ? error.message : String(error)
1071
+ };
1072
+ }
1073
+ }
1074
+ const PROJECTIONS_PATH = "/clivly/sdk/projections";
1075
+ const PROJECTIONS_REPORT_PATH = "/clivly/sdk/projections/report";
1076
+ const MAX_REASON = 2e3;
1077
+ /**
1078
+ * Merge what resolution decided with what the push actually did.
1079
+ *
1080
+ * A `status: "ok"` outcome means the projection *resolved*, not that its rows
1081
+ * landed. If the push then failed, the object did not sync and must not be
1082
+ * reported as healthy — so the push result wins for those.
1083
+ */
1084
+ function buildRemoteProjectionReport(args) {
1085
+ return {
1086
+ ...args.runId ? { runId: args.runId } : {},
1087
+ results: args.outcomes.map((outcome) => {
1088
+ if (outcome.status !== "ok") return {
1089
+ id: outcome.id,
1090
+ status: outcome.status,
1091
+ reason: outcome.reason.slice(0, MAX_REASON)
1092
+ };
1093
+ const result = args.pushed.get(outcome.source.entity);
1094
+ if (result?.ok) return {
1095
+ id: outcome.id,
1096
+ status: "ok",
1097
+ rows: result.count
1098
+ };
1099
+ return {
1100
+ id: outcome.id,
1101
+ status: "failed",
1102
+ reason: (result?.reason ?? "the push to Clivly failed; rows were read but not stored").slice(0, MAX_REASON)
1103
+ };
1104
+ })
1105
+ };
1106
+ }
1107
+ function createRemoteProjectionFetcher(args) {
1108
+ let etag = null;
1109
+ let cached = [];
1110
+ return { async fetch() {
1111
+ try {
1112
+ const response = await args.fetcher(`${args.apiUrl}${PROJECTIONS_PATH}`, { headers: {
1113
+ authorization: `Bearer ${args.apiKey}`,
1114
+ ...etag ? { "if-none-match": etag } : {}
1115
+ } });
1116
+ if (response.status === 304) return cached;
1117
+ if (!response.ok) return [];
1118
+ const body = await response.json();
1119
+ etag = body.etag ?? null;
1120
+ cached = body.projections ?? [];
1121
+ return cached;
1122
+ } catch {
1123
+ return [];
1124
+ }
1125
+ } };
1126
+ }
1127
+ const REMOTE_PAGE_SIZE = 500;
1128
+ /**
1129
+ * Page one source to exhaustion, keyset-paging on identity alone (the id
1130
+ * field, no timestamp). Split out of `pushRemoteProjections` so that
1131
+ * function's own cognitive complexity stays under the lint threshold — this
1132
+ * is the piece that can throw (a `fetchPage` execution error), kept separate
1133
+ * from push-failure handling below.
1134
+ */
1135
+ async function fetchAllPages(source, pageSize) {
1136
+ const idField = source.idField ?? "id";
1137
+ const all = [];
1138
+ let since = null;
1139
+ for (;;) {
1140
+ const rows = await source.fetchPage({
1141
+ since,
1142
+ limit: pageSize
1143
+ });
1144
+ if (rows.length === 0) break;
1145
+ all.push(...rows);
1146
+ const last = rows.at(-1);
1147
+ const nextId = String(last?.[idField]);
1148
+ if (since && nextId === since.id) break;
1149
+ since = { id: nextId };
1150
+ if (rows.length < pageSize) break;
1151
+ }
1152
+ return all;
1153
+ }
1154
+ /**
1155
+ * Page a remote projection by identity and push one `full` batch.
1156
+ *
1157
+ * `full` rather than `delta` because only a full push lets the server archive
1158
+ * rows that dropped out when a join or filter stopped matching — a delta cannot
1159
+ * express absence. And identity rather than a timestamp because a
1160
+ * dashboard-authored projection has no watermark column, while validation has
1161
+ * already proven the identity unique.
1162
+ *
1163
+ * `pageSize` defaults to `REMOTE_PAGE_SIZE` and exists so a test can force a
1164
+ * short page without either mutating the exported constant or constructing a
1165
+ * fixture hundreds of rows deep.
1166
+ *
1167
+ * Returns one entry per source, keyed by `source.entity`. `count` and `ok`
1168
+ * are independent: `count` is how many rows were pushed (0 for a projection
1169
+ * that legitimately matches nothing this run), `ok` is whether the push
1170
+ * itself succeeded. A caller that folds failures into a report must branch
1171
+ * on `ok`, never on `count === 0` — a correctly-working, legitimately-empty
1172
+ * projection has `ok: true, count: 0` and is not a failure.
1173
+ *
1174
+ * Each source's paging-and-push is wrapped individually: a projection can
1175
+ * validate cleanly and still fail at execution — permission denied, a type
1176
+ * error, a column dropped between validation and this run — and `fetchPage`
1177
+ * throwing is the realistic shape of that. Quarantine that one source as
1178
+ * `{ count: 0, ok: false, reason }` and keep going. The design's promise is
1179
+ * one bad object never takes down the others or the run's report; letting a
1180
+ * throw here escape the loop would break that promise for every source
1181
+ * queued after the failing one.
1182
+ *
1183
+ * Paging and pushing are caught separately, on purpose: `reason` is set only
1184
+ * when paging itself threw (no rows were ever read), so the reported cause
1185
+ * names execution — not the push, which is what actually failed when
1186
+ * `args.push` returns `false`. Conflating the two sent an author whose
1187
+ * projection can't even run to look at their push credentials instead of
1188
+ * their SQL.
1189
+ */
1190
+ async function pushRemoteProjections(args) {
1191
+ const pageSize = args.pageSize ?? REMOTE_PAGE_SIZE;
1192
+ const pushed = /* @__PURE__ */ new Map();
1193
+ for (const source of args.sources) {
1194
+ let all;
1195
+ try {
1196
+ all = await fetchAllPages(source, pageSize);
1197
+ } catch (error) {
1198
+ const message = error instanceof Error ? error.message : String(error);
1199
+ pushed.set(source.entity, {
1200
+ count: 0,
1201
+ ok: false,
1202
+ reason: `the projection failed to execute: ${message}`.slice(0, MAX_REASON)
1203
+ });
1204
+ continue;
1205
+ }
1206
+ try {
1207
+ const ok = await args.push(SYNC_CUSTOM_PATH$1, {
1208
+ source: source.entity,
1209
+ rows: all,
1210
+ mode: "full",
1211
+ fetched: all.length,
1212
+ cursor: null,
1213
+ objectType: source.objectType
1214
+ });
1215
+ pushed.set(source.entity, {
1216
+ count: all.length,
1217
+ ok
1218
+ });
1219
+ } catch {
1220
+ pushed.set(source.entity, {
1221
+ count: 0,
1222
+ ok: false
1223
+ });
1224
+ }
1225
+ }
1226
+ return pushed;
1227
+ }
1228
+ //#endregion
985
1229
  //#region src/self-check.ts
986
1230
  function selfCheck(input) {
987
1231
  const issues = [];
@@ -1241,12 +1485,25 @@ function resolveContactEntity(config) {
1241
1485
  }
1242
1486
  function collectSchemaMeta(config) {
1243
1487
  const byTable = /* @__PURE__ */ new Map();
1244
- for (const table of config.schema ?? []) if (table.columnsMeta) byTable.set(table.name, table.columnsMeta);
1488
+ const merge = (name, meta) => {
1489
+ const existing = byTable.get(name);
1490
+ byTable.set(name, {
1491
+ columnsMeta: meta.columnsMeta ?? existing?.columnsMeta,
1492
+ uniqueConstraints: meta.uniqueConstraints ?? existing?.uniqueConstraints
1493
+ });
1494
+ };
1495
+ for (const table of config.schema ?? []) if (table.columnsMeta || table.uniqueConstraints) merge(table.name, {
1496
+ columnsMeta: table.columnsMeta,
1497
+ uniqueConstraints: table.uniqueConstraints
1498
+ });
1245
1499
  const sources = [];
1246
1500
  if (config.source) sources.push(config.source.contacts, config.source.companies, config.source.deals, ...config.source.custom ?? []);
1247
1501
  for (const source of sources) {
1248
1502
  const meta = source?.[require_drizzle_meta.DRIZZLE_META];
1249
- if (meta?.tableName && meta.columnsMeta) byTable.set(meta.tableName, meta.columnsMeta);
1503
+ if (meta?.tableName && (meta.columnsMeta || meta.uniqueConstraints)) merge(meta.tableName, {
1504
+ columnsMeta: meta.columnsMeta,
1505
+ uniqueConstraints: meta.uniqueConstraints
1506
+ });
1250
1507
  }
1251
1508
  return byTable;
1252
1509
  }
@@ -1261,12 +1518,13 @@ function buildSchema(config) {
1261
1518
  if (config.contactEntity && !byName.has(config.contactEntity)) byName.set(config.contactEntity, new Set(config.contactFields ?? []));
1262
1519
  const metaByTable = collectSchemaMeta(config);
1263
1520
  return [...byName].map(([name, columns]) => {
1264
- const columnsMeta = metaByTable.get(name);
1265
- if (columnsMeta) for (const col of columnsMeta) columns.add(col.name);
1521
+ const meta = metaByTable.get(name);
1522
+ for (const col of meta?.columnsMeta ?? []) columns.add(col.name);
1266
1523
  return {
1267
1524
  name,
1268
1525
  columns: [...columns],
1269
- ...columnsMeta ? { columnsMeta } : {}
1526
+ ...meta?.columnsMeta ? { columnsMeta: meta.columnsMeta } : {},
1527
+ ...meta?.uniqueConstraints ? { uniqueConstraints: meta.uniqueConstraints } : {}
1270
1528
  };
1271
1529
  });
1272
1530
  }
@@ -1446,6 +1704,15 @@ async function handlePreviewRequest(config, preview) {
1446
1704
  sampledCount: rows.length
1447
1705
  }, 200);
1448
1706
  }
1707
+ async function handleProjectionPreviewRequest(config, request) {
1708
+ const run = config.introspector?.runProjection;
1709
+ if (!run) return jsonResponse({ error: "This app cannot preview derived objects. Set `introspector: drizzleIntrospector(db)` in clivly.config.ts, on a clivly version that supports runProjection." }, 400);
1710
+ const rows = await run.call(config.introspector, request.projection, { limit: request.limit });
1711
+ return jsonResponse({
1712
+ rows,
1713
+ sampledCount: rows.length
1714
+ }, 200);
1715
+ }
1449
1716
  /**
1450
1717
  * Create the Clivly SDK. The implemented surface — heartbeat/discovery reporting
1451
1718
  * and the auth-verify handler — connects a developer's app to a running Clivly
@@ -1473,9 +1740,15 @@ function createClivlySDK(config) {
1473
1740
  const cursorStore = config.cursorStore ?? createMemoryCursorStore();
1474
1741
  const fetcher = createFetcher(config.retry);
1475
1742
  let warnedUnsignedTrigger = false;
1743
+ const remoteProjections = createRemoteProjectionFetcher({
1744
+ apiKey: config.apiKey,
1745
+ apiUrl,
1746
+ fetcher
1747
+ });
1476
1748
  let observedUrl;
1477
1749
  let lastPushFailure = null;
1478
1750
  let lastPush = { failed: false };
1751
+ let lastRemoteOutcomes = [];
1479
1752
  const warnedSilentTargets = /* @__PURE__ */ new Set();
1480
1753
  const warnIfSilent = async (target, response) => {
1481
1754
  if (typeof response.json !== "function") return;
@@ -1547,18 +1820,58 @@ function createClivlySDK(config) {
1547
1820
  for (const customSource of customSources ?? []) if (!await syncOne(customSource, SYNC_CUSTOM_PATH, mode, customSource.objectType, runId)) failed.push(customSource.entity);
1548
1821
  return failed;
1549
1822
  };
1823
+ const syncRemoteSources = async (sources, runId) => {
1824
+ const pushed = await pushRemoteProjections({
1825
+ push: createPush(runId),
1826
+ sources
1827
+ });
1828
+ const failed = [];
1829
+ for (const [entity, result] of pushed) if (!result.ok) failed.push(entity);
1830
+ return {
1831
+ failed,
1832
+ pushed
1833
+ };
1834
+ };
1835
+ const reportRemoteOutcomes = async (outcomes, pushed, runId) => {
1836
+ if (outcomes.length === 0) return;
1837
+ try {
1838
+ const report = buildRemoteProjectionReport({
1839
+ outcomes,
1840
+ pushed,
1841
+ runId
1842
+ });
1843
+ await fetcher(`${apiUrl}${PROJECTIONS_REPORT_PATH}`, {
1844
+ method: "POST",
1845
+ headers: {
1846
+ authorization: `Bearer ${config.apiKey}`,
1847
+ "content-type": "application/json"
1848
+ },
1849
+ body: JSON.stringify(report)
1850
+ });
1851
+ } catch {}
1852
+ };
1550
1853
  const runSync = async (options) => {
1551
- if (!config.source) return;
1854
+ const remoteOutcomes = resolveRemoteProjections({
1855
+ entries: await remoteProjections.fetch(),
1856
+ introspector: config.introspector,
1857
+ schema: buildSchema(config)
1858
+ });
1859
+ lastRemoteOutcomes = remoteOutcomes;
1860
+ const remoteSources = remoteOutcomes.filter((o) => o.status === "ok").map((o) => o.source);
1861
+ if (!config.source && remoteSources.length === 0 && remoteOutcomes.length === 0) return;
1552
1862
  lastPushFailure = null;
1553
1863
  const mode = options?.mode ?? "delta";
1554
1864
  const runId = options?.runId;
1555
1865
  const failed = [];
1556
- if (config.source.companies) {
1866
+ if (config.source?.companies) {
1557
1867
  if (!await syncOne(config.source.companies, SYNC_COMPANIES_PATH, mode, void 0, runId)) failed.push(config.source.companies.entity);
1558
1868
  }
1559
- if (!await syncOne(config.source.contacts, SYNC_CONTACTS_PATH, mode, void 0, runId)) failed.push(config.source.contacts.entity);
1560
- if (config.source.deals && !await syncOne(config.source.deals, SYNC_DEALS_PATH, mode, void 0, runId)) failed.push(config.source.deals.entity);
1561
- failed.push(...await syncCustomSources(config.source.custom, mode, runId));
1869
+ if (config.source?.contacts && !await syncOne(config.source.contacts, SYNC_CONTACTS_PATH, mode, void 0, runId)) failed.push(config.source.contacts.entity);
1870
+ if (config.source?.deals && !await syncOne(config.source.deals, SYNC_DEALS_PATH, mode, void 0, runId)) failed.push(config.source.deals.entity);
1871
+ failed.push(...await syncCustomSources(config.source?.custom, mode, runId));
1872
+ const remote = await syncRemoteSources(remoteSources, runId);
1873
+ failed.push(...remote.failed);
1874
+ await reportRemoteOutcomes(lastRemoteOutcomes, remote.pushed, runId);
1562
1875
  if (failed.length > 0) {
1563
1876
  const message = describePushFailure(failed);
1564
1877
  lastPush = {
@@ -1575,13 +1888,31 @@ function createClivlySDK(config) {
1575
1888
  lastPushFailure = null;
1576
1889
  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]));
1577
1890
  };
1578
- const sendHeartbeat = () => {
1579
- const triggerUrl = resolveTriggerUrl({
1891
+ const describeSyncTrigger = () => ({
1892
+ configured: config.syncTrigger?.url,
1893
+ resolved: resolveTriggerUrl({
1580
1894
  configuredUrl: config.syncTrigger?.url,
1581
1895
  configuredPath: config.syncTrigger?.path,
1582
1896
  env: config.env ?? process.env,
1583
1897
  observedUrl
1584
- });
1898
+ })
1899
+ });
1900
+ const describeRemoteProjections = () => {
1901
+ const counts = {
1902
+ failed: 0,
1903
+ invalid: 0,
1904
+ ok: 0,
1905
+ skipped: 0
1906
+ };
1907
+ for (const outcome of lastRemoteOutcomes) counts[outcome.status] += 1;
1908
+ return {
1909
+ capable: Boolean(config.introspector?.projectionSource),
1910
+ ran: lastRemoteOutcomes.length > 0,
1911
+ ...counts
1912
+ };
1913
+ };
1914
+ const sendHeartbeat = () => {
1915
+ const triggerUrl = describeSyncTrigger().resolved;
1585
1916
  return fetcher(`${apiUrl}/clivly/sdk/heartbeat`, {
1586
1917
  method: "POST",
1587
1918
  headers: {
@@ -1778,7 +2109,7 @@ function createClivlySDK(config) {
1778
2109
  });
1779
2110
  const runScheduled = async (options) => {
1780
2111
  await heartbeat();
1781
- if (config.source) await runSync(options);
2112
+ if (config.source || config.introspector) await runSync(options);
1782
2113
  };
1783
2114
  const triggerRequestAllowed = async (req, rawBody) => {
1784
2115
  const secret = config.syncTrigger?.secret;
@@ -1809,6 +2140,7 @@ function createClivlySDK(config) {
1809
2140
  const startedAt = Date.now();
1810
2141
  try {
1811
2142
  if (payload.preview) return await handlePreviewRequest(config, payload.preview);
2143
+ if (payload.previewProjection) return await handleProjectionPreviewRequest(config, payload.previewProjection);
1812
2144
  if (payload.sample) await runSampleSync(payload.sample.entityKey, payload.runId);
1813
2145
  else await runScheduled({
1814
2146
  mode: payload.mode,
@@ -1871,7 +2203,7 @@ function createClivlySDK(config) {
1871
2203
  presenceClient = null;
1872
2204
  console.warn("Clivly presence socket unavailable; using HTTP heartbeat", error);
1873
2205
  }
1874
- if (!timers.sync && config.source) {
2206
+ if (!timers.sync && (config.source || config.introspector)) {
1875
2207
  timers.sync = setInterval(() => {
1876
2208
  runSync().catch(() => {});
1877
2209
  }, syncIntervalMs);
@@ -1904,6 +2236,8 @@ function createClivlySDK(config) {
1904
2236
  connect,
1905
2237
  ping,
1906
2238
  doctor,
2239
+ describeSyncTrigger,
2240
+ describeRemoteProjections,
1907
2241
  runScheduled,
1908
2242
  createClivlyHandler,
1909
2243
  sync: runSync,
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as ClivlySDK, c as CreateChatSessionHandlerOptions, d as DoctorCheck, f as DoctorReport, h as EnsuredObjectTypeResult, i as ClivlyConnectResult, l as CursorStore, m as EnsuredMappingResult, n as ChatWidgetVisitor, o as ClivlySDKConfig, p as EnsureSchemaResult, r as ClivlyConnectReason, s as ClivlyUser, t as ChatSessionRequestBody, u as DiscoveredTable } from "./types-Dz1LQYvQ.cjs";
1
+ import { a as ClivlySDK, c as CreateChatSessionHandlerOptions, d as DoctorCheck, f as DoctorReport, g as RemoteProjectionsDescription, h as EnsuredObjectTypeResult, i as ClivlyConnectResult, l as CursorStore, m as EnsuredMappingResult, n as ChatWidgetVisitor, o as ClivlySDKConfig, p as EnsureSchemaResult, r as ClivlyConnectReason, s as ClivlyUser, t as ChatSessionRequestBody, u as DiscoveredTable, v as SyncTriggerDescription } from "./types-B7HKG2Ph.cjs";
2
2
  import { createChatSessionHandler } from "./chat-session.cjs";
3
3
  import { ClivlyEntitiesConfig, ConfigValidationError, ConfigValidationResult, CustomObjectDeclaration, DiscoveredSchemaTable, customObjectDeclarations, defineClivlyConfig, validateEntitiesConfig } from "@clivly/core/entity-config";
4
4
  //#region src/trigger-url.d.ts
@@ -34,4 +34,4 @@ declare class ClivlyNetworkError extends ClivlyError {}
34
34
  */
35
35
  declare function createClivlySDK(config: ClivlySDKConfig): ClivlySDK;
36
36
  //#endregion
37
- export { type ChatSessionRequestBody, type ChatWidgetVisitor, ClivlyAuthError, type ClivlyConnectReason, type ClivlyConnectResult, type ClivlyEntitiesConfig, ClivlyError, ClivlyNetworkError, ClivlyRateLimitError, type ClivlySDK, type ClivlySDKConfig, type ClivlyUser, type ConfigValidationError, type ConfigValidationResult, type CreateChatSessionHandlerOptions, type CursorStore, type CustomObjectDeclaration, type DiscoveredSchemaTable, type DiscoveredTable, type DoctorCheck, type DoctorReport, type EnsureSchemaResult, type EnsuredMappingResult, type EnsuredObjectTypeResult, createChatSessionHandler, createClivlySDK, customObjectDeclarations, defineClivlyConfig, isPlaceholderUrl, validateEntitiesConfig };
37
+ export { type ChatSessionRequestBody, type ChatWidgetVisitor, ClivlyAuthError, type ClivlyConnectReason, type ClivlyConnectResult, type ClivlyEntitiesConfig, ClivlyError, ClivlyNetworkError, ClivlyRateLimitError, type ClivlySDK, type ClivlySDKConfig, type ClivlyUser, type ConfigValidationError, type ConfigValidationResult, type CreateChatSessionHandlerOptions, type CursorStore, type CustomObjectDeclaration, type DiscoveredSchemaTable, type DiscoveredTable, type DoctorCheck, type DoctorReport, type EnsureSchemaResult, type EnsuredMappingResult, type EnsuredObjectTypeResult, type RemoteProjectionsDescription, type SyncTriggerDescription, createChatSessionHandler, createClivlySDK, customObjectDeclarations, defineClivlyConfig, isPlaceholderUrl, validateEntitiesConfig };
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as ClivlySDK, c as CreateChatSessionHandlerOptions, d as DoctorCheck, f as DoctorReport, h as EnsuredObjectTypeResult, i as ClivlyConnectResult, l as CursorStore, m as EnsuredMappingResult, n as ChatWidgetVisitor, o as ClivlySDKConfig, p as EnsureSchemaResult, r as ClivlyConnectReason, s as ClivlyUser, t as ChatSessionRequestBody, u as DiscoveredTable } from "./types-Dz1LQYvQ.mjs";
1
+ import { a as ClivlySDK, c as CreateChatSessionHandlerOptions, d as DoctorCheck, f as DoctorReport, g as RemoteProjectionsDescription, h as EnsuredObjectTypeResult, i as ClivlyConnectResult, l as CursorStore, m as EnsuredMappingResult, n as ChatWidgetVisitor, o as ClivlySDKConfig, p as EnsureSchemaResult, r as ClivlyConnectReason, s as ClivlyUser, t as ChatSessionRequestBody, u as DiscoveredTable, v as SyncTriggerDescription } from "./types-B7HKG2Ph.mjs";
2
2
  import { createChatSessionHandler } from "./chat-session.mjs";
3
3
  import { ClivlyEntitiesConfig, ConfigValidationError, ConfigValidationResult, CustomObjectDeclaration, DiscoveredSchemaTable, customObjectDeclarations, defineClivlyConfig, validateEntitiesConfig } from "@clivly/core/entity-config";
4
4
  //#region src/trigger-url.d.ts
@@ -34,4 +34,4 @@ declare class ClivlyNetworkError extends ClivlyError {}
34
34
  */
35
35
  declare function createClivlySDK(config: ClivlySDKConfig): ClivlySDK;
36
36
  //#endregion
37
- export { type ChatSessionRequestBody, type ChatWidgetVisitor, ClivlyAuthError, type ClivlyConnectReason, type ClivlyConnectResult, type ClivlyEntitiesConfig, ClivlyError, ClivlyNetworkError, ClivlyRateLimitError, type ClivlySDK, type ClivlySDKConfig, type ClivlyUser, type ConfigValidationError, type ConfigValidationResult, type CreateChatSessionHandlerOptions, type CursorStore, type CustomObjectDeclaration, type DiscoveredSchemaTable, type DiscoveredTable, type DoctorCheck, type DoctorReport, type EnsureSchemaResult, type EnsuredMappingResult, type EnsuredObjectTypeResult, createChatSessionHandler, createClivlySDK, customObjectDeclarations, defineClivlyConfig, isPlaceholderUrl, validateEntitiesConfig };
37
+ export { type ChatSessionRequestBody, type ChatWidgetVisitor, ClivlyAuthError, type ClivlyConnectReason, type ClivlyConnectResult, type ClivlyEntitiesConfig, ClivlyError, ClivlyNetworkError, ClivlyRateLimitError, type ClivlySDK, type ClivlySDKConfig, type ClivlyUser, type ConfigValidationError, type ConfigValidationResult, type CreateChatSessionHandlerOptions, type CursorStore, type CustomObjectDeclaration, type DiscoveredSchemaTable, type DiscoveredTable, type DoctorCheck, type DoctorReport, type EnsureSchemaResult, type EnsuredMappingResult, type EnsuredObjectTypeResult, type RemoteProjectionsDescription, type SyncTriggerDescription, createChatSessionHandler, createClivlySDK, customObjectDeclarations, defineClivlyConfig, isPlaceholderUrl, validateEntitiesConfig };