@clivly/sdk 0.9.5 → 0.9.6
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 +7 -0
- package/dist/chat-session.d.cts +1 -1
- package/dist/chat-session.d.mts +1 -1
- package/dist/drizzle.cjs +17 -0
- package/dist/drizzle.d.cts +1 -1
- package/dist/drizzle.d.mts +1 -1
- package/dist/drizzle.mjs +18 -1
- package/dist/index.cjs +30 -6
- package/dist/index.d.cts +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +30 -6
- package/dist/{types-Dz1LQYvQ.d.cts → types-CiFEalbP.d.cts} +45 -2
- package/dist/{types-Dz1LQYvQ.d.mts → types-CiFEalbP.d.mts} +45 -2
- package/dist/vite.d.cts +1 -1
- package/dist/vite.d.mts +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -330,6 +330,12 @@ import { drizzleIntrospector } from "clivly/drizzle";
|
|
|
330
330
|
createClivlySDK({ /* … */, introspector: drizzleIntrospector(db) });
|
|
331
331
|
```
|
|
332
332
|
|
|
333
|
+
Setting `introspector` also lets Clivly's dashboard **preview a derived object**
|
|
334
|
+
before you save it. Clivly never connects to your database, so your app runs
|
|
335
|
+
that query — read-only, capped at 50 rows, through the sync-trigger route you
|
|
336
|
+
already expose. Without it the dashboard says preview is unavailable and names
|
|
337
|
+
this setting rather than failing.
|
|
338
|
+
|
|
333
339
|
## Common errors and troubleshooting
|
|
334
340
|
|
|
335
341
|
| Symptom | Cause | Fix |
|
|
@@ -468,6 +474,7 @@ is honoured. Other `4xx` responses return immediately.
|
|
|
468
474
|
| `createClivlyHandler()` | `(Request) => Promise<Response>` | Mount at a route |
|
|
469
475
|
| `createAuthVerifyHandler(resolveUser)` | `(Request) => Promise<Response>` | `503` unless `verifyToken` is set |
|
|
470
476
|
| `doctor()` | `Promise<DoctorReport>` | `{ ok, checks }` |
|
|
477
|
+
| `describeSyncTrigger()` | `SyncTriggerDescription` | `{ configured, resolved }` — the raw `syncTrigger.url` and the URL that would actually be reported. Computed per call: `resolved` can be `undefined` until the mounted route has served its first request |
|
|
471
478
|
| `ensureSchema()` | `Promise<EnsureSchemaResult \| null>` | Declares your custom object types **and** the mappings that make them receive rows. `null` = could not deliver; **rejects** if Clivly refuses the config |
|
|
472
479
|
| `ensureCustomObjects()` | `Promise<{ slug, id, created }[] \| null>` | Object types only, no mappings. Superseded by `ensureSchema()` |
|
|
473
480
|
|
package/dist/chat-session.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as CreateChatSessionHandlerOptions } from "./types-
|
|
1
|
+
import { c as CreateChatSessionHandlerOptions } from "./types-CiFEalbP.cjs";
|
|
2
2
|
//#region src/chat-session.d.ts
|
|
3
3
|
declare function createChatSessionHandler({ allowedOrigins, apiKey, apiUrl, fetchImpl, resolveUser }: CreateChatSessionHandlerOptions): (request: Request) => Promise<Response>;
|
|
4
4
|
//#endregion
|
package/dist/chat-session.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as CreateChatSessionHandlerOptions } from "./types-
|
|
1
|
+
import { c as CreateChatSessionHandlerOptions } from "./types-CiFEalbP.mjs";
|
|
2
2
|
//#region src/chat-session.d.ts
|
|
3
3
|
declare function createChatSessionHandler({ allowedOrigins, apiKey, apiUrl, fetchImpl, resolveUser }: CreateChatSessionHandlerOptions): (request: Request) => Promise<Response>;
|
|
4
4
|
//#endregion
|
package/dist/drizzle.cjs
CHANGED
|
@@ -3,6 +3,20 @@ const require_drizzle_meta = require("./drizzle-meta-CdCs30q3.cjs");
|
|
|
3
3
|
let _clivly_core_drizzle = require("@clivly/core/drizzle");
|
|
4
4
|
let drizzle_orm = require("drizzle-orm");
|
|
5
5
|
let _clivly_core = require("@clivly/core");
|
|
6
|
+
//#region src/projection-preview.ts
|
|
7
|
+
/** `db.execute` returns either an array or `{ rows }`, depending on driver. */
|
|
8
|
+
function rowsOf(result) {
|
|
9
|
+
if (Array.isArray(result)) return result;
|
|
10
|
+
const rows = result.rows;
|
|
11
|
+
if (Array.isArray(rows)) return rows;
|
|
12
|
+
throw new Error("previewProjection: db.execute() returned neither an array of rows nor { rows }.");
|
|
13
|
+
}
|
|
14
|
+
async function previewProjection(db, projection, opts) {
|
|
15
|
+
const client = db;
|
|
16
|
+
const { sql } = await import("drizzle-orm");
|
|
17
|
+
return rowsOf(await client.execute(sql.raw((0, _clivly_core.compileProjectionPreview)(projection, opts.limit))));
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
6
20
|
//#region src/drizzle-projection.ts
|
|
7
21
|
/** `<ref>.<column>` — the token `fields` carries and cloud-ingest reads. */
|
|
8
22
|
function refToken(ref) {
|
|
@@ -134,6 +148,9 @@ function drizzleIntrospector(db) {
|
|
|
134
148
|
},
|
|
135
149
|
async sampleSelect(table) {
|
|
136
150
|
await client.execute(drizzle_orm.sql`select 1 from ${drizzle_orm.sql.identifier(table)} limit 1`);
|
|
151
|
+
},
|
|
152
|
+
runProjection(projection, opts) {
|
|
153
|
+
return previewProjection(db, projection, opts);
|
|
137
154
|
}
|
|
138
155
|
};
|
|
139
156
|
}
|
package/dist/drizzle.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { g as SyncSource, v as NamespaceIntrospector } from "./types-CiFEalbP.cjs";
|
|
2
2
|
import { ProjectionMapping, ProjectionRef } from "@clivly/core";
|
|
3
3
|
import { Table } from "drizzle-orm";
|
|
4
4
|
import { buildUniqueConstraints, discoverFromDrizzle } from "@clivly/core/drizzle";
|
package/dist/drizzle.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { g as SyncSource, v as NamespaceIntrospector } from "./types-CiFEalbP.mjs";
|
|
2
2
|
import { buildUniqueConstraints, discoverFromDrizzle } from "@clivly/core/drizzle";
|
|
3
3
|
import { Table } from "drizzle-orm";
|
|
4
4
|
import { ProjectionMapping, ProjectionRef } from "@clivly/core";
|
package/dist/drizzle.mjs
CHANGED
|
@@ -1,7 +1,21 @@
|
|
|
1
1
|
import { t as DRIZZLE_META } from "./drizzle-meta-DNqzkREf.mjs";
|
|
2
2
|
import { buildColumnsMeta, buildUniqueConstraints, buildUniqueConstraints as buildUniqueConstraints$1, discoverFromDrizzle } from "@clivly/core/drizzle";
|
|
3
3
|
import { asc, getTableColumns, getTableName, sql } from "drizzle-orm";
|
|
4
|
-
import { BASE_REF, compileProjectionFrom, compileProjectionIdentity, compileProjectionJoins, quoteIdent } from "@clivly/core";
|
|
4
|
+
import { BASE_REF, compileProjectionFrom, compileProjectionIdentity, compileProjectionJoins, compileProjectionPreview, quoteIdent } from "@clivly/core";
|
|
5
|
+
//#region src/projection-preview.ts
|
|
6
|
+
/** `db.execute` returns either an array or `{ rows }`, depending on driver. */
|
|
7
|
+
function rowsOf(result) {
|
|
8
|
+
if (Array.isArray(result)) return result;
|
|
9
|
+
const rows = result.rows;
|
|
10
|
+
if (Array.isArray(rows)) return rows;
|
|
11
|
+
throw new Error("previewProjection: db.execute() returned neither an array of rows nor { rows }.");
|
|
12
|
+
}
|
|
13
|
+
async function previewProjection(db, projection, opts) {
|
|
14
|
+
const client = db;
|
|
15
|
+
const { sql } = await import("drizzle-orm");
|
|
16
|
+
return rowsOf(await client.execute(sql.raw(compileProjectionPreview(projection, opts.limit))));
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
5
19
|
//#region src/drizzle-projection.ts
|
|
6
20
|
/** `<ref>.<column>` — the token `fields` carries and cloud-ingest reads. */
|
|
7
21
|
function refToken(ref) {
|
|
@@ -133,6 +147,9 @@ function drizzleIntrospector(db) {
|
|
|
133
147
|
},
|
|
134
148
|
async sampleSelect(table) {
|
|
135
149
|
await client.execute(sql`select 1 from ${sql.identifier(table)} limit 1`);
|
|
150
|
+
},
|
|
151
|
+
runProjection(projection, opts) {
|
|
152
|
+
return previewProjection(db, projection, opts);
|
|
136
153
|
}
|
|
137
154
|
};
|
|
138
155
|
}
|
package/dist/index.cjs
CHANGED
|
@@ -574,12 +574,21 @@ async function runDoctor(args) {
|
|
|
574
574
|
ok: entityCount > 0,
|
|
575
575
|
detail: entityCount > 0 ? `${entityCount} entit${entityCount === 1 ? "y" : "ies"} configured.` : "No `entities` config — pass defineClivlyConfig(...) as `entities`."
|
|
576
576
|
});
|
|
577
|
+
const sourcesDetail = (count, deferred) => {
|
|
578
|
+
if (deferred) return "Deferred — the coverage check below names the entity and the source key it expected.";
|
|
579
|
+
return count === 0 ? "No `source.contacts` — nothing to sync." : `${count} source(s) to mirror.`;
|
|
580
|
+
};
|
|
581
|
+
const coverage = checkConceptCoverage(config);
|
|
582
|
+
const coverageOwnsIt = !(coverage.ok || coverage.skipped);
|
|
583
|
+
const noSources = sources.length === 0;
|
|
584
|
+
const deferred = noSources && coverageOwnsIt;
|
|
577
585
|
checks.push({
|
|
578
586
|
name: "Sources configured",
|
|
579
|
-
ok:
|
|
580
|
-
|
|
587
|
+
ok: !noSources,
|
|
588
|
+
skipped: deferred,
|
|
589
|
+
detail: sourcesDetail(sources.length, deferred)
|
|
581
590
|
});
|
|
582
|
-
checks.push(
|
|
591
|
+
checks.push(coverage);
|
|
583
592
|
checks.push(...cursorColumnChecks(sources));
|
|
584
593
|
for (const source of sources) {
|
|
585
594
|
const check = cursorMonotonicityCheck(source);
|
|
@@ -1446,6 +1455,15 @@ async function handlePreviewRequest(config, preview) {
|
|
|
1446
1455
|
sampledCount: rows.length
|
|
1447
1456
|
}, 200);
|
|
1448
1457
|
}
|
|
1458
|
+
async function handleProjectionPreviewRequest(config, request) {
|
|
1459
|
+
const run = config.introspector?.runProjection;
|
|
1460
|
+
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);
|
|
1461
|
+
const rows = await run.call(config.introspector, request.projection, { limit: request.limit });
|
|
1462
|
+
return jsonResponse({
|
|
1463
|
+
rows,
|
|
1464
|
+
sampledCount: rows.length
|
|
1465
|
+
}, 200);
|
|
1466
|
+
}
|
|
1449
1467
|
/**
|
|
1450
1468
|
* Create the Clivly SDK. The implemented surface — heartbeat/discovery reporting
|
|
1451
1469
|
* and the auth-verify handler — connects a developer's app to a running Clivly
|
|
@@ -1575,13 +1593,17 @@ function createClivlySDK(config) {
|
|
|
1575
1593
|
lastPushFailure = null;
|
|
1576
1594
|
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
1595
|
};
|
|
1578
|
-
const
|
|
1579
|
-
|
|
1596
|
+
const describeSyncTrigger = () => ({
|
|
1597
|
+
configured: config.syncTrigger?.url,
|
|
1598
|
+
resolved: resolveTriggerUrl({
|
|
1580
1599
|
configuredUrl: config.syncTrigger?.url,
|
|
1581
1600
|
configuredPath: config.syncTrigger?.path,
|
|
1582
1601
|
env: config.env ?? process.env,
|
|
1583
1602
|
observedUrl
|
|
1584
|
-
})
|
|
1603
|
+
})
|
|
1604
|
+
});
|
|
1605
|
+
const sendHeartbeat = () => {
|
|
1606
|
+
const triggerUrl = describeSyncTrigger().resolved;
|
|
1585
1607
|
return fetcher(`${apiUrl}/clivly/sdk/heartbeat`, {
|
|
1586
1608
|
method: "POST",
|
|
1587
1609
|
headers: {
|
|
@@ -1809,6 +1831,7 @@ function createClivlySDK(config) {
|
|
|
1809
1831
|
const startedAt = Date.now();
|
|
1810
1832
|
try {
|
|
1811
1833
|
if (payload.preview) return await handlePreviewRequest(config, payload.preview);
|
|
1834
|
+
if (payload.previewProjection) return await handleProjectionPreviewRequest(config, payload.previewProjection);
|
|
1812
1835
|
if (payload.sample) await runSampleSync(payload.sample.entityKey, payload.runId);
|
|
1813
1836
|
else await runScheduled({
|
|
1814
1837
|
mode: payload.mode,
|
|
@@ -1904,6 +1927,7 @@ function createClivlySDK(config) {
|
|
|
1904
1927
|
connect,
|
|
1905
1928
|
ping,
|
|
1906
1929
|
doctor,
|
|
1930
|
+
describeSyncTrigger,
|
|
1907
1931
|
runScheduled,
|
|
1908
1932
|
createClivlyHandler,
|
|
1909
1933
|
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-
|
|
1
|
+
import { _ as SyncTriggerDescription, 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-CiFEalbP.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 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-
|
|
1
|
+
import { _ as SyncTriggerDescription, 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-CiFEalbP.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 SyncTriggerDescription, createChatSessionHandler, createClivlySDK, customObjectDeclarations, defineClivlyConfig, isPlaceholderUrl, validateEntitiesConfig };
|
package/dist/index.mjs
CHANGED
|
@@ -573,12 +573,21 @@ async function runDoctor(args) {
|
|
|
573
573
|
ok: entityCount > 0,
|
|
574
574
|
detail: entityCount > 0 ? `${entityCount} entit${entityCount === 1 ? "y" : "ies"} configured.` : "No `entities` config — pass defineClivlyConfig(...) as `entities`."
|
|
575
575
|
});
|
|
576
|
+
const sourcesDetail = (count, deferred) => {
|
|
577
|
+
if (deferred) return "Deferred — the coverage check below names the entity and the source key it expected.";
|
|
578
|
+
return count === 0 ? "No `source.contacts` — nothing to sync." : `${count} source(s) to mirror.`;
|
|
579
|
+
};
|
|
580
|
+
const coverage = checkConceptCoverage(config);
|
|
581
|
+
const coverageOwnsIt = !(coverage.ok || coverage.skipped);
|
|
582
|
+
const noSources = sources.length === 0;
|
|
583
|
+
const deferred = noSources && coverageOwnsIt;
|
|
576
584
|
checks.push({
|
|
577
585
|
name: "Sources configured",
|
|
578
|
-
ok:
|
|
579
|
-
|
|
586
|
+
ok: !noSources,
|
|
587
|
+
skipped: deferred,
|
|
588
|
+
detail: sourcesDetail(sources.length, deferred)
|
|
580
589
|
});
|
|
581
|
-
checks.push(
|
|
590
|
+
checks.push(coverage);
|
|
582
591
|
checks.push(...cursorColumnChecks(sources));
|
|
583
592
|
for (const source of sources) {
|
|
584
593
|
const check = cursorMonotonicityCheck(source);
|
|
@@ -1445,6 +1454,15 @@ async function handlePreviewRequest(config, preview) {
|
|
|
1445
1454
|
sampledCount: rows.length
|
|
1446
1455
|
}, 200);
|
|
1447
1456
|
}
|
|
1457
|
+
async function handleProjectionPreviewRequest(config, request) {
|
|
1458
|
+
const run = config.introspector?.runProjection;
|
|
1459
|
+
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);
|
|
1460
|
+
const rows = await run.call(config.introspector, request.projection, { limit: request.limit });
|
|
1461
|
+
return jsonResponse({
|
|
1462
|
+
rows,
|
|
1463
|
+
sampledCount: rows.length
|
|
1464
|
+
}, 200);
|
|
1465
|
+
}
|
|
1448
1466
|
/**
|
|
1449
1467
|
* Create the Clivly SDK. The implemented surface — heartbeat/discovery reporting
|
|
1450
1468
|
* and the auth-verify handler — connects a developer's app to a running Clivly
|
|
@@ -1574,13 +1592,17 @@ function createClivlySDK(config) {
|
|
|
1574
1592
|
lastPushFailure = null;
|
|
1575
1593
|
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]));
|
|
1576
1594
|
};
|
|
1577
|
-
const
|
|
1578
|
-
|
|
1595
|
+
const describeSyncTrigger = () => ({
|
|
1596
|
+
configured: config.syncTrigger?.url,
|
|
1597
|
+
resolved: resolveTriggerUrl({
|
|
1579
1598
|
configuredUrl: config.syncTrigger?.url,
|
|
1580
1599
|
configuredPath: config.syncTrigger?.path,
|
|
1581
1600
|
env: config.env ?? process.env,
|
|
1582
1601
|
observedUrl
|
|
1583
|
-
})
|
|
1602
|
+
})
|
|
1603
|
+
});
|
|
1604
|
+
const sendHeartbeat = () => {
|
|
1605
|
+
const triggerUrl = describeSyncTrigger().resolved;
|
|
1584
1606
|
return fetcher(`${apiUrl}/clivly/sdk/heartbeat`, {
|
|
1585
1607
|
method: "POST",
|
|
1586
1608
|
headers: {
|
|
@@ -1808,6 +1830,7 @@ function createClivlySDK(config) {
|
|
|
1808
1830
|
const startedAt = Date.now();
|
|
1809
1831
|
try {
|
|
1810
1832
|
if (payload.preview) return await handlePreviewRequest(config, payload.preview);
|
|
1833
|
+
if (payload.previewProjection) return await handleProjectionPreviewRequest(config, payload.previewProjection);
|
|
1811
1834
|
if (payload.sample) await runSampleSync(payload.sample.entityKey, payload.runId);
|
|
1812
1835
|
else await runScheduled({
|
|
1813
1836
|
mode: payload.mode,
|
|
@@ -1903,6 +1926,7 @@ function createClivlySDK(config) {
|
|
|
1903
1926
|
connect,
|
|
1904
1927
|
ping,
|
|
1905
1928
|
doctor,
|
|
1929
|
+
describeSyncTrigger,
|
|
1906
1930
|
runScheduled,
|
|
1907
1931
|
createClivlyHandler,
|
|
1908
1932
|
sync: runSync,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ClivlyEntitiesConfig, IntegrationState } from "@clivly/core";
|
|
1
|
+
import { ClivlyEntitiesConfig, IntegrationState, ProjectionMapping } from "@clivly/core";
|
|
2
2
|
//#region src/drizzle-meta.d.ts
|
|
3
3
|
declare const DRIZZLE_META: "__clivly_drizzle_source_meta__";
|
|
4
4
|
interface DrizzleSourceMeta {
|
|
@@ -18,6 +18,23 @@ interface DrizzleSourceMeta {
|
|
|
18
18
|
interface NamespaceIntrospector {
|
|
19
19
|
/** All base table names in the reachable schema. */
|
|
20
20
|
listTables(): Promise<string[]>;
|
|
21
|
+
/**
|
|
22
|
+
* Read a few rows through an unsaved projection, for the dashboard's derived
|
|
23
|
+
* object preview. Rows are keyed by field name.
|
|
24
|
+
*
|
|
25
|
+
* Optional because it is the only capability here that needs more than a
|
|
26
|
+
* table name, and because an introspector may be hand-written. Absent, the
|
|
27
|
+
* dashboard says preview is unavailable rather than failing — see
|
|
28
|
+
* `previewProjection`.
|
|
29
|
+
*
|
|
30
|
+
* This is what makes preview cost the host an SDK upgrade and nothing else:
|
|
31
|
+
* `drizzleIntrospector(db)` already closes over the connection, so a config
|
|
32
|
+
* that set `introspector` for the "Namespace reachable" check gains preview
|
|
33
|
+
* without touching a line.
|
|
34
|
+
*/
|
|
35
|
+
runProjection?(projection: ProjectionMapping, opts: {
|
|
36
|
+
limit: number;
|
|
37
|
+
}): Promise<Record<string, unknown>[]>;
|
|
21
38
|
/** Prove a table is genuinely selectable (SELECT ... LIMIT 1). Throws if unreachable/denied. */
|
|
22
39
|
sampleSelect(table: string): Promise<void>;
|
|
23
40
|
}
|
|
@@ -347,6 +364,20 @@ interface CursorStore {
|
|
|
347
364
|
get(entity: string): Promise<SyncCursor | null> | SyncCursor | null;
|
|
348
365
|
set(entity: string, cursor: SyncCursor | null): Promise<void> | void;
|
|
349
366
|
}
|
|
367
|
+
/** @see ClivlySDK.describeSyncTrigger */
|
|
368
|
+
interface SyncTriggerDescription {
|
|
369
|
+
/**
|
|
370
|
+
* `syncTrigger.url` exactly as configured, placeholder and all. The raw
|
|
371
|
+
* value, so a caller can say what is in the file rather than what survived
|
|
372
|
+
* resolution.
|
|
373
|
+
*/
|
|
374
|
+
configured?: string;
|
|
375
|
+
/**
|
|
376
|
+
* The URL that would be reported to Clivly on the next heartbeat, or
|
|
377
|
+
* `undefined` when nothing resolves. An unedited placeholder never resolves.
|
|
378
|
+
*/
|
|
379
|
+
resolved?: string;
|
|
380
|
+
}
|
|
350
381
|
interface ClivlySDK {
|
|
351
382
|
/**
|
|
352
383
|
* Send a single heartbeat and report the outcome richly: `ok`, the HTTP
|
|
@@ -382,6 +413,18 @@ interface ClivlySDK {
|
|
|
382
413
|
* anyone who can reach it — set a secret if it's publicly routable.
|
|
383
414
|
*/
|
|
384
415
|
createClivlyHandler(): (req: Request) => Promise<Response>;
|
|
416
|
+
/**
|
|
417
|
+
* What this app would tell Clivly about its sync trigger route, right now.
|
|
418
|
+
*
|
|
419
|
+
* Computed per call rather than frozen at creation: `resolved` can start as
|
|
420
|
+
* `undefined` and become a real URL once the mounted route has served its
|
|
421
|
+
* first request and observed its own origin.
|
|
422
|
+
*
|
|
423
|
+
* Exists for `clivly status`, which has to tell "no trigger configured"
|
|
424
|
+
* apart from "configured, but still the scaffolded placeholder" — a
|
|
425
|
+
* distinction `resolved` alone erases, since it skips placeholders by design.
|
|
426
|
+
*/
|
|
427
|
+
describeSyncTrigger(): SyncTriggerDescription;
|
|
385
428
|
/** Validate this setup end-to-end (read-only) before the first sync. */
|
|
386
429
|
doctor(): Promise<DoctorReport>;
|
|
387
430
|
/**
|
|
@@ -454,4 +497,4 @@ interface ClivlySDK {
|
|
|
454
497
|
}): Promise<void>;
|
|
455
498
|
}
|
|
456
499
|
//#endregion
|
|
457
|
-
export {
|
|
500
|
+
export { SyncTriggerDescription as _, ClivlySDK as a, CreateChatSessionHandlerOptions as c, DoctorCheck as d, DoctorReport as f, SyncSource as g, EnsuredObjectTypeResult as h, ClivlyConnectResult as i, CursorStore as l, EnsuredMappingResult as m, ChatWidgetVisitor as n, ClivlySDKConfig as o, EnsureSchemaResult as p, ClivlyConnectReason as r, ClivlyUser as s, ChatSessionRequestBody as t, DiscoveredTable as u, NamespaceIntrospector as v };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ClivlyEntitiesConfig, IntegrationState } from "@clivly/core";
|
|
1
|
+
import { ClivlyEntitiesConfig, IntegrationState, ProjectionMapping } from "@clivly/core";
|
|
2
2
|
//#region src/drizzle-meta.d.ts
|
|
3
3
|
declare const DRIZZLE_META: "__clivly_drizzle_source_meta__";
|
|
4
4
|
interface DrizzleSourceMeta {
|
|
@@ -18,6 +18,23 @@ interface DrizzleSourceMeta {
|
|
|
18
18
|
interface NamespaceIntrospector {
|
|
19
19
|
/** All base table names in the reachable schema. */
|
|
20
20
|
listTables(): Promise<string[]>;
|
|
21
|
+
/**
|
|
22
|
+
* Read a few rows through an unsaved projection, for the dashboard's derived
|
|
23
|
+
* object preview. Rows are keyed by field name.
|
|
24
|
+
*
|
|
25
|
+
* Optional because it is the only capability here that needs more than a
|
|
26
|
+
* table name, and because an introspector may be hand-written. Absent, the
|
|
27
|
+
* dashboard says preview is unavailable rather than failing — see
|
|
28
|
+
* `previewProjection`.
|
|
29
|
+
*
|
|
30
|
+
* This is what makes preview cost the host an SDK upgrade and nothing else:
|
|
31
|
+
* `drizzleIntrospector(db)` already closes over the connection, so a config
|
|
32
|
+
* that set `introspector` for the "Namespace reachable" check gains preview
|
|
33
|
+
* without touching a line.
|
|
34
|
+
*/
|
|
35
|
+
runProjection?(projection: ProjectionMapping, opts: {
|
|
36
|
+
limit: number;
|
|
37
|
+
}): Promise<Record<string, unknown>[]>;
|
|
21
38
|
/** Prove a table is genuinely selectable (SELECT ... LIMIT 1). Throws if unreachable/denied. */
|
|
22
39
|
sampleSelect(table: string): Promise<void>;
|
|
23
40
|
}
|
|
@@ -347,6 +364,20 @@ interface CursorStore {
|
|
|
347
364
|
get(entity: string): Promise<SyncCursor | null> | SyncCursor | null;
|
|
348
365
|
set(entity: string, cursor: SyncCursor | null): Promise<void> | void;
|
|
349
366
|
}
|
|
367
|
+
/** @see ClivlySDK.describeSyncTrigger */
|
|
368
|
+
interface SyncTriggerDescription {
|
|
369
|
+
/**
|
|
370
|
+
* `syncTrigger.url` exactly as configured, placeholder and all. The raw
|
|
371
|
+
* value, so a caller can say what is in the file rather than what survived
|
|
372
|
+
* resolution.
|
|
373
|
+
*/
|
|
374
|
+
configured?: string;
|
|
375
|
+
/**
|
|
376
|
+
* The URL that would be reported to Clivly on the next heartbeat, or
|
|
377
|
+
* `undefined` when nothing resolves. An unedited placeholder never resolves.
|
|
378
|
+
*/
|
|
379
|
+
resolved?: string;
|
|
380
|
+
}
|
|
350
381
|
interface ClivlySDK {
|
|
351
382
|
/**
|
|
352
383
|
* Send a single heartbeat and report the outcome richly: `ok`, the HTTP
|
|
@@ -382,6 +413,18 @@ interface ClivlySDK {
|
|
|
382
413
|
* anyone who can reach it — set a secret if it's publicly routable.
|
|
383
414
|
*/
|
|
384
415
|
createClivlyHandler(): (req: Request) => Promise<Response>;
|
|
416
|
+
/**
|
|
417
|
+
* What this app would tell Clivly about its sync trigger route, right now.
|
|
418
|
+
*
|
|
419
|
+
* Computed per call rather than frozen at creation: `resolved` can start as
|
|
420
|
+
* `undefined` and become a real URL once the mounted route has served its
|
|
421
|
+
* first request and observed its own origin.
|
|
422
|
+
*
|
|
423
|
+
* Exists for `clivly status`, which has to tell "no trigger configured"
|
|
424
|
+
* apart from "configured, but still the scaffolded placeholder" — a
|
|
425
|
+
* distinction `resolved` alone erases, since it skips placeholders by design.
|
|
426
|
+
*/
|
|
427
|
+
describeSyncTrigger(): SyncTriggerDescription;
|
|
385
428
|
/** Validate this setup end-to-end (read-only) before the first sync. */
|
|
386
429
|
doctor(): Promise<DoctorReport>;
|
|
387
430
|
/**
|
|
@@ -454,4 +497,4 @@ interface ClivlySDK {
|
|
|
454
497
|
}): Promise<void>;
|
|
455
498
|
}
|
|
456
499
|
//#endregion
|
|
457
|
-
export {
|
|
500
|
+
export { SyncTriggerDescription as _, ClivlySDK as a, CreateChatSessionHandlerOptions as c, DoctorCheck as d, DoctorReport as f, SyncSource as g, EnsuredObjectTypeResult as h, ClivlyConnectResult as i, CursorStore as l, EnsuredMappingResult as m, ChatWidgetVisitor as n, ClivlySDKConfig as o, EnsureSchemaResult as p, ClivlyConnectReason as r, ClivlyUser as s, ChatSessionRequestBody as t, DiscoveredTable as u, NamespaceIntrospector as v };
|
package/dist/vite.d.cts
CHANGED
package/dist/vite.d.mts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clivly/sdk",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.6",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Clivly SDK — connect your app to Clivly Cloud (heartbeat, discovery, auth bridge).",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"access": "public"
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
|
-
"@clivly/core": "0.9.
|
|
58
|
+
"@clivly/core": "0.9.6"
|
|
59
59
|
},
|
|
60
60
|
"peerDependencies": {
|
|
61
61
|
"drizzle-orm": ">=0.30 || >=1.0.0-rc.0",
|