clivly 0.9.2 → 0.9.4
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 +60 -6
- package/core-entity-config.cjs +1 -1
- package/core-entity-config.mjs +1 -1
- package/core-mappings-config.cjs +1 -1
- package/core-mappings-config.mjs +1 -1
- package/core-sync-engine.cjs +1 -1
- package/core-sync-engine.mjs +1 -1
- package/core-view-compiler.cjs +1 -1
- package/core-view-compiler.mjs +1 -1
- package/core.cjs +11 -6
- package/core.d.cts +3 -2
- package/core.d.mts +3 -2
- package/core.mjs +8 -7
- package/drizzle.cjs +74 -9
- package/drizzle.d.cts +83 -7
- package/drizzle.d.mts +83 -7
- package/drizzle.mjs +74 -10
- package/{entity-config-DtBsl2MQ.cjs → entity-config-7sGCk_nw.cjs} +1 -1
- package/{entity-config-kUHri9yA.mjs → entity-config-B74SPOUZ.mjs} +1 -1
- package/index.cjs +141 -29
- package/index.d.cts +2 -1
- package/index.d.mts +2 -1
- package/index.mjs +141 -30
- package/integration-state-BK-riZnm.d.cts +130 -0
- package/integration-state-BK-riZnm.d.mts +130 -0
- package/{internal-Df-PtOH_.mjs → internal-CykXpEGk.mjs} +2 -2
- package/{internal-CwnEG6Wl.cjs → internal-DqduZU3l.cjs} +2 -2
- package/internal-entity-config.cjs +1 -1
- package/internal-entity-config.mjs +1 -1
- package/internal-entity-heuristics.cjs +1 -1
- package/internal-entity-heuristics.mjs +1 -1
- package/internal-mappings-config.cjs +1 -1
- package/internal-mappings-config.mjs +1 -1
- package/{internal-CDY8g9Ic.d.mts → internal-o2M7FGlR.d.mts} +29 -1
- package/internal-sync-engine.cjs +1 -1
- package/internal-sync-engine.mjs +1 -1
- package/internal-view-compiler.cjs +1 -1
- package/internal-view-compiler.mjs +1 -1
- package/{internal-Da_dlcQ_.d.cts → internal-wCXSELDN.d.cts} +29 -1
- package/internal.cjs +11 -6
- package/internal.d.cts +3 -2
- package/internal.d.mts +3 -2
- package/internal.mjs +8 -7
- package/{mappings-config-B0RqFLxu.cjs → mappings-config-CLyEg0sf.cjs} +1 -1
- package/{mappings-config-B1b1HVBg.mjs → mappings-config-DQS_s4dg.mjs} +1 -1
- package/{namespace-probe-C6aHjedO.d.cts → namespace-probe-BoGI18dQ.d.cts} +15 -0
- package/{namespace-probe-Dj-0Q3Hb.d.mts → namespace-probe-Cn7voAd0.d.mts} +15 -0
- package/package.json +1 -1
- package/{projection-D7xmQP4S.mjs → projection-CSGJbZlZ.mjs} +25 -4
- package/{projection-C5244vGa.cjs → projection-Dz6_i1-w.cjs} +25 -4
- package/projection-sql-BgnfiGqF.mjs +65 -0
- package/projection-sql-Cl1qIn8m.cjs +94 -0
- package/{sdk-D4Wp_uxb.cjs → sdk-CM1UO-I7.cjs} +59 -15
- package/{sdk-DXV8M_Io.mjs → sdk-DYBDR939.mjs} +54 -16
- package/sdk.cjs +3 -2
- package/sdk.d.cts +5 -2
- package/sdk.d.mts +5 -2
- package/sdk.mjs +3 -3
- package/{sync-engine-abE94g6X.cjs → sync-engine-C4A5jgA4.cjs} +1 -1
- package/{sync-engine-SuxSzbZ4.mjs → sync-engine-Dt5QQSKO.mjs} +1 -1
- package/{view-compiler-Blu4OXJ7.cjs → view-compiler-B5nDal14.cjs} +21 -74
- package/{view-compiler-CQx8TRrW.mjs → view-compiler-CSYwEWf5.mjs} +2 -55
- package/vite.d.cts +1 -1
- package/vite.d.mts +1 -1
package/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { t as CANONICAL_FIELDS } from "./entity-config-
|
|
2
|
+
import { t as CANONICAL_FIELDS } from "./entity-config-B74SPOUZ.mjs";
|
|
3
3
|
import "./mapping-score-C68uAqGB.mjs";
|
|
4
4
|
import { matchesConcept, requiredFieldsFor, scoreFieldMap, scoreRelationships, suggestCursorField, toFieldMap } from "./internal-entity-heuristics.mjs";
|
|
5
|
-
import { t as createClivlySDK } from "./sdk-
|
|
5
|
+
import { s as isPlaceholderUrl, t as createClivlySDK } from "./sdk-DYBDR939.mjs";
|
|
6
6
|
import { createRequire } from "node:module";
|
|
7
7
|
import { spawn } from "node:child_process";
|
|
8
8
|
import { accessSync, constants, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, statSync, watch, writeFileSync } from "node:fs";
|
|
@@ -291,10 +291,37 @@ function addCommand(pm, packages) {
|
|
|
291
291
|
};
|
|
292
292
|
}
|
|
293
293
|
//#endregion
|
|
294
|
+
//#region src/tick-route.ts
|
|
295
|
+
/**
|
|
296
|
+
* The tick route's public URL path, and the template `clivly login` writes for
|
|
297
|
+
* it.
|
|
298
|
+
*
|
|
299
|
+
* Every framework the CLI scaffolds mounts the tick handler at the same URL —
|
|
300
|
+
* the *files* differ (`app/api/clivly/tick/route.ts`,
|
|
301
|
+
* `src/routes/api/clivly/tick.ts`, `app/routes/api.clivly.tick.ts`, …) but each
|
|
302
|
+
* one resolves to `/api/clivly/tick`. So this is a constant, not a per-framework
|
|
303
|
+
* lookup, and `clivly dev`, the generated config and the `.env` template can all
|
|
304
|
+
* read from one place instead of repeating the string and drifting.
|
|
305
|
+
*/
|
|
306
|
+
const CLIVLY_TICK_PATH = "/api/clivly/tick";
|
|
307
|
+
/**
|
|
308
|
+
* What `clivly login` writes into `.env` for `CLIVLY_SYNC_TRIGGER_URL`.
|
|
309
|
+
*
|
|
310
|
+
* A visible template beats an empty line: the developer sees the shape they
|
|
311
|
+
* eventually need — a public origin plus the tick path — instead of an unhelpful
|
|
312
|
+
* `CLIVLY_SYNC_TRIGGER_URL=` they have to go and look up.
|
|
313
|
+
*
|
|
314
|
+
* It is safe to ship a truthy non-address here only because the SDK refuses to
|
|
315
|
+
* treat a bracketed value as configured (`isPlaceholderUrl` in
|
|
316
|
+
* `@clivly/sdk/trigger-url`). Change the brackets here and that guard stops
|
|
317
|
+
* matching, so the two must stay in step.
|
|
318
|
+
*/
|
|
319
|
+
const TRIGGER_URL_PLACEHOLDER = `https://<yourDomain.com>${CLIVLY_TICK_PATH}`;
|
|
320
|
+
//#endregion
|
|
294
321
|
//#region src/dev-tunnel.ts
|
|
295
322
|
const CLOUDFLARE_QUICK_TUNNEL_REGEX = /https:\/\/[a-z0-9-]+\.trycloudflare\.com/;
|
|
296
323
|
const DEFAULT_PORT = "3000";
|
|
297
|
-
const DEFAULT_ROUTE_PATH =
|
|
324
|
+
const DEFAULT_ROUTE_PATH = CLIVLY_TICK_PATH;
|
|
298
325
|
function parseTunnelUrl(chunk) {
|
|
299
326
|
const match = chunk.match(CLOUDFLARE_QUICK_TUNNEL_REGEX);
|
|
300
327
|
return match ? match[0] : null;
|
|
@@ -1001,7 +1028,7 @@ Usage:
|
|
|
1001
1028
|
clivly init [options] Scaffold Clivly (config, routes, env file) for your app
|
|
1002
1029
|
clivly add widget [options] Scaffold the chat-session route
|
|
1003
1030
|
clivly status [options] Check your setup, step by step
|
|
1004
|
-
clivly push-schema [options]
|
|
1031
|
+
clivly push-schema [options] Push schema, custom objects and mappings to Clivly
|
|
1005
1032
|
clivly dev [options] Tunnel localhost so remote sync can reach you
|
|
1006
1033
|
clivly login [options] Pair this machine and write credentials to your env file
|
|
1007
1034
|
clivly --version Print the installed clivly version
|
|
@@ -1057,7 +1084,9 @@ Options:
|
|
|
1057
1084
|
Usage:
|
|
1058
1085
|
clivly status [--verbose] [--config <path>] [--env-file <path>]
|
|
1059
1086
|
|
|
1060
|
-
|
|
1087
|
+
Prints two sections. "Workspace" is how far setup has got on Clivly's side,
|
|
1088
|
+
computed by the server so the terminal and the dashboard agree. "This project"
|
|
1089
|
+
runs the local ladder: credential, reachability, config load, edge-safety,
|
|
1061
1090
|
setup checks, sync trigger. Read-only — writes nothing.
|
|
1062
1091
|
|
|
1063
1092
|
Options:
|
|
@@ -1065,13 +1094,23 @@ Options:
|
|
|
1065
1094
|
--config <path> Path to clivly.config.ts
|
|
1066
1095
|
--env-file <path> Extra env file to load
|
|
1067
1096
|
`,
|
|
1068
|
-
"push-schema": `clivly push-schema —
|
|
1097
|
+
"push-schema": `clivly push-schema — push everything your config declares
|
|
1069
1098
|
|
|
1070
1099
|
Usage:
|
|
1071
1100
|
clivly push-schema [--config <path>]
|
|
1072
1101
|
|
|
1073
|
-
|
|
1074
|
-
|
|
1102
|
+
Pushes three things to Clivly, without deploying your app:
|
|
1103
|
+
1. Your table and column NAMES, so the dashboard can offer them for mapping.
|
|
1104
|
+
Never row data — discovery is names only.
|
|
1105
|
+
2. The custom object types you declared with \`objectType\`.
|
|
1106
|
+
3. The mappings that make those objects actually receive rows.
|
|
1107
|
+
|
|
1108
|
+
Reads your config; writes nothing locally. Safe to re-run — it reports what it
|
|
1109
|
+
created, updated, or left alone.
|
|
1110
|
+
|
|
1111
|
+
Exits 1 if Clivly refuses your config, most often because an entity would
|
|
1112
|
+
overwrite a mapping edited in the dashboard. That makes it usable in CI as a
|
|
1113
|
+
schema gate.
|
|
1075
1114
|
`,
|
|
1076
1115
|
dev: `clivly dev — tunnel localhost so remote sync can reach you
|
|
1077
1116
|
|
|
@@ -1387,15 +1426,23 @@ async function confirmWorkspace(deps, credentials) {
|
|
|
1387
1426
|
return await deps.confirm("Write this workspace's credentials to .env? [y/N] ");
|
|
1388
1427
|
}
|
|
1389
1428
|
function credentialEntries(credentials) {
|
|
1390
|
-
return [
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1429
|
+
return [
|
|
1430
|
+
{
|
|
1431
|
+
key: SECRET_KEY_ENV,
|
|
1432
|
+
value: credentials.apiKey,
|
|
1433
|
+
comment: `Org secret key — ${CREDENTIALS_LOCATION}`
|
|
1434
|
+
},
|
|
1435
|
+
{
|
|
1436
|
+
key: "CLIVLY_SYNC_TRIGGER_SECRET",
|
|
1437
|
+
value: credentials.triggerSecret,
|
|
1438
|
+
comment: `Signing secret — ${SYNC_SECRET_LOCATION}`
|
|
1439
|
+
},
|
|
1440
|
+
{
|
|
1441
|
+
key: "CLIVLY_SYNC_TRIGGER_URL",
|
|
1442
|
+
value: TRIGGER_URL_PLACEHOLDER,
|
|
1443
|
+
comment: "Public URL of your tick route — replace <yourDomain.com> once deployed. Local dev: run `clivly dev` instead."
|
|
1444
|
+
}
|
|
1445
|
+
];
|
|
1399
1446
|
}
|
|
1400
1447
|
function writeCredentials(options, credentials) {
|
|
1401
1448
|
const { deps } = options;
|
|
@@ -1860,7 +1907,13 @@ export default createClivlySDK({
|
|
|
1860
1907
|
${sources.join(",\n")},
|
|
1861
1908
|
},
|
|
1862
1909
|
syncTrigger: {
|
|
1863
|
-
//
|
|
1910
|
+
// Where Clivly Cloud calls to start a sync — your mounted tick route.
|
|
1911
|
+
//
|
|
1912
|
+
// \`path\` lets the SDK build its own public URL from the hosting platform's
|
|
1913
|
+
// environment (VERCEL_URL, CF_PAGES_URL, RAILWAY_PUBLIC_DOMAIN,
|
|
1914
|
+
// RENDER_EXTERNAL_URL, FLY_APP_NAME), so on those platforms you never have
|
|
1915
|
+
// to set the URL by hand. \`url\` overrides that when you do.
|
|
1916
|
+
path: "${CLIVLY_TICK_PATH}",
|
|
1864
1917
|
url: process.env.CLIVLY_SYNC_TRIGGER_URL,
|
|
1865
1918
|
secret: process.env.CLIVLY_SYNC_TRIGGER_SECRET,
|
|
1866
1919
|
},
|
|
@@ -2860,6 +2913,17 @@ function defaultReadConfigSource(path) {
|
|
|
2860
2913
|
return null;
|
|
2861
2914
|
}
|
|
2862
2915
|
}
|
|
2916
|
+
/**
|
|
2917
|
+
* Ping Clivly at most once per command, memoised on the context.
|
|
2918
|
+
*
|
|
2919
|
+
* `clivly status` asks two questions of one response — "did this call reach
|
|
2920
|
+
* Clivly" and "how far has this workspace got" — and they must come from the
|
|
2921
|
+
* same answer, not two calls that could disagree.
|
|
2922
|
+
*/
|
|
2923
|
+
function pingOnce(ctx) {
|
|
2924
|
+
ctx.pingResult ??= ctx.connect(ctx.apiKey, ctx.apiUrl);
|
|
2925
|
+
return ctx.pingResult;
|
|
2926
|
+
}
|
|
2863
2927
|
const REASON_HINT = {
|
|
2864
2928
|
ok: "",
|
|
2865
2929
|
invalid_api_key: "The key was rejected (401). Check CLIVLY_SECRET_KEY (or legacy CLIVLY_API_KEY).",
|
|
@@ -2871,7 +2935,7 @@ const REASON_HINT = {
|
|
|
2871
2935
|
network_error: "Could not reach Clivly. Check your network or --api-url."
|
|
2872
2936
|
};
|
|
2873
2937
|
const apiKeyStage = {
|
|
2874
|
-
id: "
|
|
2938
|
+
id: "api-key",
|
|
2875
2939
|
label: "API key present",
|
|
2876
2940
|
run(ctx) {
|
|
2877
2941
|
if (ctx.apiKey) return Promise.resolve({
|
|
@@ -2886,11 +2950,11 @@ const apiKeyStage = {
|
|
|
2886
2950
|
}
|
|
2887
2951
|
};
|
|
2888
2952
|
const reachabilityStage = {
|
|
2889
|
-
id: "
|
|
2953
|
+
id: "reachable",
|
|
2890
2954
|
label: "Clivly reachable",
|
|
2891
2955
|
needs: [apiKeyStage.id],
|
|
2892
2956
|
async run(ctx) {
|
|
2893
|
-
const result = await
|
|
2957
|
+
const result = await pingOnce(ctx);
|
|
2894
2958
|
if (result.ok) {
|
|
2895
2959
|
const name = result.org?.name ?? "(unnamed org)";
|
|
2896
2960
|
const id = result.org?.id ? ` (${result.org.id})` : "";
|
|
@@ -2962,14 +3026,15 @@ const triggerUrlStage = {
|
|
|
2962
3026
|
needs: [configStage.id],
|
|
2963
3027
|
run(ctx) {
|
|
2964
3028
|
const url = ctx.sdk?.syncTrigger?.url;
|
|
2965
|
-
if (url) return Promise.resolve({
|
|
3029
|
+
if (url && !isPlaceholderUrl(url)) return Promise.resolve({
|
|
2966
3030
|
state: "pass",
|
|
2967
3031
|
detail: url
|
|
2968
3032
|
});
|
|
3033
|
+
const isPlaceholder = url !== void 0;
|
|
2969
3034
|
return Promise.resolve({
|
|
2970
3035
|
state: "warn",
|
|
2971
|
-
detail: "Not set — remote sync cannot call back into this app.",
|
|
2972
|
-
hint: "
|
|
3036
|
+
detail: isPlaceholder ? `Still the placeholder (${url}) — remote sync cannot call back into this app.` : "Not set — remote sync cannot call back into this app.",
|
|
3037
|
+
hint: "Replace <yourDomain.com> with your deployed origin, or delete the line — on Vercel, Cloudflare Pages, Railway, Render and Fly the SDK derives the URL from the platform. For local development run `clivly dev`: it opens a tunnel and registers the URL for you, outranking whatever is in .env. Push-only setups can ignore this."
|
|
2973
3038
|
});
|
|
2974
3039
|
}
|
|
2975
3040
|
};
|
|
@@ -3024,19 +3089,57 @@ function defaultLoadSdk(path) {
|
|
|
3024
3089
|
return loadConfigSdk(path, "doctor");
|
|
3025
3090
|
}
|
|
3026
3091
|
//#endregion
|
|
3092
|
+
//#region src/workspace-section.ts
|
|
3093
|
+
const MARK$1 = {
|
|
3094
|
+
active: "⏳",
|
|
3095
|
+
blocked: "🔒",
|
|
3096
|
+
done: "✅",
|
|
3097
|
+
failed: "❌",
|
|
3098
|
+
skipped: "⏭️ "
|
|
3099
|
+
};
|
|
3100
|
+
/**
|
|
3101
|
+
* The workspace half of `clivly status`, rendered from what the server sent.
|
|
3102
|
+
*
|
|
3103
|
+
* This module decides nothing about a stage's state — it prints the verdict the
|
|
3104
|
+
* server computed. A CLI that computed its own would be a second ladder, which
|
|
3105
|
+
* is the drift this whole change removes.
|
|
3106
|
+
*
|
|
3107
|
+
* "Unavailable" and "nothing is set up" are deliberately different outcomes. An
|
|
3108
|
+
* old server, a rejected key and a dead network all mean we could not ask; none
|
|
3109
|
+
* of them means the workspace is empty.
|
|
3110
|
+
*/
|
|
3111
|
+
function workspaceSection(result) {
|
|
3112
|
+
if (!result) return {
|
|
3113
|
+
kind: "unavailable",
|
|
3114
|
+
reason: "Could not reach Clivly."
|
|
3115
|
+
};
|
|
3116
|
+
if (!result.ok) return {
|
|
3117
|
+
kind: "unavailable",
|
|
3118
|
+
reason: result.status === null ? "Could not reach Clivly (no response)." : `Clivly rejected the request — ${result.reason} (HTTP ${result.status}).`
|
|
3119
|
+
};
|
|
3120
|
+
if (!result.workspace) return {
|
|
3121
|
+
kind: "unavailable",
|
|
3122
|
+
reason: "This Clivly server does not report setup progress. Upgrade the server to see it here."
|
|
3123
|
+
};
|
|
3124
|
+
return {
|
|
3125
|
+
kind: "ladder",
|
|
3126
|
+
lines: result.workspace.steps.map((step) => ` ${MARK$1[step.state]} ${step.label}${step.evidence ? ` ${step.evidence}` : ""}`)
|
|
3127
|
+
};
|
|
3128
|
+
}
|
|
3129
|
+
//#endregion
|
|
3027
3130
|
//#region src/status.ts
|
|
3028
3131
|
/** Stage ids each legacy command maps onto. */
|
|
3029
3132
|
const ALIAS_STAGE_IDS = {
|
|
3030
3133
|
status: STATUS_STAGES.map((stage) => stage.id),
|
|
3031
|
-
ping: ["
|
|
3134
|
+
ping: ["api-key", "reachable"],
|
|
3032
3135
|
doctor: [
|
|
3033
3136
|
"config",
|
|
3034
3137
|
"runtime",
|
|
3035
3138
|
"setup-checks"
|
|
3036
3139
|
],
|
|
3037
3140
|
connect: [
|
|
3038
|
-
"
|
|
3039
|
-
"
|
|
3141
|
+
"api-key",
|
|
3142
|
+
"reachable",
|
|
3040
3143
|
"config",
|
|
3041
3144
|
"trigger-url"
|
|
3042
3145
|
]
|
|
@@ -3092,6 +3195,13 @@ async function runStatus(options) {
|
|
|
3092
3195
|
};
|
|
3093
3196
|
out("\nClivly status\n");
|
|
3094
3197
|
const results = await runLadder(stages, ctx);
|
|
3198
|
+
if (selected.has("reachable")) {
|
|
3199
|
+
const section = workspaceSection(ctx.pingResult ? await ctx.pingResult : null);
|
|
3200
|
+
out("\nWorkspace\n");
|
|
3201
|
+
if (section.kind === "ladder") for (const line of section.lines) out(`${line}\n`);
|
|
3202
|
+
else out(` ⚠️ ${section.reason}\n`);
|
|
3203
|
+
}
|
|
3204
|
+
out("\nThis project\n");
|
|
3095
3205
|
for (const result of results) renderStage(result, options.verbose ?? false, out);
|
|
3096
3206
|
const code = ladderExitCode(results);
|
|
3097
3207
|
if (code !== 0 && results.some((result) => result.state === "pending")) out("\nSetup is incomplete — finish the steps above and re-run.\n");
|
|
@@ -3121,6 +3231,7 @@ const KNOWN_COMMANDS = [
|
|
|
3121
3231
|
"init",
|
|
3122
3232
|
"status",
|
|
3123
3233
|
"push-schema",
|
|
3234
|
+
"add",
|
|
3124
3235
|
"dev",
|
|
3125
3236
|
"login",
|
|
3126
3237
|
"help",
|
|
@@ -3477,8 +3588,8 @@ function previewMutations(deps, mutations, options) {
|
|
|
3477
3588
|
function printNextSteps(out, authComplete, loginRan, envFile) {
|
|
3478
3589
|
out("\nNext steps:\n");
|
|
3479
3590
|
out(loginRan ? " 1. Credentials written by `clivly login`\n" : ` 1. Add CLIVLY_SECRET_KEY to ${envFile} — create one at\n ${CREDENTIALS_LOCATION}\n (the legacy CLIVLY_API_KEY is still accepted)\n`);
|
|
3480
|
-
out(" 2.
|
|
3481
|
-
out("
|
|
3591
|
+
out(" 2. Local dev: run `clivly dev` for dashboard-triggered sync\n");
|
|
3592
|
+
out(" It tunnels your app and registers the URL for you — remote sync is\n deploy-only otherwise, so nothing is broken if you skip it. Once deployed\n to Vercel/Cloudflare/Railway/Render/Fly the URL is detected automatically;\n set CLIVLY_SYNC_TRIGGER_URL by hand only for a custom domain.\n");
|
|
3482
3593
|
const step = authComplete ? 3 : 4;
|
|
3483
3594
|
if (!authComplete) out(" 3. Finish resolveUser in the auth/verify route\n");
|
|
3484
3595
|
out(` ${step}. Run \`clivly status\` to see where you are\n`);
|
|
@@ -3888,4 +3999,4 @@ if (isEntrypoint(process.argv[1], import.meta.url)) main(process.argv.slice(2)).
|
|
|
3888
3999
|
process.exitCode = code;
|
|
3889
4000
|
});
|
|
3890
4001
|
//#endregion
|
|
3891
|
-
export { credentialsMissing, main, nearestCommand, runAdd, runInit, runInitFromArgv };
|
|
4002
|
+
export { KNOWN_COMMANDS, credentialsMissing, main, nearestCommand, runAdd, runInit, runInitFromArgv };
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
//#region ../core/src/integration-state.d.ts
|
|
2
|
+
declare const INTEGRATION_STEPS: readonly ["credential", "application", "schema", "mapping", "trigger", "sync", "widget"];
|
|
3
|
+
type StepId = (typeof INTEGRATION_STEPS)[number];
|
|
4
|
+
/** `blocked` means "prerequisite unmet", never "broken" — see C-19. */
|
|
5
|
+
type StepState = "done" | "active" | "blocked" | "failed" | "skipped";
|
|
6
|
+
type IntegrationStatus = "not_configured" | "sdk_installed" | "awaiting_connection" | "connected" | "schema_discovered" | "mapping_required" | "trigger_required" | "ready_to_sync" | "syncing" | "healthy" | "warning" | "error" |
|
|
7
|
+
/**
|
|
8
|
+
* @deprecated No longer derived. Liveness is its own dimension — read
|
|
9
|
+
* `ConnectionHealth` from `deriveLiveness`. Setup progress does not move
|
|
10
|
+
* backwards just because an app stopped running.
|
|
11
|
+
*/
|
|
12
|
+
"disconnected";
|
|
13
|
+
interface RunFact {
|
|
14
|
+
at: number;
|
|
15
|
+
message?: string;
|
|
16
|
+
ok: boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Where the reporting app is running, as the app itself understands it.
|
|
20
|
+
*
|
|
21
|
+
* `unknown` is a real value, not an absence: `detectHostEnvironment` refuses to
|
|
22
|
+
* guess when nothing places the app, and a bare `node server.js` with no
|
|
23
|
+
* NODE_ENV must still be able to connect and be shown as itself. Defaulting
|
|
24
|
+
* such a host to `local_dev` would file a production box's faults under local
|
|
25
|
+
* development, where nobody would look for them.
|
|
26
|
+
*/
|
|
27
|
+
type HostEnvironment = "local_dev" | "preview" | "production" | "unknown";
|
|
28
|
+
interface IntegrationFacts {
|
|
29
|
+
discoveredTableCount: number;
|
|
30
|
+
/** The environment the app reported on its last heartbeat. */
|
|
31
|
+
environment?: HostEnvironment | null;
|
|
32
|
+
/**
|
|
33
|
+
* Has this app ever checked in? Distinguishes "never installed" from "was
|
|
34
|
+
* working, now silent" — the same absent heartbeat means different things,
|
|
35
|
+
* and only one of them is diagnosed rather than set up.
|
|
36
|
+
*/
|
|
37
|
+
everConnected: boolean;
|
|
38
|
+
hasCredential: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* The cadence the app said it heartbeats at. Liveness windows are multiples
|
|
41
|
+
* of this rather than a fixed constant, so an app that deliberately reports
|
|
42
|
+
* slowly is not called dead for obeying its own configuration.
|
|
43
|
+
*/
|
|
44
|
+
heartbeatIntervalMs?: number | null;
|
|
45
|
+
lastHeartbeatAt: number | null;
|
|
46
|
+
lastRun: RunFact | null;
|
|
47
|
+
mappedEntityCount: number;
|
|
48
|
+
now: number;
|
|
49
|
+
/**
|
|
50
|
+
* Deployment presence, when this deployment has one. Absent or `http`
|
|
51
|
+
* transport keeps the cadence-derived behaviour every SDK in the field uses.
|
|
52
|
+
*/
|
|
53
|
+
presence?: DeploymentPresence | null;
|
|
54
|
+
/**
|
|
55
|
+
* Faults the running app diagnosed about itself on its last heartbeat — an
|
|
56
|
+
* unreadable database, a secret missing from the process environment. These
|
|
57
|
+
* are what make a reachable app `live_with_issues`; unfinished setup is not.
|
|
58
|
+
*/
|
|
59
|
+
reportedIssues?: StepProblem[];
|
|
60
|
+
syncInFlight: boolean;
|
|
61
|
+
/** A trigger URL and secret both exist, so Clivly can ask the app to sync. */
|
|
62
|
+
triggerConfigured: boolean;
|
|
63
|
+
warnings: string[];
|
|
64
|
+
widgetInstalled: boolean;
|
|
65
|
+
}
|
|
66
|
+
interface StepProblem {
|
|
67
|
+
code: string;
|
|
68
|
+
fix: string;
|
|
69
|
+
message: string;
|
|
70
|
+
}
|
|
71
|
+
interface IntegrationStep {
|
|
72
|
+
blockedBy: StepId | null;
|
|
73
|
+
evidence: string | null;
|
|
74
|
+
id: StepId;
|
|
75
|
+
label: string;
|
|
76
|
+
problem: StepProblem | null;
|
|
77
|
+
state: StepState;
|
|
78
|
+
}
|
|
79
|
+
type ConnectionHealth = "live" | "live_with_issues" | "stale" | "offline";
|
|
80
|
+
/** Which algorithm decides whether this deployment is up. */
|
|
81
|
+
type PresenceTransport = "websocket" | "http";
|
|
82
|
+
type PresenceConnectionState = "connected" | "reconnecting" | "disconnected";
|
|
83
|
+
/**
|
|
84
|
+
* A deployment's connection state as its `PresenceRoom` last persisted it.
|
|
85
|
+
*
|
|
86
|
+
* This exists because a socket has an explicit state and a timestamp does not.
|
|
87
|
+
* Deriving WebSocket liveness from heartbeat age would leave a disconnected app
|
|
88
|
+
* shown as live for 150 seconds and offline only after ten minutes, which is
|
|
89
|
+
* the whole reason this transport exists.
|
|
90
|
+
*/
|
|
91
|
+
interface DeploymentPresence {
|
|
92
|
+
/** Sockets currently accepted in the room. */
|
|
93
|
+
activeInstanceCount: number;
|
|
94
|
+
connectionState: PresenceConnectionState;
|
|
95
|
+
transport: PresenceTransport;
|
|
96
|
+
}
|
|
97
|
+
interface ConnectionLiveness {
|
|
98
|
+
/** `"8s ago"`, or `"No heartbeat yet"` when the app has never reported. */
|
|
99
|
+
ageLabel: string;
|
|
100
|
+
ageMs: number | null;
|
|
101
|
+
environment: HostEnvironment | null;
|
|
102
|
+
health: ConnectionHealth;
|
|
103
|
+
/**
|
|
104
|
+
* Faults on a reachable app. Empty unless `health` is `live_with_issues`.
|
|
105
|
+
*/
|
|
106
|
+
issues: StepProblem[];
|
|
107
|
+
/** Present when a deployment-presence record supplied this liveness result. */
|
|
108
|
+
presence?: Pick<DeploymentPresence, "connectionState" | "transport">;
|
|
109
|
+
/** Is a heartbeat recent enough to speak for the app as it is right now? */
|
|
110
|
+
reachable: boolean;
|
|
111
|
+
}
|
|
112
|
+
interface NextAction {
|
|
113
|
+
command: string | null;
|
|
114
|
+
envFile: string | null;
|
|
115
|
+
label: string;
|
|
116
|
+
}
|
|
117
|
+
interface IntegrationState {
|
|
118
|
+
/**
|
|
119
|
+
* Is the app running right now. Deliberately alongside `status` rather than
|
|
120
|
+
* folded into it: a surface must be able to say "setup reached mapping" and
|
|
121
|
+
* "the app is offline" in the same breath without either overwriting the
|
|
122
|
+
* other.
|
|
123
|
+
*/
|
|
124
|
+
liveness: ConnectionLiveness;
|
|
125
|
+
next: NextAction | null;
|
|
126
|
+
status: IntegrationStatus;
|
|
127
|
+
steps: IntegrationStep[];
|
|
128
|
+
}
|
|
129
|
+
//#endregion
|
|
130
|
+
export { IntegrationStep as a, StepProblem as c, IntegrationStatus as i, StepState as l, IntegrationFacts as n, NextAction as o, IntegrationState as r, StepId as s, ConnectionLiveness as t };
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
//#region ../core/src/integration-state.d.ts
|
|
2
|
+
declare const INTEGRATION_STEPS: readonly ["credential", "application", "schema", "mapping", "trigger", "sync", "widget"];
|
|
3
|
+
type StepId = (typeof INTEGRATION_STEPS)[number];
|
|
4
|
+
/** `blocked` means "prerequisite unmet", never "broken" — see C-19. */
|
|
5
|
+
type StepState = "done" | "active" | "blocked" | "failed" | "skipped";
|
|
6
|
+
type IntegrationStatus = "not_configured" | "sdk_installed" | "awaiting_connection" | "connected" | "schema_discovered" | "mapping_required" | "trigger_required" | "ready_to_sync" | "syncing" | "healthy" | "warning" | "error" |
|
|
7
|
+
/**
|
|
8
|
+
* @deprecated No longer derived. Liveness is its own dimension — read
|
|
9
|
+
* `ConnectionHealth` from `deriveLiveness`. Setup progress does not move
|
|
10
|
+
* backwards just because an app stopped running.
|
|
11
|
+
*/
|
|
12
|
+
"disconnected";
|
|
13
|
+
interface RunFact {
|
|
14
|
+
at: number;
|
|
15
|
+
message?: string;
|
|
16
|
+
ok: boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Where the reporting app is running, as the app itself understands it.
|
|
20
|
+
*
|
|
21
|
+
* `unknown` is a real value, not an absence: `detectHostEnvironment` refuses to
|
|
22
|
+
* guess when nothing places the app, and a bare `node server.js` with no
|
|
23
|
+
* NODE_ENV must still be able to connect and be shown as itself. Defaulting
|
|
24
|
+
* such a host to `local_dev` would file a production box's faults under local
|
|
25
|
+
* development, where nobody would look for them.
|
|
26
|
+
*/
|
|
27
|
+
type HostEnvironment = "local_dev" | "preview" | "production" | "unknown";
|
|
28
|
+
interface IntegrationFacts {
|
|
29
|
+
discoveredTableCount: number;
|
|
30
|
+
/** The environment the app reported on its last heartbeat. */
|
|
31
|
+
environment?: HostEnvironment | null;
|
|
32
|
+
/**
|
|
33
|
+
* Has this app ever checked in? Distinguishes "never installed" from "was
|
|
34
|
+
* working, now silent" — the same absent heartbeat means different things,
|
|
35
|
+
* and only one of them is diagnosed rather than set up.
|
|
36
|
+
*/
|
|
37
|
+
everConnected: boolean;
|
|
38
|
+
hasCredential: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* The cadence the app said it heartbeats at. Liveness windows are multiples
|
|
41
|
+
* of this rather than a fixed constant, so an app that deliberately reports
|
|
42
|
+
* slowly is not called dead for obeying its own configuration.
|
|
43
|
+
*/
|
|
44
|
+
heartbeatIntervalMs?: number | null;
|
|
45
|
+
lastHeartbeatAt: number | null;
|
|
46
|
+
lastRun: RunFact | null;
|
|
47
|
+
mappedEntityCount: number;
|
|
48
|
+
now: number;
|
|
49
|
+
/**
|
|
50
|
+
* Deployment presence, when this deployment has one. Absent or `http`
|
|
51
|
+
* transport keeps the cadence-derived behaviour every SDK in the field uses.
|
|
52
|
+
*/
|
|
53
|
+
presence?: DeploymentPresence | null;
|
|
54
|
+
/**
|
|
55
|
+
* Faults the running app diagnosed about itself on its last heartbeat — an
|
|
56
|
+
* unreadable database, a secret missing from the process environment. These
|
|
57
|
+
* are what make a reachable app `live_with_issues`; unfinished setup is not.
|
|
58
|
+
*/
|
|
59
|
+
reportedIssues?: StepProblem[];
|
|
60
|
+
syncInFlight: boolean;
|
|
61
|
+
/** A trigger URL and secret both exist, so Clivly can ask the app to sync. */
|
|
62
|
+
triggerConfigured: boolean;
|
|
63
|
+
warnings: string[];
|
|
64
|
+
widgetInstalled: boolean;
|
|
65
|
+
}
|
|
66
|
+
interface StepProblem {
|
|
67
|
+
code: string;
|
|
68
|
+
fix: string;
|
|
69
|
+
message: string;
|
|
70
|
+
}
|
|
71
|
+
interface IntegrationStep {
|
|
72
|
+
blockedBy: StepId | null;
|
|
73
|
+
evidence: string | null;
|
|
74
|
+
id: StepId;
|
|
75
|
+
label: string;
|
|
76
|
+
problem: StepProblem | null;
|
|
77
|
+
state: StepState;
|
|
78
|
+
}
|
|
79
|
+
type ConnectionHealth = "live" | "live_with_issues" | "stale" | "offline";
|
|
80
|
+
/** Which algorithm decides whether this deployment is up. */
|
|
81
|
+
type PresenceTransport = "websocket" | "http";
|
|
82
|
+
type PresenceConnectionState = "connected" | "reconnecting" | "disconnected";
|
|
83
|
+
/**
|
|
84
|
+
* A deployment's connection state as its `PresenceRoom` last persisted it.
|
|
85
|
+
*
|
|
86
|
+
* This exists because a socket has an explicit state and a timestamp does not.
|
|
87
|
+
* Deriving WebSocket liveness from heartbeat age would leave a disconnected app
|
|
88
|
+
* shown as live for 150 seconds and offline only after ten minutes, which is
|
|
89
|
+
* the whole reason this transport exists.
|
|
90
|
+
*/
|
|
91
|
+
interface DeploymentPresence {
|
|
92
|
+
/** Sockets currently accepted in the room. */
|
|
93
|
+
activeInstanceCount: number;
|
|
94
|
+
connectionState: PresenceConnectionState;
|
|
95
|
+
transport: PresenceTransport;
|
|
96
|
+
}
|
|
97
|
+
interface ConnectionLiveness {
|
|
98
|
+
/** `"8s ago"`, or `"No heartbeat yet"` when the app has never reported. */
|
|
99
|
+
ageLabel: string;
|
|
100
|
+
ageMs: number | null;
|
|
101
|
+
environment: HostEnvironment | null;
|
|
102
|
+
health: ConnectionHealth;
|
|
103
|
+
/**
|
|
104
|
+
* Faults on a reachable app. Empty unless `health` is `live_with_issues`.
|
|
105
|
+
*/
|
|
106
|
+
issues: StepProblem[];
|
|
107
|
+
/** Present when a deployment-presence record supplied this liveness result. */
|
|
108
|
+
presence?: Pick<DeploymentPresence, "connectionState" | "transport">;
|
|
109
|
+
/** Is a heartbeat recent enough to speak for the app as it is right now? */
|
|
110
|
+
reachable: boolean;
|
|
111
|
+
}
|
|
112
|
+
interface NextAction {
|
|
113
|
+
command: string | null;
|
|
114
|
+
envFile: string | null;
|
|
115
|
+
label: string;
|
|
116
|
+
}
|
|
117
|
+
interface IntegrationState {
|
|
118
|
+
/**
|
|
119
|
+
* Is the app running right now. Deliberately alongside `status` rather than
|
|
120
|
+
* folded into it: a surface must be able to say "setup reached mapping" and
|
|
121
|
+
* "the app is offline" in the same breath without either overwriting the
|
|
122
|
+
* other.
|
|
123
|
+
*/
|
|
124
|
+
liveness: ConnectionLiveness;
|
|
125
|
+
next: NextAction | null;
|
|
126
|
+
status: IntegrationStatus;
|
|
127
|
+
steps: IntegrationStep[];
|
|
128
|
+
}
|
|
129
|
+
//#endregion
|
|
130
|
+
export { IntegrationStep as a, StepProblem as c, IntegrationStatus as i, StepState as l, IntegrationFacts as n, NextAction as o, IntegrationState as r, StepId as s, ConnectionLiveness as t };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as PROJECTION_KEY_PATTERN, t as BASE_REF } from "./projection-
|
|
2
|
-
import { h as fanOutJoins, m as validateProjection } from "./entity-config-
|
|
1
|
+
import { n as PROJECTION_KEY_PATTERN, t as BASE_REF } from "./projection-CSGJbZlZ.mjs";
|
|
2
|
+
import { h as fanOutJoins, m as validateProjection } from "./entity-config-B74SPOUZ.mjs";
|
|
3
3
|
//#region ../core/src/projection-draft.ts
|
|
4
4
|
function emptyProjectionDraft(baseTable) {
|
|
5
5
|
return {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const require_projection = require("./projection-
|
|
2
|
-
const require_entity_config = require("./entity-config-
|
|
1
|
+
const require_projection = require("./projection-Dz6_i1-w.cjs");
|
|
2
|
+
const require_entity_config = require("./entity-config-7sGCk_nw.cjs");
|
|
3
3
|
//#region ../core/src/projection-draft.ts
|
|
4
4
|
function emptyProjectionDraft(baseTable) {
|
|
5
5
|
return {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_entity_config = require("./entity-config-
|
|
2
|
+
const require_entity_config = require("./entity-config-7sGCk_nw.cjs");
|
|
3
3
|
exports.CANONICAL_FIELDS = require_entity_config.CANONICAL_FIELDS;
|
|
4
4
|
exports.CRM_CONCEPTS = require_entity_config.CRM_CONCEPTS;
|
|
5
5
|
exports.CUSTOM_CONCEPT = require_entity_config.CUSTOM_CONCEPT;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as MATERIALIZING_CONCEPTS, c as entitiesConfigSchema, d as relationshipSchema, f as relationshipsSchema, i as ClivlyConfigError, l as filterSchema, n as CRM_CONCEPTS, o as customObjectDeclarations, p as validateEntitiesConfig, r as CUSTOM_CONCEPT, s as defineClivlyConfig, t as CANONICAL_FIELDS, u as projectionFields } from "./entity-config-
|
|
1
|
+
import { a as MATERIALIZING_CONCEPTS, c as entitiesConfigSchema, d as relationshipSchema, f as relationshipsSchema, i as ClivlyConfigError, l as filterSchema, n as CRM_CONCEPTS, o as customObjectDeclarations, p as validateEntitiesConfig, r as CUSTOM_CONCEPT, s as defineClivlyConfig, t as CANONICAL_FIELDS, u as projectionFields } from "./entity-config-B74SPOUZ.mjs";
|
|
2
2
|
export { CANONICAL_FIELDS, CRM_CONCEPTS, CUSTOM_CONCEPT, ClivlyConfigError, MATERIALIZING_CONCEPTS, customObjectDeclarations, defineClivlyConfig, entitiesConfigSchema, filterSchema, projectionFields, relationshipSchema, relationshipsSchema, validateEntitiesConfig };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_entity_config = require("./entity-config-
|
|
2
|
+
const require_entity_config = require("./entity-config-7sGCk_nw.cjs");
|
|
3
3
|
const require_mapping_score = require("./mapping-score-CaB3F4Uu.cjs");
|
|
4
4
|
//#region ../core/src/entity-heuristics.ts
|
|
5
5
|
const TARGET_CONCEPTS = [
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as MATERIALIZING_CONCEPTS } from "./entity-config-
|
|
1
|
+
import { a as MATERIALIZING_CONCEPTS } from "./entity-config-B74SPOUZ.mjs";
|
|
2
2
|
import { a as W, c as isTextLike, i as RELATIONSHIP_TIERS, l as isTimestampLike, n as CONCEPT_TIERS, o as isAmbiguous, r as FIELD_TIERS, s as isNumericLike, u as tierFor } from "./mapping-score-C68uAqGB.mjs";
|
|
3
3
|
//#region ../core/src/entity-heuristics.ts
|
|
4
4
|
const TARGET_CONCEPTS = [
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_mappings_config = require("./mappings-config-
|
|
2
|
+
const require_mappings_config = require("./mappings-config-CLyEg0sf.cjs");
|
|
3
3
|
exports.entitiesToMappings = require_mappings_config.entitiesToMappings;
|
|
4
4
|
exports.mappingsToEntitiesConfig = require_mappings_config.mappingsToEntitiesConfig;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as mappingsToEntitiesConfig, t as entitiesToMappings } from "./mappings-config-
|
|
1
|
+
import { n as mappingsToEntitiesConfig, t as entitiesToMappings } from "./mappings-config-DQS_s4dg.mjs";
|
|
2
2
|
export { entitiesToMappings, mappingsToEntitiesConfig };
|
|
@@ -36,6 +36,34 @@ declare function describeCardinality(draft: ProjectionDraft, schema: DiscoveredS
|
|
|
36
36
|
joinKey: string;
|
|
37
37
|
}>;
|
|
38
38
|
//#endregion
|
|
39
|
+
//#region ../core/src/projection-sql.d.ts
|
|
40
|
+
/**
|
|
41
|
+
* SQL generation for projection-backed entities.
|
|
42
|
+
*
|
|
43
|
+
* Base and joins are aliased by their ref key (`base`, then each join's `key`),
|
|
44
|
+
* which is what allows the same table to be joined twice and keeps generated
|
|
45
|
+
* SQL independent of host table names.
|
|
46
|
+
*
|
|
47
|
+
* Pure string generation — no DB access. Identifiers are escaped so output is
|
|
48
|
+
* deterministic and injection-safe.
|
|
49
|
+
*/
|
|
50
|
+
/** Quote a single identifier part, escaping embedded double-quotes. */
|
|
51
|
+
declare function quoteIdent(name: string): string;
|
|
52
|
+
/**
|
|
53
|
+
* The identity expression. Composite identities concatenate their parts with
|
|
54
|
+
* `::`; validation guarantees every part is NOT NULL and non-text, which is
|
|
55
|
+
* what makes the concatenation collision-free.
|
|
56
|
+
*
|
|
57
|
+
* Exported unaliased because two paths need the same expression under different
|
|
58
|
+
* names: the view compiler aliases it `id`, and the SDK's projection push source
|
|
59
|
+
* also orders and keyset-paginates by it. Sharing one definition is what keeps
|
|
60
|
+
* a pushed row's `id` identical to the one the view would have produced.
|
|
61
|
+
*/
|
|
62
|
+
declare function compileProjectionIdentity(projection: ProjectionMapping, basePkColumn: string): string;
|
|
63
|
+
declare function compileProjectionJoins(projection: ProjectionMapping): string[];
|
|
64
|
+
/** `FROM <baseTable> AS "base"` — the alias every ref resolves against. */
|
|
65
|
+
declare function compileProjectionFrom(projection: ProjectionMapping): string;
|
|
66
|
+
//#endregion
|
|
39
67
|
//#region ../core/src/projection-validate.d.ts
|
|
40
68
|
/**
|
|
41
69
|
* Validate a projection against the discovered schema. Returns every error
|
|
@@ -69,4 +97,4 @@ declare function coversUniqueKey(table: DiscoveredSchemaTable, columns: string[]
|
|
|
69
97
|
/** Is this table's payload rich enough to validate a projection against? */
|
|
70
98
|
declare function hasRichMeta(table: DiscoveredSchemaTable): boolean;
|
|
71
99
|
//#endregion
|
|
72
|
-
export { validateProjection as a,
|
|
100
|
+
export { joinFansOut as _, validateProjection as a, compileProjectionJoins as c, describeCardinality as d, draftErrors as f, fanOutJoins as g, suggestJoinConditions as h, primaryKeyColumns as i, quoteIdent as l, emptyProjectionDraft as m, coversUniqueKey as n, compileProjectionFrom as o, draftToProjection as p, hasRichMeta as r, compileProjectionIdentity as s, columnMetaFor as t, ProjectionDraft as u };
|