@rapidd/core 2.1.1 → 2.1.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.
package/main.ts CHANGED
@@ -16,6 +16,12 @@ export async function start(): Promise<void> {
16
16
  await app.listen({ port, host });
17
17
  console.log(`[Rapidd] Server running at http://${host}:${port}`);
18
18
  console.log(`[Rapidd] Environment: ${getEnv('NODE_ENV')}`);
19
+
20
+ // Warn if running compiled build with development NODE_ENV
21
+ if (process.argv[1]?.includes('/dist/') && getEnv('NODE_ENV') === 'development') {
22
+ console.warn('[Rapidd] Warning: Running compiled build with NODE_ENV=development.');
23
+ console.warn('[Rapidd] Set NODE_ENV=production in your .env for production use.');
24
+ }
19
25
  } catch (err) {
20
26
  console.error('[Startup Error]', (err as Error).message);
21
27
  process.exit(1);
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "2.1.1",
6
+ "version": "2.1.2",
7
7
  "description": "Code-first REST API framework for TypeScript. Database in, API out.",
8
8
  "main": "dist/main.js",
9
9
  "bin": {
@@ -83,7 +83,7 @@
83
83
  "pg": "^8.16.3"
84
84
  },
85
85
  "devDependencies": {
86
- "@rapidd/build": "^2.1.3",
86
+ "@rapidd/build": "^2.1.5",
87
87
  "@types/bcrypt": "^6.0.0",
88
88
  "@types/ejs": "^3.1.5",
89
89
  "@types/jest": "^30.0.0",
package/src/app.ts CHANGED
@@ -22,6 +22,13 @@ import rlsPlugin from './plugins/rls';
22
22
 
23
23
  import type { RapiddOptions } from './types';
24
24
 
25
+ // ─── BigInt Serialization ────────────────────────────
26
+ // Prisma returns BigInt values that JSON.stringify cannot handle natively.
27
+ // This polyfill converts them to strings during serialization.
28
+ (BigInt.prototype as any).toJSON = function () {
29
+ return this.toString();
30
+ };
31
+
25
32
  // ─── Path Setup ─────────────────────────────────────
26
33
  // Use process.cwd() as the project root — works from both source (tsx) and compiled (dist/) contexts.
27
34
 
package/src/orm/Model.ts CHANGED
@@ -177,6 +177,7 @@ class Model {
177
177
  const field = this.fields[fieldName];
178
178
  if (!field) return value;
179
179
  if (field.type === 'Int') return parseInt(value, 10);
180
+ if (field.type === 'BigInt') return BigInt(value);
180
181
  if (field.type === 'Float' || field.type === 'Decimal') return parseFloat(value);
181
182
  if (field.type === 'Boolean') return value === 'true';
182
183
  return value;
@@ -239,10 +240,13 @@ class Model {
239
240
  sortBy = sortBy?.trim();
240
241
  sortOrder = sortOrder?.trim();
241
242
 
242
- // Validate sort field - fall back to default for composite PK names
243
+ // Validate sort field - fall back to default for composite PK names or missing 'id'
243
244
  if (!sortBy.includes('.') && this.fields[sortBy] == undefined) {
244
- // If the sortBy is a composite key name (e.g., "email_companyId"), use first PK field
245
245
  if (sortBy === this.primaryKey && this.isCompositePK) {
246
+ // Composite key name (e.g., "email_companyId") → use first PK field
247
+ sortBy = this.defaultSortField;
248
+ } else if (sortBy === 'id') {
249
+ // Model doesn't have an 'id' field → fall back to actual primary key
246
250
  sortBy = this.defaultSortField;
247
251
  } else {
248
252
  throw new ErrorResponse(400, "invalid_sort_field", { sortBy, modelName: this.constructor.name });
@@ -501,10 +501,10 @@ class QueryBuilder {
501
501
  /**
502
502
  * Parse numeric filter operators
503
503
  */
504
- #filterNumber(value: string): Record<string, number> | null {
504
+ #filterNumber(value: string): Record<string, number | bigint> | null {
505
505
  const numOperators = ['lt:', 'lte:', 'gt:', 'gte:', 'eq:', 'ne:', 'between:'];
506
506
  const foundOperator = numOperators.find((op: string) => value.startsWith(op));
507
- let numValue: string | number = value;
507
+ let numValue: string = value;
508
508
  let prismaOp = 'equals';
509
509
 
510
510
  if (foundOperator) {
@@ -517,19 +517,36 @@ class QueryBuilder {
517
517
  case 'eq:': prismaOp = 'equals'; break;
518
518
  case 'ne:': prismaOp = 'not'; break;
519
519
  case 'between:': {
520
- // Support between for decimals: between:1.5;3.7
521
- const [start, end] = (numValue as string).split(';').map((v: string) => parseFloat(v.trim()));
522
- if (isNaN(start) || isNaN(end)) return null;
520
+ const parts = numValue.split(';').map((v: string) => v.trim());
521
+ const [startStr, endStr] = parts;
522
+ const start = this.#parseNumericValue(startStr);
523
+ const end = this.#parseNumericValue(endStr);
524
+ if (start == null || end == null) return null;
523
525
  return { gte: start, lte: end };
524
526
  }
525
527
  }
526
528
  }
527
529
 
528
- // Support decimal numbers
529
- numValue = parseFloat(numValue as string);
530
- if (isNaN(numValue)) return null;
530
+ const parsed = this.#parseNumericValue(numValue);
531
+ if (parsed == null) return null;
531
532
 
532
- return { [prismaOp]: numValue };
533
+ return { [prismaOp]: parsed };
534
+ }
535
+
536
+ /**
537
+ * Parse a numeric string, returning BigInt for integers beyond safe range
538
+ */
539
+ #parseNumericValue(value: string): number | bigint | null {
540
+ if (value.includes('.')) {
541
+ const num = parseFloat(value);
542
+ return isNaN(num) ? null : num;
543
+ }
544
+ const num = Number(value);
545
+ if (isNaN(num)) return null;
546
+ if (!Number.isSafeInteger(num)) {
547
+ try { return BigInt(value); } catch { return null; }
548
+ }
549
+ return num;
533
550
  }
534
551
 
535
552
  /**