@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 +6 -0
- package/package.json +2 -2
- package/src/app.ts +7 -0
- package/src/orm/Model.ts +6 -2
- package/src/orm/QueryBuilder.ts +26 -9
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.
|
|
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.
|
|
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 });
|
package/src/orm/QueryBuilder.ts
CHANGED
|
@@ -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
|
|
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
|
-
|
|
521
|
-
const [
|
|
522
|
-
|
|
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
|
-
|
|
529
|
-
|
|
530
|
-
if (isNaN(numValue)) return null;
|
|
530
|
+
const parsed = this.#parseNumericValue(numValue);
|
|
531
|
+
if (parsed == null) return null;
|
|
531
532
|
|
|
532
|
-
return { [prismaOp]:
|
|
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
|
/**
|