clivly 0.10.2 → 0.10.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 CHANGED
@@ -117,6 +117,21 @@ on your side. It costs nothing if you never author one.
117
117
  name it is exported under in your schema module — whichever you have in front of
118
118
  you.
119
119
 
120
+ `--auth-import` accepts three forms, and tells them apart by shape:
121
+
122
+ | You pass | Emitted as |
123
+ | --- | --- |
124
+ | An alias or subpath import — `$lib/server/auth`, `@/lib/auth`, `#auth` | Verbatim; it already resolves |
125
+ | A package name — `@acme/auth`, `my-auth` | Verbatim |
126
+ | A project-relative path — `src/lib/server/auth` | Rebased to the route file's depth |
127
+
128
+ You rarely need it. `clivly init` probes the conventional locations for each
129
+ framework — including SvelteKit's `src/lib/server/auth` and Nuxt's
130
+ `server/utils/auth` — and prints `Auth: better-auth (wired)` when it finds one.
131
+ The flag is for an instance the on-disk probe cannot see, such as one in a
132
+ sibling workspace package. If the probe comes up empty, `init` says so and
133
+ repeats this flag rather than scaffolding an import that will not resolve.
134
+
120
135
  When `init` finishes and `CLIVLY_SECRET_KEY` is still empty, it hands you
121
136
  straight into `clivly login` so you don't end up staring at a blank env file.
122
137
  That step is interactive, so it is skipped automatically when there is no TTY or
@@ -414,6 +429,25 @@ deploy-only.
414
429
  npx clivly dev --port 3000 -- npm run dev
415
430
  ```
416
431
 
432
+ The tunnel target defaults to your framework's usual dev port — `5173` for the
433
+ Vite-based frameworks (SvelteKit, TanStack Start, Remix), `3000` for Next.js and
434
+ Nuxt. Pass `--port` when your app listens somewhere else. If nothing is
435
+ listening on the port being tunnelled, the command says so rather than
436
+ registering a tunnel that resolves but reaches nothing.
437
+
438
+ **Vite apps need one line of config.** Vite rejects any `Host` header it does
439
+ not recognise, so a tunnelled request is refused with `403` before it reaches
440
+ your tick route. `clivly dev` prints this when it applies:
441
+
442
+ ```ts
443
+ // vite.config.ts
444
+ server: { allowedHosts: [".trycloudflare.com"] }
445
+ ```
446
+
447
+ Without it every sync fails with "The host app returned 403" and the dashboard
448
+ names Clivly, not Vite. This affects every Vite-based framework; Next.js is
449
+ unaffected.
450
+
417
451
  It also watches `clivly.config.ts` and tells you when it changes. It cannot
418
452
  reload it for you — the config belongs to your app's process, not to the CLI.
419
453
 
@@ -424,7 +458,7 @@ network failure. Your app keeps running either way, and outbound sync (your app
424
458
 
425
459
  | Flag | Effect |
426
460
  | --- | --- |
427
- | `--port <n>` | Local port your app listens on |
461
+ | `--port <n>` | Local port your app listens on (defaults to your framework's: `5173` for Vite-based, `3000` for Next.js/Nuxt) |
428
462
  | `--route-path <path>` | Path to your mounted tick route |
429
463
  | `--api-url <url>` | Point at a non-default backend |
430
464
 
package/core.d.cts CHANGED
@@ -6,6 +6,6 @@ import { i as relationshipViaSignature, n as buildFilterFromDiscriminator, r as
6
6
  import { i as mappingsToEntitiesConfig, t as EntityMappingRow } from "./mappings-config-DUt4-9iO.cjs";
7
7
  import { a as compileEntityView, i as ExplainResult, n as CompiledView, o as compileEntityViews, r as DEFAULT_VIEW_PREFIX, s as explainEntitiesConfig, t as CompileOptions } from "./view-compiler-D3zp9S3K.cjs";
8
8
  import { c as SyncResult, f as reconcile, l as SyncStore, n as MirrorRecord, o as SourceRow, p as runSync, r as PersistAction, s as SyncAction, t as EntitySyncResult } from "./sync-engine-Df0kHAoJ.cjs";
9
- import { a as IntegrationStep, c as StepProblem, i as IntegrationStatus, l as StepState, n as IntegrationFacts, o as NextAction, r as IntegrationState, s as StepId, t as ConnectionLiveness } from "./integration-state-BK-riZnm.cjs";
9
+ import { a as IntegrationStep, c as StepProblem, i as IntegrationStatus, l as StepState, n as IntegrationFacts, o as NextAction, r as IntegrationState, s as StepId, t as ConnectionLiveness } from "./integration-state-DfqRSdTn.cjs";
10
10
  import { S as joinFansOut, _ as compileProjectionJoins, a as matchesCondition, b as quoteIdent, c as columnMetaFor, d as primaryKeyColumns, f as validateProjection, g as compileProjectionIdentity, h as compileProjectionFrom, i as evaluateStage, l as coversUniqueKey, m as PREVIEW_MAX_ROWS, n as ConditionOp, o as slugify, p as validateProjectionFilter, r as EvaluableRule, s as toObjectTypeSlug, t as Condition, u as hasRichMeta, v as compileProjectionPreview, x as fanOutJoins, y as compileProjectionSelect } from "./internal-BHJ0X3fC.cjs";
11
11
  export { type Activity, type ActivityType, BASE_REF, CANONICAL_FIELDS, type CRMAdapter, CRM_CONCEPTS, type ClivlyAuthAdapter, ClivlyConfigError, type ClivlyEntitiesConfig, type ClivlyEntityConfig, type ClivlyUser, type Company, type CompileOptions, type CompiledView, type Condition, type ConditionOp, type ConfigValidationError, type ConfigValidationResult, type ConnectionLiveness, type Contact, type ContactFilters, type ContactStatus, type CreateActivityInput, type CreateCompanyInput, type CreateContactInput, type CreateDealInput, type CreateTaskInput, type CrmConcept, type CrmMembership, type CrmRole, DEFAULT_VIEW_PREFIX, type Deal, type DealStage, type DiscoveredColumnMeta, type DiscoveredSchemaTable, type EntityMappingRow, type EntitySyncResult, type EvaluableRule, type ExplainResult, type FilterExpr, type FilterValue, type IntegrationFacts, type IntegrationState, type IntegrationStatus, type IntegrationStep, MATERIALIZING_CONCEPTS, type MaterializingConcept, type MembershipResolver, type MirrorRecord, type NextAction, type Note, OPEN_STAGE_TYPE, PREVIEW_MAX_ROWS, PROJECTION_KEY_PATTERN, type PersistAction, type PipelineConfig, type PipelineKind, type PipelineStageConfig, type ProjectionFieldBinding, type ProjectionIdentity, type ProjectionJoin, type ProjectionMapping, type ProjectionRef, type RecordSource, type RelationshipCandidate, type RelationshipSpec, type RelationshipVia, type RelationshipsMap, type SourceRow, type StageType, type StepId, type StepProblem, type StepState, type SyncAction, type SyncResult, type SyncStore, type Task, type TaskEntityType, type TaskFilters, type TaskPriority, type TaskStatus, type UpdateContactInput, type UpdateDealInput, type UpdateTaskInput, type ValueInterval, authCustom, buildFilterFromDiscriminator, buildRelationships, columnMetaFor, compileEntityView, compileEntityViews, compileProjectionFrom, compileProjectionIdentity, compileProjectionJoins, compileProjectionPreview, compileProjectionSelect, coversUniqueKey, defineClivlyConfig, entitiesConfigSchema, evaluateStage, explainEntitiesConfig, fanOutJoins, filterSchema, hasRichMeta, identityRefs, isClosedStageType, joinFansOut, mappingsToEntitiesConfig, matchesCondition, primaryKeyColumns, projectionFields, projectionSchema, quoteIdent, reconcile, relationshipSchema, relationshipViaSignature, relationshipsSchema, runSync, slugify, toObjectTypeSlug, validateEntitiesConfig, validateProjection, validateProjectionFilter };
package/core.d.mts CHANGED
@@ -6,6 +6,6 @@ import { i as relationshipViaSignature, n as buildFilterFromDiscriminator, r as
6
6
  import { i as mappingsToEntitiesConfig, t as EntityMappingRow } from "./mappings-config-Be9D43Np.mjs";
7
7
  import { a as compileEntityView, i as ExplainResult, n as CompiledView, o as compileEntityViews, r as DEFAULT_VIEW_PREFIX, s as explainEntitiesConfig, t as CompileOptions } from "./view-compiler-TpzDTu7z.mjs";
8
8
  import { c as SyncResult, f as reconcile, l as SyncStore, n as MirrorRecord, o as SourceRow, p as runSync, r as PersistAction, s as SyncAction, t as EntitySyncResult } from "./sync-engine-ugFacJ6G.mjs";
9
- import { a as IntegrationStep, c as StepProblem, i as IntegrationStatus, l as StepState, n as IntegrationFacts, o as NextAction, r as IntegrationState, s as StepId, t as ConnectionLiveness } from "./integration-state-BK-riZnm.mjs";
9
+ import { a as IntegrationStep, c as StepProblem, i as IntegrationStatus, l as StepState, n as IntegrationFacts, o as NextAction, r as IntegrationState, s as StepId, t as ConnectionLiveness } from "./integration-state-DfqRSdTn.mjs";
10
10
  import { S as joinFansOut, _ as compileProjectionJoins, a as matchesCondition, b as quoteIdent, c as columnMetaFor, d as primaryKeyColumns, f as validateProjection, g as compileProjectionIdentity, h as compileProjectionFrom, i as evaluateStage, l as coversUniqueKey, m as PREVIEW_MAX_ROWS, n as ConditionOp, o as slugify, p as validateProjectionFilter, r as EvaluableRule, s as toObjectTypeSlug, t as Condition, u as hasRichMeta, v as compileProjectionPreview, x as fanOutJoins, y as compileProjectionSelect } from "./internal-BAOG5eE4.mjs";
11
11
  export { type Activity, type ActivityType, BASE_REF, CANONICAL_FIELDS, type CRMAdapter, CRM_CONCEPTS, type ClivlyAuthAdapter, ClivlyConfigError, type ClivlyEntitiesConfig, type ClivlyEntityConfig, type ClivlyUser, type Company, type CompileOptions, type CompiledView, type Condition, type ConditionOp, type ConfigValidationError, type ConfigValidationResult, type ConnectionLiveness, type Contact, type ContactFilters, type ContactStatus, type CreateActivityInput, type CreateCompanyInput, type CreateContactInput, type CreateDealInput, type CreateTaskInput, type CrmConcept, type CrmMembership, type CrmRole, DEFAULT_VIEW_PREFIX, type Deal, type DealStage, type DiscoveredColumnMeta, type DiscoveredSchemaTable, type EntityMappingRow, type EntitySyncResult, type EvaluableRule, type ExplainResult, type FilterExpr, type FilterValue, type IntegrationFacts, type IntegrationState, type IntegrationStatus, type IntegrationStep, MATERIALIZING_CONCEPTS, type MaterializingConcept, type MembershipResolver, type MirrorRecord, type NextAction, type Note, OPEN_STAGE_TYPE, PREVIEW_MAX_ROWS, PROJECTION_KEY_PATTERN, type PersistAction, type PipelineConfig, type PipelineKind, type PipelineStageConfig, type ProjectionFieldBinding, type ProjectionIdentity, type ProjectionJoin, type ProjectionMapping, type ProjectionRef, type RecordSource, type RelationshipCandidate, type RelationshipSpec, type RelationshipVia, type RelationshipsMap, type SourceRow, type StageType, type StepId, type StepProblem, type StepState, type SyncAction, type SyncResult, type SyncStore, type Task, type TaskEntityType, type TaskFilters, type TaskPriority, type TaskStatus, type UpdateContactInput, type UpdateDealInput, type UpdateTaskInput, type ValueInterval, authCustom, buildFilterFromDiscriminator, buildRelationships, columnMetaFor, compileEntityView, compileEntityViews, compileProjectionFrom, compileProjectionIdentity, compileProjectionJoins, compileProjectionPreview, compileProjectionSelect, coversUniqueKey, defineClivlyConfig, entitiesConfigSchema, evaluateStage, explainEntitiesConfig, fanOutJoins, filterSchema, hasRichMeta, identityRefs, isClosedStageType, joinFansOut, mappingsToEntitiesConfig, matchesCondition, primaryKeyColumns, projectionFields, projectionSchema, quoteIdent, reconcile, relationshipSchema, relationshipViaSignature, relationshipsSchema, runSync, slugify, toObjectTypeSlug, validateEntitiesConfig, validateProjection, validateProjectionFilter };
package/drizzle.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { F as ProjectionMapping, I as ProjectionRef } from "./entity-config-DNQAMbMS.cjs";
2
2
  import { n as buildUniqueConstraints, r as discoverFromDrizzle } from "./drizzle-DTOuGXEn.cjs";
3
- import { t as NamespaceIntrospector, v as SyncSource } from "./namespace-probe-DC4tbmLj.cjs";
3
+ import { t as NamespaceIntrospector, v as SyncSource } from "./namespace-probe-DXN20mds.cjs";
4
4
  import { Table } from "drizzle-orm";
5
5
  //#region ../sdk/src/drizzle-projection.d.ts
6
6
  /**
package/drizzle.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { F as ProjectionMapping, I as ProjectionRef } from "./entity-config-DNQAMbMS.mjs";
2
2
  import { n as buildUniqueConstraints, r as discoverFromDrizzle } from "./drizzle-SGapoly2.mjs";
3
- import { t as NamespaceIntrospector, v as SyncSource } from "./namespace-probe-9ScMDR0t.mjs";
3
+ import { t as NamespaceIntrospector, v as SyncSource } from "./namespace-probe-BZuLxW3r.mjs";
4
4
  import { Table } from "drizzle-orm";
5
5
  //#region ../sdk/src/drizzle-projection.d.ts
6
6
  /**
package/index.cjs CHANGED
@@ -12,6 +12,7 @@ let node_path = require("node:path");
12
12
  let node_readline_promises = require("node:readline/promises");
13
13
  let jiti = require("jiti");
14
14
  let node_url = require("node:url");
15
+ let node_net = require("node:net");
15
16
  //#region ../core/src/env-file.ts
16
17
  const ENV_FILES = {
17
18
  next: ".env.local",
@@ -333,6 +334,28 @@ const TRIGGER_URL_PLACEHOLDER = `https://<yourDomain.com>${CLIVLY_TICK_PATH}`;
333
334
  //#region src/dev-tunnel.ts
334
335
  const CLOUDFLARE_QUICK_TUNNEL_REGEX = /https:\/\/[a-z0-9-]+\.trycloudflare\.com/;
335
336
  const DEFAULT_PORT = "3000";
337
+ /**
338
+ * The port each framework's dev server actually listens on out of the box.
339
+ *
340
+ * `clivly dev` used to tunnel to 3000 for everyone. Vite-based frameworks start
341
+ * on 5173, so the tunnel pointed at whatever else happened to hold 3000 — in one
342
+ * smoke run, a *different app entirely*, which answered, which made the tunnel
343
+ * look healthy while syncing the wrong project. Every symptom pointed away from
344
+ * the cause.
345
+ *
346
+ * These are defaults, not detections: a project that sets its own port must
347
+ * still pass `--port`. Guessing right for the untouched case is the win.
348
+ */
349
+ const FRAMEWORK_DEFAULT_PORTS = {
350
+ remix: "5173",
351
+ sveltekit: "5173",
352
+ "tanstack-start": "5173",
353
+ nextjs: "3000",
354
+ nuxt: "3000"
355
+ };
356
+ function defaultPortFor(framework) {
357
+ return (framework && FRAMEWORK_DEFAULT_PORTS[framework]) ?? DEFAULT_PORT;
358
+ }
336
359
  const DEFAULT_ROUTE_PATH = CLIVLY_TICK_PATH;
337
360
  function parseTunnelUrl(chunk) {
338
361
  const match = chunk.match(CLOUDFLARE_QUICK_TUNNEL_REGEX);
@@ -342,11 +365,49 @@ function buildTriggerUrl(origin, path) {
342
365
  return `${origin.endsWith("/") ? origin.slice(0, -1) : origin}${path.startsWith("/") ? path : `/${path}`}`;
343
366
  }
344
367
  function resolveDevTarget(flags) {
368
+ const port = flags.port ?? defaultPortFor(flags.framework);
345
369
  return {
346
- target: flags.url ?? `http://localhost:${flags.port ?? DEFAULT_PORT}`,
347
- triggerPath: flags.routePath ?? DEFAULT_ROUTE_PATH
370
+ target: flags.url ?? `http://localhost:${port}`,
371
+ triggerPath: flags.routePath ?? DEFAULT_ROUTE_PATH,
372
+ port
348
373
  };
349
374
  }
375
+ const VITE_FRAMEWORKS = /* @__PURE__ */ new Set([
376
+ "remix",
377
+ "sveltekit",
378
+ "tanstack-start"
379
+ ]);
380
+ function isViteFramework(framework) {
381
+ return framework ? VITE_FRAMEWORKS.has(framework) : false;
382
+ }
383
+ /**
384
+ * The one config line that makes a Vite dev server answer the tunnel.
385
+ *
386
+ * Without it every sync fails with a bare "The host app returned 403" and the
387
+ * explanation — Vite's own, naming this exact setting — is buried in a
388
+ * diagnostics column. This affects four of the five supported frameworks, so
389
+ * `clivly dev` says it up front rather than letting each developer rediscover
390
+ * it. Printed unconditionally for Vite apps: reading the app's vite config well
391
+ * enough to know whether it is already set means parsing arbitrary TypeScript,
392
+ * and a redundant hint is far cheaper than a silent 403.
393
+ */
394
+ function viteAllowedHostsHint() {
395
+ return [
396
+ "Vite blocks unknown hosts, so the tunnel needs one line in your Vite config:",
397
+ " server: { allowedHosts: [\".trycloudflare.com\"] }",
398
+ "Without it, sync fails with 403 before it reaches your app."
399
+ ].join("\n");
400
+ }
401
+ /**
402
+ * Warns that nothing is listening on the port about to be tunnelled.
403
+ *
404
+ * A quick tunnel to a dead port still resolves and still answers, so the failure
405
+ * surfaces later as an HTTP error attributed to the host app. Naming it here —
406
+ * while the developer is still looking at `clivly dev` output — costs one line.
407
+ */
408
+ function deadPortMessage(port, framework) {
409
+ return [`Warning: nothing is listening on port ${port} yet, but the tunnel points there.${framework ? ` ${FRAMEWORK_LABELS[framework]} usually serves on ${defaultPortFor(framework)}.` : ""}`, "If your dev server uses a different port, restart with: clivly dev --port <port>"].join("\n");
410
+ }
350
411
  function detectDevCommand(scripts, explicit) {
351
412
  if (explicit && explicit.length > 0) return explicit;
352
413
  if (scripts.dev) return [
@@ -404,10 +465,12 @@ function explicitCommand(argv) {
404
465
  }
405
466
  async function runDev(options) {
406
467
  const { deps } = options;
407
- const { target, triggerPath } = resolveDevTarget({
468
+ const framework = deps.detectFramework?.(options.cwd);
469
+ const { target, triggerPath, port } = resolveDevTarget({
408
470
  url: flagValue$1(options.argv, "--url"),
409
471
  port: flagValue$1(options.argv, "--port"),
410
- routePath: flagValue$1(options.argv, "--route-path")
472
+ routePath: flagValue$1(options.argv, "--route-path"),
473
+ framework
411
474
  });
412
475
  const command = detectDevCommand(deps.readScripts(options.cwd), explicitCommand(options.argv));
413
476
  if (!deps.hasCloudflared()) {
@@ -424,6 +487,7 @@ async function runDev(options) {
424
487
  deps.out("CLIVLY_SECRET_KEY is not set — cannot register the tunnel URL. Set it and re-run.");
425
488
  return 1;
426
489
  }
490
+ if (isViteFramework(framework)) deps.out(viteAllowedHostsHint());
427
491
  const app = command ? deps.spawnApp(command) : null;
428
492
  const tunnel = deps.spawnTunnel(target);
429
493
  let shuttingDown = false;
@@ -431,6 +495,9 @@ async function runDev(options) {
431
495
  if (shuttingDown) return;
432
496
  const triggerUrl = buildTriggerUrl(origin, triggerPath);
433
497
  deps.out(`Tunnel: ${triggerUrl}`);
498
+ if (deps.isPortListening && !flagValue$1(options.argv, "--url")) {
499
+ if (!await deps.isPortListening(port).catch(() => true)) deps.out(deadPortMessage(port, framework));
500
+ }
434
501
  const result = await deps.register(triggerUrl).catch(() => UNREACHABLE_RESULT);
435
502
  if (result.ok) deps.out("Dashboard-triggered sync now points at this local session.");
436
503
  else deps.out(overrideFailureMessage(result.kind, result.status));
@@ -1809,6 +1876,10 @@ const NEXT_INSTRUMENTATION_FLAG_STEP = `Your Next.js version reads instrumentati
1809
1876
  const REMIX_BOOT_STEP = `Start Clivly when your app boots. Add this near the top of app/entry.server.tsx:
1810
1877
  import clivly from "./clivly.config";
1811
1878
  clivly.start().catch((error) => console.warn("[clivly] could not check in:", error));`;
1879
+ const MISSING_VITE_CONFIG_STEP = `Could not find a vite config, so nothing starts Clivly when your app boots. Add the plugin to your Vite config:
1880
+ import { clivlyVite } from "clivly/vite";
1881
+ import clivly from "./clivly.config";
1882
+ // then, inside defineConfig: plugins: [clivlyVite(clivly), …]`;
1812
1883
  /** The boot hook as a scaffold entry, or null when this framework is patched. */
1813
1884
  function planBootHook(framework, layout) {
1814
1885
  const path = bootHookPath(framework, layout);
@@ -2402,7 +2473,7 @@ function planBootPatch(args) {
2402
2473
  }
2403
2474
  return {
2404
2475
  file: null,
2405
- manual: null
2476
+ manual: MISSING_VITE_CONFIG_STEP
2406
2477
  };
2407
2478
  }
2408
2479
  function planMutations(args) {
@@ -2426,6 +2497,43 @@ function planWidgetMutations(args) {
2426
2497
  });
2427
2498
  }
2428
2499
  //#endregion
2500
+ //#region src/port-probe.ts
2501
+ const PORT_PROBE_TIMEOUT_MS = 1500;
2502
+ /** One connect attempt against a single address family. */
2503
+ function connectsOn(port, host) {
2504
+ return new Promise((resolve) => {
2505
+ const socket = new node_net.Socket();
2506
+ let settled = false;
2507
+ const finish = (listening) => {
2508
+ if (settled) return;
2509
+ settled = true;
2510
+ socket.destroy();
2511
+ resolve(listening);
2512
+ };
2513
+ socket.setTimeout(PORT_PROBE_TIMEOUT_MS);
2514
+ socket.once("connect", () => finish(true));
2515
+ socket.once("timeout", () => finish(false));
2516
+ socket.once("error", () => finish(false));
2517
+ socket.connect(Number(port), host);
2518
+ });
2519
+ }
2520
+ /**
2521
+ * Whether anything is accepting TCP connections on a local port.
2522
+ *
2523
+ * Deliberately a bare socket connect rather than an HTTP request: the dev
2524
+ * server may legitimately answer 404/403/500 on `/`, and any of those still
2525
+ * proves something is listening. Only a refused or timed-out connection means
2526
+ * the tunnel is pointed at nothing.
2527
+ *
2528
+ * Both address families are tried, because Vite binds `::1` ONLY by default —
2529
+ * so an IPv4-only probe reports "nothing is listening" while the dev server is
2530
+ * running perfectly. A false warning here is worse than none: it sends the
2531
+ * developer to fix a port that was already correct.
2532
+ */
2533
+ function isLocalPortListening(port) {
2534
+ return Promise.all([connectsOn(port, "127.0.0.1"), connectsOn(port, "::1")]).then((results) => results.some(Boolean));
2535
+ }
2536
+ //#endregion
2429
2537
  //#region src/quiet-host-output.ts
2430
2538
  /**
2431
2539
  * Run `fn` with the process's stdout/stderr captured. Always restores the
@@ -4005,6 +4113,8 @@ function defaultDevDeps(options) {
4005
4113
  spawnTunnel: spawnCloudflaredTunnel,
4006
4114
  spawnApp: spawnAppProcess,
4007
4115
  readScripts: readPackageScripts,
4116
+ detectFramework: (cwd) => detectFramework(readDeps(cwd)),
4117
+ isPortListening: isLocalPortListening,
4008
4118
  register: (triggerUrl) => registerTunnelOverride({
4009
4119
  apiKey: options.apiKey ?? "",
4010
4120
  apiUrl: options.apiUrl,
package/index.mjs CHANGED
@@ -11,6 +11,7 @@ import { delimiter, dirname, isAbsolute, join, relative, resolve, sep } from "no
11
11
  import { createInterface } from "node:readline/promises";
12
12
  import { createJiti } from "jiti";
13
13
  import { fileURLToPath, pathToFileURL } from "node:url";
14
+ import { Socket } from "node:net";
14
15
  //#region ../core/src/env-file.ts
15
16
  const ENV_FILES = {
16
17
  next: ".env.local",
@@ -332,6 +333,28 @@ const TRIGGER_URL_PLACEHOLDER = `https://<yourDomain.com>${CLIVLY_TICK_PATH}`;
332
333
  //#region src/dev-tunnel.ts
333
334
  const CLOUDFLARE_QUICK_TUNNEL_REGEX = /https:\/\/[a-z0-9-]+\.trycloudflare\.com/;
334
335
  const DEFAULT_PORT = "3000";
336
+ /**
337
+ * The port each framework's dev server actually listens on out of the box.
338
+ *
339
+ * `clivly dev` used to tunnel to 3000 for everyone. Vite-based frameworks start
340
+ * on 5173, so the tunnel pointed at whatever else happened to hold 3000 — in one
341
+ * smoke run, a *different app entirely*, which answered, which made the tunnel
342
+ * look healthy while syncing the wrong project. Every symptom pointed away from
343
+ * the cause.
344
+ *
345
+ * These are defaults, not detections: a project that sets its own port must
346
+ * still pass `--port`. Guessing right for the untouched case is the win.
347
+ */
348
+ const FRAMEWORK_DEFAULT_PORTS = {
349
+ remix: "5173",
350
+ sveltekit: "5173",
351
+ "tanstack-start": "5173",
352
+ nextjs: "3000",
353
+ nuxt: "3000"
354
+ };
355
+ function defaultPortFor(framework) {
356
+ return (framework && FRAMEWORK_DEFAULT_PORTS[framework]) ?? DEFAULT_PORT;
357
+ }
335
358
  const DEFAULT_ROUTE_PATH = CLIVLY_TICK_PATH;
336
359
  function parseTunnelUrl(chunk) {
337
360
  const match = chunk.match(CLOUDFLARE_QUICK_TUNNEL_REGEX);
@@ -341,11 +364,49 @@ function buildTriggerUrl(origin, path) {
341
364
  return `${origin.endsWith("/") ? origin.slice(0, -1) : origin}${path.startsWith("/") ? path : `/${path}`}`;
342
365
  }
343
366
  function resolveDevTarget(flags) {
367
+ const port = flags.port ?? defaultPortFor(flags.framework);
344
368
  return {
345
- target: flags.url ?? `http://localhost:${flags.port ?? DEFAULT_PORT}`,
346
- triggerPath: flags.routePath ?? DEFAULT_ROUTE_PATH
369
+ target: flags.url ?? `http://localhost:${port}`,
370
+ triggerPath: flags.routePath ?? DEFAULT_ROUTE_PATH,
371
+ port
347
372
  };
348
373
  }
374
+ const VITE_FRAMEWORKS = /* @__PURE__ */ new Set([
375
+ "remix",
376
+ "sveltekit",
377
+ "tanstack-start"
378
+ ]);
379
+ function isViteFramework(framework) {
380
+ return framework ? VITE_FRAMEWORKS.has(framework) : false;
381
+ }
382
+ /**
383
+ * The one config line that makes a Vite dev server answer the tunnel.
384
+ *
385
+ * Without it every sync fails with a bare "The host app returned 403" and the
386
+ * explanation — Vite's own, naming this exact setting — is buried in a
387
+ * diagnostics column. This affects four of the five supported frameworks, so
388
+ * `clivly dev` says it up front rather than letting each developer rediscover
389
+ * it. Printed unconditionally for Vite apps: reading the app's vite config well
390
+ * enough to know whether it is already set means parsing arbitrary TypeScript,
391
+ * and a redundant hint is far cheaper than a silent 403.
392
+ */
393
+ function viteAllowedHostsHint() {
394
+ return [
395
+ "Vite blocks unknown hosts, so the tunnel needs one line in your Vite config:",
396
+ " server: { allowedHosts: [\".trycloudflare.com\"] }",
397
+ "Without it, sync fails with 403 before it reaches your app."
398
+ ].join("\n");
399
+ }
400
+ /**
401
+ * Warns that nothing is listening on the port about to be tunnelled.
402
+ *
403
+ * A quick tunnel to a dead port still resolves and still answers, so the failure
404
+ * surfaces later as an HTTP error attributed to the host app. Naming it here —
405
+ * while the developer is still looking at `clivly dev` output — costs one line.
406
+ */
407
+ function deadPortMessage(port, framework) {
408
+ return [`Warning: nothing is listening on port ${port} yet, but the tunnel points there.${framework ? ` ${FRAMEWORK_LABELS[framework]} usually serves on ${defaultPortFor(framework)}.` : ""}`, "If your dev server uses a different port, restart with: clivly dev --port <port>"].join("\n");
409
+ }
349
410
  function detectDevCommand(scripts, explicit) {
350
411
  if (explicit && explicit.length > 0) return explicit;
351
412
  if (scripts.dev) return [
@@ -403,10 +464,12 @@ function explicitCommand(argv) {
403
464
  }
404
465
  async function runDev(options) {
405
466
  const { deps } = options;
406
- const { target, triggerPath } = resolveDevTarget({
467
+ const framework = deps.detectFramework?.(options.cwd);
468
+ const { target, triggerPath, port } = resolveDevTarget({
407
469
  url: flagValue$1(options.argv, "--url"),
408
470
  port: flagValue$1(options.argv, "--port"),
409
- routePath: flagValue$1(options.argv, "--route-path")
471
+ routePath: flagValue$1(options.argv, "--route-path"),
472
+ framework
410
473
  });
411
474
  const command = detectDevCommand(deps.readScripts(options.cwd), explicitCommand(options.argv));
412
475
  if (!deps.hasCloudflared()) {
@@ -423,6 +486,7 @@ async function runDev(options) {
423
486
  deps.out("CLIVLY_SECRET_KEY is not set — cannot register the tunnel URL. Set it and re-run.");
424
487
  return 1;
425
488
  }
489
+ if (isViteFramework(framework)) deps.out(viteAllowedHostsHint());
426
490
  const app = command ? deps.spawnApp(command) : null;
427
491
  const tunnel = deps.spawnTunnel(target);
428
492
  let shuttingDown = false;
@@ -430,6 +494,9 @@ async function runDev(options) {
430
494
  if (shuttingDown) return;
431
495
  const triggerUrl = buildTriggerUrl(origin, triggerPath);
432
496
  deps.out(`Tunnel: ${triggerUrl}`);
497
+ if (deps.isPortListening && !flagValue$1(options.argv, "--url")) {
498
+ if (!await deps.isPortListening(port).catch(() => true)) deps.out(deadPortMessage(port, framework));
499
+ }
433
500
  const result = await deps.register(triggerUrl).catch(() => UNREACHABLE_RESULT);
434
501
  if (result.ok) deps.out("Dashboard-triggered sync now points at this local session.");
435
502
  else deps.out(overrideFailureMessage(result.kind, result.status));
@@ -1808,6 +1875,10 @@ const NEXT_INSTRUMENTATION_FLAG_STEP = `Your Next.js version reads instrumentati
1808
1875
  const REMIX_BOOT_STEP = `Start Clivly when your app boots. Add this near the top of app/entry.server.tsx:
1809
1876
  import clivly from "./clivly.config";
1810
1877
  clivly.start().catch((error) => console.warn("[clivly] could not check in:", error));`;
1878
+ const MISSING_VITE_CONFIG_STEP = `Could not find a vite config, so nothing starts Clivly when your app boots. Add the plugin to your Vite config:
1879
+ import { clivlyVite } from "clivly/vite";
1880
+ import clivly from "./clivly.config";
1881
+ // then, inside defineConfig: plugins: [clivlyVite(clivly), …]`;
1811
1882
  /** The boot hook as a scaffold entry, or null when this framework is patched. */
1812
1883
  function planBootHook(framework, layout) {
1813
1884
  const path = bootHookPath(framework, layout);
@@ -2401,7 +2472,7 @@ function planBootPatch(args) {
2401
2472
  }
2402
2473
  return {
2403
2474
  file: null,
2404
- manual: null
2475
+ manual: MISSING_VITE_CONFIG_STEP
2405
2476
  };
2406
2477
  }
2407
2478
  function planMutations(args) {
@@ -2425,6 +2496,43 @@ function planWidgetMutations(args) {
2425
2496
  });
2426
2497
  }
2427
2498
  //#endregion
2499
+ //#region src/port-probe.ts
2500
+ const PORT_PROBE_TIMEOUT_MS = 1500;
2501
+ /** One connect attempt against a single address family. */
2502
+ function connectsOn(port, host) {
2503
+ return new Promise((resolve) => {
2504
+ const socket = new Socket();
2505
+ let settled = false;
2506
+ const finish = (listening) => {
2507
+ if (settled) return;
2508
+ settled = true;
2509
+ socket.destroy();
2510
+ resolve(listening);
2511
+ };
2512
+ socket.setTimeout(PORT_PROBE_TIMEOUT_MS);
2513
+ socket.once("connect", () => finish(true));
2514
+ socket.once("timeout", () => finish(false));
2515
+ socket.once("error", () => finish(false));
2516
+ socket.connect(Number(port), host);
2517
+ });
2518
+ }
2519
+ /**
2520
+ * Whether anything is accepting TCP connections on a local port.
2521
+ *
2522
+ * Deliberately a bare socket connect rather than an HTTP request: the dev
2523
+ * server may legitimately answer 404/403/500 on `/`, and any of those still
2524
+ * proves something is listening. Only a refused or timed-out connection means
2525
+ * the tunnel is pointed at nothing.
2526
+ *
2527
+ * Both address families are tried, because Vite binds `::1` ONLY by default —
2528
+ * so an IPv4-only probe reports "nothing is listening" while the dev server is
2529
+ * running perfectly. A false warning here is worse than none: it sends the
2530
+ * developer to fix a port that was already correct.
2531
+ */
2532
+ function isLocalPortListening(port) {
2533
+ return Promise.all([connectsOn(port, "127.0.0.1"), connectsOn(port, "::1")]).then((results) => results.some(Boolean));
2534
+ }
2535
+ //#endregion
2428
2536
  //#region src/quiet-host-output.ts
2429
2537
  /**
2430
2538
  * Run `fn` with the process's stdout/stderr captured. Always restores the
@@ -4004,6 +4112,8 @@ function defaultDevDeps(options) {
4004
4112
  spawnTunnel: spawnCloudflaredTunnel,
4005
4113
  spawnApp: spawnAppProcess,
4006
4114
  readScripts: readPackageScripts,
4115
+ detectFramework: (cwd) => detectFramework(readDeps(cwd)),
4116
+ isPortListening: isLocalPortListening,
4007
4117
  register: (triggerUrl) => registerTunnelOverride({
4008
4118
  apiKey: options.apiKey ?? "",
4009
4119
  apiUrl: options.apiUrl,
@@ -14,6 +14,14 @@ interface RunFact {
14
14
  at: number;
15
15
  message?: string;
16
16
  ok: boolean;
17
+ /**
18
+ * The HTTP status the host answered with, when there was one.
19
+ *
20
+ * Absent for a run that failed before any response existed, and for callers
21
+ * that do not track it — so every read must treat `undefined` as "unknown",
22
+ * never as "fine".
23
+ */
24
+ status?: number;
17
25
  }
18
26
  /**
19
27
  * Where the reporting app is running, as the app itself understands it.
@@ -51,6 +59,19 @@ interface IntegrationFacts {
51
59
  * transport keeps the cadence-derived behaviour every SDK in the field uses.
52
60
  */
53
61
  presence?: DeploymentPresence | null;
62
+ /**
63
+ * The result of Clivly asking the app directly whether it is up.
64
+ *
65
+ * Every other signal here is *pushed* by the app. On a serverless host there
66
+ * is no process between requests to push anything: the SDK's heartbeat timer
67
+ * fires once at boot and is then frozen, so cadence alone reports a perfectly
68
+ * healthy deployment as offline forever. A probe inverts that — Clivly asks,
69
+ * and a host that answers is up by definition.
70
+ *
71
+ * Absent keeps the pure cadence behaviour, so nothing changes for apps that
72
+ * do hold a long-lived process.
73
+ */
74
+ reachabilityProbe?: ReachabilityProbe | null;
54
75
  /**
55
76
  * Faults the running app diagnosed about itself on its last heartbeat — an
56
77
  * unreadable database, a secret missing from the process environment. These
@@ -77,6 +98,21 @@ interface IntegrationStep {
77
98
  state: StepState;
78
99
  }
79
100
  type ConnectionHealth = "live" | "live_with_issues" | "stale" | "offline";
101
+ /**
102
+ * Clivly's own answer to "is this app up", obtained by asking it rather than
103
+ * waiting to be told.
104
+ *
105
+ * `reachable` is deliberately about the *connection*, not the response body: an
106
+ * app that answers `401` has proved it is running just as conclusively as one
107
+ * that answers `200`. Only silence — a timeout, a refused connection, DNS that
108
+ * does not resolve — means offline.
109
+ */
110
+ interface ReachabilityProbe {
111
+ /** When Clivly performed the check. Probe results are cached, so this ages. */
112
+ checkedAt: number;
113
+ /** Did anything answer at the other end? */
114
+ reachable: boolean;
115
+ }
80
116
  /** Which algorithm decides whether this deployment is up. */
81
117
  type PresenceTransport = "websocket" | "http";
82
118
  type PresenceConnectionState = "connected" | "reconnecting" | "disconnected";
@@ -14,6 +14,14 @@ interface RunFact {
14
14
  at: number;
15
15
  message?: string;
16
16
  ok: boolean;
17
+ /**
18
+ * The HTTP status the host answered with, when there was one.
19
+ *
20
+ * Absent for a run that failed before any response existed, and for callers
21
+ * that do not track it — so every read must treat `undefined` as "unknown",
22
+ * never as "fine".
23
+ */
24
+ status?: number;
17
25
  }
18
26
  /**
19
27
  * Where the reporting app is running, as the app itself understands it.
@@ -51,6 +59,19 @@ interface IntegrationFacts {
51
59
  * transport keeps the cadence-derived behaviour every SDK in the field uses.
52
60
  */
53
61
  presence?: DeploymentPresence | null;
62
+ /**
63
+ * The result of Clivly asking the app directly whether it is up.
64
+ *
65
+ * Every other signal here is *pushed* by the app. On a serverless host there
66
+ * is no process between requests to push anything: the SDK's heartbeat timer
67
+ * fires once at boot and is then frozen, so cadence alone reports a perfectly
68
+ * healthy deployment as offline forever. A probe inverts that — Clivly asks,
69
+ * and a host that answers is up by definition.
70
+ *
71
+ * Absent keeps the pure cadence behaviour, so nothing changes for apps that
72
+ * do hold a long-lived process.
73
+ */
74
+ reachabilityProbe?: ReachabilityProbe | null;
54
75
  /**
55
76
  * Faults the running app diagnosed about itself on its last heartbeat — an
56
77
  * unreadable database, a secret missing from the process environment. These
@@ -77,6 +98,21 @@ interface IntegrationStep {
77
98
  state: StepState;
78
99
  }
79
100
  type ConnectionHealth = "live" | "live_with_issues" | "stale" | "offline";
101
+ /**
102
+ * Clivly's own answer to "is this app up", obtained by asking it rather than
103
+ * waiting to be told.
104
+ *
105
+ * `reachable` is deliberately about the *connection*, not the response body: an
106
+ * app that answers `401` has proved it is running just as conclusively as one
107
+ * that answers `200`. Only silence — a timeout, a refused connection, DNS that
108
+ * does not resolve — means offline.
109
+ */
110
+ interface ReachabilityProbe {
111
+ /** When Clivly performed the check. Probe results are cached, so this ages. */
112
+ checkedAt: number;
113
+ /** Did anything answer at the other end? */
114
+ reachable: boolean;
115
+ }
80
116
  /** Which algorithm decides whether this deployment is up. */
81
117
  type PresenceTransport = "websocket" | "http";
82
118
  type PresenceConnectionState = "connected" | "reconnecting" | "disconnected";
package/internal.d.cts CHANGED
@@ -6,6 +6,6 @@ import { i as relationshipViaSignature, n as buildFilterFromDiscriminator, r as
6
6
  import { i as mappingsToEntitiesConfig, t as EntityMappingRow } from "./mappings-config-DUt4-9iO.cjs";
7
7
  import { a as compileEntityView, i as ExplainResult, n as CompiledView, o as compileEntityViews, r as DEFAULT_VIEW_PREFIX, s as explainEntitiesConfig, t as CompileOptions } from "./view-compiler-D3zp9S3K.cjs";
8
8
  import { c as SyncResult, f as reconcile, l as SyncStore, n as MirrorRecord, o as SourceRow, p as runSync, r as PersistAction, s as SyncAction, t as EntitySyncResult } from "./sync-engine-Df0kHAoJ.cjs";
9
- import { a as IntegrationStep, c as StepProblem, i as IntegrationStatus, l as StepState, n as IntegrationFacts, o as NextAction, r as IntegrationState, s as StepId, t as ConnectionLiveness } from "./integration-state-BK-riZnm.cjs";
9
+ import { a as IntegrationStep, c as StepProblem, i as IntegrationStatus, l as StepState, n as IntegrationFacts, o as NextAction, r as IntegrationState, s as StepId, t as ConnectionLiveness } from "./integration-state-DfqRSdTn.cjs";
10
10
  import { S as joinFansOut, _ as compileProjectionJoins, a as matchesCondition, b as quoteIdent, c as columnMetaFor, d as primaryKeyColumns, f as validateProjection, g as compileProjectionIdentity, h as compileProjectionFrom, i as evaluateStage, l as coversUniqueKey, m as PREVIEW_MAX_ROWS, n as ConditionOp, o as slugify, p as validateProjectionFilter, r as EvaluableRule, s as toObjectTypeSlug, t as Condition, u as hasRichMeta, v as compileProjectionPreview, x as fanOutJoins, y as compileProjectionSelect } from "./internal-BHJ0X3fC.cjs";
11
11
  export { type Activity, type ActivityType, BASE_REF, CANONICAL_FIELDS, type CRMAdapter, CRM_CONCEPTS, type ClivlyAuthAdapter, ClivlyConfigError, type ClivlyEntitiesConfig, type ClivlyEntityConfig, type ClivlyUser, type Company, type CompileOptions, type CompiledView, type Condition, type ConditionOp, type ConfigValidationError, type ConfigValidationResult, type ConnectionLiveness, type Contact, type ContactFilters, type ContactStatus, type CreateActivityInput, type CreateCompanyInput, type CreateContactInput, type CreateDealInput, type CreateTaskInput, type CrmConcept, type CrmMembership, type CrmRole, DEFAULT_VIEW_PREFIX, type Deal, type DealStage, type DiscoveredColumnMeta, type DiscoveredSchemaTable, type EntityMappingRow, type EntitySyncResult, type EvaluableRule, type ExplainResult, type FilterExpr, type FilterValue, type IntegrationFacts, type IntegrationState, type IntegrationStatus, type IntegrationStep, MATERIALIZING_CONCEPTS, type MaterializingConcept, type MembershipResolver, type MirrorRecord, type NextAction, type Note, OPEN_STAGE_TYPE, PREVIEW_MAX_ROWS, PROJECTION_KEY_PATTERN, type PersistAction, type PipelineConfig, type PipelineKind, type PipelineStageConfig, type ProjectionFieldBinding, type ProjectionIdentity, type ProjectionJoin, type ProjectionMapping, type ProjectionRef, type RecordSource, type RelationshipCandidate, type RelationshipSpec, type RelationshipVia, type RelationshipsMap, type SourceRow, type StageType, type StepId, type StepProblem, type StepState, type SyncAction, type SyncResult, type SyncStore, type Task, type TaskEntityType, type TaskFilters, type TaskPriority, type TaskStatus, type UpdateContactInput, type UpdateDealInput, type UpdateTaskInput, type ValueInterval, authCustom, buildFilterFromDiscriminator, buildRelationships, columnMetaFor, compileEntityView, compileEntityViews, compileProjectionFrom, compileProjectionIdentity, compileProjectionJoins, compileProjectionPreview, compileProjectionSelect, coversUniqueKey, defineClivlyConfig, entitiesConfigSchema, evaluateStage, explainEntitiesConfig, fanOutJoins, filterSchema, hasRichMeta, identityRefs, isClosedStageType, joinFansOut, mappingsToEntitiesConfig, matchesCondition, primaryKeyColumns, projectionFields, projectionSchema, quoteIdent, reconcile, relationshipSchema, relationshipViaSignature, relationshipsSchema, runSync, slugify, toObjectTypeSlug, validateEntitiesConfig, validateProjection, validateProjectionFilter };
package/internal.d.mts CHANGED
@@ -6,6 +6,6 @@ import { i as relationshipViaSignature, n as buildFilterFromDiscriminator, r as
6
6
  import { i as mappingsToEntitiesConfig, t as EntityMappingRow } from "./mappings-config-Be9D43Np.mjs";
7
7
  import { a as compileEntityView, i as ExplainResult, n as CompiledView, o as compileEntityViews, r as DEFAULT_VIEW_PREFIX, s as explainEntitiesConfig, t as CompileOptions } from "./view-compiler-TpzDTu7z.mjs";
8
8
  import { c as SyncResult, f as reconcile, l as SyncStore, n as MirrorRecord, o as SourceRow, p as runSync, r as PersistAction, s as SyncAction, t as EntitySyncResult } from "./sync-engine-ugFacJ6G.mjs";
9
- import { a as IntegrationStep, c as StepProblem, i as IntegrationStatus, l as StepState, n as IntegrationFacts, o as NextAction, r as IntegrationState, s as StepId, t as ConnectionLiveness } from "./integration-state-BK-riZnm.mjs";
9
+ import { a as IntegrationStep, c as StepProblem, i as IntegrationStatus, l as StepState, n as IntegrationFacts, o as NextAction, r as IntegrationState, s as StepId, t as ConnectionLiveness } from "./integration-state-DfqRSdTn.mjs";
10
10
  import { S as joinFansOut, _ as compileProjectionJoins, a as matchesCondition, b as quoteIdent, c as columnMetaFor, d as primaryKeyColumns, f as validateProjection, g as compileProjectionIdentity, h as compileProjectionFrom, i as evaluateStage, l as coversUniqueKey, m as PREVIEW_MAX_ROWS, n as ConditionOp, o as slugify, p as validateProjectionFilter, r as EvaluableRule, s as toObjectTypeSlug, t as Condition, u as hasRichMeta, v as compileProjectionPreview, x as fanOutJoins, y as compileProjectionSelect } from "./internal-BAOG5eE4.mjs";
11
11
  export { type Activity, type ActivityType, BASE_REF, CANONICAL_FIELDS, type CRMAdapter, CRM_CONCEPTS, type ClivlyAuthAdapter, ClivlyConfigError, type ClivlyEntitiesConfig, type ClivlyEntityConfig, type ClivlyUser, type Company, type CompileOptions, type CompiledView, type Condition, type ConditionOp, type ConfigValidationError, type ConfigValidationResult, type ConnectionLiveness, type Contact, type ContactFilters, type ContactStatus, type CreateActivityInput, type CreateCompanyInput, type CreateContactInput, type CreateDealInput, type CreateTaskInput, type CrmConcept, type CrmMembership, type CrmRole, DEFAULT_VIEW_PREFIX, type Deal, type DealStage, type DiscoveredColumnMeta, type DiscoveredSchemaTable, type EntityMappingRow, type EntitySyncResult, type EvaluableRule, type ExplainResult, type FilterExpr, type FilterValue, type IntegrationFacts, type IntegrationState, type IntegrationStatus, type IntegrationStep, MATERIALIZING_CONCEPTS, type MaterializingConcept, type MembershipResolver, type MirrorRecord, type NextAction, type Note, OPEN_STAGE_TYPE, PREVIEW_MAX_ROWS, PROJECTION_KEY_PATTERN, type PersistAction, type PipelineConfig, type PipelineKind, type PipelineStageConfig, type ProjectionFieldBinding, type ProjectionIdentity, type ProjectionJoin, type ProjectionMapping, type ProjectionRef, type RecordSource, type RelationshipCandidate, type RelationshipSpec, type RelationshipVia, type RelationshipsMap, type SourceRow, type StageType, type StepId, type StepProblem, type StepState, type SyncAction, type SyncResult, type SyncStore, type Task, type TaskEntityType, type TaskFilters, type TaskPriority, type TaskStatus, type UpdateContactInput, type UpdateDealInput, type UpdateTaskInput, type ValueInterval, authCustom, buildFilterFromDiscriminator, buildRelationships, columnMetaFor, compileEntityView, compileEntityViews, compileProjectionFrom, compileProjectionIdentity, compileProjectionJoins, compileProjectionPreview, compileProjectionSelect, coversUniqueKey, defineClivlyConfig, entitiesConfigSchema, evaluateStage, explainEntitiesConfig, fanOutJoins, filterSchema, hasRichMeta, identityRefs, isClosedStageType, joinFansOut, mappingsToEntitiesConfig, matchesCondition, primaryKeyColumns, projectionFields, projectionSchema, quoteIdent, reconcile, relationshipSchema, relationshipViaSignature, relationshipsSchema, runSync, slugify, toObjectTypeSlug, validateEntitiesConfig, validateProjection, validateProjectionFilter };
@@ -1,5 +1,5 @@
1
1
  import { F as ProjectionMapping, a as ClivlyEntitiesConfig } from "./entity-config-DNQAMbMS.mjs";
2
- import { r as IntegrationState } from "./integration-state-BK-riZnm.mjs";
2
+ import { r as IntegrationState } from "./integration-state-DfqRSdTn.mjs";
3
3
  //#region ../sdk/src/drizzle-meta.d.ts
4
4
  declare const DRIZZLE_META: "__clivly_drizzle_source_meta__";
5
5
  interface DrizzleSourceMeta {
@@ -1,5 +1,5 @@
1
1
  import { F as ProjectionMapping, a as ClivlyEntitiesConfig } from "./entity-config-DNQAMbMS.cjs";
2
- import { r as IntegrationState } from "./integration-state-BK-riZnm.cjs";
2
+ import { r as IntegrationState } from "./integration-state-DfqRSdTn.cjs";
3
3
  //#region ../sdk/src/drizzle-meta.d.ts
4
4
  declare const DRIZZLE_META: "__clivly_drizzle_source_meta__";
5
5
  interface DrizzleSourceMeta {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clivly",
3
- "version": "0.10.2",
3
+ "version": "0.10.4",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Clivly CLI — scaffold a CRM into your app (clivly init).",
package/sdk.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { C as defineClivlyConfig, S as customObjectDeclarations, a as ClivlyEntitiesConfig, f as CustomObjectDeclaration, k as validateEntitiesConfig, l as ConfigValidationError, m as DiscoveredSchemaTable, u as ConfigValidationResult } from "./entity-config-DNQAMbMS.cjs";
2
- import { _ as RemoteProjectionsDescription, a as ClivlyConnectResult, c as ClivlyUser, d as DiscoveredTable, f as DoctorCheck, g as EnsuredObjectTypeResult, h as EnsuredMappingResult, i as ClivlyConnectReason, l as CreateChatSessionHandlerOptions, m as EnsureSchemaResult, n as ChatSessionRequestBody, o as ClivlySDK, p as DoctorReport, r as ChatWidgetVisitor, s as ClivlySDKConfig, u as CursorStore, y as SyncTriggerDescription } from "./namespace-probe-DC4tbmLj.cjs";
2
+ import { _ as RemoteProjectionsDescription, a as ClivlyConnectResult, c as ClivlyUser, d as DiscoveredTable, f as DoctorCheck, g as EnsuredObjectTypeResult, h as EnsuredMappingResult, i as ClivlyConnectReason, l as CreateChatSessionHandlerOptions, m as EnsureSchemaResult, n as ChatSessionRequestBody, o as ClivlySDK, p as DoctorReport, r as ChatWidgetVisitor, s as ClivlySDKConfig, u as CursorStore, y as SyncTriggerDescription } from "./namespace-probe-DXN20mds.cjs";
3
3
  //#region ../sdk/src/trigger-url.d.ts
4
4
  declare function isPlaceholderUrl(value: string): boolean;
5
5
  //#endregion
package/sdk.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { C as defineClivlyConfig, S as customObjectDeclarations, a as ClivlyEntitiesConfig, f as CustomObjectDeclaration, k as validateEntitiesConfig, l as ConfigValidationError, m as DiscoveredSchemaTable, u as ConfigValidationResult } from "./entity-config-DNQAMbMS.mjs";
2
- import { _ as RemoteProjectionsDescription, a as ClivlyConnectResult, c as ClivlyUser, d as DiscoveredTable, f as DoctorCheck, g as EnsuredObjectTypeResult, h as EnsuredMappingResult, i as ClivlyConnectReason, l as CreateChatSessionHandlerOptions, m as EnsureSchemaResult, n as ChatSessionRequestBody, o as ClivlySDK, p as DoctorReport, r as ChatWidgetVisitor, s as ClivlySDKConfig, u as CursorStore, y as SyncTriggerDescription } from "./namespace-probe-9ScMDR0t.mjs";
2
+ import { _ as RemoteProjectionsDescription, a as ClivlyConnectResult, c as ClivlyUser, d as DiscoveredTable, f as DoctorCheck, g as EnsuredObjectTypeResult, h as EnsuredMappingResult, i as ClivlyConnectReason, l as CreateChatSessionHandlerOptions, m as EnsureSchemaResult, n as ChatSessionRequestBody, o as ClivlySDK, p as DoctorReport, r as ChatWidgetVisitor, s as ClivlySDKConfig, u as CursorStore, y as SyncTriggerDescription } from "./namespace-probe-BZuLxW3r.mjs";
3
3
  //#region ../sdk/src/trigger-url.d.ts
4
4
  declare function isPlaceholderUrl(value: string): boolean;
5
5
  //#endregion
package/vite.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { o as ClivlySDK } from "./namespace-probe-DC4tbmLj.cjs";
1
+ import { o as ClivlySDK } from "./namespace-probe-DXN20mds.cjs";
2
2
  import { Plugin } from "vite";
3
3
  //#region ../sdk/src/vite.d.ts
4
4
  interface ClivlyViteOptions {
package/vite.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { o as ClivlySDK } from "./namespace-probe-9ScMDR0t.mjs";
1
+ import { o as ClivlySDK } from "./namespace-probe-BZuLxW3r.mjs";
2
2
  import { Plugin } from "vite";
3
3
  //#region ../sdk/src/vite.d.ts
4
4
  interface ClivlyViteOptions {