clivly 0.4.1 → 0.7.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.
- package/README.md +89 -10
- package/{adapter-CXr12l4K.d.cts → adapter-C2eRbBfo.d.cts} +1 -2
- package/{adapter-CvNBD_3y.d.mts → adapter-D5LRa-NA.d.mts} +1 -2
- package/{auth-adapter-CdLODn2p.d.cts → auth-adapter-Bjgo8LhN.d.mts} +1 -2
- package/{auth-adapter-CRBW4elj.d.mts → auth-adapter-DWzgl5Yq.d.cts} +1 -2
- package/auth-better-auth.d.cts +1 -2
- package/auth-better-auth.d.mts +1 -2
- package/auth-clerk.d.cts +1 -2
- package/auth-clerk.d.mts +1 -2
- package/auth-workos.d.cts +1 -2
- package/auth-workos.d.mts +1 -2
- package/core-adapter.cjs +5 -0
- package/core-adapter.d.cts +1 -1
- package/core-adapter.d.mts +1 -1
- package/core-adapter.mjs +5 -0
- package/core-auth-adapter.cjs +6 -1
- package/core-auth-adapter.d.cts +1 -1
- package/core-auth-adapter.d.mts +1 -1
- package/core-auth-adapter.mjs +6 -1
- package/core-drizzle.cjs +6 -30
- package/core-drizzle.d.cts +1 -20
- package/core-drizzle.d.mts +1 -20
- package/core-drizzle.mjs +5 -29
- package/core-entity-config.cjs +4 -1
- package/core-entity-config.d.cts +1 -1
- package/core-entity-config.d.mts +1 -1
- package/core-entity-config.mjs +5 -1
- package/core-entity-heuristics.cjs +14 -453
- package/core-entity-heuristics.d.cts +1 -67
- package/core-entity-heuristics.d.mts +1 -67
- package/core-entity-heuristics.mjs +4 -442
- package/core-mapping-form.cjs +6 -1
- package/core-mapping-form.d.cts +1 -1
- package/core-mapping-form.d.mts +1 -1
- package/core-mapping-form.mjs +6 -1
- package/core-mapping-score.cjs +4 -1
- package/core-mapping-score.d.cts +1 -1
- package/core-mapping-score.d.mts +1 -1
- package/core-mapping-score.mjs +5 -1
- package/core-mappings-config.cjs +6 -1
- package/core-mappings-config.d.cts +1 -1
- package/core-mappings-config.d.mts +1 -1
- package/core-mappings-config.mjs +6 -1
- package/core-sync-engine.cjs +6 -1
- package/core-sync-engine.d.cts +1 -1
- package/core-sync-engine.d.mts +1 -1
- package/core-sync-engine.mjs +6 -1
- package/core-types.cjs +5 -0
- package/core-types.d.cts +1 -1
- package/core-types.d.mts +1 -1
- package/core-types.mjs +5 -0
- package/core-view-compiler.cjs +6 -1
- package/core-view-compiler.d.cts +1 -1
- package/core-view-compiler.d.mts +1 -1
- package/core-view-compiler.mjs +6 -1
- package/core.cjs +11 -6
- package/core.d.cts +9 -9
- package/core.d.mts +9 -9
- package/core.mjs +11 -6
- package/deprecate-subpath-B9ngvizt.cjs +30 -0
- package/deprecate-subpath-CmvZtCpE.mjs +25 -0
- package/drizzle-B89tLo4T.mjs +31 -0
- package/drizzle-CFOr9dU9.d.mts +20 -0
- package/drizzle-DiReZf6d.cjs +36 -0
- package/drizzle-DmeKSqeF.d.cts +20 -0
- package/drizzle.cjs +3 -1
- package/drizzle.d.cts +11 -3
- package/drizzle.d.mts +11 -3
- package/drizzle.mjs +3 -2
- package/{entity-config-DMuZpchz.cjs → entity-config-D87rV-tE.cjs} +194 -192
- package/{entity-config-D1DFyeUp.d.mts → entity-config-DoUCXtq5.d.cts} +48 -35
- package/{entity-config-GczjD3y8.d.cts → entity-config-DoUCXtq5.d.mts} +48 -35
- package/{entity-config-D3YpQB0z.mjs → entity-config-va2bkH5o.mjs} +194 -192
- package/index.cjs +910 -234
- package/index.d.cts +30 -2
- package/index.d.mts +30 -2
- package/index.mjs +900 -226
- package/internal-adapter.cjs +0 -0
- package/internal-adapter.d.cts +2 -0
- package/internal-adapter.d.mts +2 -0
- package/internal-adapter.mjs +1 -0
- package/internal-auth-adapter.cjs +3 -0
- package/internal-auth-adapter.d.cts +2 -0
- package/internal-auth-adapter.d.mts +2 -0
- package/internal-auth-adapter.mjs +2 -0
- package/internal-drizzle.cjs +3 -0
- package/internal-drizzle.d.cts +2 -0
- package/internal-drizzle.d.mts +2 -0
- package/internal-drizzle.mjs +2 -0
- package/internal-entity-config.cjs +13 -0
- package/internal-entity-config.d.cts +2 -0
- package/internal-entity-config.d.mts +2 -0
- package/internal-entity-config.mjs +2 -0
- package/internal-entity-heuristics.cjs +455 -0
- package/internal-entity-heuristics.d.cts +67 -0
- package/internal-entity-heuristics.d.mts +67 -0
- package/internal-entity-heuristics.mjs +444 -0
- package/internal-mapping-form.cjs +5 -0
- package/internal-mapping-form.d.cts +2 -0
- package/internal-mapping-form.d.mts +2 -0
- package/internal-mapping-form.mjs +2 -0
- package/internal-mapping-score.cjs +12 -0
- package/internal-mapping-score.d.cts +2 -0
- package/internal-mapping-score.d.mts +2 -0
- package/internal-mapping-score.mjs +2 -0
- package/internal-mappings-config.cjs +3 -0
- package/internal-mappings-config.d.cts +2 -0
- package/internal-mappings-config.d.mts +2 -0
- package/internal-mappings-config.mjs +2 -0
- package/internal-sync-engine.cjs +6 -0
- package/internal-sync-engine.d.cts +2 -0
- package/internal-sync-engine.d.mts +2 -0
- package/internal-sync-engine.mjs +2 -0
- package/internal-types.cjs +0 -0
- package/internal-types.d.cts +2 -0
- package/internal-types.d.mts +2 -0
- package/internal-types.mjs +1 -0
- package/internal-view-compiler.cjs +6 -0
- package/internal-view-compiler.d.cts +2 -0
- package/internal-view-compiler.d.mts +2 -0
- package/internal-view-compiler.mjs +2 -0
- package/internal.cjs +28 -0
- package/internal.d.cts +9 -0
- package/internal.d.mts +9 -0
- package/internal.mjs +7 -0
- package/{mapping-form-CUlFVfG8.d.cts → mapping-form-D93lhHEc.d.mts} +1 -2
- package/{mapping-form-kjSiJaCX.d.mts → mapping-form-lfyEE7EJ.d.cts} +1 -2
- package/{mappings-config-DYZ_uOOB.cjs → mappings-config-BsRADNRv.cjs} +1 -1
- package/{mappings-config-CNTZRD4g.mjs → mappings-config-Dbgxx3Nv.mjs} +1 -1
- package/{mappings-config-DjRLCb2Q.d.cts → mappings-config-DsUa7-1w.d.mts} +1 -2
- package/{mappings-config-GxiYsSBQ.d.mts → mappings-config-XmzB4dag.d.cts} +1 -2
- package/package.json +152 -4
- package/{sdk-DdzBxgMx.mjs → sdk-C6WANW8M.mjs} +173 -34
- package/{sdk-D1BHQDf5.cjs → sdk-iqOXeWIp.cjs} +173 -34
- package/sdk.cjs +3 -1
- package/sdk.d.cts +1 -41
- package/sdk.d.mts +1 -41
- package/sdk.mjs +3 -2
- package/{sync-engine-DIVMG9EV.d.cts → sync-engine-B7QIbXro.d.cts} +6 -5
- package/{sync-engine-BtzkzKmY.cjs → sync-engine-CzEbpQGv.cjs} +1 -1
- package/{sync-engine-DLx243iw.d.mts → sync-engine-DGk_AeHJ.d.mts} +6 -5
- package/{sync-engine-D9vghQab.mjs → sync-engine-Do5ru3WF.mjs} +1 -1
- package/types-CsL0rFYh.d.cts +155 -0
- package/types-CsL0rFYh.d.mts +155 -0
- package/{view-compiler-4HsRUTWH.d.cts → view-compiler-BWz0sGdm.d.cts} +1 -2
- package/{view-compiler-BpIDExjd.d.mts → view-compiler-Bn_EJJMO.d.mts} +1 -2
- package/vite.d.cts +1 -2
- package/vite.d.mts +1 -2
- package/namespace-probe-CA4Vj40g.d.mts +0 -331
- package/namespace-probe-CkHCj9tQ.d.cts +0 -331
- /package/{auth-adapter-CBOKDhlc.cjs → auth-adapter-CiEXcweZ.cjs} +0 -0
- /package/{auth-adapter-wqnnEjo3.mjs → auth-adapter-gfVDxf3d.mjs} +0 -0
- /package/{drizzle-meta-BVR1qiyu.cjs → drizzle-meta-Bb1JWYqf.cjs} +0 -0
- /package/{drizzle-meta-DpMKataU.mjs → drizzle-meta-D-5k3n0i.mjs} +0 -0
- /package/{mapping-form-DdmDebC3.cjs → mapping-form-BpfON_ME.cjs} +0 -0
- /package/{mapping-form-BHtPexzI.mjs → mapping-form-DigkuvFc.mjs} +0 -0
- /package/{mapping-score-CPPvwoZC.mjs → mapping-score-C68uAqGB.mjs} +0 -0
- /package/{mapping-score-i3p8LWDF.cjs → mapping-score-CaB3F4Uu.cjs} +0 -0
- /package/{mapping-score-A2lRnlpk.d.mts → mapping-score-D7Gzg7Ii.d.cts} +0 -0
- /package/{mapping-score-BcjC0ZQs.d.cts → mapping-score-D7Gzg7Ii.d.mts} +0 -0
- /package/{types-BpsKVccL.d.cts → types-B1oRXdNP.d.cts} +0 -0
- /package/{types-_qbJXMt_.d.mts → types-B1oRXdNP.d.mts} +0 -0
- /package/{view-compiler-BcgJINr7.cjs → view-compiler-BIwvm-o2.cjs} +0 -0
- /package/{view-compiler-DeZzx_jn.mjs → view-compiler-ChWr_Lml.mjs} +0 -0
|
@@ -1,4 +1,83 @@
|
|
|
1
|
-
const require_drizzle_meta = require("./drizzle-meta-
|
|
1
|
+
const require_drizzle_meta = require("./drizzle-meta-Bb1JWYqf.cjs");
|
|
2
|
+
//#region ../sdk/src/entity-source.ts
|
|
3
|
+
const CONCEPT_KEYS = {
|
|
4
|
+
contact: "contacts",
|
|
5
|
+
company: "companies",
|
|
6
|
+
deal: "deals"
|
|
7
|
+
};
|
|
8
|
+
function resolveEntitySource(config, entityKey) {
|
|
9
|
+
const entity = config.entities?.entities?.[entityKey];
|
|
10
|
+
const src = config.source;
|
|
11
|
+
if (!(entity && src)) return null;
|
|
12
|
+
const conceptKey = CONCEPT_KEYS[entity.concept];
|
|
13
|
+
if (conceptKey) {
|
|
14
|
+
const source = src[conceptKey];
|
|
15
|
+
return source ? {
|
|
16
|
+
concept: entity.concept,
|
|
17
|
+
expectedKey: conceptKey,
|
|
18
|
+
source
|
|
19
|
+
} : null;
|
|
20
|
+
}
|
|
21
|
+
if (entity.concept === "custom") {
|
|
22
|
+
const match = (src.custom ?? []).find((c) => c.entity === entity.source || c.entity === entityKey);
|
|
23
|
+
return match ? {
|
|
24
|
+
concept: "custom",
|
|
25
|
+
expectedKey: "custom",
|
|
26
|
+
source: match
|
|
27
|
+
} : null;
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
/** The `config.source` key an entity's concept requires, present or not. */
|
|
32
|
+
function expectedSourceKey(concept) {
|
|
33
|
+
return CONCEPT_KEYS[concept] ?? "custom";
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region ../sdk/src/concept-coverage.ts
|
|
37
|
+
const CHECK_NAME = "Entity → source coverage";
|
|
38
|
+
function configuredKeys(config) {
|
|
39
|
+
const src = config.source;
|
|
40
|
+
if (!src) return [];
|
|
41
|
+
return Object.entries(src).filter(([, value]) => Array.isArray(value) ? value.length > 0 : Boolean(value)).map(([key]) => key);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Every configured entity must resolve to a source, or it silently syncs
|
|
45
|
+
* nothing. This is the check that catches `source` keyed by entity name rather
|
|
46
|
+
* than by CRM concept — a mistake that otherwise shows up only as a run that
|
|
47
|
+
* succeeds with zero rows.
|
|
48
|
+
*/
|
|
49
|
+
function checkConceptCoverage(config) {
|
|
50
|
+
const entities = config.entities?.entities ?? {};
|
|
51
|
+
const keys = Object.keys(entities);
|
|
52
|
+
if (keys.length === 0) return {
|
|
53
|
+
name: CHECK_NAME,
|
|
54
|
+
ok: false,
|
|
55
|
+
skipped: true,
|
|
56
|
+
detail: "No entities configured — nothing to cross-check."
|
|
57
|
+
};
|
|
58
|
+
const unresolved = keys.filter((key) => resolveEntitySource(config, key) === null);
|
|
59
|
+
if (unresolved.length === 0) return {
|
|
60
|
+
name: CHECK_NAME,
|
|
61
|
+
ok: true,
|
|
62
|
+
detail: `${keys.length} entit${keys.length === 1 ? "y" : "ies"} resolve to a configured source.`
|
|
63
|
+
};
|
|
64
|
+
const available = configuredKeys(config);
|
|
65
|
+
const listed = available.length > 0 ? available.join(", ") : "(none)";
|
|
66
|
+
const problems = unresolved.map((key) => {
|
|
67
|
+
const concept = entities[key]?.concept ?? "unknown";
|
|
68
|
+
if (concept === "custom") {
|
|
69
|
+
const custom = config.source?.custom ?? [];
|
|
70
|
+
if (custom.length > 0) return `"${key}" has concept "custom" but no entry in \`source.custom\` matches it — it will never sync. Entities configured in \`source.custom\`: ${custom.map((c) => c.entity).join(", ")}.`;
|
|
71
|
+
}
|
|
72
|
+
return `"${key}" has concept "${concept}" but no \`source.${expectedSourceKey(concept)}\` is configured — it will never sync.`;
|
|
73
|
+
});
|
|
74
|
+
return {
|
|
75
|
+
name: CHECK_NAME,
|
|
76
|
+
ok: false,
|
|
77
|
+
detail: `${problems.join(" ")} Configured source keys: ${listed}. \`source\` is keyed by CRM concept, not by entity key.`
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
2
81
|
//#region ../sdk/src/custom-slug.ts
|
|
3
82
|
const NON_ALNUM = /[^a-z0-9]+/g;
|
|
4
83
|
const EDGE_HYPHENS = /(^-|-$)/g;
|
|
@@ -79,6 +158,56 @@ function describeFetchError(entity, error) {
|
|
|
79
158
|
return `Couldn't read "${entity}": ${firstLine(root instanceof Error ? root.message : String(root?.message ?? root ?? "unknown error"))}`;
|
|
80
159
|
}
|
|
81
160
|
//#endregion
|
|
161
|
+
//#region ../sdk/src/mapping-dry-run.ts
|
|
162
|
+
/**
|
|
163
|
+
* Fetch one real row and push it through the entity's field map.
|
|
164
|
+
*
|
|
165
|
+
* The distinction from "Sample fetch": that proves the query runs, this proves
|
|
166
|
+
* the row that comes back actually carries the columns `fields` names. A
|
|
167
|
+
* mapping that points at a column the row lacks produces empty CRM records
|
|
168
|
+
* rather than an error, so it has to be caught here.
|
|
169
|
+
*
|
|
170
|
+
* An empty table SKIPS rather than fails — a fresh database is not a
|
|
171
|
+
* misconfiguration, and failing on it would train people to ignore the check.
|
|
172
|
+
*/
|
|
173
|
+
async function runMappingDryRun(config, entityKey) {
|
|
174
|
+
const name = `Field mapping dry run (${entityKey})`;
|
|
175
|
+
const resolved = resolveEntitySource(config, entityKey);
|
|
176
|
+
if (!resolved) return {
|
|
177
|
+
name,
|
|
178
|
+
ok: false,
|
|
179
|
+
skipped: true,
|
|
180
|
+
detail: `"${entityKey}" resolves to no source — see the coverage check above.`
|
|
181
|
+
};
|
|
182
|
+
const fields = config.entities?.entities?.[entityKey]?.fields ?? {};
|
|
183
|
+
let rows;
|
|
184
|
+
try {
|
|
185
|
+
rows = await resolved.source.fetchPage({
|
|
186
|
+
since: null,
|
|
187
|
+
limit: 1
|
|
188
|
+
});
|
|
189
|
+
} catch (error) {
|
|
190
|
+
return {
|
|
191
|
+
name,
|
|
192
|
+
ok: false,
|
|
193
|
+
detail: describeFetchError(entityKey, error)
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
const row = rows[0];
|
|
197
|
+
if (row === void 0) return {
|
|
198
|
+
name,
|
|
199
|
+
ok: false,
|
|
200
|
+
skipped: true,
|
|
201
|
+
detail: `"${resolved.source.entity}" is empty — nothing to map yet.`
|
|
202
|
+
};
|
|
203
|
+
const missing = Object.entries(fields).filter(([, column]) => !(column in row)).map(([field, column]) => `${field} → ${column}`);
|
|
204
|
+
return {
|
|
205
|
+
name,
|
|
206
|
+
ok: missing.length === 0,
|
|
207
|
+
detail: missing.length === 0 ? `Mapped ${Object.keys(fields).length} field(s) from a real "${resolved.source.entity}" row.` : `Row from "${resolved.source.entity}" is missing mapped column(s): ${missing.join(", ")}.`
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
//#endregion
|
|
82
211
|
//#region ../sdk/src/namespace-probe.ts
|
|
83
212
|
/**
|
|
84
213
|
* Verify the tables the integration depends on are genuinely reachable:
|
|
@@ -169,6 +298,11 @@ async function namespaceCheck(config) {
|
|
|
169
298
|
detail: probeResult.reachable ? "All required source tables are reachable." : probeResult.error?.message ?? "namespace unreachable"
|
|
170
299
|
};
|
|
171
300
|
}
|
|
301
|
+
async function mappingDryRunChecks(config) {
|
|
302
|
+
const checks = [];
|
|
303
|
+
for (const entityKey of Object.keys(config.entities?.entities ?? {})) checks.push(await runMappingDryRun(config, entityKey));
|
|
304
|
+
return checks;
|
|
305
|
+
}
|
|
172
306
|
/**
|
|
173
307
|
* Run the read-only setup checks and return a structured report. Every check
|
|
174
308
|
* captures its own error into `detail` rather than throwing, so one failure never
|
|
@@ -195,6 +329,7 @@ async function runDoctor(args) {
|
|
|
195
329
|
ok: sources.length > 0,
|
|
196
330
|
detail: sources.length > 0 ? `${sources.length} source(s) to mirror.` : "No `source.contacts` — nothing to sync."
|
|
197
331
|
});
|
|
332
|
+
checks.push(checkConceptCoverage(config));
|
|
198
333
|
for (const source of sources) {
|
|
199
334
|
const meta = source[require_drizzle_meta.DRIZZLE_META];
|
|
200
335
|
if (!meta) {
|
|
@@ -233,6 +368,7 @@ async function runDoctor(args) {
|
|
|
233
368
|
detail: describeFetchError(source.entity, error)
|
|
234
369
|
});
|
|
235
370
|
}
|
|
371
|
+
checks.push(...await mappingDryRunChecks(config));
|
|
236
372
|
const result = await connect();
|
|
237
373
|
checks.push({
|
|
238
374
|
name: "Heartbeat",
|
|
@@ -276,6 +412,7 @@ async function syncSource(args) {
|
|
|
276
412
|
const idField = source.idField ?? DEFAULT_ID_FIELD;
|
|
277
413
|
let cursor = since;
|
|
278
414
|
let pushed = 0;
|
|
415
|
+
const resumedFrom = since === null ? null : since.time.toISOString();
|
|
279
416
|
for (;;) {
|
|
280
417
|
const rows = await source.fetchPage({
|
|
281
418
|
since: cursor,
|
|
@@ -287,6 +424,8 @@ async function syncSource(args) {
|
|
|
287
424
|
source: source.entity,
|
|
288
425
|
rows: capped,
|
|
289
426
|
mode: "delta",
|
|
427
|
+
fetched: rows.length,
|
|
428
|
+
cursor: resumedFrom,
|
|
290
429
|
...objectType ? { objectType } : {}
|
|
291
430
|
})) return {
|
|
292
431
|
pushed,
|
|
@@ -329,6 +468,8 @@ async function fullSyncSource(args) {
|
|
|
329
468
|
source: source.entity,
|
|
330
469
|
rows: all,
|
|
331
470
|
mode: "full",
|
|
471
|
+
fetched: all.length,
|
|
472
|
+
cursor: null,
|
|
332
473
|
...objectType ? { objectType } : {}
|
|
333
474
|
});
|
|
334
475
|
return {
|
|
@@ -393,9 +534,7 @@ function observedTriggerUrl(req) {
|
|
|
393
534
|
const proto = forwardedProto || requestUrl.protocol.replace(":", "");
|
|
394
535
|
try {
|
|
395
536
|
return new URL(`${proto}://${host}${requestUrl.pathname}`).toString();
|
|
396
|
-
} catch {
|
|
397
|
-
return;
|
|
398
|
-
}
|
|
537
|
+
} catch {}
|
|
399
538
|
}
|
|
400
539
|
/**
|
|
401
540
|
* Decide which trigger URL to report, in precedence order:
|
|
@@ -454,7 +593,7 @@ function originAllowed(origin, allowedOrigins) {
|
|
|
454
593
|
if (!origin) return false;
|
|
455
594
|
return allowedOrigins.includes(origin);
|
|
456
595
|
}
|
|
457
|
-
function createChatSessionHandler({ allowedOrigins, apiKey, apiUrl = DEFAULT_API_URL$1, fetchImpl = fetch }) {
|
|
596
|
+
function createChatSessionHandler({ allowedOrigins, apiKey, apiUrl = DEFAULT_API_URL$1, fetchImpl = fetch, resolveUser }) {
|
|
458
597
|
const sessionUrl = `${normalizeApiUrl(apiUrl)}/clivly/widget/session`;
|
|
459
598
|
return async (request) => {
|
|
460
599
|
if (request.method !== "POST") return new Response(JSON.stringify({ error: "method_not_allowed" }), {
|
|
@@ -473,6 +612,12 @@ function createChatSessionHandler({ allowedOrigins, apiKey, apiUrl = DEFAULT_API
|
|
|
473
612
|
return jsonResponse$1({ error: "invalid_json" }, 400);
|
|
474
613
|
}
|
|
475
614
|
if (!isChatSessionRequestBody(body)) return jsonResponse$1({ error: "invalid_request" }, 400);
|
|
615
|
+
let hostUser = null;
|
|
616
|
+
if (resolveUser) try {
|
|
617
|
+
hostUser = await resolveUser(request);
|
|
618
|
+
} catch {
|
|
619
|
+
console.warn("[clivly] resolveUser threw; continuing without host identity");
|
|
620
|
+
}
|
|
476
621
|
try {
|
|
477
622
|
const upstream = await fetchImpl(sessionUrl, {
|
|
478
623
|
method: "POST",
|
|
@@ -485,7 +630,8 @@ function createChatSessionHandler({ allowedOrigins, apiKey, apiUrl = DEFAULT_API
|
|
|
485
630
|
widgetId: body.widgetId,
|
|
486
631
|
visitor: body.visitor,
|
|
487
632
|
visitorToken: body.visitorToken,
|
|
488
|
-
origin
|
|
633
|
+
origin,
|
|
634
|
+
...hostUser ? { hostUser } : {}
|
|
489
635
|
})
|
|
490
636
|
});
|
|
491
637
|
const responseBody = await upstream.text();
|
|
@@ -675,9 +821,7 @@ function connectReason(status) {
|
|
|
675
821
|
async function resolveOrg(response) {
|
|
676
822
|
try {
|
|
677
823
|
return (await response.json())?.org;
|
|
678
|
-
} catch {
|
|
679
|
-
return;
|
|
680
|
-
}
|
|
824
|
+
} catch {}
|
|
681
825
|
}
|
|
682
826
|
function retryAfterMs(res) {
|
|
683
827
|
const header = res.headers.get("retry-after");
|
|
@@ -700,35 +844,30 @@ function createFetcher(retry) {
|
|
|
700
844
|
}
|
|
701
845
|
if (!(res === null || isRetryableStatus(res.status)) || attempt >= maxAttempts) return res;
|
|
702
846
|
const backoff = baseDelayMs * 2 ** (attempt - 1) + Math.random() * baseDelayMs;
|
|
703
|
-
|
|
847
|
+
const wait = res ? retryAfterMs(res) ?? backoff : backoff;
|
|
848
|
+
await sleep(wait);
|
|
704
849
|
}
|
|
705
850
|
};
|
|
706
851
|
}
|
|
852
|
+
const CONCEPT_PATHS = {
|
|
853
|
+
contacts: SYNC_CONTACTS_PATH,
|
|
854
|
+
companies: SYNC_COMPANIES_PATH,
|
|
855
|
+
deals: SYNC_DEALS_PATH,
|
|
856
|
+
custom: SYNC_CUSTOM_PATH
|
|
857
|
+
};
|
|
707
858
|
function resolveSampleSource(config, entityKey) {
|
|
708
|
-
const
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
if (
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
if (entity.concept === "deal" && src.deals) return {
|
|
720
|
-
source: src.deals,
|
|
721
|
-
path: SYNC_DEALS_PATH
|
|
859
|
+
const resolved = resolveEntitySource(config, entityKey);
|
|
860
|
+
if (!resolved) return null;
|
|
861
|
+
const path = CONCEPT_PATHS[resolved.expectedKey];
|
|
862
|
+
if (!path) return null;
|
|
863
|
+
return resolved.expectedKey === "custom" ? {
|
|
864
|
+
source: resolved.source,
|
|
865
|
+
path,
|
|
866
|
+
objectType: resolved.source.objectType
|
|
867
|
+
} : {
|
|
868
|
+
source: resolved.source,
|
|
869
|
+
path
|
|
722
870
|
};
|
|
723
|
-
if (entity.concept === "custom") {
|
|
724
|
-
const custom = (src.custom ?? []).find((c) => c.entity === entity.source || c.entity === entityKey);
|
|
725
|
-
return custom ? {
|
|
726
|
-
source: custom,
|
|
727
|
-
path: SYNC_CUSTOM_PATH,
|
|
728
|
-
objectType: custom.objectType
|
|
729
|
-
} : null;
|
|
730
|
-
}
|
|
731
|
-
return null;
|
|
732
871
|
}
|
|
733
872
|
const PREVIEW_LIMIT = 5;
|
|
734
873
|
function resolvePreviewSource(config, sourceTable) {
|
|
@@ -757,7 +896,7 @@ async function handlePreviewRequest(config, preview) {
|
|
|
757
896
|
if (!source) return jsonResponse({ error: `No configured source for table "${preview.sourceTable}"` }, 400);
|
|
758
897
|
const idField = source.idField ?? "id";
|
|
759
898
|
const filterColumns = preview.filter ? Object.keys(preview.filter).filter((k) => k !== "$raw") : [];
|
|
760
|
-
const needed = new Set([
|
|
899
|
+
const needed = /* @__PURE__ */ new Set([
|
|
761
900
|
idField,
|
|
762
901
|
...Object.values(preview.fieldMap),
|
|
763
902
|
...filterColumns
|
package/sdk.cjs
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const
|
|
2
|
+
const require_entity_config = require("./entity-config-D87rV-tE.cjs");
|
|
3
|
+
const require_sdk = require("./sdk-iqOXeWIp.cjs");
|
|
3
4
|
exports.ClivlyAuthError = require_sdk.ClivlyAuthError;
|
|
4
5
|
exports.ClivlyError = require_sdk.ClivlyError;
|
|
5
6
|
exports.ClivlyNetworkError = require_sdk.ClivlyNetworkError;
|
|
6
7
|
exports.ClivlyRateLimitError = require_sdk.ClivlyRateLimitError;
|
|
7
8
|
exports.createChatSessionHandler = require_sdk.createChatSessionHandler;
|
|
8
9
|
exports.createClivlySDK = require_sdk.createClivlySDK;
|
|
10
|
+
exports.defineClivlyConfig = require_entity_config.defineClivlyConfig;
|
package/sdk.d.cts
CHANGED
|
@@ -1,41 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
//#region ../sdk/src/chat-session.d.ts
|
|
4
|
-
declare function createChatSessionHandler({
|
|
5
|
-
allowedOrigins,
|
|
6
|
-
apiKey,
|
|
7
|
-
apiUrl,
|
|
8
|
-
fetchImpl
|
|
9
|
-
}: CreateChatSessionHandlerOptions): (request: Request) => Promise<Response>;
|
|
10
|
-
//#endregion
|
|
11
|
-
//#region ../sdk/src/errors.d.ts
|
|
12
|
-
declare class ClivlyError extends Error {
|
|
13
|
-
/** HTTP status, or null when the request never completed (network error). */
|
|
14
|
-
readonly status: number | null;
|
|
15
|
-
readonly reason: ClivlyConnectResult["reason"];
|
|
16
|
-
/** Whether a retry could plausibly succeed (network, 429, 5xx). */
|
|
17
|
-
readonly retryable: boolean;
|
|
18
|
-
constructor(result: ClivlyConnectResult, message?: string);
|
|
19
|
-
/**
|
|
20
|
-
* Build the error subclass matching a failed result's reason, or `null` when
|
|
21
|
-
* the result is `ok`.
|
|
22
|
-
*/
|
|
23
|
-
static from(result: ClivlyConnectResult): ClivlyError | null;
|
|
24
|
-
}
|
|
25
|
-
/** The API key was rejected (401) or lacks access (403). Not retryable. */
|
|
26
|
-
declare class ClivlyAuthError extends ClivlyError {}
|
|
27
|
-
/** Rate limited (429). Retryable after backoff / `Retry-After`. */
|
|
28
|
-
declare class ClivlyRateLimitError extends ClivlyError {}
|
|
29
|
-
/** The request never reached the server (network/DNS/TLS). Retryable. */
|
|
30
|
-
declare class ClivlyNetworkError extends ClivlyError {}
|
|
31
|
-
//#endregion
|
|
32
|
-
//#region ../sdk/src/index.d.ts
|
|
33
|
-
/**
|
|
34
|
-
* Create the Clivly SDK. The implemented surface — heartbeat/discovery reporting
|
|
35
|
-
* and the auth-verify handler — connects a developer's app to a running Clivly
|
|
36
|
-
* backend today. Tunnel management and CRM write-back are scaffolded (see the
|
|
37
|
-
* clearly-marked TODOs) and land with the Cloudflare infrastructure.
|
|
38
|
-
*/
|
|
39
|
-
declare function createClivlySDK(config: ClivlySDKConfig): ClivlySDK;
|
|
40
|
-
//#endregion
|
|
41
|
-
export { type ChatSessionRequestBody, type ChatWidgetVisitor, ClivlyAuthError, type ClivlyConnectReason, type ClivlyConnectResult, ClivlyError, ClivlyNetworkError, ClivlyRateLimitError, type ClivlySDK, type ClivlySDKConfig, type ClivlyUser, type CreateChatSessionHandlerOptions, type CursorStore, type DiscoveredTable, type DoctorCheck, type DoctorReport, createChatSessionHandler, createClivlySDK };
|
|
1
|
+
export {}
|
package/sdk.d.mts
CHANGED
|
@@ -1,41 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
//#region ../sdk/src/chat-session.d.ts
|
|
4
|
-
declare function createChatSessionHandler({
|
|
5
|
-
allowedOrigins,
|
|
6
|
-
apiKey,
|
|
7
|
-
apiUrl,
|
|
8
|
-
fetchImpl
|
|
9
|
-
}: CreateChatSessionHandlerOptions): (request: Request) => Promise<Response>;
|
|
10
|
-
//#endregion
|
|
11
|
-
//#region ../sdk/src/errors.d.ts
|
|
12
|
-
declare class ClivlyError extends Error {
|
|
13
|
-
/** HTTP status, or null when the request never completed (network error). */
|
|
14
|
-
readonly status: number | null;
|
|
15
|
-
readonly reason: ClivlyConnectResult["reason"];
|
|
16
|
-
/** Whether a retry could plausibly succeed (network, 429, 5xx). */
|
|
17
|
-
readonly retryable: boolean;
|
|
18
|
-
constructor(result: ClivlyConnectResult, message?: string);
|
|
19
|
-
/**
|
|
20
|
-
* Build the error subclass matching a failed result's reason, or `null` when
|
|
21
|
-
* the result is `ok`.
|
|
22
|
-
*/
|
|
23
|
-
static from(result: ClivlyConnectResult): ClivlyError | null;
|
|
24
|
-
}
|
|
25
|
-
/** The API key was rejected (401) or lacks access (403). Not retryable. */
|
|
26
|
-
declare class ClivlyAuthError extends ClivlyError {}
|
|
27
|
-
/** Rate limited (429). Retryable after backoff / `Retry-After`. */
|
|
28
|
-
declare class ClivlyRateLimitError extends ClivlyError {}
|
|
29
|
-
/** The request never reached the server (network/DNS/TLS). Retryable. */
|
|
30
|
-
declare class ClivlyNetworkError extends ClivlyError {}
|
|
31
|
-
//#endregion
|
|
32
|
-
//#region ../sdk/src/index.d.ts
|
|
33
|
-
/**
|
|
34
|
-
* Create the Clivly SDK. The implemented surface — heartbeat/discovery reporting
|
|
35
|
-
* and the auth-verify handler — connects a developer's app to a running Clivly
|
|
36
|
-
* backend today. Tunnel management and CRM write-back are scaffolded (see the
|
|
37
|
-
* clearly-marked TODOs) and land with the Cloudflare infrastructure.
|
|
38
|
-
*/
|
|
39
|
-
declare function createClivlySDK(config: ClivlySDKConfig): ClivlySDK;
|
|
40
|
-
//#endregion
|
|
41
|
-
export { type ChatSessionRequestBody, type ChatWidgetVisitor, ClivlyAuthError, type ClivlyConnectReason, type ClivlyConnectResult, ClivlyError, ClivlyNetworkError, ClivlyRateLimitError, type ClivlySDK, type ClivlySDKConfig, type ClivlyUser, type CreateChatSessionHandlerOptions, type CursorStore, type DiscoveredTable, type DoctorCheck, type DoctorReport, createChatSessionHandler, createClivlySDK };
|
|
1
|
+
export {}
|
package/sdk.mjs
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { o as defineClivlyConfig } from "./entity-config-va2bkH5o.mjs";
|
|
2
|
+
import { a as ClivlyRateLimitError, i as ClivlyNetworkError, n as ClivlyAuthError, o as createChatSessionHandler, r as ClivlyError, t as createClivlySDK } from "./sdk-C6WANW8M.mjs";
|
|
3
|
+
export { ClivlyAuthError, ClivlyError, ClivlyNetworkError, ClivlyRateLimitError, createChatSessionHandler, createClivlySDK, defineClivlyConfig };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { a as ClivlyEntitiesConfig } from "./entity-config-
|
|
2
|
-
import { n as CompiledView, t as CompileOptions } from "./view-compiler-
|
|
3
|
-
|
|
1
|
+
import { a as ClivlyEntitiesConfig } from "./entity-config-DoUCXtq5.cjs";
|
|
2
|
+
import { n as CompiledView, t as CompileOptions } from "./view-compiler-BWz0sGdm.cjs";
|
|
4
3
|
//#region ../core/src/sync-engine.d.ts
|
|
5
4
|
/**
|
|
6
5
|
* Sync engine (Phase 3) — materializes the read-only entity views into Clivly's
|
|
@@ -108,13 +107,15 @@ interface SyncStore {
|
|
|
108
107
|
/** Current mirror rows for a concept + role (including archived). */
|
|
109
108
|
listMirror(input: {
|
|
110
109
|
concept: string;
|
|
111
|
-
role: string;
|
|
110
|
+
role: string;
|
|
111
|
+
/** Target custom-object type id, for `concept: "custom"` entities. */
|
|
112
112
|
objectTypeId?: string;
|
|
113
113
|
}): Promise<MirrorRecord[]>;
|
|
114
114
|
/** Apply the resolved actions to the mirror table for this concept + role. */
|
|
115
115
|
persist(input: {
|
|
116
116
|
concept: string;
|
|
117
|
-
role: string;
|
|
117
|
+
role: string;
|
|
118
|
+
/** Target custom-object type id, for `concept: "custom"` entities. */
|
|
118
119
|
objectTypeId?: string;
|
|
119
120
|
actions: PersistAction[];
|
|
120
121
|
}): Promise<void>;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { a as ClivlyEntitiesConfig } from "./entity-config-
|
|
2
|
-
import { n as CompiledView, t as CompileOptions } from "./view-compiler-
|
|
3
|
-
|
|
1
|
+
import { a as ClivlyEntitiesConfig } from "./entity-config-DoUCXtq5.mjs";
|
|
2
|
+
import { n as CompiledView, t as CompileOptions } from "./view-compiler-Bn_EJJMO.mjs";
|
|
4
3
|
//#region ../core/src/sync-engine.d.ts
|
|
5
4
|
/**
|
|
6
5
|
* Sync engine (Phase 3) — materializes the read-only entity views into Clivly's
|
|
@@ -108,13 +107,15 @@ interface SyncStore {
|
|
|
108
107
|
/** Current mirror rows for a concept + role (including archived). */
|
|
109
108
|
listMirror(input: {
|
|
110
109
|
concept: string;
|
|
111
|
-
role: string;
|
|
110
|
+
role: string;
|
|
111
|
+
/** Target custom-object type id, for `concept: "custom"` entities. */
|
|
112
112
|
objectTypeId?: string;
|
|
113
113
|
}): Promise<MirrorRecord[]>;
|
|
114
114
|
/** Apply the resolved actions to the mirror table for this concept + role. */
|
|
115
115
|
persist(input: {
|
|
116
116
|
concept: string;
|
|
117
|
-
role: string;
|
|
117
|
+
role: string;
|
|
118
|
+
/** Target custom-object type id, for `concept: "custom"` entities. */
|
|
118
119
|
objectTypeId?: string;
|
|
119
120
|
actions: PersistAction[];
|
|
120
121
|
}): Promise<void>;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
//#region ../sdk/src/drizzle-meta.d.ts
|
|
2
|
+
declare const DRIZZLE_META: "__clivly_drizzle_source_meta__";
|
|
3
|
+
interface DrizzleSourceMeta {
|
|
4
|
+
/** Drizzle property keys of every column on the source table. */
|
|
5
|
+
columnKeys: string[];
|
|
6
|
+
/** Schema-v2 typed column metadata derived from the Drizzle table. */
|
|
7
|
+
columnsMeta?: DiscoveredColumn[];
|
|
8
|
+
cursorField: string;
|
|
9
|
+
idField: string;
|
|
10
|
+
/** The backing table's real name, so discovery can key metadata by table. */
|
|
11
|
+
tableName?: string;
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region ../sdk/src/types.d.ts
|
|
15
|
+
interface DiscoveredColumn {
|
|
16
|
+
isForeignKey?: boolean;
|
|
17
|
+
isPrimaryKey?: boolean;
|
|
18
|
+
name: string;
|
|
19
|
+
nullable?: boolean;
|
|
20
|
+
/** The column this FK points at, when it is a foreign key. */
|
|
21
|
+
references?: {
|
|
22
|
+
table: string;
|
|
23
|
+
column: string;
|
|
24
|
+
};
|
|
25
|
+
/** SQL/driver type, e.g. "text", "integer", "timestamp". */
|
|
26
|
+
type?: string;
|
|
27
|
+
}
|
|
28
|
+
interface ClivlyUser {
|
|
29
|
+
email: string;
|
|
30
|
+
id: string;
|
|
31
|
+
name?: string;
|
|
32
|
+
}
|
|
33
|
+
interface DoctorCheck {
|
|
34
|
+
/** Human-readable outcome — what passed, or why it failed. */
|
|
35
|
+
detail: string;
|
|
36
|
+
/** Short label, e.g. "API key present". */
|
|
37
|
+
name: string;
|
|
38
|
+
/** True when the check passed. Ignored when `skipped` is true. */
|
|
39
|
+
ok: boolean;
|
|
40
|
+
/** True when the check could not run (e.g. a hand-written source). */
|
|
41
|
+
skipped?: boolean;
|
|
42
|
+
}
|
|
43
|
+
interface DoctorReport {
|
|
44
|
+
checks: DoctorCheck[];
|
|
45
|
+
/** AND of every non-skipped check's `ok`. */
|
|
46
|
+
ok: boolean;
|
|
47
|
+
}
|
|
48
|
+
type ClivlyConnectReason = "ok" | "invalid_api_key" | "forbidden" | "rate_limited" | "server_error" | "client_error" | "network_error";
|
|
49
|
+
interface ClivlyConnectResult {
|
|
50
|
+
ok: boolean;
|
|
51
|
+
/** Org resolved from a successful heartbeat, when the server returns it. */
|
|
52
|
+
org?: {
|
|
53
|
+
id?: string;
|
|
54
|
+
name?: string;
|
|
55
|
+
};
|
|
56
|
+
reason: ClivlyConnectReason;
|
|
57
|
+
/** Whether a retry could plausibly succeed (network, 429, 5xx). */
|
|
58
|
+
retryable: boolean;
|
|
59
|
+
/** HTTP status, or null when the request never completed (network error). */
|
|
60
|
+
status: number | null;
|
|
61
|
+
}
|
|
62
|
+
interface SyncCursor {
|
|
63
|
+
/** The row's id, breaking ties when timestamps collide. */
|
|
64
|
+
id: string;
|
|
65
|
+
/** The row's cursor-field value (typically `updated_at`). */
|
|
66
|
+
time: Date;
|
|
67
|
+
}
|
|
68
|
+
interface SyncSource {
|
|
69
|
+
cursorField: string;
|
|
70
|
+
entity: string;
|
|
71
|
+
fetchPage(args: {
|
|
72
|
+
since: SyncCursor | null;
|
|
73
|
+
limit: number;
|
|
74
|
+
}): Promise<Record<string, unknown>[]>;
|
|
75
|
+
/** Stable, unique tie-breaker column for keyset paging. Defaults to "id". */
|
|
76
|
+
idField?: string;
|
|
77
|
+
/** Set by `fromDrizzle`; lets `doctor` introspect the backing table. */
|
|
78
|
+
[DRIZZLE_META]?: DrizzleSourceMeta;
|
|
79
|
+
}
|
|
80
|
+
type SyncMode = "delta" | "full";
|
|
81
|
+
interface ClivlySDK {
|
|
82
|
+
/**
|
|
83
|
+
* Send a single heartbeat and report the outcome richly: `ok`, the HTTP
|
|
84
|
+
* `status` (null on network failure), a coarse `reason`, whether it's
|
|
85
|
+
* `retryable`, and the resolved `org` when the server returns it. Never throws
|
|
86
|
+
* — inspect the result, or turn a failure into an exception with
|
|
87
|
+
* `ClivlyError.from(result)`. Prefer this over `heartbeat()` for onboarding,
|
|
88
|
+
* health checks, and one-shot serverless calls.
|
|
89
|
+
*/
|
|
90
|
+
connect(): Promise<ClivlyConnectResult>;
|
|
91
|
+
/**
|
|
92
|
+
* Build the `/clivly/auth/verify` handler the developer mounts in their app.
|
|
93
|
+
* Clivly's cloud calls it to validate a CRM user's identity against the host
|
|
94
|
+
* app's session. Pass a resolver that reads the host session from the request.
|
|
95
|
+
*
|
|
96
|
+
* The handler authenticates the caller against `config.verifyToken` (presented
|
|
97
|
+
* as `Authorization: Bearer <token>`) before invoking the resolver, so the
|
|
98
|
+
* endpoint can't be used by third parties to probe host sessions. It fails
|
|
99
|
+
* closed with HTTP 503 if `verifyToken` is unset (a missing config value, not a
|
|
100
|
+
* server fault — so it won't trip the host's 5xx error alerting).
|
|
101
|
+
*/
|
|
102
|
+
createAuthVerifyHandler(resolveUser: (req: Request) => Promise<ClivlyUser | null> | ClivlyUser | null): (req: Request) => Promise<Response>;
|
|
103
|
+
/**
|
|
104
|
+
* A fetch handler to mount at a route. Each request runs one `runScheduled()`
|
|
105
|
+
* and returns JSON: 200 when the sync succeeded, 500 otherwise.
|
|
106
|
+
*
|
|
107
|
+
* Two callers use it: Clivly Cloud (when someone clicks "Run sync" — those
|
|
108
|
+
* requests are HMAC-signed and carry a run id), and the host's own scheduler
|
|
109
|
+
* (Workers cron, an uptime ping — no body).
|
|
110
|
+
*
|
|
111
|
+
* When `syncTrigger.secret` is set the handler rejects unsigned or
|
|
112
|
+
* badly-signed requests with 401. When it isn't, the route stays open to
|
|
113
|
+
* anyone who can reach it — set a secret if it's publicly routable.
|
|
114
|
+
*/
|
|
115
|
+
createClivlyHandler(): (req: Request) => Promise<Response>;
|
|
116
|
+
/** Validate this setup end-to-end (read-only) before the first sync. */
|
|
117
|
+
doctor(): Promise<DoctorReport>;
|
|
118
|
+
/**
|
|
119
|
+
* Send a single heartbeat now. Returns true on success. Kept for the keep-alive
|
|
120
|
+
* loop and back-compat; use `connect()` when you need the failure reason.
|
|
121
|
+
*/
|
|
122
|
+
heartbeat(): Promise<boolean>;
|
|
123
|
+
/**
|
|
124
|
+
* One-shot heartbeat + a single sync, with no keep-alive timers. The correct
|
|
125
|
+
* entry point on serverless/edge (Workers, Vercel, Lambda), where `start()`'s
|
|
126
|
+
* `setInterval` loop can't persist between invocations — call this from a
|
|
127
|
+
* scheduled/cron trigger. Throws if a sync push fails (so the run is marked
|
|
128
|
+
* failed); heartbeat is best-effort.
|
|
129
|
+
*/
|
|
130
|
+
runScheduled(options?: {
|
|
131
|
+
mode?: SyncMode;
|
|
132
|
+
runId?: string;
|
|
133
|
+
}): Promise<void>;
|
|
134
|
+
/**
|
|
135
|
+
* Send the initial heartbeat and start the keep-alive loop. For long-lived
|
|
136
|
+
* hosts only — on serverless/edge the interval can't persist, so prefer
|
|
137
|
+
* `runScheduled()` / `createClivlyHandler()`. Emits a warning if a serverless
|
|
138
|
+
* runtime is detected.
|
|
139
|
+
*/
|
|
140
|
+
start(): Promise<void>;
|
|
141
|
+
/** Stop the keep-alive loop. */
|
|
142
|
+
stop(): void;
|
|
143
|
+
/**
|
|
144
|
+
* Sync all configured sources now. `delta` (default) pages changed rows since
|
|
145
|
+
* the last cursor; `full` pushes each complete entity so the server can
|
|
146
|
+
* reconcile leavers (archive-on-leave). Throws if a push fails, so callers see
|
|
147
|
+
* the failure — the background keep-alive loop swallows and retries instead.
|
|
148
|
+
*/
|
|
149
|
+
sync(options?: {
|
|
150
|
+
mode?: SyncMode;
|
|
151
|
+
runId?: string;
|
|
152
|
+
}): Promise<void>;
|
|
153
|
+
}
|
|
154
|
+
//#endregion
|
|
155
|
+
export { SyncSource as n, ClivlySDK as t };
|