@carbonorm/carbonnode 6.0.6 → 6.0.7

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.
Files changed (68) hide show
  1. package/dist/index.cjs.js +159 -28
  2. package/dist/index.cjs.js.map +1 -1
  3. package/dist/index.d.ts +3 -1
  4. package/dist/index.esm.js +156 -28
  5. package/dist/index.esm.js.map +1 -1
  6. package/dist/utils/colorSql.d.ts +1 -0
  7. package/dist/variables/getEnv.d.ts +10 -0
  8. package/dist/variables/isTest.d.ts +1 -1
  9. package/package.json +2 -1
  10. package/src/__tests__/sakila-db/C6.js +1 -1
  11. package/src/__tests__/sakila-db/C6.mysqldump.json +1 -1
  12. package/src/__tests__/sakila-db/C6.mysqldump.sql +1 -1
  13. package/src/__tests__/sakila-db/C6.ts +1 -1
  14. package/src/__tests__/sakila-db/sqlResponses/C6.actor.post.json +3 -3
  15. package/src/__tests__/sakila-db/sqlResponses/C6.actor.post.latest.json +3 -3
  16. package/src/__tests__/sakila-db/sqlResponses/C6.actor.put.json +1 -1
  17. package/src/__tests__/sakila-db/sqlResponses/C6.actor.put.lookup.json +3 -3
  18. package/src/__tests__/sakila-db/sqlResponses/C6.address.post.json +5 -5
  19. package/src/__tests__/sakila-db/sqlResponses/C6.address.post.latest.json +5 -5
  20. package/src/__tests__/sakila-db/sqlResponses/C6.address.put.json +1 -1
  21. package/src/__tests__/sakila-db/sqlResponses/C6.address.put.lookup.json +5 -5
  22. package/src/__tests__/sakila-db/sqlResponses/C6.category.post.json +2 -2
  23. package/src/__tests__/sakila-db/sqlResponses/C6.category.post.latest.json +2 -2
  24. package/src/__tests__/sakila-db/sqlResponses/C6.category.put.json +1 -1
  25. package/src/__tests__/sakila-db/sqlResponses/C6.category.put.lookup.json +2 -2
  26. package/src/__tests__/sakila-db/sqlResponses/C6.city.post.json +2 -2
  27. package/src/__tests__/sakila-db/sqlResponses/C6.city.post.latest.json +2 -2
  28. package/src/__tests__/sakila-db/sqlResponses/C6.city.put.json +1 -1
  29. package/src/__tests__/sakila-db/sqlResponses/C6.city.put.lookup.json +2 -2
  30. package/src/__tests__/sakila-db/sqlResponses/C6.country.post.json +2 -2
  31. package/src/__tests__/sakila-db/sqlResponses/C6.country.post.latest.json +2 -2
  32. package/src/__tests__/sakila-db/sqlResponses/C6.country.put.json +1 -1
  33. package/src/__tests__/sakila-db/sqlResponses/C6.country.put.lookup.json +2 -2
  34. package/src/__tests__/sakila-db/sqlResponses/C6.customer.post.json +5 -5
  35. package/src/__tests__/sakila-db/sqlResponses/C6.customer.post.latest.json +5 -5
  36. package/src/__tests__/sakila-db/sqlResponses/C6.customer.put.json +1 -1
  37. package/src/__tests__/sakila-db/sqlResponses/C6.customer.put.lookup.json +5 -5
  38. package/src/__tests__/sakila-db/sqlResponses/C6.film.post.json +2 -2
  39. package/src/__tests__/sakila-db/sqlResponses/C6.film.post.latest.json +2 -2
  40. package/src/__tests__/sakila-db/sqlResponses/C6.film.put.json +1 -1
  41. package/src/__tests__/sakila-db/sqlResponses/C6.film.put.lookup.json +2 -2
  42. package/src/__tests__/sakila-db/sqlResponses/C6.inventory.post.json +1 -1
  43. package/src/__tests__/sakila-db/sqlResponses/C6.inventory.post.latest.json +1 -1
  44. package/src/__tests__/sakila-db/sqlResponses/C6.inventory.put.json +1 -1
  45. package/src/__tests__/sakila-db/sqlResponses/C6.inventory.put.lookup.json +1 -1
  46. package/src/__tests__/sakila-db/sqlResponses/C6.language.post.json +2 -2
  47. package/src/__tests__/sakila-db/sqlResponses/C6.language.post.latest.json +2 -2
  48. package/src/__tests__/sakila-db/sqlResponses/C6.language.put.json +1 -1
  49. package/src/__tests__/sakila-db/sqlResponses/C6.language.put.lookup.json +2 -2
  50. package/src/__tests__/sakila-db/sqlResponses/C6.payment.post.json +2 -2
  51. package/src/__tests__/sakila-db/sqlResponses/C6.payment.post.latest.json +2 -2
  52. package/src/__tests__/sakila-db/sqlResponses/C6.payment.put.lookup.json +2 -2
  53. package/src/__tests__/sakila-db/sqlResponses/C6.rental.post.json +3 -3
  54. package/src/__tests__/sakila-db/sqlResponses/C6.rental.post.latest.json +3 -3
  55. package/src/__tests__/sakila-db/sqlResponses/C6.rental.put.json +1 -1
  56. package/src/__tests__/sakila-db/sqlResponses/C6.rental.put.lookup.json +3 -3
  57. package/src/__tests__/sqlBuilders.test.ts +21 -1
  58. package/src/api/restRequest.ts +9 -10
  59. package/src/executors/HttpExecutor.ts +1 -1
  60. package/src/index.ts +3 -1
  61. package/src/orm/queries/SelectQueryBuilder.ts +14 -1
  62. package/src/utils/colorSql.ts +112 -0
  63. package/src/variables/getEnv.ts +71 -0
  64. package/src/variables/isLocal.ts +2 -2
  65. package/src/variables/isTest.ts +4 -4
  66. package/src/variables/isVerbose.ts +2 -3
  67. package/dist/variables/getEnvVar.d.ts +0 -1
  68. package/src/variables/getEnvVar.ts +0 -15
@@ -1,10 +1,11 @@
1
- import { describe, it, expect } from 'vitest';
1
+ import { describe, it, expect, vi } from 'vitest';
2
2
  import { C6C } from '../constants/C6Constants';
3
3
  import { SelectQueryBuilder } from '../orm/queries/SelectQueryBuilder';
4
4
  import { PostQueryBuilder } from '../orm/queries/PostQueryBuilder';
5
5
  import { UpdateQueryBuilder } from '../orm/queries/UpdateQueryBuilder';
6
6
  import { DeleteQueryBuilder } from '../orm/queries/DeleteQueryBuilder';
7
7
  import { buildTestConfig, buildBinaryTestConfig, buildBinaryTestConfigFqn } from './fixtures/c6.fixture';
8
+ import { version } from '../../package.json';
8
9
 
9
10
  describe('SQL Builders', () => {
10
11
  it('builds SELECT with JOIN, WHERE, GROUP BY, HAVING and default LIMIT', () => {
@@ -43,6 +44,25 @@ describe('SQL Builders', () => {
43
44
  expect(params).toEqual(['%A%', 10, 1]);
44
45
  });
45
46
 
47
+ it('logs SELECT statements with package version', () => {
48
+ const config = buildTestConfig();
49
+ const logSpy = vi.spyOn(console, 'log').mockImplementation(() => undefined);
50
+ const qb = new SelectQueryBuilder(config as any, {
51
+ SELECT: ['actor.first_name'],
52
+ } as any, false);
53
+
54
+ qb.build('actor');
55
+
56
+ const logLines = logSpy.mock.calls
57
+ .map((call) => call[0])
58
+ .filter((entry): entry is string => typeof entry === 'string');
59
+ const selectLine = logLines.find((line) => line.includes('[SELECT]'));
60
+
61
+ expect(selectLine).toBeDefined();
62
+ expect(selectLine).toContain(`[${version}]`);
63
+ logSpy.mockRestore();
64
+ });
65
+
46
66
  it('builds INSERT with ON DUPLICATE KEY UPDATE', () => {
47
67
  const config = buildTestConfig();
48
68
  const qb = new PostQueryBuilder(config as any, {
@@ -35,22 +35,21 @@ export default function restRequest<
35
35
 
36
36
  config.verbose ??= isVerbose(); // Default to env-driven verbosity if not set
37
37
 
38
+ if (!config.mysqlPool && !config.axios) {
39
+ throw new Error("No execution method available: neither mysqlPool nor axios instance provided in config.");
40
+ }
41
+
38
42
  // SQL path if on Node with a provided pool
39
- if (isNode() && config.mysqlPool) {
40
- if (config.verbose) {
41
- console.log("Using SQL Executor");
42
- }
43
+ if (config.mysqlPool) {
44
+ config.verbose && console.log("Using SQL Executor");
43
45
  const {SqlExecutor} = await import('../executors/SqlExecutor');
44
46
  const executor = new SqlExecutor<G>(config, request);
45
47
  return executor.execute();
46
48
  }
47
49
 
48
- if (config.verbose) {
49
- console.log("Using HTTP Executor", {
50
- isNode: isNode(),
51
- hasPool: !!config.mysqlPool
52
- });
53
- }
50
+ config.verbose && console.log("Using HTTP Executor", {
51
+ isNode: isNode(),
52
+ });
54
53
 
55
54
  // HTTP path fallback
56
55
  const {HttpExecutor} = await import('../executors/HttpExecutor');
@@ -844,7 +844,7 @@ export class HttpExecutor<
844
844
 
845
845
  console.groupEnd()
846
846
 
847
- throw new Error(JSON.stringify(throwableError))
847
+ throw throwableError
848
848
 
849
849
  }
850
850
 
package/src/index.ts CHANGED
@@ -33,6 +33,8 @@ export * from "./types/ormGenerics";
33
33
  export * from "./types/ormInterfaces";
34
34
  export * from "./utils/apiHelpers";
35
35
  export * from "./utils/cacheManager";
36
+ export { default as colorSql } from "./utils/colorSql";
37
+ export * from "./utils/colorSql";
36
38
  export * from "./utils/determineRuntimeJsType";
37
39
  export * from "./utils/logger";
38
40
  export * from "./utils/normalizeSingularRequest";
@@ -41,7 +43,7 @@ export * from "./utils/sqlAllowList";
41
43
  export * from "./utils/testHelpers";
42
44
  export * from "./utils/toastNotifier";
43
45
  export * from "./utils/toastRuntime";
44
- export * from "./variables/getEnvVar";
46
+ export * from "./variables/getEnv";
45
47
  export { default as isLocal } from "./variables/isLocal";
46
48
  export * from "./variables/isLocal";
47
49
  export { default as isNode } from "./variables/isNode";
@@ -1,6 +1,17 @@
1
1
  import {OrmGenerics} from "../../types/ormGenerics";
2
2
  import {PaginationBuilder} from "../builders/PaginationBuilder";
3
3
  import {SqlBuilderResult} from "../utils/sqlUtils";
4
+ import {getEnvBool} from "../../variables/getEnv";
5
+ import colorSql from "../../utils/colorSql";
6
+ import { version } from "../../../package.json";
7
+
8
+
9
+ const C = {
10
+ SSR: "\x1b[95m", // bright magenta
11
+ HTTP: "\x1b[94m", // bright blue
12
+ SQL: "\x1b[96m", // bright cyan (your SELECT teal)
13
+ RESET: "\x1b[0m",
14
+ };
4
15
 
5
16
  export class SelectQueryBuilder<G extends OrmGenerics> extends PaginationBuilder<G>{
6
17
 
@@ -56,7 +67,9 @@ export class SelectQueryBuilder<G extends OrmGenerics> extends PaginationBuilder
56
67
  sql += ` LIMIT 100`;
57
68
  }
58
69
 
59
- console.log(`[SELECT] ${sql.trim()}`);
70
+ const preText = getEnvBool("SSR", false) ? `${C.SSR}[SSR]${C.RESET} ` : `${C.HTTP}[API]${C.RESET} `;
71
+
72
+ console.log(`[${version}] ${preText}${C.SQL}[SELECT]${C.RESET} ${colorSql(sql)}`);
60
73
 
61
74
  return { sql, params };
62
75
  }
@@ -0,0 +1,112 @@
1
+ /* eslint-disable no-control-regex */
2
+
3
+ const RESET = "\x1b[0m";
4
+
5
+ const C = {
6
+ KEYWORD: "\x1b[94m", // blue
7
+ LIMIT: "\x1b[93m", // yellow
8
+ NUMBER: "\x1b[92m", // green
9
+ DIM: "\x1b[90m", // gray
10
+ };
11
+
12
+ /* ---------- ANSI helpers ---------- */
13
+
14
+ const ansi256 = (n: number) => `\x1b[38;5;${n}m`;
15
+
16
+ /* ---------- hashing ---------- */
17
+
18
+ function hashString(str: string): number {
19
+ let hash = 0;
20
+ for (let i = 0; i < str.length; i++) {
21
+ hash = (hash * 31 + str.charCodeAt(i)) | 0;
22
+ }
23
+ return Math.abs(hash);
24
+ }
25
+
26
+ /* ---------- table color ---------- */
27
+
28
+ function tableRGB(tableName: string): [number, number, number] {
29
+ const name = tableName.replace(/[`"]/g, "").toLowerCase();
30
+ const hash = hashString(name);
31
+
32
+ // Stable hue bucket by first letter
33
+ const first = name.charCodeAt(0) || 97;
34
+ const hueBase = (first - 97) % 6;
35
+
36
+ const r = (hueBase + (hash % 3)) % 6;
37
+ const g = (hash >> 3) % 6;
38
+ const b = (hash >> 6) % 6;
39
+
40
+ return [r, g, Math.max(2, b)]; // avoid muddy dark blues
41
+ }
42
+
43
+ function tableColor(table: string): string {
44
+ const [r, g, b] = tableRGB(table);
45
+ return ansi256(16 + 36 * r + 6 * g + b);
46
+ }
47
+
48
+ /* ---------- column color (same hue, lighter) ---------- */
49
+
50
+ function columnColorFromTable(table: string): string {
51
+ const [r, g, b] = tableRGB(table);
52
+
53
+ // Lift toward white, preserve hue
54
+ const lr = Math.min(5, r + 1);
55
+ const lg = Math.min(5, g + 1);
56
+ const lb = Math.min(5, b + 2);
57
+
58
+ return ansi256(16 + 36 * lr + 6 * lg + lb);
59
+ }
60
+
61
+ /* ---------- bind collapsing ---------- */
62
+ /**
63
+ * ?, ?, ?, ?, ?, ? → ? ×6
64
+ * triggers at 4+
65
+ */
66
+ function collapseBinds(sql: string): string {
67
+ return sql.replace(
68
+ /(\?\s*,\s*){3,}\?/g,
69
+ (match) => {
70
+ const count = match.split("?").length - 1;
71
+ return `${C.DIM}? ×${count}${RESET}`;
72
+ },
73
+ );
74
+ }
75
+
76
+ /* ---------- main formatter ---------- */
77
+
78
+ export default function colorSql(sql: string): string {
79
+ let s = sql.trim();
80
+
81
+ /* 1️⃣ collapse bind noise */
82
+ s = collapseBinds(s);
83
+
84
+ /* 2️⃣ table.column coloring (core visual grouping) */
85
+ s = s.replace(
86
+ /\b(`?\w+`?)\.(\w+)\b/g,
87
+ (_, table, column) =>
88
+ `${tableColor(table)}${table}${RESET}.` +
89
+ `${columnColorFromTable(table)}${column}${RESET}`,
90
+ );
91
+
92
+ /* 3️⃣ FROM / JOIN tables */
93
+ s = s.replace(
94
+ /\b(FROM|JOIN|UPDATE|INTO)\s+(`[^`]+`|\w+)/gi,
95
+ (_, kw, table) =>
96
+ `${C.KEYWORD}${kw}${RESET} ${tableColor(table)}${table}${RESET}`,
97
+ );
98
+
99
+ /* 4️⃣ SQL keywords */
100
+ s = s.replace(
101
+ /\b(SELECT|WHERE|AND|OR|ON|IN|BETWEEN|EXISTS|ORDER BY)\b/gi,
102
+ `${C.KEYWORD}$1${RESET}`,
103
+ );
104
+
105
+ /* 5️⃣ LIMIT */
106
+ s = s.replace(
107
+ /\bLIMIT\s+(\d+)/gi,
108
+ `${C.LIMIT}LIMIT${RESET} ${C.NUMBER}$1${RESET}`,
109
+ );
110
+
111
+ return s;
112
+ }
@@ -0,0 +1,71 @@
1
+ /** biome-ignore-all lint/suspicious/noExplicitAny: working with global browser objects */
2
+ function getRuntimeEnv(key: string): any {
3
+ return typeof window !== "undefined" && (window as any).__ENV__?.[key];
4
+ }
5
+ // Do not import anything here
6
+ function getViteEnv(key: string): any {
7
+ // @ts-expect-error
8
+ return typeof import.meta !== "undefined" && import.meta.env?.[key];
9
+ }
10
+
11
+ export function getEnv(key: string, fallback?: string): string;
12
+ export function getEnv<T>(key: string, fallback: T): T;
13
+ export function getEnv<T = string>(key: string, fallback?: T): T {
14
+ try {
15
+ const viteEnv = getViteEnv(key);
16
+ if (viteEnv !== undefined) return viteEnv as T;
17
+ } catch {}
18
+
19
+ const runtimeEnv = getRuntimeEnv(key);
20
+ if (runtimeEnv !== undefined) return runtimeEnv as T;
21
+
22
+ if (typeof process !== "undefined" && process.env?.[key] !== undefined) {
23
+ return process.env[key] as T;
24
+ }
25
+
26
+ if (fallback !== undefined) return fallback;
27
+ throw new Error(`Missing environment variable: ${key}`);
28
+ }
29
+
30
+ export function getEnvBool(key: string, fallback = false): boolean {
31
+ const raw = getEnv(key, fallback);
32
+ const v = String(raw).trim().toLowerCase();
33
+
34
+ return (
35
+ v === "true" ||
36
+ v === "1" ||
37
+ v === "yes" ||
38
+ v === "y" ||
39
+ v === "on" ||
40
+ v === "enabled"
41
+ );
42
+ }
43
+
44
+ type EnvSource = "vite" | "runtime" | "process" | "fallback" | "missing";
45
+
46
+ export function getEnvDebug<T = string>(
47
+ key: string,
48
+ fallback?: T,
49
+ ): { key: string; value: T; source: EnvSource } {
50
+ try {
51
+ const viteEnv = getViteEnv(key);
52
+ if (viteEnv !== undefined) {
53
+ return { key, value: viteEnv as T, source: "vite" };
54
+ }
55
+ } catch {}
56
+
57
+ const runtimeEnv = getRuntimeEnv(key);
58
+ if (runtimeEnv !== undefined) {
59
+ return { key, value: runtimeEnv as T, source: "runtime" };
60
+ }
61
+
62
+ if (typeof process !== "undefined" && process.env?.[key] !== undefined) {
63
+ return { key, value: process.env[key] as T, source: "process" };
64
+ }
65
+
66
+ if (fallback !== undefined) {
67
+ return { key, value: fallback, source: "fallback" };
68
+ }
69
+
70
+ return { key, value: undefined as any, source: "missing" };
71
+ }
@@ -1,7 +1,7 @@
1
- import {getEnvVar} from "./getEnvVar";
1
+ import {getEnv} from "./getEnv";
2
2
 
3
3
 
4
4
 
5
5
  export default function () {
6
- return getEnvVar('NODE_ENV', '') === 'development';
6
+ return getEnv('NODE_ENV', '') === 'development';
7
7
  };
@@ -1,8 +1,8 @@
1
- import {getEnvVar} from "./getEnvVar";
1
+ import {getEnv, getEnvBool} from "./getEnv";
2
2
 
3
3
 
4
4
  export default function () {
5
- return getEnvVar('JEST_WORKER_ID') || getEnvVar('NODE_ENV') === 'test'
6
- || getEnvVar('REACT_APP_TEST') === 'true' || getEnvVar('VITE_TEST') === 'true'
7
- || getEnvVar('MODE') === 'test' || getEnvVar('VITE_TEST_MODE') === 'true';
5
+ return getEnv('JEST_WORKER_ID', null) || getEnv('NODE_ENV', "") === 'test'
6
+ || getEnvBool('REACT_APP_TEST', false) || getEnvBool('VITE_TEST', false)
7
+ || getEnv('MODE', '') === 'test' || getEnvBool('VITE_TEST_MODE', false);
8
8
  };
@@ -1,6 +1,5 @@
1
- import {getEnvVar} from "./getEnvVar";
1
+ import {getEnvBool} from "./getEnv";
2
2
 
3
3
  export default function () {
4
- const envVerbose = getEnvVar('VERBOSE') || getEnvVar('REACT_APP_VERBOSE') || getEnvVar('VITE_VERBOSE') || ''
5
- return ['true', '1', 'yes', 'on'].includes(envVerbose.toLowerCase());
4
+ return getEnvBool('VERBOSE', false) || getEnvBool('REACT_APP_VERBOSE', false) || getEnvBool('VITE_VERBOSE', false)
6
5
  }
@@ -1 +0,0 @@
1
- export declare function getEnvVar(key: string, fallback?: string): string;
@@ -1,15 +0,0 @@
1
- export function getEnvVar(key: string, fallback = ''): string {
2
- // Vite-style injection
3
- // @ts-ignore
4
- if (typeof import.meta !== 'undefined' && import.meta.env && key in import.meta.env) {
5
- // @ts-ignore
6
- return import.meta.env[key];
7
- }
8
-
9
- // Node or SSR
10
- if (typeof process !== 'undefined' && process.env && key in process.env) {
11
- return process.env[key]!;
12
- }
13
-
14
- return fallback;
15
- }