@rebasepro/server-postgresql 0.6.0 → 0.6.1
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/index.es.js +46 -7
- package/dist/index.es.js.map +1 -1
- package/dist/utils/pg-array-null-patch.d.ts +16 -0
- package/package.json +6 -6
- package/src/PostgresBootstrapper.ts +15 -18
- package/src/data-transformer.ts +9 -1
- package/src/services/EntityPersistService.ts +8 -8
- package/src/utils/pg-array-null-patch.ts +42 -0
- package/test/array-null-safety.test.ts +335 -0
- package/test/data-transformer.test.ts +53 -2
- package/test/pg-array-null-patch.test.ts +65 -0
- package/vite.config.ts +8 -6
- package/dist/index.umd.js +0 -11055
- package/dist/index.umd.js.map +0 -1
package/dist/index.es.js
CHANGED
|
@@ -4,8 +4,8 @@ const require = __createRequire(import.meta.url);
|
|
|
4
4
|
import { Client, Pool } from "pg";
|
|
5
5
|
import { drizzle } from "drizzle-orm/node-postgres";
|
|
6
6
|
import { createEmailService, extractUserFromToken, logger } from "@rebasepro/server-core";
|
|
7
|
-
import { and, asc, count, desc, eq, getTableName, gt, ilike, inArray, isTable, lt, or, relations, sql } from "drizzle-orm";
|
|
8
|
-
import { PgChar, PgText, PgVarchar, boolean, getTableConfig, jsonb, pgSchema, pgTable, text, timestamp, unique, uuid, varchar } from "drizzle-orm/pg-core";
|
|
7
|
+
import { and, asc, count, desc, eq, getTableColumns, getTableName, gt, ilike, inArray, isTable, lt, or, relations, sql } from "drizzle-orm";
|
|
8
|
+
import { PgArray, PgChar, PgTable, PgText, PgVarchar, boolean, getTableConfig, jsonb, pgSchema, pgTable, text, timestamp, unique, uuid, varchar } from "drizzle-orm/pg-core";
|
|
9
9
|
import { createHash, randomUUID } from "crypto";
|
|
10
10
|
import * as fs from "fs";
|
|
11
11
|
import { promises } from "fs";
|
|
@@ -3720,8 +3720,10 @@ function serializePropertyToServer(value, property) {
|
|
|
3720
3720
|
};
|
|
3721
3721
|
});
|
|
3722
3722
|
}
|
|
3723
|
+
return value;
|
|
3723
3724
|
}
|
|
3724
|
-
|
|
3725
|
+
logger.warn(`Expected array value for array property, got ${typeof value}. Coercing to empty array.`);
|
|
3726
|
+
return [];
|
|
3725
3727
|
case "map":
|
|
3726
3728
|
if (typeof value === "object" && property.properties) {
|
|
3727
3729
|
const result = {};
|
|
@@ -3943,6 +3945,9 @@ function parsePropertyFromServer(value, property, collection, propertyKey) {
|
|
|
3943
3945
|
};
|
|
3944
3946
|
});
|
|
3945
3947
|
}
|
|
3948
|
+
} else {
|
|
3949
|
+
logger.warn(`Expected array value from DB for array property, got ${typeof value}. Coercing to array.`);
|
|
3950
|
+
return typeof value === "string" ? [value] : [];
|
|
3946
3951
|
}
|
|
3947
3952
|
return value;
|
|
3948
3953
|
case "map":
|
|
@@ -5929,12 +5934,12 @@ var EntityPersistService = class {
|
|
|
5929
5934
|
}
|
|
5930
5935
|
}
|
|
5931
5936
|
}
|
|
5932
|
-
const serializedResult = serializeDataToServer(otherValues, collection.properties, collection, this.registry);
|
|
5933
|
-
const inverseRelationUpdates = serializedResult.inverseRelationUpdates;
|
|
5934
|
-
const joinPathRelationUpdates = serializedResult.joinPathRelationUpdates;
|
|
5935
|
-
const entityData = sanitizeAndConvertDates(serializedResult.scalarData);
|
|
5936
5937
|
let savedId;
|
|
5937
5938
|
try {
|
|
5939
|
+
const serializedResult = serializeDataToServer(otherValues, collection.properties, collection, this.registry);
|
|
5940
|
+
const inverseRelationUpdates = serializedResult.inverseRelationUpdates;
|
|
5941
|
+
const joinPathRelationUpdates = serializedResult.joinPathRelationUpdates;
|
|
5942
|
+
const entityData = sanitizeAndConvertDates(serializedResult.scalarData);
|
|
5938
5943
|
savedId = await this.db.transaction(async (tx) => {
|
|
5939
5944
|
let currentId;
|
|
5940
5945
|
if (entityId) {
|
|
@@ -10552,6 +10557,39 @@ async function ensureHistoryTableExists(db) {
|
|
|
10552
10557
|
}
|
|
10553
10558
|
}
|
|
10554
10559
|
//#endregion
|
|
10560
|
+
//#region src/utils/pg-array-null-patch.ts
|
|
10561
|
+
/**
|
|
10562
|
+
* Patches all PgArray columns on the given tables to handle NULL values safely.
|
|
10563
|
+
*
|
|
10564
|
+
* Drizzle ORM's `PgArray.mapFromDriverValue` calls `value.map(...)` without
|
|
10565
|
+
* guarding against `null`. When a PostgreSQL native array column (`text[]`,
|
|
10566
|
+
* `integer[]`, etc.) contains NULL, the pg driver returns `null` in JavaScript,
|
|
10567
|
+
* and `null.map(...)` throws `TypeError: value.map is not a function`.
|
|
10568
|
+
*
|
|
10569
|
+
* This function walks every column of every registered table and, for any
|
|
10570
|
+
* `PgArray` column, wraps its `mapFromDriverValue` to return `null` when the
|
|
10571
|
+
* database value is nullish.
|
|
10572
|
+
*
|
|
10573
|
+
* This is a workaround for a known Drizzle ORM issue. Should be removed once
|
|
10574
|
+
* Drizzle handles nullable arrays natively.
|
|
10575
|
+
*/
|
|
10576
|
+
function patchPgArrayNullSafety(tables) {
|
|
10577
|
+
let patchedCount = 0;
|
|
10578
|
+
for (const tableOrRelation of Object.values(tables)) {
|
|
10579
|
+
if (!(tableOrRelation instanceof PgTable)) continue;
|
|
10580
|
+
const columns = getTableColumns(tableOrRelation);
|
|
10581
|
+
for (const column of Object.values(columns)) if (column instanceof PgArray) {
|
|
10582
|
+
const original = column.mapFromDriverValue.bind(column);
|
|
10583
|
+
column.mapFromDriverValue = function(value) {
|
|
10584
|
+
if (value == null) return null;
|
|
10585
|
+
return original(value);
|
|
10586
|
+
};
|
|
10587
|
+
patchedCount++;
|
|
10588
|
+
}
|
|
10589
|
+
}
|
|
10590
|
+
if (patchedCount > 0) logger.debug(`[PgArray] Patched ${patchedCount} array column(s) for null-safety`);
|
|
10591
|
+
}
|
|
10592
|
+
//#endregion
|
|
10555
10593
|
//#region src/PostgresBootstrapper.ts
|
|
10556
10594
|
/**
|
|
10557
10595
|
* PostgresBootstrapper
|
|
@@ -10587,6 +10625,7 @@ function createPostgresBootstrapper(pgConfig) {
|
|
|
10587
10625
|
});
|
|
10588
10626
|
if (pgConfig.schema?.enums) registry.registerEnums(pgConfig.schema.enums);
|
|
10589
10627
|
if (pgConfig.schema?.relations) registry.registerRelations(pgConfig.schema.relations);
|
|
10628
|
+
if (pgConfig.schema?.tables) patchPgArrayNullSafety(pgConfig.schema.tables);
|
|
10590
10629
|
const mergedSchema = {
|
|
10591
10630
|
...pgConfig.schema?.tables,
|
|
10592
10631
|
...pgConfig.schema?.relations || {}
|