@clivly/sdk 0.9.1 → 0.9.3
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 +92 -22
- package/dist/chat-session.d.cts +1 -1
- package/dist/chat-session.d.mts +1 -1
- package/dist/drizzle.d.cts +1 -1
- package/dist/drizzle.d.mts +1 -1
- package/dist/index.cjs +766 -44
- package/dist/index.d.cts +3 -3
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +756 -46
- package/dist/{types-QPbki8ur.d.cts → types-Cgwxd4h_.d.cts} +71 -3
- package/dist/{types-QPbki8ur.d.mts → types-Cgwxd4h_.d.mts} +71 -3
- package/dist/vite.d.cts +1 -1
- package/dist/vite.d.mts +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# @clivly/sdk
|
|
2
2
|
|
|
3
|
-
Connect your app to Clivly Cloud. The SDK
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
Connect your app to Clivly Cloud. The SDK reports your schema for entity mapping
|
|
4
|
+
and mirrors your contacts and companies over outbound HTTPS. Long-lived Node
|
|
5
|
+
hosts can opt into WebSocket presence, which replaces the periodic HTTP
|
|
6
|
+
liveness heartbeat while the socket is ready and falls back automatically if it
|
|
7
|
+
is not.
|
|
7
8
|
|
|
8
9
|
Clivly never connects to your database — the SDK runs inside your app and is the
|
|
9
10
|
only thing that touches your data.
|
|
@@ -69,6 +70,9 @@ export default createClivlySDK({
|
|
|
69
70
|
cursorField: "updatedAt",
|
|
70
71
|
}),
|
|
71
72
|
},
|
|
73
|
+
presence: {
|
|
74
|
+
transport: "websocket", // long-lived Node hosts only; HTTP fallback is automatic
|
|
75
|
+
},
|
|
72
76
|
});
|
|
73
77
|
```
|
|
74
78
|
|
|
@@ -96,13 +100,44 @@ The single most consequential decision in the setup.
|
|
|
96
100
|
|
|
97
101
|
| Host runtime | Use | Why |
|
|
98
102
|
| --- | --- | --- |
|
|
99
|
-
| Long-lived Node server, Vite dev | `clivlyVite(sdk)` or `await sdk.start()` | The
|
|
103
|
+
| Long-lived Node server, Vite dev | `clivlyVite(sdk)` or `await sdk.start()` | The keep-alive loop persists; optionally use WebSocket presence |
|
|
100
104
|
| Cloudflare Workers, Vercel, Lambda | `sdk.createClivlyHandler()` at a route, and/or `sdk.runScheduled()` from cron | `setInterval` does **not** survive between invocations |
|
|
101
105
|
| One-off health check | `await sdk.connect()` | Structured `{ ok, status, reason, retryable, org? }` |
|
|
102
106
|
|
|
103
107
|
`start()` emits a console warning when it detects a serverless runtime, because
|
|
104
108
|
there it stops ticking after the first response.
|
|
105
109
|
|
|
110
|
+
### WebSocket presence
|
|
111
|
+
|
|
112
|
+
HTTP is the default presence transport. To replace the 60-second HTTP liveness
|
|
113
|
+
cadence on a long-lived Node host, opt in:
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
createClivlySDK({
|
|
117
|
+
// ...the rest of your configuration
|
|
118
|
+
presence: {
|
|
119
|
+
transport: "websocket",
|
|
120
|
+
deploymentId: process.env.CLIVLY_DEPLOYMENT_ID, // optional
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
`start()` performs its initial HTTP check-in, then opens an authenticated
|
|
126
|
+
outbound socket. When the server confirms it is ready, the socket becomes the
|
|
127
|
+
liveness signal and the periodic HTTP heartbeat pauses. A closed or unavailable
|
|
128
|
+
socket reconnects with backoff while the SDK resumes HTTP heartbeat
|
|
129
|
+
automatically. This does not alter `sync.intervalMs`; it only changes presence.
|
|
130
|
+
|
|
131
|
+
Keep the default HTTP transport for serverless and edge runtimes. They cannot
|
|
132
|
+
retain a WebSocket across invocations, so use `runScheduled()` or
|
|
133
|
+
`createClivlyHandler()` there.
|
|
134
|
+
|
|
135
|
+
For rollout reliability, enabled WebSocket presence emits bounded lifecycle
|
|
136
|
+
metrics (event, transport, outcome, state, duration and attempt). It never
|
|
137
|
+
includes credentials, organization or deployment identifiers, host URLs, row
|
|
138
|
+
data, error messages, or stack traces. See
|
|
139
|
+
[SECURITY.md](../../SECURITY.md#telemetry).
|
|
140
|
+
|
|
106
141
|
### Entities vs. sources — the distinction that bites
|
|
107
142
|
|
|
108
143
|
```ts
|
|
@@ -397,7 +432,9 @@ is honoured. Other `4xx` responses return immediately.
|
|
|
397
432
|
| `schema` | `DiscoveredTable[]` | — | Use `discoverFromDrizzle(schema)`. It reports keys, types, nullability and FK edges alongside column names; [derived objects](#derived-objects) cannot be authored without them, so prefer it over a hand-written array |
|
|
398
433
|
| `source` | `{ contacts, companies?, deals?, custom? }` | — | Keyed by CRM concept. `contacts` is required when `source` is present |
|
|
399
434
|
| `cursorStore` | `CursorStore` | in-memory | Persist on serverless |
|
|
400
|
-
| `heartbeatIntervalMs` | `number` | `60000` | |
|
|
435
|
+
| `heartbeatIntervalMs` | `number` | `60000` | HTTP presence cadence and default sync cadence; paused when opt-in WebSocket presence is ready |
|
|
436
|
+
| `presence.transport` | `"http" \\| "websocket"` | `"http"` | WebSocket is for long-lived Node hosts; HTTP resumes during reconnect/fallback |
|
|
437
|
+
| `presence.deploymentId` | `string` | platform-derived | Optional release identity for a WebSocket deployment |
|
|
401
438
|
| `sync.onBoot` | `boolean` | `true` | Only affects `start()` |
|
|
402
439
|
| `sync.intervalMs` | `number` | `heartbeatIntervalMs` | |
|
|
403
440
|
| `sync.batchSize` | `number` | `500` | Rows per page |
|
|
@@ -416,14 +453,28 @@ is honoured. Other `4xx` responses return immediately.
|
|
|
416
453
|
| Method | Returns | Notes |
|
|
417
454
|
| --- | --- | --- |
|
|
418
455
|
| `connect()` | `Promise<ClivlyConnectResult>` | One heartbeat, structured. Never throws |
|
|
456
|
+
| `ping()` | `Promise<ClivlyConnectResult>` | Zero-write reachability probe. Same shape as `connect()`, but records no heartbeat — and it alone returns `workspace` |
|
|
419
457
|
| `heartbeat()` | `Promise<boolean>` | One heartbeat, boolean |
|
|
420
|
-
| `start()` | `Promise<void>` | Heartbeat + boot sync + timers. Long-lived hosts only |
|
|
458
|
+
| `start()` | `Promise<void>` | Heartbeat + boot sync + timers. Long-lived hosts only; opens opt-in WebSocket presence |
|
|
421
459
|
| `stop()` | `void` | Clears the timers |
|
|
422
460
|
| `sync(options?)` | `Promise<void>` | `{ mode?: "delta" \| "full", runId?: string }`. Throws on push failure |
|
|
423
461
|
| `runScheduled(options?)` | `Promise<void>` | One heartbeat + one sync, no timers. Throws on push failure |
|
|
424
462
|
| `createClivlyHandler()` | `(Request) => Promise<Response>` | Mount at a route |
|
|
425
463
|
| `createAuthVerifyHandler(resolveUser)` | `(Request) => Promise<Response>` | `503` unless `verifyToken` is set |
|
|
426
464
|
| `doctor()` | `Promise<DoctorReport>` | `{ ok, checks }` |
|
|
465
|
+
| `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 |
|
|
466
|
+
| `ensureCustomObjects()` | `Promise<{ slug, id, created }[] \| null>` | Object types only, no mappings. Superseded by `ensureSchema()` |
|
|
467
|
+
|
|
468
|
+
`clivly push-schema` calls `ensureSchema()` for you, so you rarely need it
|
|
469
|
+
directly — reach for it when you want config-as-code to run from your own
|
|
470
|
+
deploy script or a CI step.
|
|
471
|
+
|
|
472
|
+
The `null`-vs-throw split is deliberate. `null` means the request never landed
|
|
473
|
+
(network, or a Clivly older than the route), which is worth a retry. A rejection
|
|
474
|
+
means Clivly understood the config and declined it — most often because an
|
|
475
|
+
entity would overwrite a mapping someone edited in the dashboard — which is
|
|
476
|
+
worth reading, not retrying. A config-declared mapping is owned by your config;
|
|
477
|
+
a dashboard-edited one is never silently overwritten.
|
|
427
478
|
|
|
428
479
|
### `ClivlyConnectResult.reason`
|
|
429
480
|
|
|
@@ -431,6 +482,17 @@ is honoured. Other `4xx` responses return immediately.
|
|
|
431
482
|
`server_error` (5xx) · `client_error` (other 4xx) · `network_error` (no
|
|
432
483
|
response).
|
|
433
484
|
|
|
485
|
+
### `ClivlyConnectResult.workspace`
|
|
486
|
+
|
|
487
|
+
`ping()` also returns `workspace` — the setup ladder for the org behind your key
|
|
488
|
+
(`IntegrationState` from `@clivly/core`), computed by the server so the CLI and
|
|
489
|
+
the dashboard cannot disagree about how far setup has got. This is what
|
|
490
|
+
`clivly status` prints under **Workspace**.
|
|
491
|
+
|
|
492
|
+
It is `undefined` or `null` when the server predates the field or could not read
|
|
493
|
+
it. Treat that as *unknown*, never as *nothing is set up* — an old server, a
|
|
494
|
+
rejected key and a dead network all mean the question went unanswered.
|
|
495
|
+
|
|
434
496
|
### Subpath exports
|
|
435
497
|
|
|
436
498
|
| From `clivly` | From `@clivly/sdk` | Contents |
|
|
@@ -444,21 +506,27 @@ response).
|
|
|
444
506
|
|
|
445
507
|
A custom object can be derived from a base table plus explicit joins, instead of
|
|
446
508
|
mapping exactly one table — an `Enrollment` built from `enrollments` joined to
|
|
447
|
-
`students` and `courses`. You
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
509
|
+
`students` and `courses`. You declare one on an entity in `clivly.config.ts`,
|
|
510
|
+
alongside the object it materializes into; `validateEntitiesConfig` checks it in
|
|
511
|
+
CI.
|
|
512
|
+
|
|
513
|
+
Two things have to line up:
|
|
514
|
+
|
|
515
|
+
- **Discovery must be rich enough.** `discoverFromDrizzle` sends primary keys,
|
|
516
|
+
unique constraints, nullability, column types and foreign-key edges.
|
|
517
|
+
Validation is fail-closed, so if your config hand-writes `schema` as
|
|
518
|
+
`{ name, columns }` pairs, every table is rejected with *"has no key/column
|
|
519
|
+
metadata — upgrade `@clivly/sdk` and re-run discovery"*. Pass
|
|
520
|
+
`discoverFromDrizzle(schema)` and redeploy so a fresh heartbeat carries the
|
|
521
|
+
richer payload — and re-run it after schema changes, since projections
|
|
522
|
+
validate against the last reported snapshot.
|
|
523
|
+
- **The server must have the capability enabled.** Authoring sits behind
|
|
524
|
+
`CLIVLY_PROJECTION_MAPPINGS=true` while the surface settles. Reading and
|
|
525
|
+
syncing an existing derived object is never gated.
|
|
526
|
+
|
|
527
|
+
> Requires `clivly@0.9.0` / `@clivly/sdk@0.9.0` or later for the discovery
|
|
528
|
+
> metadata. Earlier versions reported names only, so no host app could author a
|
|
529
|
+
> derived object.
|
|
462
530
|
|
|
463
531
|
Full guide: [`docs/guides/derived-objects.md`](../../docs/guides/derived-objects.md).
|
|
464
532
|
|
|
@@ -466,6 +534,8 @@ Full guide: [`docs/guides/derived-objects.md`](../../docs/guides/derived-objects
|
|
|
466
534
|
|
|
467
535
|
- `start()` is for long-lived runtimes only. On serverless/edge, use
|
|
468
536
|
`runScheduled()` or `createClivlyHandler()`.
|
|
537
|
+
- WebSocket presence is opt-in and long-lived Node-only. It does not replace
|
|
538
|
+
HTTP presence on serverless or edge hosts.
|
|
469
539
|
- `fromDrizzle` is the only generated source path. Non-Drizzle hosts write
|
|
470
540
|
`fetchPage` by hand.
|
|
471
541
|
- The flat `contactEntity` / `contactFields` config still works but is
|
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-Cgwxd4h_.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-Cgwxd4h_.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.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { _ as NamespaceIntrospector, g as SyncSource } from "./types-Cgwxd4h_.cjs";
|
|
2
2
|
import { Table } from "drizzle-orm";
|
|
3
3
|
import { buildUniqueConstraints, discoverFromDrizzle } from "@clivly/core/drizzle";
|
|
4
4
|
//#region src/drizzle.d.ts
|
package/dist/drizzle.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { _ as NamespaceIntrospector, g as SyncSource } from "./types-Cgwxd4h_.mjs";
|
|
2
2
|
import { buildUniqueConstraints, discoverFromDrizzle } from "@clivly/core/drizzle";
|
|
3
3
|
import { Table } from "drizzle-orm";
|
|
4
4
|
//#region src/drizzle.d.ts
|