@beignet/cli 0.0.24 → 0.0.25

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/dist/inspect.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { readdir, readFile, stat, writeFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
- import { fileURLToPath } from "node:url";
4
- import { parseProviderPackageMetadata, } from "@beignet/core/providers";
3
+ import { parseProviderPackageMetadata } from "@beignet/core/providers";
5
4
  import { createPainter } from "./ansi.js";
6
- import { clientDirPath, clientFormsPath, clientIndexPath, directoryPath, loadBeignetConfig, normalizePath, resolveConfig, } from "./config.js";
5
+ import { clientDirPath, clientFormsPath, clientIndexPath, defaultBeignetConfig, directoryPath, loadBeignetConfig, normalizePath, resolveConfig, } from "./config.js";
7
6
  import { formatGithubAnnotation, } from "./github-annotations.js";
7
+ import { detectedProviderVariants, diagnosticPackageJsonFile, formatProviderMetadataIssues, installedPackageNames, providerDoctorRulesForInstalledPackages, providerOperationalConfigFiles, providerRequiredEnvExists, providerRequiredTableExists, providerVariantRegistrationSeverity, readProviderListEntries, readProviderPackageMetadataSource, registrationTokenHint, serverProviderFiles, } from "./provider-audit.js";
8
8
  import { appendToArrayExpression, appendToNamedArray, appendToOutboxRegistryArray, arrayInitializerInfo, identifiersFromArrayExpression, insertAfterImports, matchingDelimiterIndex, outboxRegistryValueInfo, } from "./registry-edits.js";
9
9
  import { getCliVersion } from "./version.js";
10
10
  export async function inspectApp(options = {}) {
@@ -157,6 +157,7 @@ const productionHardeningDiagnosticCodes = new Set([
157
157
  "BEIGNET_PAYMENTS_STRIPE_MEMORY_PROVIDER",
158
158
  "BEIGNET_PAYMENTS_WEBHOOK_ROUTE_MISSING",
159
159
  "BEIGNET_PROVIDER_ENV_MISSING",
160
+ "BEIGNET_PROVIDER_TABLE_MISSING",
160
161
  "BEIGNET_READINESS_ROUTE_MISSING",
161
162
  "BEIGNET_TENANT_HEADER_AUTHORITY",
162
163
  "BEIGNET_UPLOAD_AUTHORIZATION_MISSING",
@@ -1224,6 +1225,8 @@ async function inspectProductionReadiness(targetDir, files, config, convention)
1224
1225
  const installedPackages = installedPackageNames(packageJson);
1225
1226
  const providerDoctorRules = await providerDoctorRulesForInstalledPackages(targetDir, packageJson);
1226
1227
  const configFiles = operationalConfigFiles(files, config);
1228
+ const infrastructurePath = directoryPath(path.dirname(config.paths.infrastructurePorts));
1229
+ const schemaDir = `${infrastructurePath}/db/schema`;
1227
1230
  for (const file of files) {
1228
1231
  if (!file.endsWith(".ts") || file.endsWith(".test.ts"))
1229
1232
  continue;
@@ -1313,7 +1316,7 @@ async function inspectProductionReadiness(targetDir, files, config, convention)
1313
1316
  // app actually registers; an unused variant must not warn.
1314
1317
  for (const variant of detectedProviderVariants(provider.variants, providerEntries)) {
1315
1318
  for (const envVar of variant.requiredEnv) {
1316
- if (await anyFileContains(targetDir, configFiles, envVar, sourceCache)) {
1319
+ if (await providerRequiredEnvExists(targetDir, configFiles, envVar, sourceCache)) {
1317
1320
  continue;
1318
1321
  }
1319
1322
  diagnostics.push({
@@ -1323,11 +1326,22 @@ async function inspectProductionReadiness(targetDir, files, config, convention)
1323
1326
  message: `${provider.packageName} is installed and ${variant.displayName} is registered, but ${envVar} is not mentioned in app env/config files. ${variant.displayName} requires ${envVar}; add the environment variable or remove the provider registration.`,
1324
1327
  });
1325
1328
  }
1329
+ for (const tableName of configuredProviderRequiredTables(variant.requiredTables, config)) {
1330
+ if (await providerRequiredTableExists(targetDir, files, config, infrastructurePath, schemaDir, tableName, sourceCache)) {
1331
+ continue;
1332
+ }
1333
+ diagnostics.push({
1334
+ severity: "warning",
1335
+ code: "BEIGNET_PROVIDER_TABLE_MISSING",
1336
+ file: schemaDir,
1337
+ message: `${provider.packageName} is installed and ${variant.displayName} is registered, but required table ${tableName} was not found in ${schemaDir}/, drizzle/, or app database setup files. Add it with beignet db schema generate, include it in your Drizzle schema/migrations, or remove the provider registration.`,
1338
+ });
1339
+ }
1326
1340
  }
1327
1341
  continue;
1328
1342
  }
1329
1343
  for (const envVar of provider.requiredEnv ?? []) {
1330
- if (await anyFileContains(targetDir, configFiles, envVar, sourceCache)) {
1344
+ if (await providerRequiredEnvExists(targetDir, configFiles, envVar, sourceCache)) {
1331
1345
  continue;
1332
1346
  }
1333
1347
  diagnostics.push({
@@ -1337,6 +1351,17 @@ async function inspectProductionReadiness(targetDir, files, config, convention)
1337
1351
  message: `${provider.packageName} is installed, but ${envVar} is not mentioned in app env/config files. Add the expected environment variable or remove the unused provider package.`,
1338
1352
  });
1339
1353
  }
1354
+ for (const tableName of configuredProviderRequiredTables(provider.requiredTables ?? [], config)) {
1355
+ if (await providerRequiredTableExists(targetDir, files, config, infrastructurePath, schemaDir, tableName, sourceCache)) {
1356
+ continue;
1357
+ }
1358
+ diagnostics.push({
1359
+ severity: "warning",
1360
+ code: "BEIGNET_PROVIDER_TABLE_MISSING",
1361
+ file: schemaDir,
1362
+ message: `${provider.packageName} is installed, but required table ${tableName} was not found in ${schemaDir}/, drizzle/, or app database setup files. Add it with beignet db schema generate, include it in your Drizzle schema/migrations, or remove the unused provider package.`,
1363
+ });
1364
+ }
1340
1365
  }
1341
1366
  if (installedPackages.has("@beignet/provider-auth-better-auth")) {
1342
1367
  if (!hasBetterAuthRoute(files, config)) {
@@ -1680,58 +1705,13 @@ function isFeatureUploadDefinition(file, config) {
1680
1705
  return new RegExp(`^${escapeRegExp(featuresPath)}/[^/]+/uploads/[^/]+\\.ts$`).test(file);
1681
1706
  }
1682
1707
  function operationalConfigFiles(files, config) {
1683
- return files.filter((file) => {
1684
- if (file === ".env.example")
1685
- return true;
1686
- if (file === "drizzle.config.ts")
1687
- return true;
1688
- if (file === "lib/env.ts" || file === "lib/better-auth.ts")
1689
- return true;
1690
- if (isInfraOrServerFile(file, config) && file.endsWith(".ts"))
1691
- return true;
1692
- return false;
1693
- });
1708
+ return providerOperationalConfigFiles(files, config);
1694
1709
  }
1695
1710
  async function readPackageJson(targetDir, files) {
1696
1711
  if (!files.includes("package.json"))
1697
1712
  return undefined;
1698
1713
  return JSON.parse(await readFile(path.join(targetDir, "package.json"), "utf8"));
1699
1714
  }
1700
- async function providerDoctorRulesForInstalledPackages(targetDir, packageJson) {
1701
- const packageNames = [...installedPackageNames(packageJson)];
1702
- const rules = await Promise.all(packageNames.map(async (packageName) => {
1703
- const source = await readProviderPackageMetadataSource(targetDir, packageName);
1704
- if (!source)
1705
- return undefined;
1706
- const result = parseProviderPackageMetadata(source.metadata);
1707
- if (!result.success)
1708
- return undefined;
1709
- return providerDoctorRuleFromMetadata(packageName, result.metadata);
1710
- }));
1711
- return rules.filter((rule) => Boolean(rule));
1712
- }
1713
- async function readProviderPackageMetadataSource(targetDir, packageName) {
1714
- for (const candidate of providerPackageJsonCandidates(targetDir, packageName)) {
1715
- const packageJson = await readJsonIfExists(candidate);
1716
- const metadata = packageJson?.beignet?.provider;
1717
- if (metadata !== undefined) {
1718
- return { packageName, file: candidate, metadata };
1719
- }
1720
- }
1721
- return undefined;
1722
- }
1723
- function providerPackageJsonCandidates(targetDir, packageName) {
1724
- const candidates = [
1725
- path.join(targetDir, "node_modules", ...packageName.split("/"), "package.json"),
1726
- ];
1727
- if (packageName.startsWith("@beignet/")) {
1728
- candidates.push(path.resolve(
1729
- // import.meta.dir is Bun-only; the published bin also runs under
1730
- // plain Node, where only import.meta.url is available.
1731
- path.dirname(fileURLToPath(import.meta.url)), "../../..", "packages", packageName.slice("@beignet/".length), "package.json"));
1732
- }
1733
- return candidates;
1734
- }
1735
1715
  async function readJsonIfExists(file) {
1736
1716
  try {
1737
1717
  return JSON.parse(await readFile(file, "utf8"));
@@ -1745,158 +1725,6 @@ async function readJsonIfExists(file) {
1745
1725
  throw error;
1746
1726
  }
1747
1727
  }
1748
- function providerDoctorRuleFromMetadata(packageName, metadata) {
1749
- const registration = metadata.registration;
1750
- return {
1751
- packageName,
1752
- displayName: metadata.displayName ?? packageName,
1753
- tokens: registration?.tokens ?? [],
1754
- appPorts: metadata.appPorts ?? [],
1755
- requiredEnv: metadata.requiredEnv ?? [],
1756
- registrationSeverity: registrationSeverityFromMetadata(registration),
1757
- variants: metadata.variants?.map((variant) => ({
1758
- name: variant.name,
1759
- displayName: variant.displayName ?? variant.name,
1760
- tokens: variant.registration?.tokens ?? [],
1761
- requiredEnv: variant.requiredEnv ?? [],
1762
- registrationSeverity: registrationSeverityFromMetadata(variant.registration),
1763
- })),
1764
- };
1765
- }
1766
- function registrationSeverityFromMetadata(registration) {
1767
- return (registration?.severity ??
1768
- (registration?.required === true ? "warning" : undefined));
1769
- }
1770
- function detectedProviderVariants(variants, providerEntries) {
1771
- return variants.filter((variant) => variant.tokens.some((token) => providerEntries.some((entry) => entry.includes(token))));
1772
- }
1773
- function providerVariantRegistrationSeverity(variants) {
1774
- const severities = variants.map((variant) => variant.registrationSeverity);
1775
- if (severities.includes("warning"))
1776
- return "warning";
1777
- if (severities.includes("hint"))
1778
- return "hint";
1779
- return undefined;
1780
- }
1781
- async function readProviderListEntries(targetDir, files, config, sourceCache) {
1782
- const providerFiles = serverProviderFiles(files, config);
1783
- const providerSource = (await Promise.all(providerFiles.map((file) => readCachedSource(targetDir, file, sourceCache)))).join("\n");
1784
- const providerListSource = extractProviderListSource(providerSource);
1785
- return providerListSource === undefined
1786
- ? []
1787
- : providerListEntries(providerListSource);
1788
- }
1789
- function registrationTokenHint(tokens) {
1790
- const token = tokens[0];
1791
- if (!token)
1792
- return "the provider";
1793
- return token.startsWith("create") ? `${token}()` : token;
1794
- }
1795
- function diagnosticPackageJsonFile(targetDir, file) {
1796
- const relative = normalizePath(path.relative(targetDir, file));
1797
- if (relative.startsWith("../"))
1798
- return "package.json";
1799
- return relative;
1800
- }
1801
- function formatProviderMetadataIssues(issues) {
1802
- return issues.map((issue) => `${issue.path} ${issue.message}`).join("; ");
1803
- }
1804
- function installedPackageNames(packageJson) {
1805
- return new Set([
1806
- ...Object.keys(packageJson?.dependencies ?? {}),
1807
- ...Object.keys(packageJson?.devDependencies ?? {}),
1808
- ...Object.keys(packageJson?.peerDependencies ?? {}),
1809
- ]);
1810
- }
1811
- function serverProviderFiles(files, config) {
1812
- const serverDir = directoryPath(path.dirname(config.paths.server));
1813
- const candidates = [`${serverDir}/providers.ts`, config.paths.server];
1814
- return candidates.filter((file) => files.includes(file));
1815
- }
1816
- function extractProviderListSource(source) {
1817
- const listMatches = [
1818
- ...source.matchAll(/\bproviders\s*=\s*\[([\s\S]*?)\]\s*(?:;|as const)/g),
1819
- ...source.matchAll(/\bproviders\s*:\s*\[([\s\S]*?)\]/g),
1820
- ];
1821
- if (listMatches.length === 0)
1822
- return undefined;
1823
- return listMatches.map((match) => match[1]).join("\n");
1824
- }
1825
- function providerListEntries(source) {
1826
- const entries = [];
1827
- let current = "";
1828
- let depth = 0;
1829
- let quote;
1830
- let escaped = false;
1831
- let lineComment = false;
1832
- let blockComment = false;
1833
- for (let index = 0; index < source.length; index += 1) {
1834
- const char = source[index];
1835
- const next = source[index + 1];
1836
- if (lineComment) {
1837
- if (char === "\n") {
1838
- lineComment = false;
1839
- current += char;
1840
- }
1841
- continue;
1842
- }
1843
- if (blockComment) {
1844
- if (char === "*" && next === "/") {
1845
- blockComment = false;
1846
- index += 1;
1847
- }
1848
- continue;
1849
- }
1850
- if (quote) {
1851
- current += char;
1852
- if (escaped) {
1853
- escaped = false;
1854
- continue;
1855
- }
1856
- if (char === "\\") {
1857
- escaped = true;
1858
- continue;
1859
- }
1860
- if (char === quote) {
1861
- quote = undefined;
1862
- }
1863
- continue;
1864
- }
1865
- if (char === "/" && next === "/") {
1866
- lineComment = true;
1867
- index += 1;
1868
- continue;
1869
- }
1870
- if (char === "/" && next === "*") {
1871
- blockComment = true;
1872
- index += 1;
1873
- continue;
1874
- }
1875
- if (char === '"' || char === "'" || char === "`") {
1876
- quote = char;
1877
- current += char;
1878
- continue;
1879
- }
1880
- if (char === "(" || char === "[" || char === "{") {
1881
- depth += 1;
1882
- }
1883
- else if (char === ")" || char === "]" || char === "}") {
1884
- depth = Math.max(0, depth - 1);
1885
- }
1886
- if (char === "," && depth === 0) {
1887
- const entry = current.trim();
1888
- if (entry)
1889
- entries.push(entry);
1890
- current = "";
1891
- continue;
1892
- }
1893
- current += char;
1894
- }
1895
- const entry = current.trim();
1896
- if (entry)
1897
- entries.push(entry);
1898
- return entries;
1899
- }
1900
1728
  function findProviderEntryIndex(entries, tokens) {
1901
1729
  return entries.findIndex((entry) => tokens.some((token) => entry.includes(token)));
1902
1730
  }
@@ -2794,35 +2622,65 @@ async function inspectDatabaseLifecycleDrift(targetDir, files, config, conventio
2794
2622
  const durableDrizzleTableRequirements = [
2795
2623
  {
2796
2624
  capability: "idempotency",
2797
- defaultTableName: "idempotency_records",
2625
+ table: "idempotency",
2798
2626
  code: "BEIGNET_IDEMPOTENCY_TABLE_MISSING",
2799
- factoryPattern: "createDrizzle[A-Za-z]+IdempotencyPort",
2800
- setupPattern: "createDrizzle[A-Za-z]+IdempotencySetupStatements",
2627
+ factoryNames: [
2628
+ "createDrizzleSqliteIdempotencyPort",
2629
+ "createDrizzlePostgresIdempotencyPort",
2630
+ "createDrizzleMysqlIdempotencyPort",
2631
+ ],
2632
+ setupNames: [
2633
+ "createDrizzleSqliteIdempotencySetupStatements",
2634
+ "createDrizzlePostgresIdempotencySetupStatements",
2635
+ "createDrizzleMysqlIdempotencySetupStatements",
2636
+ ],
2637
+ explicitFactoryPattern: "createDrizzle[A-Za-z]*Idempotency(?:Port)?",
2638
+ explicitSetupPattern: "createDrizzle[A-Za-z]*IdempotencySetupStatements",
2801
2639
  },
2802
2640
  {
2803
2641
  capability: "outbox",
2804
- defaultTableName: "outbox_messages",
2642
+ table: "outbox",
2805
2643
  code: "BEIGNET_OUTBOX_TABLE_MISSING",
2806
- factoryPattern: "createDrizzle[A-Za-z]+OutboxPort",
2807
- setupPattern: "createDrizzle[A-Za-z]+OutboxSetupStatements",
2644
+ factoryNames: [
2645
+ "createDrizzleSqliteOutboxPort",
2646
+ "createDrizzlePostgresOutboxPort",
2647
+ "createDrizzleMysqlOutboxPort",
2648
+ ],
2649
+ setupNames: [
2650
+ "createDrizzleSqliteOutboxSetupStatements",
2651
+ "createDrizzlePostgresOutboxSetupStatements",
2652
+ "createDrizzleMysqlOutboxSetupStatements",
2653
+ ],
2654
+ explicitFactoryPattern: "createDrizzle[A-Za-z]*Outbox(?:Port)?",
2655
+ explicitSetupPattern: "createDrizzle[A-Za-z]*OutboxSetupStatements",
2808
2656
  },
2809
2657
  {
2810
2658
  capability: "audit",
2811
- defaultTableName: "audit_log",
2659
+ table: "audit",
2812
2660
  code: "BEIGNET_AUDIT_TABLE_MISSING",
2813
- factoryPattern: "createDrizzle(?:[A-Za-z]+AuditLogPort|AuditLog)",
2814
- setupPattern: "createDrizzle[A-Za-z]+AuditLogSetupStatements",
2661
+ factoryNames: [
2662
+ "createDrizzleSqliteAuditLogPort",
2663
+ "createDrizzlePostgresAuditLogPort",
2664
+ "createDrizzleMysqlAuditLogPort",
2665
+ ],
2666
+ setupNames: [
2667
+ "createDrizzleSqliteAuditLogSetupStatements",
2668
+ "createDrizzlePostgresAuditLogSetupStatements",
2669
+ "createDrizzleMysqlAuditLogSetupStatements",
2670
+ ],
2671
+ explicitFactoryPattern: "createDrizzle[A-Za-z]*AuditLog(?:Port)?",
2672
+ explicitSetupPattern: "createDrizzle[A-Za-z]*AuditLogSetupStatements",
2815
2673
  },
2816
2674
  ];
2817
2675
  async function inspectDurableDrizzleTableDrift(targetDir, files, config, infrastructurePath, schemaDir, sourceCache) {
2818
2676
  const diagnostics = [];
2819
2677
  const usageFiles = durableDrizzleUsageFiles(files, config);
2820
2678
  for (const requirement of durableDrizzleTableRequirements) {
2821
- const tableNames = await configuredDurableDrizzleTableNames(targetDir, usageFiles, requirement, sourceCache);
2679
+ const tableNames = await configuredDurableDrizzleTableNames(targetDir, usageFiles, config, requirement, sourceCache);
2822
2680
  if (tableNames.size === 0)
2823
2681
  continue;
2824
2682
  for (const tableName of tableNames) {
2825
- if (await durableDrizzleTableExists(targetDir, files, infrastructurePath, schemaDir, requirement, tableName, sourceCache)) {
2683
+ if (await durableDrizzleTableExists(targetDir, files, config, infrastructurePath, schemaDir, requirement, tableName, sourceCache)) {
2826
2684
  continue;
2827
2685
  }
2828
2686
  diagnostics.push({
@@ -2840,45 +2698,50 @@ function durableDrizzleUsageFiles(files, config) {
2840
2698
  !isTestSourceFile(file) &&
2841
2699
  isInfraOrServerFile(file, config));
2842
2700
  }
2843
- async function configuredDurableDrizzleTableNames(targetDir, files, requirement, sourceCache) {
2701
+ async function configuredDurableDrizzleTableNames(targetDir, files, config, requirement, sourceCache) {
2844
2702
  const tableNames = new Set();
2703
+ const providerDefaultTableName = defaultBeignetConfig.database.tables[requirement.table];
2704
+ const configuredTableName = config.database.tables[requirement.table];
2845
2705
  for (const file of files) {
2846
2706
  const source = await readCachedSource(targetDir, file, sourceCache);
2847
- for (const tableName of tableNamesFromCalls(source, requirement.factoryPattern, requirement.defaultTableName)) {
2707
+ for (const tableName of tableNamesFromBeignetProviderCalls(source, requirement.factoryNames, providerDefaultTableName, configuredTableName)) {
2708
+ tableNames.add(tableName);
2709
+ }
2710
+ for (const tableName of tableNamesFromExplicitCalls(source, requirement.explicitFactoryPattern, configuredTableName)) {
2848
2711
  tableNames.add(tableName);
2849
2712
  }
2850
2713
  }
2851
2714
  return tableNames;
2852
2715
  }
2853
- async function durableDrizzleTableExists(targetDir, files, infrastructurePath, schemaDir, requirement, tableName, sourceCache) {
2854
- const schemaFiles = files.filter((file) => file.startsWith(`${schemaDir}/`) &&
2855
- /\.(?:ts|mts|cts)$/.test(file) &&
2856
- !isTestSourceFile(file));
2857
- for (const file of schemaFiles) {
2858
- const source = await readCachedSource(targetDir, file, sourceCache);
2859
- if (source.includes(tableName))
2860
- return true;
2716
+ async function durableDrizzleTableExists(targetDir, files, config, infrastructurePath, schemaDir, requirement, tableName, sourceCache) {
2717
+ if (await providerRequiredTableExists(targetDir, files, config, infrastructurePath, schemaDir, tableName, sourceCache)) {
2718
+ return true;
2861
2719
  }
2862
2720
  const setupFiles = files.filter((file) => !isTestSourceFile(file) &&
2863
2721
  (file.startsWith("drizzle/") ||
2864
2722
  file.startsWith(`${infrastructurePath}/db/`)) &&
2865
2723
  /\.(?:ts|js|mts|mjs|cts|cjs|sql)$/.test(file));
2724
+ const providerDefaultTableName = defaultBeignetConfig.database.tables[requirement.table];
2725
+ const configuredTableName = config.database.tables[requirement.table];
2866
2726
  for (const file of setupFiles) {
2867
2727
  const source = await readCachedSource(targetDir, file, sourceCache);
2868
- if (sourceCreatesTable(source, tableName))
2869
- return true;
2870
- if (!requirement.setupPattern)
2871
- continue;
2872
- if (tableNamesFromCalls(source, requirement.setupPattern, requirement.defaultTableName).has(tableName)) {
2728
+ if (tableNamesFromBeignetProviderCalls(source, requirement.setupNames, providerDefaultTableName, configuredTableName).has(tableName) ||
2729
+ tableNamesFromExplicitCalls(source, requirement.explicitSetupPattern, configuredTableName).has(tableName)) {
2873
2730
  return true;
2874
2731
  }
2875
2732
  }
2876
2733
  return false;
2877
2734
  }
2878
- function sourceCreatesTable(source, tableName) {
2879
- return new RegExp(`\\bCREATE\\s+TABLE\\b[\\s\\S]{0,200}${escapeRegExp(tableName)}\\b`, "i").test(source);
2735
+ function tableNamesFromBeignetProviderCalls(source, exportNames, defaultTableName, configuredTableName) {
2736
+ const tableNames = new Set();
2737
+ for (const callee of beignetProviderCallees(source, exportNames)) {
2738
+ for (const tableName of tableNamesFromCallee(source, callee, defaultTableName, configuredTableName)) {
2739
+ tableNames.add(tableName);
2740
+ }
2741
+ }
2742
+ return tableNames;
2880
2743
  }
2881
- function tableNamesFromCalls(source, calleePattern, defaultTableName) {
2744
+ function tableNamesFromExplicitCalls(source, calleePattern, configuredTableName) {
2882
2745
  const tableNames = new Set();
2883
2746
  const callPattern = new RegExp(`\\b${calleePattern}\\s*\\(([\\s\\S]{0,800}?)\\)`, "g");
2884
2747
  for (const match of source.matchAll(callPattern)) {
@@ -2886,12 +2749,71 @@ function tableNamesFromCalls(source, calleePattern, defaultTableName) {
2886
2749
  const prefix = source.slice(Math.max(0, matchIndex - 32), matchIndex);
2887
2750
  if (/\bfunction\s+$/.test(prefix))
2888
2751
  continue;
2889
- const argsSource = match[1] ?? "";
2890
- const tableName = /\btableName\s*:\s*["'`]([^"'`]+)["'`]/.exec(argsSource)?.[1];
2891
- tableNames.add(tableName ?? defaultTableName);
2752
+ const tableName = tableNameFromArgs(match[1] ?? "", configuredTableName);
2753
+ if (tableName)
2754
+ tableNames.add(tableName);
2755
+ }
2756
+ return tableNames;
2757
+ }
2758
+ function beignetProviderCallees(source, exportNames) {
2759
+ const exportNameSet = new Set(exportNames);
2760
+ const callees = [];
2761
+ for (const match of source.matchAll(/\bimport\s+(?:type\s+)?\{([^}]*)\}\s+from\s+["']@beignet\/provider-db-drizzle\/(?:sqlite|postgres|mysql)["']/g)) {
2762
+ for (const specifier of match[1].split(",")) {
2763
+ const cleaned = specifier.trim().replace(/^type\s+/, "");
2764
+ if (!cleaned)
2765
+ continue;
2766
+ const [imported, local] = cleaned.split(/\s+as\s+/);
2767
+ if (!exportNameSet.has(imported))
2768
+ continue;
2769
+ callees.push({ kind: "identifier", name: local?.trim() || imported });
2770
+ }
2771
+ }
2772
+ for (const match of source.matchAll(/\bimport\s+\*\s+as\s+([A-Za-z_$][\w$]*)\s+from\s+["']@beignet\/provider-db-drizzle\/(?:sqlite|postgres|mysql)["']/g)) {
2773
+ for (const exportName of exportNames) {
2774
+ callees.push({
2775
+ kind: "namespace",
2776
+ namespace: match[1],
2777
+ name: exportName,
2778
+ });
2779
+ }
2780
+ }
2781
+ return callees;
2782
+ }
2783
+ function tableNamesFromCallee(source, callee, defaultTableName, configuredTableName) {
2784
+ const tableNames = new Set();
2785
+ const calleePattern = callee.kind === "identifier"
2786
+ ? `\\b${escapeRegExp(callee.name)}`
2787
+ : `\\b${escapeRegExp(callee.namespace)}\\s*\\.\\s*${escapeRegExp(callee.name)}`;
2788
+ const callPattern = new RegExp(`${calleePattern}\\s*\\(([\\s\\S]{0,800}?)\\)`, "g");
2789
+ for (const match of source.matchAll(callPattern)) {
2790
+ const matchIndex = match.index ?? 0;
2791
+ const prefix = source.slice(Math.max(0, matchIndex - 32), matchIndex);
2792
+ if (/\bfunction\s+$/.test(prefix))
2793
+ continue;
2794
+ tableNames.add(tableNameFromArgs(match[1] ?? "", configuredTableName) ??
2795
+ defaultTableName);
2892
2796
  }
2893
2797
  return tableNames;
2894
2798
  }
2799
+ function tableNameFromArgs(argsSource, configuredTableName) {
2800
+ const literal = /\btableName\s*:\s*["'`]([^"'`]+)["'`]/.exec(argsSource)?.[1];
2801
+ if (literal)
2802
+ return literal;
2803
+ if (/\btableName\s*:/.test(argsSource))
2804
+ return configuredTableName;
2805
+ return undefined;
2806
+ }
2807
+ function configuredProviderRequiredTables(tableNames, config) {
2808
+ return tableNames.map((tableName) => configuredProviderRequiredTable(tableName, config));
2809
+ }
2810
+ function configuredProviderRequiredTable(tableName, config) {
2811
+ for (const [key, defaultTableName] of Object.entries(defaultBeignetConfig.database.tables)) {
2812
+ if (tableName === defaultTableName)
2813
+ return config.database.tables[key];
2814
+ }
2815
+ return tableName;
2816
+ }
2895
2817
  function hasRepositoryAdapterFile(files, infrastructureDir) {
2896
2818
  return files.some((file) => file.startsWith(infrastructureDir) &&
2897
2819
  file.endsWith("-repository.ts") &&