@rebasepro/server-postgresql 0.0.1-canary.f81da60 → 0.1.0
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 +287 -21
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +287 -21
- package/dist/index.umd.js.map +1 -1
- package/dist/server-postgresql/src/PostgresBackendDriver.d.ts +2 -1
- package/dist/server-postgresql/src/schema/introspect-db-inference.d.ts +5 -0
- package/dist/server-postgresql/src/schema/introspect-db-logic.d.ts +44 -9
- package/dist/server-postgresql/src/services/EntityPersistService.d.ts +9 -0
- package/dist/types/src/controllers/auth.d.ts +8 -2
- package/dist/types/src/controllers/client.d.ts +13 -0
- package/dist/types/src/controllers/navigation.d.ts +18 -6
- package/dist/types/src/controllers/registry.d.ts +9 -1
- package/dist/types/src/controllers/side_entity_controller.d.ts +7 -0
- package/dist/types/src/rebase_context.d.ts +17 -0
- package/dist/types/src/types/collections.d.ts +20 -1
- package/dist/types/src/types/component_ref.d.ts +47 -0
- package/dist/types/src/types/entity_views.d.ts +2 -1
- package/dist/types/src/types/index.d.ts +1 -0
- package/dist/types/src/types/properties.d.ts +15 -3
- package/dist/types/src/types/translations.d.ts +2 -0
- package/examples/sdk-demo/node_modules/esbuild/LICENSE.md +21 -0
- package/examples/sdk-demo/node_modules/esbuild/README.md +3 -0
- package/examples/sdk-demo/node_modules/esbuild/bin/esbuild +223 -0
- package/examples/sdk-demo/node_modules/esbuild/install.js +289 -0
- package/examples/sdk-demo/node_modules/esbuild/lib/main.d.ts +716 -0
- package/examples/sdk-demo/node_modules/esbuild/lib/main.js +2242 -0
- package/examples/sdk-demo/node_modules/esbuild/package.json +49 -0
- package/package.json +5 -5
- package/src/PostgresBackendDriver.ts +23 -6
- package/src/cli.ts +10 -2
- package/src/data-transformer.ts +84 -1
- package/src/schema/doctor.ts +14 -2
- package/src/schema/generate-drizzle-schema-logic.ts +52 -5
- package/src/schema/introspect-db-inference.ts +238 -0
- package/src/schema/introspect-db-logic.ts +365 -61
- package/src/schema/introspect-db.ts +66 -23
- package/src/services/EntityFetchService.ts +16 -0
- package/src/services/EntityPersistService.ts +88 -12
- package/test/generate-drizzle-schema.test.ts +295 -0
- package/test/introspect-db-generation.test.ts +32 -10
- package/test/property-ordering.test.ts +395 -0
- package/jest-all.log +0 -3128
- package/jest.log +0 -49
- package/scratch.ts +0 -41
- package/test-drizzle-bug.ts +0 -18
- package/test-drizzle-out/0000_cultured_freak.sql +0 -7
- package/test-drizzle-out/0001_tiresome_professor_monster.sql +0 -1
- package/test-drizzle-out/meta/0000_snapshot.json +0 -55
- package/test-drizzle-out/meta/0001_snapshot.json +0 -63
- package/test-drizzle-out/meta/_journal.json +0 -20
- package/test-drizzle-prompt.sh +0 -2
- package/test-policy-prompt.sh +0 -3
- package/test-programmatic.ts +0 -30
- package/test-programmatic2.ts +0 -59
- package/test-schema-no-policies.ts +0 -12
- package/test_drizzle_mock.js +0 -3
- package/test_find_changed.mjs +0 -32
- package/test_hash.js +0 -14
- package/test_output.txt +0 -3145
package/dist/index.umd.js
CHANGED
|
@@ -132,7 +132,8 @@
|
|
|
132
132
|
}
|
|
133
133
|
const DEFAULT_ONE_OF_TYPE = "type";
|
|
134
134
|
const DEFAULT_ONE_OF_VALUE = "value";
|
|
135
|
-
const
|
|
135
|
+
const tokenizeRegex = /[A-Z]{2,}(?=[A-Z][a-z]|\b)|[A-Z]?[a-z]+|[0-9]+(?:[a-z](?![a-z]))?|[A-Z]/g;
|
|
136
|
+
const snakeCaseRegex = tokenizeRegex;
|
|
136
137
|
const toSnakeCase = (str) => {
|
|
137
138
|
const regExpMatchArray = str.match(snakeCaseRegex);
|
|
138
139
|
if (!regExpMatchArray) return "";
|
|
@@ -1029,6 +1030,80 @@
|
|
|
1029
1030
|
const singularName = snakeCaseName.endsWith("s") ? snakeCaseName.slice(0, -1) : snakeCaseName;
|
|
1030
1031
|
return `${singularName}_id`;
|
|
1031
1032
|
}
|
|
1033
|
+
function updateDateAutoValues({
|
|
1034
|
+
inputValues,
|
|
1035
|
+
properties,
|
|
1036
|
+
status,
|
|
1037
|
+
timestampNowValue
|
|
1038
|
+
}) {
|
|
1039
|
+
return traverseValuesProperties(inputValues, properties, (inputValue, property) => {
|
|
1040
|
+
if (property.type === "date") {
|
|
1041
|
+
if (status === "existing" && property.autoValue === "on_update") {
|
|
1042
|
+
return timestampNowValue;
|
|
1043
|
+
} else if ((status === "new" || status === "copy") && (property.autoValue === "on_update" || property.autoValue === "on_create")) {
|
|
1044
|
+
return timestampNowValue;
|
|
1045
|
+
} else {
|
|
1046
|
+
return inputValue;
|
|
1047
|
+
}
|
|
1048
|
+
} else {
|
|
1049
|
+
return inputValue;
|
|
1050
|
+
}
|
|
1051
|
+
}) ?? {};
|
|
1052
|
+
}
|
|
1053
|
+
function traverseValuesProperties(inputValues, properties, operation) {
|
|
1054
|
+
const safeInputValues = inputValues ?? {};
|
|
1055
|
+
const updatedValues = Object.entries(properties).map(([key, property]) => {
|
|
1056
|
+
const inputValue = safeInputValues && safeInputValues[key];
|
|
1057
|
+
const updatedValue = traverseValueProperty(inputValue, property, operation);
|
|
1058
|
+
if (updatedValue === null) return null;
|
|
1059
|
+
if (updatedValue === void 0) return void 0;
|
|
1060
|
+
return {
|
|
1061
|
+
[key]: updatedValue
|
|
1062
|
+
};
|
|
1063
|
+
}).reduce((a, b) => ({
|
|
1064
|
+
...a,
|
|
1065
|
+
...b
|
|
1066
|
+
}), {});
|
|
1067
|
+
const result = mergeDeep(safeInputValues, updatedValues);
|
|
1068
|
+
if (!result || Object.keys(result).length === 0) return void 0;
|
|
1069
|
+
return result;
|
|
1070
|
+
}
|
|
1071
|
+
function traverseValueProperty(inputValue, property, operation) {
|
|
1072
|
+
let value;
|
|
1073
|
+
if (property.type === "map" && property.properties) {
|
|
1074
|
+
value = traverseValuesProperties(inputValue, property.properties, operation);
|
|
1075
|
+
} else if (property.type === "array") {
|
|
1076
|
+
const of = property.of;
|
|
1077
|
+
if (of && Array.isArray(inputValue) && !Array.isArray(of)) {
|
|
1078
|
+
value = inputValue.map((e) => traverseValueProperty(e, of, operation));
|
|
1079
|
+
} else if (of && Array.isArray(inputValue) && Array.isArray(of)) {
|
|
1080
|
+
value = inputValue.map((e, i) => {
|
|
1081
|
+
if (i < of.length) return traverseValueProperty(e, of[i], operation);
|
|
1082
|
+
return null;
|
|
1083
|
+
}).filter(Boolean);
|
|
1084
|
+
} else if (property.oneOf && Array.isArray(inputValue)) {
|
|
1085
|
+
const typeField = property.oneOf?.typeField ?? DEFAULT_ONE_OF_TYPE;
|
|
1086
|
+
const valueField = property.oneOf?.valueField ?? DEFAULT_ONE_OF_VALUE;
|
|
1087
|
+
value = inputValue.map((e) => {
|
|
1088
|
+
if (e === null) return null;
|
|
1089
|
+
if (typeof e !== "object") return e;
|
|
1090
|
+
const rec = e;
|
|
1091
|
+
const type = rec[typeField];
|
|
1092
|
+
const childProperty = property.oneOf?.properties[type];
|
|
1093
|
+
if (!type || !childProperty) return e;
|
|
1094
|
+
return {
|
|
1095
|
+
[typeField]: type,
|
|
1096
|
+
[valueField]: traverseValueProperty(rec[valueField], childProperty, operation)
|
|
1097
|
+
};
|
|
1098
|
+
});
|
|
1099
|
+
} else {
|
|
1100
|
+
value = inputValue;
|
|
1101
|
+
}
|
|
1102
|
+
} else {
|
|
1103
|
+
value = operation(inputValue, property);
|
|
1104
|
+
}
|
|
1105
|
+
return value;
|
|
1106
|
+
}
|
|
1032
1107
|
function createRelationRef(id, path2) {
|
|
1033
1108
|
return {
|
|
1034
1109
|
id,
|
|
@@ -2616,8 +2691,8 @@
|
|
|
2616
2691
|
var freeExports = exports$1 && !exports$1.nodeType && exports$1;
|
|
2617
2692
|
var freeModule = freeExports && true && module2 && !module2.nodeType && module2;
|
|
2618
2693
|
var moduleExports = freeModule && freeModule.exports === freeExports;
|
|
2619
|
-
var
|
|
2620
|
-
var nativeIsBuffer =
|
|
2694
|
+
var Buffer2 = moduleExports ? root2.Buffer : void 0;
|
|
2695
|
+
var nativeIsBuffer = Buffer2 ? Buffer2.isBuffer : void 0;
|
|
2621
2696
|
var isBuffer2 = nativeIsBuffer || stubFalse2;
|
|
2622
2697
|
module2.exports = isBuffer2;
|
|
2623
2698
|
})(isBuffer$2, isBuffer$2.exports);
|
|
@@ -2782,7 +2857,7 @@
|
|
|
2782
2857
|
var freeExports = exports$1 && !exports$1.nodeType && exports$1;
|
|
2783
2858
|
var freeModule = freeExports && true && module2 && !module2.nodeType && module2;
|
|
2784
2859
|
var moduleExports = freeModule && freeModule.exports === freeExports;
|
|
2785
|
-
var
|
|
2860
|
+
var Buffer2 = moduleExports ? root2.Buffer : void 0, allocUnsafe = Buffer2 ? Buffer2.allocUnsafe : void 0;
|
|
2786
2861
|
function cloneBuffer2(buffer, isDeep) {
|
|
2787
2862
|
if (isDeep) {
|
|
2788
2863
|
return buffer.slice();
|
|
@@ -4485,7 +4560,25 @@
|
|
|
4485
4560
|
return result;
|
|
4486
4561
|
}
|
|
4487
4562
|
return value;
|
|
4563
|
+
case "string":
|
|
4564
|
+
if (typeof value === "string") {
|
|
4565
|
+
if (value.startsWith("data:application/octet-stream;base64,")) {
|
|
4566
|
+
const base64Data = value.split(",")[1];
|
|
4567
|
+
if (base64Data) {
|
|
4568
|
+
return Buffer.from(base64Data, "base64");
|
|
4569
|
+
}
|
|
4570
|
+
}
|
|
4571
|
+
}
|
|
4572
|
+
return value;
|
|
4488
4573
|
default:
|
|
4574
|
+
if (typeof value === "string") {
|
|
4575
|
+
if (value.startsWith("data:application/octet-stream;base64,")) {
|
|
4576
|
+
const base64Data = value.split(",")[1];
|
|
4577
|
+
if (base64Data) {
|
|
4578
|
+
return Buffer.from(base64Data, "base64");
|
|
4579
|
+
}
|
|
4580
|
+
}
|
|
4581
|
+
}
|
|
4489
4582
|
return value;
|
|
4490
4583
|
}
|
|
4491
4584
|
}
|
|
@@ -4611,6 +4704,37 @@
|
|
|
4611
4704
|
return value;
|
|
4612
4705
|
}
|
|
4613
4706
|
switch (property.type) {
|
|
4707
|
+
case "string": {
|
|
4708
|
+
if (typeof value === "string") return value;
|
|
4709
|
+
let isBuffer2 = false;
|
|
4710
|
+
let buf = null;
|
|
4711
|
+
if (Buffer.isBuffer(value)) {
|
|
4712
|
+
isBuffer2 = true;
|
|
4713
|
+
buf = value;
|
|
4714
|
+
} else if (typeof value === "object" && value !== null && value.type === "Buffer" && Array.isArray(value.data)) {
|
|
4715
|
+
isBuffer2 = true;
|
|
4716
|
+
buf = Buffer.from(value.data);
|
|
4717
|
+
}
|
|
4718
|
+
if (isBuffer2 && buf) {
|
|
4719
|
+
let isPrintable = true;
|
|
4720
|
+
for (let i = 0; i < buf.length; i++) {
|
|
4721
|
+
const b = buf[i];
|
|
4722
|
+
if ((b < 32 || b > 126) && b !== 9 && b !== 10 && b !== 13) {
|
|
4723
|
+
isPrintable = false;
|
|
4724
|
+
break;
|
|
4725
|
+
}
|
|
4726
|
+
}
|
|
4727
|
+
return isPrintable ? buf.toString("utf8") : `data:application/octet-stream;base64,${buf.toString("base64")}`;
|
|
4728
|
+
}
|
|
4729
|
+
if (typeof value === "object" && value !== null) {
|
|
4730
|
+
try {
|
|
4731
|
+
return JSON.stringify(value);
|
|
4732
|
+
} catch {
|
|
4733
|
+
return String(value);
|
|
4734
|
+
}
|
|
4735
|
+
}
|
|
4736
|
+
return String(value);
|
|
4737
|
+
}
|
|
4614
4738
|
case "relation":
|
|
4615
4739
|
if (typeof value === "string" || typeof value === "number") {
|
|
4616
4740
|
let relationDef = property.relation;
|
|
@@ -4694,8 +4818,29 @@
|
|
|
4694
4818
|
}
|
|
4695
4819
|
return null;
|
|
4696
4820
|
}
|
|
4697
|
-
default:
|
|
4821
|
+
default: {
|
|
4822
|
+
let isBuffer2 = false;
|
|
4823
|
+
let buf = null;
|
|
4824
|
+
if (Buffer.isBuffer(value)) {
|
|
4825
|
+
isBuffer2 = true;
|
|
4826
|
+
buf = value;
|
|
4827
|
+
} else if (typeof value === "object" && value !== null && value.type === "Buffer" && Array.isArray(value.data)) {
|
|
4828
|
+
isBuffer2 = true;
|
|
4829
|
+
buf = Buffer.from(value.data);
|
|
4830
|
+
}
|
|
4831
|
+
if (isBuffer2 && buf) {
|
|
4832
|
+
let isPrintable = true;
|
|
4833
|
+
for (let i = 0; i < buf.length; i++) {
|
|
4834
|
+
const b = buf[i];
|
|
4835
|
+
if ((b < 32 || b > 126) && b !== 9 && b !== 10 && b !== 13) {
|
|
4836
|
+
isPrintable = false;
|
|
4837
|
+
break;
|
|
4838
|
+
}
|
|
4839
|
+
}
|
|
4840
|
+
return isPrintable ? buf.toString("utf8") : `data:application/octet-stream;base64,${buf.toString("base64")}`;
|
|
4841
|
+
}
|
|
4698
4842
|
return value;
|
|
4843
|
+
}
|
|
4699
4844
|
}
|
|
4700
4845
|
}
|
|
4701
4846
|
function normalizeScalarValues(data, properties, collection, resolvedRelations, options) {
|
|
@@ -5963,6 +6108,10 @@
|
|
|
5963
6108
|
await this.resolveJoinPathRelations(entity, collection, collectionPath, parsedId, databaseId);
|
|
5964
6109
|
return entity;
|
|
5965
6110
|
} catch (e) {
|
|
6111
|
+
if (e instanceof Error && e.message.includes("not enough information to infer relation")) {
|
|
6112
|
+
console.error(`[EntityFetchService] Relation inference error for collection '${collectionPath}': ${e.message}`);
|
|
6113
|
+
console.error(`Hint: This usually means a relation in your drizzle schema is missing a reciprocal 'one()' or 'many()' definition. Run 'rebase schema generate' to fix this.`);
|
|
6114
|
+
}
|
|
5966
6115
|
console.warn(`[EntityFetchService] db.query.findFirst failed for ${collectionPath}, falling back to db.select:`, e);
|
|
5967
6116
|
}
|
|
5968
6117
|
}
|
|
@@ -6023,6 +6172,10 @@
|
|
|
6023
6172
|
const entities = results2.map((row) => this.drizzleResultToEntity(row, collection, collectionPath, idInfo, options.databaseId, idInfoArray));
|
|
6024
6173
|
return entities;
|
|
6025
6174
|
} catch (e) {
|
|
6175
|
+
if (e instanceof Error && e.message.includes("not enough information to infer relation")) {
|
|
6176
|
+
console.error(`[EntityFetchService] Relation inference error for collection '${collectionPath}': ${e.message}`);
|
|
6177
|
+
console.error(`Hint: This usually means a relation in your drizzle schema is missing a reciprocal 'one()' or 'many()' definition. Run 'rebase schema generate' to fix this.`);
|
|
6178
|
+
}
|
|
6026
6179
|
console.warn(`[EntityFetchService] db.query.findMany failed for ${collectionPath}, falling back to db.select:`, e);
|
|
6027
6180
|
}
|
|
6028
6181
|
}
|
|
@@ -6292,6 +6445,10 @@
|
|
|
6292
6445
|
await this.resolveJoinPathRelationsBatchRest(restRows, collection, collectionPath, idInfoArray, include);
|
|
6293
6446
|
return restRows;
|
|
6294
6447
|
} catch (e) {
|
|
6448
|
+
if (e instanceof Error && e.message.includes("not enough information to infer relation")) {
|
|
6449
|
+
console.error(`[EntityFetchService] Relation inference error for collection '${collectionPath}': ${e.message}`);
|
|
6450
|
+
console.error(`Hint: This usually means a relation in your drizzle schema is missing a reciprocal 'one()' or 'many()' definition. Run 'rebase schema generate' to fix this.`);
|
|
6451
|
+
}
|
|
6295
6452
|
console.warn(`[fetchCollectionForRest] db.query.findMany failed for ${collectionPath}, falling back:`, e);
|
|
6296
6453
|
}
|
|
6297
6454
|
}
|
|
@@ -6372,6 +6529,10 @@
|
|
|
6372
6529
|
await this.resolveJoinPathRelationsBatchRest([restRow], collection, collectionPath, idInfoArray, include);
|
|
6373
6530
|
return restRow;
|
|
6374
6531
|
} catch (e) {
|
|
6532
|
+
if (e instanceof Error && e.message.includes("not enough information to infer relation")) {
|
|
6533
|
+
console.error(`[EntityFetchService] Relation inference error for collection '${collectionPath}': ${e.message}`);
|
|
6534
|
+
console.error(`Hint: This usually means a relation in your drizzle schema is missing a reciprocal 'one()' or 'many()' definition. Run 'rebase schema generate' to fix this.`);
|
|
6535
|
+
}
|
|
6375
6536
|
console.warn(`[fetchEntityForRest] db.query.findFirst failed for ${collectionPath}, falling back:`, e);
|
|
6376
6537
|
}
|
|
6377
6538
|
}
|
|
@@ -6765,22 +6926,78 @@
|
|
|
6765
6926
|
const pgError = this.extractPgError(error);
|
|
6766
6927
|
if (pgError) {
|
|
6767
6928
|
const detail = pgError.detail;
|
|
6929
|
+
const hint = pgError.hint;
|
|
6768
6930
|
const constraint = pgError.constraint;
|
|
6769
6931
|
const column = pgError.column;
|
|
6770
6932
|
const table = pgError.table;
|
|
6933
|
+
const dataType = pgError.dataType;
|
|
6934
|
+
const pgMessage = pgError.message || "Unknown database error";
|
|
6935
|
+
const suffix = hint ? ` Hint: ${hint}` : "";
|
|
6936
|
+
const tableRef = table ?? collectionSlug;
|
|
6771
6937
|
switch (pgError.code) {
|
|
6772
6938
|
case "23503":
|
|
6773
|
-
return new Error(detail ? `Foreign key constraint violated: ${detail}` : `Cannot save: a foreign key constraint${constraint ? ` (${constraint})` : ""} was violated in "${collectionSlug}"
|
|
6939
|
+
return new Error(detail ? `Foreign key constraint violated: ${detail}${suffix}` : `Cannot save: a foreign key constraint${constraint ? ` (${constraint})` : ""} was violated in "${collectionSlug}".${suffix}`);
|
|
6774
6940
|
case "23505":
|
|
6775
|
-
return new Error(detail ? `Duplicate value: ${detail}` : `Cannot save: a unique constraint${constraint ? ` (${constraint})` : ""} was violated in "${collectionSlug}"
|
|
6941
|
+
return new Error(detail ? `Duplicate value: ${detail}${suffix}` : `Cannot save: a unique constraint${constraint ? ` (${constraint})` : ""} was violated in "${collectionSlug}".${suffix}`);
|
|
6776
6942
|
case "23502":
|
|
6777
|
-
return new Error(`Missing required field: "${column ?? "unknown"}" in "${
|
|
6943
|
+
return new Error(`Missing required field: "${column ?? "unknown"}" in "${tableRef}" cannot be empty.${suffix}`);
|
|
6778
6944
|
case "23514":
|
|
6779
|
-
return new Error(`Validation failed: a check constraint${constraint ? ` (${constraint})` : ""} was violated in "${collectionSlug}"
|
|
6945
|
+
return new Error(`Validation failed: a check constraint${constraint ? ` (${constraint})` : ""} was violated in "${collectionSlug}".${suffix}`);
|
|
6946
|
+
case "22P02":
|
|
6947
|
+
return new Error(`Invalid data format in "${collectionSlug}": ${pgMessage}${suffix}`);
|
|
6948
|
+
case "22001":
|
|
6949
|
+
return new Error(`Value too long for column "${column ?? "unknown"}" in "${tableRef}": ${pgMessage}${suffix}`);
|
|
6950
|
+
case "22003":
|
|
6951
|
+
return new Error(`Numeric value out of range for column "${column ?? "unknown"}" in "${tableRef}": ${pgMessage}${suffix}`);
|
|
6952
|
+
case "42703":
|
|
6953
|
+
return new Error(`Unknown column in "${tableRef}": ${pgMessage}. Check if your schema is up to date (run migrations).${suffix}`);
|
|
6954
|
+
case "42P01":
|
|
6955
|
+
return new Error(`Table not found for "${collectionSlug}": ${pgMessage}. Check if your schema is up to date (run migrations).${suffix}`);
|
|
6956
|
+
default: {
|
|
6957
|
+
const parts = [`Database error in "${collectionSlug}" [${pgError.code}]: ${pgMessage}`];
|
|
6958
|
+
if (detail) parts.push(`Detail: ${detail}`);
|
|
6959
|
+
if (column) parts.push(`Column: ${column}`);
|
|
6960
|
+
if (dataType) parts.push(`Data type: ${dataType}`);
|
|
6961
|
+
if (constraint) parts.push(`Constraint: ${constraint}`);
|
|
6962
|
+
if (hint) parts.push(`Hint: ${hint}`);
|
|
6963
|
+
return new Error(parts.join(". "));
|
|
6964
|
+
}
|
|
6965
|
+
}
|
|
6966
|
+
}
|
|
6967
|
+
const causeMessage = this.extractCauseMessage(error);
|
|
6968
|
+
if (causeMessage) {
|
|
6969
|
+
return new Error(`Database error in "${collectionSlug}": ${causeMessage}`);
|
|
6970
|
+
}
|
|
6971
|
+
if (error instanceof Error) {
|
|
6972
|
+
const cleaned = this.stripSqlFromMessage(error.message, collectionSlug);
|
|
6973
|
+
return new Error(cleaned);
|
|
6974
|
+
}
|
|
6975
|
+
return new Error(`Database error in "${collectionSlug}": ${String(error)}`);
|
|
6976
|
+
}
|
|
6977
|
+
/**
|
|
6978
|
+
* Walk the error cause chain and return the deepest meaningful message.
|
|
6979
|
+
*/
|
|
6980
|
+
extractCauseMessage(error) {
|
|
6981
|
+
if (!error || typeof error !== "object") return null;
|
|
6982
|
+
const err = error;
|
|
6983
|
+
if (err.cause && typeof err.cause === "object") {
|
|
6984
|
+
const deeper = this.extractCauseMessage(err.cause);
|
|
6985
|
+
if (deeper) return deeper;
|
|
6986
|
+
if (err.cause instanceof Error && err.cause.message) {
|
|
6987
|
+
return err.cause.message;
|
|
6780
6988
|
}
|
|
6781
6989
|
}
|
|
6782
|
-
|
|
6783
|
-
|
|
6990
|
+
return null;
|
|
6991
|
+
}
|
|
6992
|
+
/**
|
|
6993
|
+
* Strip the raw SQL query from a Drizzle "Failed query: ..." message,
|
|
6994
|
+
* keeping only the error description.
|
|
6995
|
+
*/
|
|
6996
|
+
stripSqlFromMessage(message, collectionSlug) {
|
|
6997
|
+
if (message.startsWith("Failed query:")) {
|
|
6998
|
+
return `Failed to save entity in "${collectionSlug}". Check server logs for details.`;
|
|
6999
|
+
}
|
|
7000
|
+
return message;
|
|
6784
7001
|
}
|
|
6785
7002
|
/**
|
|
6786
7003
|
* Extract the underlying PostgreSQL error from a Drizzle wrapper.
|
|
@@ -6789,7 +7006,7 @@
|
|
|
6789
7006
|
extractPgError(error) {
|
|
6790
7007
|
if (!error || typeof error !== "object") return null;
|
|
6791
7008
|
const err = error;
|
|
6792
|
-
if (err.code && /^[0-
|
|
7009
|
+
if (err.code && /^[0-9A-Z]{5}$/.test(err.code)) {
|
|
6793
7010
|
return err;
|
|
6794
7011
|
}
|
|
6795
7012
|
if (err.cause && typeof err.cause === "object") {
|
|
@@ -7077,6 +7294,7 @@
|
|
|
7077
7294
|
branchService;
|
|
7078
7295
|
user;
|
|
7079
7296
|
data;
|
|
7297
|
+
client;
|
|
7080
7298
|
/**
|
|
7081
7299
|
* When true, realtime notifications are deferred until after the
|
|
7082
7300
|
* wrapping transaction commits. Set by `withAuth` → `withTransaction`.
|
|
@@ -7166,7 +7384,8 @@
|
|
|
7166
7384
|
const contextForCallback = {
|
|
7167
7385
|
user: this.user,
|
|
7168
7386
|
driver: this,
|
|
7169
|
-
data: this.data
|
|
7387
|
+
data: this.data,
|
|
7388
|
+
client: this.client
|
|
7170
7389
|
};
|
|
7171
7390
|
return Promise.all(entities.map(async (entity) => {
|
|
7172
7391
|
let fetched = entity;
|
|
@@ -7260,7 +7479,8 @@
|
|
|
7260
7479
|
const contextForCallback = {
|
|
7261
7480
|
user: this.user,
|
|
7262
7481
|
driver: this,
|
|
7263
|
-
data: this.data
|
|
7482
|
+
data: this.data,
|
|
7483
|
+
client: this.client
|
|
7264
7484
|
};
|
|
7265
7485
|
if (callbacks?.afterRead) {
|
|
7266
7486
|
entity = await callbacks.afterRead({
|
|
@@ -7329,7 +7549,8 @@
|
|
|
7329
7549
|
const contextForCallback = {
|
|
7330
7550
|
user: this.user,
|
|
7331
7551
|
driver: this,
|
|
7332
|
-
data: this.data
|
|
7552
|
+
data: this.data,
|
|
7553
|
+
client: this.client
|
|
7333
7554
|
};
|
|
7334
7555
|
let previousValuesForHistory;
|
|
7335
7556
|
if (status === "existing" && entityId) {
|
|
@@ -7364,6 +7585,14 @@
|
|
|
7364
7585
|
if (result) updatedValues = mergeDeep(updatedValues, result);
|
|
7365
7586
|
}
|
|
7366
7587
|
}
|
|
7588
|
+
if (resolvedCollection?.properties) {
|
|
7589
|
+
updatedValues = updateDateAutoValues({
|
|
7590
|
+
inputValues: updatedValues,
|
|
7591
|
+
properties: resolvedCollection.properties,
|
|
7592
|
+
status: status ?? "new",
|
|
7593
|
+
timestampNowValue: /* @__PURE__ */ new Date()
|
|
7594
|
+
});
|
|
7595
|
+
}
|
|
7367
7596
|
try {
|
|
7368
7597
|
let savedEntity = await this.entityService.saveEntity(path2, updatedValues, entityId, resolvedCollection?.databaseId);
|
|
7369
7598
|
if (savedEntity && (callbacks?.afterRead || propertyCallbacks?.afterRead)) {
|
|
@@ -7469,7 +7698,8 @@
|
|
|
7469
7698
|
const contextForCallback = {
|
|
7470
7699
|
user: this.user,
|
|
7471
7700
|
driver: this,
|
|
7472
|
-
data: this.data
|
|
7701
|
+
data: this.data,
|
|
7702
|
+
client: this.client
|
|
7473
7703
|
};
|
|
7474
7704
|
if (callbacks?.beforeDelete || propertyCallbacks?.beforeDelete) {
|
|
7475
7705
|
if (callbacks?.beforeDelete) {
|
|
@@ -7796,6 +8026,7 @@
|
|
|
7796
8026
|
txDelegate.entityService = txEntityService;
|
|
7797
8027
|
txDelegate._deferNotifications = true;
|
|
7798
8028
|
txDelegate._pendingNotifications = pendingNotifications;
|
|
8029
|
+
txDelegate.client = this.delegate.client;
|
|
7799
8030
|
return await operation(txDelegate);
|
|
7800
8031
|
});
|
|
7801
8032
|
for (const notification of pendingNotifications) {
|
|
@@ -8077,6 +8308,12 @@
|
|
|
8077
8308
|
references: [users.id]
|
|
8078
8309
|
})
|
|
8079
8310
|
}));
|
|
8311
|
+
const resolveColumnName = (propName, prop) => {
|
|
8312
|
+
if (prop && "columnName" in prop && typeof prop.columnName === "string") {
|
|
8313
|
+
return prop.columnName;
|
|
8314
|
+
}
|
|
8315
|
+
return toSnakeCase(propName);
|
|
8316
|
+
};
|
|
8080
8317
|
const getPrimaryKeyProp = (collection) => {
|
|
8081
8318
|
if (collection.properties) {
|
|
8082
8319
|
const idPropEntry = Object.entries(collection.properties).find(([_, prop]) => "isId" in prop && Boolean(prop.isId));
|
|
@@ -8117,7 +8354,7 @@
|
|
|
8117
8354
|
return !hasExplicitId && propName === "id";
|
|
8118
8355
|
};
|
|
8119
8356
|
const getDrizzleColumn = (propName, prop, collection, collections) => {
|
|
8120
|
-
const colName =
|
|
8357
|
+
const colName = resolveColumnName(propName, prop);
|
|
8121
8358
|
let columnDefinition;
|
|
8122
8359
|
switch (prop.type) {
|
|
8123
8360
|
case "string": {
|
|
@@ -8189,6 +8426,9 @@
|
|
|
8189
8426
|
} else {
|
|
8190
8427
|
columnDefinition = `timestamp("${colName}", { withTimezone: true, mode: 'string' })`;
|
|
8191
8428
|
}
|
|
8429
|
+
if (dateProp.autoValue === "on_create" || dateProp.autoValue === "on_update") {
|
|
8430
|
+
columnDefinition += `.default(sql\`now()\`)`;
|
|
8431
|
+
}
|
|
8192
8432
|
break;
|
|
8193
8433
|
}
|
|
8194
8434
|
case "map":
|
|
@@ -8221,7 +8461,7 @@
|
|
|
8221
8461
|
} catch {
|
|
8222
8462
|
return null;
|
|
8223
8463
|
}
|
|
8224
|
-
const fkColumnName =
|
|
8464
|
+
const fkColumnName = relation.localKey;
|
|
8225
8465
|
const targetTableVar = getTableVarName(getTableName(targetCollection));
|
|
8226
8466
|
const pkProp = getPrimaryKeyProp(targetCollection);
|
|
8227
8467
|
const targetIdField = pkProp.name;
|
|
@@ -8409,7 +8649,7 @@
|
|
|
8409
8649
|
Object.entries(collection.properties ?? {}).forEach(([propName, prop]) => {
|
|
8410
8650
|
if ("enum" in prop && (prop.type === "string" || prop.type === "number") && prop.enum) {
|
|
8411
8651
|
const enumVarName = getEnumVarName(collectionPath, propName);
|
|
8412
|
-
const enumDbName = `${collectionPath}_${
|
|
8652
|
+
const enumDbName = `${collectionPath}_${resolveColumnName(propName, prop)}`;
|
|
8413
8653
|
const values = Array.isArray(prop.enum) ? prop.enum.map((v) => String(v.id ?? v)) : Object.keys(prop.enum);
|
|
8414
8654
|
if (values.length > 0) {
|
|
8415
8655
|
schemaContent += `export const ${enumVarName} = pgEnum("${enumDbName}", [${values.map((v) => `'${v}'`).join(", ")}]);
|
|
@@ -8466,9 +8706,9 @@
|
|
|
8466
8706
|
const targetId = getPrimaryKeyName(targetCollection);
|
|
8467
8707
|
schemaContent += `export const ${tableVarName} = pgTable("${tableName}", {
|
|
8468
8708
|
`;
|
|
8469
|
-
schemaContent += ` ${sourceColumn}: ${sourceColType}("${
|
|
8709
|
+
schemaContent += ` ${sourceColumn}: ${sourceColType}("${sourceColumn}").notNull().references(() => ${getTableVarName(getTableName(sourceCollection))}.${sourceId}, ${refOptions}),
|
|
8470
8710
|
`;
|
|
8471
|
-
schemaContent += ` ${targetColumn}: ${targetColType}("${
|
|
8711
|
+
schemaContent += ` ${targetColumn}: ${targetColType}("${targetColumn}").notNull().references(() => ${getTableVarName(getTableName(targetCollection))}.${targetId}, ${refOptions}),
|
|
8472
8712
|
`;
|
|
8473
8713
|
schemaContent += "}, (table) => ({\n";
|
|
8474
8714
|
schemaContent += ` pk: primaryKey({ columns: [table.${sourceColumn}, table.${targetColumn}] })
|
|
@@ -8592,6 +8832,32 @@
|
|
|
8592
8832
|
console.warn(`Could not generate relation ${relationKey} for ${collection.name}:`, e);
|
|
8593
8833
|
}
|
|
8594
8834
|
}
|
|
8835
|
+
for (const otherCollection of collections) {
|
|
8836
|
+
if (otherCollection.slug === collection.slug) continue;
|
|
8837
|
+
const otherRelations = resolveCollectionRelations(otherCollection);
|
|
8838
|
+
for (const [otherKey, otherRel] of Object.entries(otherRelations)) {
|
|
8839
|
+
if (otherRel.direction === "inverse" && otherRel.foreignKeyOnTarget) {
|
|
8840
|
+
try {
|
|
8841
|
+
const otherTarget = otherRel.target();
|
|
8842
|
+
if (otherTarget.slug === collection.slug) {
|
|
8843
|
+
const drizzleRelationName = computeSharedRelationName(otherRel, otherCollection, collections);
|
|
8844
|
+
const deduplicationKey = `${drizzleRelationName}::owning`;
|
|
8845
|
+
if (!emittedRelationNames.has(deduplicationKey)) {
|
|
8846
|
+
const otherTableVar = getTableVarName(getTableName(otherCollection));
|
|
8847
|
+
const synthKey = `_synth_${otherTableVar}_${otherRel.foreignKeyOnTarget}`;
|
|
8848
|
+
tableRelations.push(` "${synthKey}": one(${otherTableVar}, {
|
|
8849
|
+
fields: [${tableVarName}.${otherRel.foreignKeyOnTarget}],
|
|
8850
|
+
references: [${otherTableVar}.${getPrimaryKeyName(otherCollection)}],
|
|
8851
|
+
relationName: "${drizzleRelationName}"
|
|
8852
|
+
})`);
|
|
8853
|
+
emittedRelationNames.add(deduplicationKey);
|
|
8854
|
+
}
|
|
8855
|
+
}
|
|
8856
|
+
} catch (e) {
|
|
8857
|
+
}
|
|
8858
|
+
}
|
|
8859
|
+
}
|
|
8860
|
+
}
|
|
8595
8861
|
}
|
|
8596
8862
|
if (tableRelations.length > 0) {
|
|
8597
8863
|
const relVarName = `${tableVarName}Relations`;
|