clivly 0.10.0 → 0.10.2

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.
@@ -4,6 +4,7 @@ const require_internal_entity_heuristics = require("./internal-entity-heuristics
4
4
  require("./deprecate-subpath-B9ngvizt.cjs").warnDeprecatedSubpath("clivly/core/entity-heuristics", "clivly/internal/entity-heuristics");
5
5
  //#endregion
6
6
  exports.TARGET_CONCEPTS = require_internal_entity_heuristics.TARGET_CONCEPTS;
7
+ exports.hasDefiningColumn = require_internal_entity_heuristics.hasDefiningColumn;
7
8
  exports.hasSensitiveColumns = require_internal_entity_heuristics.hasSensitiveColumns;
8
9
  exports.matchesConcept = require_internal_entity_heuristics.matchesConcept;
9
10
  exports.requiredFieldsFor = require_internal_entity_heuristics.requiredFieldsFor;
@@ -1,3 +1,3 @@
1
1
  import { p as DiscoveredColumnMeta } from "./entity-config-DNQAMbMS.cjs";
2
- import { FieldSuggestion, RelationshipSuggestion, ScoredTable, SuggestedVia, TARGET_CONCEPTS, TargetConcept, hasSensitiveColumns, matchesConcept, requiredFieldsFor, scoreConcepts, scoreFieldMap, scoreRelationships, suggestCursorField, suggestDiscriminatorColumns, toFieldMap, topConcept, validateMapping } from "./internal-entity-heuristics.cjs";
3
- export { type DiscoveredColumnMeta, FieldSuggestion, RelationshipSuggestion, ScoredTable, SuggestedVia, TARGET_CONCEPTS, TargetConcept, hasSensitiveColumns, matchesConcept, requiredFieldsFor, scoreConcepts, scoreFieldMap, scoreRelationships, suggestCursorField, suggestDiscriminatorColumns, toFieldMap, topConcept, validateMapping };
2
+ import { FieldSuggestion, RelationshipSuggestion, ScoredTable, SuggestedVia, TARGET_CONCEPTS, TargetConcept, hasDefiningColumn, hasSensitiveColumns, matchesConcept, requiredFieldsFor, scoreConcepts, scoreFieldMap, scoreRelationships, suggestCursorField, suggestDiscriminatorColumns, toFieldMap, topConcept, validateMapping } from "./internal-entity-heuristics.cjs";
3
+ export { type DiscoveredColumnMeta, FieldSuggestion, RelationshipSuggestion, ScoredTable, SuggestedVia, TARGET_CONCEPTS, TargetConcept, hasDefiningColumn, hasSensitiveColumns, matchesConcept, requiredFieldsFor, scoreConcepts, scoreFieldMap, scoreRelationships, suggestCursorField, suggestDiscriminatorColumns, toFieldMap, topConcept, validateMapping };
@@ -1,3 +1,3 @@
1
1
  import { p as DiscoveredColumnMeta } from "./entity-config-DNQAMbMS.mjs";
2
- import { FieldSuggestion, RelationshipSuggestion, ScoredTable, SuggestedVia, TARGET_CONCEPTS, TargetConcept, hasSensitiveColumns, matchesConcept, requiredFieldsFor, scoreConcepts, scoreFieldMap, scoreRelationships, suggestCursorField, suggestDiscriminatorColumns, toFieldMap, topConcept, validateMapping } from "./internal-entity-heuristics.mjs";
3
- export { type DiscoveredColumnMeta, FieldSuggestion, RelationshipSuggestion, ScoredTable, SuggestedVia, TARGET_CONCEPTS, TargetConcept, hasSensitiveColumns, matchesConcept, requiredFieldsFor, scoreConcepts, scoreFieldMap, scoreRelationships, suggestCursorField, suggestDiscriminatorColumns, toFieldMap, topConcept, validateMapping };
2
+ import { FieldSuggestion, RelationshipSuggestion, ScoredTable, SuggestedVia, TARGET_CONCEPTS, TargetConcept, hasDefiningColumn, hasSensitiveColumns, matchesConcept, requiredFieldsFor, scoreConcepts, scoreFieldMap, scoreRelationships, suggestCursorField, suggestDiscriminatorColumns, toFieldMap, topConcept, validateMapping } from "./internal-entity-heuristics.mjs";
3
+ export { type DiscoveredColumnMeta, FieldSuggestion, RelationshipSuggestion, ScoredTable, SuggestedVia, TARGET_CONCEPTS, TargetConcept, hasDefiningColumn, hasSensitiveColumns, matchesConcept, requiredFieldsFor, scoreConcepts, scoreFieldMap, scoreRelationships, suggestCursorField, suggestDiscriminatorColumns, toFieldMap, topConcept, validateMapping };
@@ -1,6 +1,6 @@
1
- import { TARGET_CONCEPTS, hasSensitiveColumns, matchesConcept, requiredFieldsFor, scoreConcepts, scoreFieldMap, scoreRelationships, suggestCursorField, suggestDiscriminatorColumns, toFieldMap, topConcept, validateMapping } from "./internal-entity-heuristics.mjs";
1
+ import { TARGET_CONCEPTS, hasDefiningColumn, hasSensitiveColumns, matchesConcept, requiredFieldsFor, scoreConcepts, scoreFieldMap, scoreRelationships, suggestCursorField, suggestDiscriminatorColumns, toFieldMap, topConcept, validateMapping } from "./internal-entity-heuristics.mjs";
2
2
  import { t as warnDeprecatedSubpath } from "./deprecate-subpath-CmvZtCpE.mjs";
3
3
  //#region src/core-entity-heuristics.ts
4
4
  warnDeprecatedSubpath("clivly/core/entity-heuristics", "clivly/internal/entity-heuristics");
5
5
  //#endregion
6
- export { TARGET_CONCEPTS, hasSensitiveColumns, matchesConcept, requiredFieldsFor, scoreConcepts, scoreFieldMap, scoreRelationships, suggestCursorField, suggestDiscriminatorColumns, toFieldMap, topConcept, validateMapping };
6
+ export { TARGET_CONCEPTS, hasDefiningColumn, hasSensitiveColumns, matchesConcept, requiredFieldsFor, scoreConcepts, scoreFieldMap, scoreRelationships, suggestCursorField, suggestDiscriminatorColumns, toFieldMap, topConcept, validateMapping };
package/index.cjs CHANGED
@@ -1501,11 +1501,17 @@ function toScoredTable(table) {
1501
1501
  }))
1502
1502
  };
1503
1503
  }
1504
+ function columnNames(table) {
1505
+ return table.columns.map((column) => column.dbName);
1506
+ }
1504
1507
  function rankCandidates$1(tables, concept) {
1505
1508
  return tables.map((t) => ({
1506
1509
  t,
1507
1510
  m: require_internal_entity_heuristics.matchesConcept(t.tableName, concept)
1508
- })).filter((c) => Boolean(c.m)).sort((a, b) => Number(b.m.exact) - Number(a.m.exact)).map((c) => c.t);
1511
+ })).filter((c) => Boolean(c.m)).sort((a, b) => {
1512
+ const usable = Number(require_internal_entity_heuristics.hasDefiningColumn(concept, columnNames(b.t))) - Number(require_internal_entity_heuristics.hasDefiningColumn(concept, columnNames(a.t)));
1513
+ return usable === 0 ? Number(b.m.exact) - Number(a.m.exact) : usable;
1514
+ }).map((c) => c.t);
1509
1515
  }
1510
1516
  function selectEntities(tables) {
1511
1517
  const contactCands = rankCandidates$1(tables, "contact");
@@ -1768,7 +1774,8 @@ export default defineNitroPlugin(() => {
1768
1774
  `;
1769
1775
  }
1770
1776
  function sveltekitHook(importPath) {
1771
- return `import clivly from "${importPath}";
1777
+ return `import "dotenv/config";
1778
+ import clivly from "${importPath}";
1772
1779
 
1773
1780
  // Clivly boot hook. This module is evaluated once when the server starts.
1774
1781
  // ${FAILURE_NOTE}
@@ -2133,7 +2140,25 @@ function routePath(framework, kind, layout = "app") {
2133
2140
  }
2134
2141
  const WINDOWS_SEP = /\\/g;
2135
2142
  const LEADING_DOT_SLASH = /^\.\//;
2143
+ const ALIAS_MARKER = /^[$~#]/;
2144
+ const SCOPED_PACKAGE = /^@[^/]+\/[^/]+/;
2145
+ const PROJECT_ROOT_DIRS = /* @__PURE__ */ new Set([
2146
+ "app",
2147
+ "lib",
2148
+ "server",
2149
+ "src",
2150
+ "source",
2151
+ "utils"
2152
+ ]);
2153
+ function isAlreadyResolvable(specifier) {
2154
+ if (specifier.startsWith(".") || specifier.startsWith("/")) return false;
2155
+ if (ALIAS_MARKER.test(specifier) || SCOPED_PACKAGE.test(specifier)) return true;
2156
+ if (specifier.startsWith("@")) return true;
2157
+ const [head] = specifier.split("/");
2158
+ return !(head && PROJECT_ROOT_DIRS.has(head));
2159
+ }
2136
2160
  function hostImportPath(routeFilePath, appRelative, paths) {
2161
+ if (isAlreadyResolvable(appRelative)) return appRelative;
2137
2162
  const fromRoot = appRelative.replace(LEADING_DOT_SLASH, "");
2138
2163
  const alias = aliasFor({
2139
2164
  paths,
@@ -2452,7 +2477,12 @@ const AUTH_MODULE_PATHS = [
2452
2477
  "src/lib/auth",
2453
2478
  "app/lib/auth",
2454
2479
  "server/auth",
2455
- "src/server/auth"
2480
+ "src/server/auth",
2481
+ "src/lib/server/auth",
2482
+ "lib/server/auth",
2483
+ "server/utils/auth",
2484
+ "src/lib/auth/index",
2485
+ "lib/auth/index"
2456
2486
  ];
2457
2487
  /** A better-auth instance, duck-typed by the surface the adapter calls. */
2458
2488
  function isBetterAuthInstance(value) {
@@ -3640,6 +3670,7 @@ async function acquireSchema(options, deps) {
3640
3670
  out(` ORM: ${orm}\n`);
3641
3671
  out(` Auth: ${auth}${deps.authWiring.complete ? " (wired)" : ""}\n`);
3642
3672
  if (framework === "nextjs") out(` Layout: ${layout}\n`);
3673
+ if (auth === "better-auth" && !deps.authWiring.complete) out("\n ⚠️ Could not locate your better-auth instance.\n The verify route will import a placeholder path that does not\n resolve. Point it at the real module with:\n\n clivly init --force --auth-import <specifier>\n\n Accepts an alias ($lib/server/auth, @/lib/auth), a package name,\n or a project-relative path (src/lib/server/auth).\n");
3643
3674
  out("\n");
3644
3675
  if (orm !== "drizzle") {
3645
3676
  out(`clivly init currently generates config for Drizzle only. Detected ORM: ${orm}\n`);
@@ -3647,6 +3678,7 @@ async function acquireSchema(options, deps) {
3647
3678
  }
3648
3679
  if (framework === "unknown") {
3649
3680
  out("clivly init doesn't recognise your framework yet (looked for Next.js, TanStack Start, SvelteKit, Remix, Nuxt). Route file generation needs one of these.\n");
3681
+ if ("vue" in appDeps) out("\nUsing Vue? Clivly supports Vue through Nuxt. A Vite SPA has no server\nto mount the sync route on, so the route files need Nuxt's server\ndirectory (or any of the frameworks above).\n");
3650
3682
  return { code: 1 };
3651
3683
  }
3652
3684
  const schemaPath = deps.resolvePath(options.cwd);
@@ -3745,13 +3777,19 @@ function previewMutations(deps, mutations, options) {
3745
3777
  }
3746
3778
  out(options.diff ? "\nRe-run without --diff to apply; --force to overwrite the files marked conflict.\n" : "\nRe-run without --dry-run to apply.\n");
3747
3779
  }
3748
- function printNextSteps(out, authComplete, loginRan, envFile) {
3780
+ function printNextSteps(out, authComplete, loginRan, envFile, needsViteDotenv = false) {
3749
3781
  out("\nNext steps:\n");
3750
3782
  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`);
3751
3783
  out(" 2. Local dev: run `clivly dev` for dashboard-triggered sync\n");
3752
3784
  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");
3753
- const step = authComplete ? 3 : 4;
3785
+ let step = authComplete ? 3 : 4;
3754
3786
  if (!authComplete) out(" 3. Finish resolveUser in the auth/verify route\n");
3787
+ if (needsViteDotenv) {
3788
+ out(` ${step}. Add \`import "dotenv/config";\` to vite.config.ts\n SvelteKit populates \$env/*, not process.env, which better-auth and
3789
+ clivly.config both read — without it \`vite build\` fails.
3790
+ `);
3791
+ step += 1;
3792
+ }
3755
3793
  out(` ${step}. Run \`clivly status\` to see where you are\n`);
3756
3794
  out("\nAfter editing clivly.config.ts, restart your app — it loads the config\nat boot, so a running server keeps using the old one.\n");
3757
3795
  }
@@ -3807,7 +3845,8 @@ async function scaffold(options, deps, plan) {
3807
3845
  out(" Run `clivly login` from a terminal to write CLIVLY_SECRET_KEY and CLIVLY_SYNC_TRIGGER_SECRET.\n");
3808
3846
  } else loginRan = await deps.login(options.cwd) === 0;
3809
3847
  }
3810
- printNextSteps(out, deps.authWiring.complete, loginRan, envFileFor(detectFramework(deps.readDepsFn(options.cwd))));
3848
+ const initFramework = detectFramework(deps.readDepsFn(options.cwd));
3849
+ printNextSteps(out, deps.authWiring.complete, loginRan, envFileFor(initFramework), initFramework === "sveltekit" && deps.authWiring.hostImport !== null);
3811
3850
  return 0;
3812
3851
  }
3813
3852
  function normalizeAppRoot(options) {
package/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { t as CANONICAL_FIELDS } from "./entity-config-C4d3K9CP.mjs";
3
3
  import "./mapping-score-C68uAqGB.mjs";
4
- import { matchesConcept, requiredFieldsFor, scoreFieldMap, scoreRelationships, suggestCursorField, toFieldMap } from "./internal-entity-heuristics.mjs";
4
+ import { hasDefiningColumn, matchesConcept, requiredFieldsFor, scoreFieldMap, scoreRelationships, suggestCursorField, toFieldMap } from "./internal-entity-heuristics.mjs";
5
5
  import { s as isPlaceholderUrl, t as createClivlySDK } from "./sdk-D6gX0TXS.mjs";
6
6
  import { createRequire } from "node:module";
7
7
  import { spawn } from "node:child_process";
@@ -1500,11 +1500,17 @@ function toScoredTable(table) {
1500
1500
  }))
1501
1501
  };
1502
1502
  }
1503
+ function columnNames(table) {
1504
+ return table.columns.map((column) => column.dbName);
1505
+ }
1503
1506
  function rankCandidates$1(tables, concept) {
1504
1507
  return tables.map((t) => ({
1505
1508
  t,
1506
1509
  m: matchesConcept(t.tableName, concept)
1507
- })).filter((c) => Boolean(c.m)).sort((a, b) => Number(b.m.exact) - Number(a.m.exact)).map((c) => c.t);
1510
+ })).filter((c) => Boolean(c.m)).sort((a, b) => {
1511
+ const usable = Number(hasDefiningColumn(concept, columnNames(b.t))) - Number(hasDefiningColumn(concept, columnNames(a.t)));
1512
+ return usable === 0 ? Number(b.m.exact) - Number(a.m.exact) : usable;
1513
+ }).map((c) => c.t);
1508
1514
  }
1509
1515
  function selectEntities(tables) {
1510
1516
  const contactCands = rankCandidates$1(tables, "contact");
@@ -1767,7 +1773,8 @@ export default defineNitroPlugin(() => {
1767
1773
  `;
1768
1774
  }
1769
1775
  function sveltekitHook(importPath) {
1770
- return `import clivly from "${importPath}";
1776
+ return `import "dotenv/config";
1777
+ import clivly from "${importPath}";
1771
1778
 
1772
1779
  // Clivly boot hook. This module is evaluated once when the server starts.
1773
1780
  // ${FAILURE_NOTE}
@@ -2132,7 +2139,25 @@ function routePath(framework, kind, layout = "app") {
2132
2139
  }
2133
2140
  const WINDOWS_SEP = /\\/g;
2134
2141
  const LEADING_DOT_SLASH = /^\.\//;
2142
+ const ALIAS_MARKER = /^[$~#]/;
2143
+ const SCOPED_PACKAGE = /^@[^/]+\/[^/]+/;
2144
+ const PROJECT_ROOT_DIRS = /* @__PURE__ */ new Set([
2145
+ "app",
2146
+ "lib",
2147
+ "server",
2148
+ "src",
2149
+ "source",
2150
+ "utils"
2151
+ ]);
2152
+ function isAlreadyResolvable(specifier) {
2153
+ if (specifier.startsWith(".") || specifier.startsWith("/")) return false;
2154
+ if (ALIAS_MARKER.test(specifier) || SCOPED_PACKAGE.test(specifier)) return true;
2155
+ if (specifier.startsWith("@")) return true;
2156
+ const [head] = specifier.split("/");
2157
+ return !(head && PROJECT_ROOT_DIRS.has(head));
2158
+ }
2135
2159
  function hostImportPath(routeFilePath, appRelative, paths) {
2160
+ if (isAlreadyResolvable(appRelative)) return appRelative;
2136
2161
  const fromRoot = appRelative.replace(LEADING_DOT_SLASH, "");
2137
2162
  const alias = aliasFor({
2138
2163
  paths,
@@ -2451,7 +2476,12 @@ const AUTH_MODULE_PATHS = [
2451
2476
  "src/lib/auth",
2452
2477
  "app/lib/auth",
2453
2478
  "server/auth",
2454
- "src/server/auth"
2479
+ "src/server/auth",
2480
+ "src/lib/server/auth",
2481
+ "lib/server/auth",
2482
+ "server/utils/auth",
2483
+ "src/lib/auth/index",
2484
+ "lib/auth/index"
2455
2485
  ];
2456
2486
  /** A better-auth instance, duck-typed by the surface the adapter calls. */
2457
2487
  function isBetterAuthInstance(value) {
@@ -3639,6 +3669,7 @@ async function acquireSchema(options, deps) {
3639
3669
  out(` ORM: ${orm}\n`);
3640
3670
  out(` Auth: ${auth}${deps.authWiring.complete ? " (wired)" : ""}\n`);
3641
3671
  if (framework === "nextjs") out(` Layout: ${layout}\n`);
3672
+ if (auth === "better-auth" && !deps.authWiring.complete) out("\n ⚠️ Could not locate your better-auth instance.\n The verify route will import a placeholder path that does not\n resolve. Point it at the real module with:\n\n clivly init --force --auth-import <specifier>\n\n Accepts an alias ($lib/server/auth, @/lib/auth), a package name,\n or a project-relative path (src/lib/server/auth).\n");
3642
3673
  out("\n");
3643
3674
  if (orm !== "drizzle") {
3644
3675
  out(`clivly init currently generates config for Drizzle only. Detected ORM: ${orm}\n`);
@@ -3646,6 +3677,7 @@ async function acquireSchema(options, deps) {
3646
3677
  }
3647
3678
  if (framework === "unknown") {
3648
3679
  out("clivly init doesn't recognise your framework yet (looked for Next.js, TanStack Start, SvelteKit, Remix, Nuxt). Route file generation needs one of these.\n");
3680
+ if ("vue" in appDeps) out("\nUsing Vue? Clivly supports Vue through Nuxt. A Vite SPA has no server\nto mount the sync route on, so the route files need Nuxt's server\ndirectory (or any of the frameworks above).\n");
3649
3681
  return { code: 1 };
3650
3682
  }
3651
3683
  const schemaPath = deps.resolvePath(options.cwd);
@@ -3744,13 +3776,19 @@ function previewMutations(deps, mutations, options) {
3744
3776
  }
3745
3777
  out(options.diff ? "\nRe-run without --diff to apply; --force to overwrite the files marked conflict.\n" : "\nRe-run without --dry-run to apply.\n");
3746
3778
  }
3747
- function printNextSteps(out, authComplete, loginRan, envFile) {
3779
+ function printNextSteps(out, authComplete, loginRan, envFile, needsViteDotenv = false) {
3748
3780
  out("\nNext steps:\n");
3749
3781
  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`);
3750
3782
  out(" 2. Local dev: run `clivly dev` for dashboard-triggered sync\n");
3751
3783
  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");
3752
- const step = authComplete ? 3 : 4;
3784
+ let step = authComplete ? 3 : 4;
3753
3785
  if (!authComplete) out(" 3. Finish resolveUser in the auth/verify route\n");
3786
+ if (needsViteDotenv) {
3787
+ out(` ${step}. Add \`import "dotenv/config";\` to vite.config.ts\n SvelteKit populates \$env/*, not process.env, which better-auth and
3788
+ clivly.config both read — without it \`vite build\` fails.
3789
+ `);
3790
+ step += 1;
3791
+ }
3754
3792
  out(` ${step}. Run \`clivly status\` to see where you are\n`);
3755
3793
  out("\nAfter editing clivly.config.ts, restart your app — it loads the config\nat boot, so a running server keeps using the old one.\n");
3756
3794
  }
@@ -3806,7 +3844,8 @@ async function scaffold(options, deps, plan) {
3806
3844
  out(" Run `clivly login` from a terminal to write CLIVLY_SECRET_KEY and CLIVLY_SYNC_TRIGGER_SECRET.\n");
3807
3845
  } else loginRan = await deps.login(options.cwd) === 0;
3808
3846
  }
3809
- printNextSteps(out, deps.authWiring.complete, loginRan, envFileFor(detectFramework(deps.readDepsFn(options.cwd))));
3847
+ const initFramework = detectFramework(deps.readDepsFn(options.cwd));
3848
+ printNextSteps(out, deps.authWiring.complete, loginRan, envFileFor(initFramework), initFramework === "sveltekit" && deps.authWiring.hostImport !== null);
3810
3849
  return 0;
3811
3850
  }
3812
3851
  function normalizeAppRoot(options) {
@@ -495,6 +495,7 @@ function toFieldMap(scored) {
495
495
  }
496
496
  //#endregion
497
497
  exports.TARGET_CONCEPTS = TARGET_CONCEPTS;
498
+ exports.hasDefiningColumn = hasDefiningColumn;
498
499
  exports.hasSensitiveColumns = hasSensitiveColumns;
499
500
  exports.matchesConcept = matchesConcept;
500
501
  exports.requiredFieldsFor = requiredFieldsFor;
@@ -19,6 +19,7 @@ interface ScoredTable {
19
19
  * They stay manually selectable — an explicit, informed choice is legitimate.
20
20
  */
21
21
  declare function hasSensitiveColumns(columns: string[]): boolean;
22
+ declare function hasDefiningColumn(concept: TargetConcept, columns: string[]): boolean;
22
23
  declare function scoreConcepts(table: ScoredTable): Scored<TargetConcept>[];
23
24
  declare function topConcept(table: ScoredTable): {
24
25
  best: Scored<TargetConcept>;
@@ -60,4 +61,4 @@ interface FieldSuggestion {
60
61
  declare function scoreFieldMap(concept: TargetConcept, table: ScoredTable): Scored<FieldSuggestion>[];
61
62
  declare function toFieldMap(scored: Scored<FieldSuggestion>[]): Record<string, string>;
62
63
  //#endregion
63
- export { type DiscoveredColumnMeta, FieldSuggestion, RelationshipSuggestion, ScoredTable, SuggestedVia, TARGET_CONCEPTS, TargetConcept, hasSensitiveColumns, matchesConcept, requiredFieldsFor, scoreConcepts, scoreFieldMap, scoreRelationships, suggestCursorField, suggestDiscriminatorColumns, toFieldMap, topConcept, validateMapping };
64
+ export { type DiscoveredColumnMeta, FieldSuggestion, RelationshipSuggestion, ScoredTable, SuggestedVia, TARGET_CONCEPTS, TargetConcept, hasDefiningColumn, hasSensitiveColumns, matchesConcept, requiredFieldsFor, scoreConcepts, scoreFieldMap, scoreRelationships, suggestCursorField, suggestDiscriminatorColumns, toFieldMap, topConcept, validateMapping };
@@ -19,6 +19,7 @@ interface ScoredTable {
19
19
  * They stay manually selectable — an explicit, informed choice is legitimate.
20
20
  */
21
21
  declare function hasSensitiveColumns(columns: string[]): boolean;
22
+ declare function hasDefiningColumn(concept: TargetConcept, columns: string[]): boolean;
22
23
  declare function scoreConcepts(table: ScoredTable): Scored<TargetConcept>[];
23
24
  declare function topConcept(table: ScoredTable): {
24
25
  best: Scored<TargetConcept>;
@@ -60,4 +61,4 @@ interface FieldSuggestion {
60
61
  declare function scoreFieldMap(concept: TargetConcept, table: ScoredTable): Scored<FieldSuggestion>[];
61
62
  declare function toFieldMap(scored: Scored<FieldSuggestion>[]): Record<string, string>;
62
63
  //#endregion
63
- export { type DiscoveredColumnMeta, FieldSuggestion, RelationshipSuggestion, ScoredTable, SuggestedVia, TARGET_CONCEPTS, TargetConcept, hasSensitiveColumns, matchesConcept, requiredFieldsFor, scoreConcepts, scoreFieldMap, scoreRelationships, suggestCursorField, suggestDiscriminatorColumns, toFieldMap, topConcept, validateMapping };
64
+ export { type DiscoveredColumnMeta, FieldSuggestion, RelationshipSuggestion, ScoredTable, SuggestedVia, TARGET_CONCEPTS, TargetConcept, hasDefiningColumn, hasSensitiveColumns, matchesConcept, requiredFieldsFor, scoreConcepts, scoreFieldMap, scoreRelationships, suggestCursorField, suggestDiscriminatorColumns, toFieldMap, topConcept, validateMapping };
@@ -493,4 +493,4 @@ function toFieldMap(scored) {
493
493
  return map;
494
494
  }
495
495
  //#endregion
496
- export { TARGET_CONCEPTS, hasSensitiveColumns, matchesConcept, requiredFieldsFor, scoreConcepts, scoreFieldMap, scoreRelationships, suggestCursorField, suggestDiscriminatorColumns, toFieldMap, topConcept, validateMapping };
496
+ export { TARGET_CONCEPTS, hasDefiningColumn, hasSensitiveColumns, matchesConcept, requiredFieldsFor, scoreConcepts, scoreFieldMap, scoreRelationships, suggestCursorField, suggestDiscriminatorColumns, toFieldMap, topConcept, validateMapping };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clivly",
3
- "version": "0.10.0",
3
+ "version": "0.10.2",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Clivly CLI — scaffold a CRM into your app (clivly init).",