@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.
Files changed (59) hide show
  1. package/dist/index.es.js +287 -21
  2. package/dist/index.es.js.map +1 -1
  3. package/dist/index.umd.js +287 -21
  4. package/dist/index.umd.js.map +1 -1
  5. package/dist/server-postgresql/src/PostgresBackendDriver.d.ts +2 -1
  6. package/dist/server-postgresql/src/schema/introspect-db-inference.d.ts +5 -0
  7. package/dist/server-postgresql/src/schema/introspect-db-logic.d.ts +44 -9
  8. package/dist/server-postgresql/src/services/EntityPersistService.d.ts +9 -0
  9. package/dist/types/src/controllers/auth.d.ts +8 -2
  10. package/dist/types/src/controllers/client.d.ts +13 -0
  11. package/dist/types/src/controllers/navigation.d.ts +18 -6
  12. package/dist/types/src/controllers/registry.d.ts +9 -1
  13. package/dist/types/src/controllers/side_entity_controller.d.ts +7 -0
  14. package/dist/types/src/rebase_context.d.ts +17 -0
  15. package/dist/types/src/types/collections.d.ts +20 -1
  16. package/dist/types/src/types/component_ref.d.ts +47 -0
  17. package/dist/types/src/types/entity_views.d.ts +2 -1
  18. package/dist/types/src/types/index.d.ts +1 -0
  19. package/dist/types/src/types/properties.d.ts +15 -3
  20. package/dist/types/src/types/translations.d.ts +2 -0
  21. package/examples/sdk-demo/node_modules/esbuild/LICENSE.md +21 -0
  22. package/examples/sdk-demo/node_modules/esbuild/README.md +3 -0
  23. package/examples/sdk-demo/node_modules/esbuild/bin/esbuild +223 -0
  24. package/examples/sdk-demo/node_modules/esbuild/install.js +289 -0
  25. package/examples/sdk-demo/node_modules/esbuild/lib/main.d.ts +716 -0
  26. package/examples/sdk-demo/node_modules/esbuild/lib/main.js +2242 -0
  27. package/examples/sdk-demo/node_modules/esbuild/package.json +49 -0
  28. package/package.json +5 -5
  29. package/src/PostgresBackendDriver.ts +23 -6
  30. package/src/cli.ts +10 -2
  31. package/src/data-transformer.ts +84 -1
  32. package/src/schema/doctor.ts +14 -2
  33. package/src/schema/generate-drizzle-schema-logic.ts +52 -5
  34. package/src/schema/introspect-db-inference.ts +238 -0
  35. package/src/schema/introspect-db-logic.ts +365 -61
  36. package/src/schema/introspect-db.ts +66 -23
  37. package/src/services/EntityFetchService.ts +16 -0
  38. package/src/services/EntityPersistService.ts +88 -12
  39. package/test/generate-drizzle-schema.test.ts +295 -0
  40. package/test/introspect-db-generation.test.ts +32 -10
  41. package/test/property-ordering.test.ts +395 -0
  42. package/jest-all.log +0 -3128
  43. package/jest.log +0 -49
  44. package/scratch.ts +0 -41
  45. package/test-drizzle-bug.ts +0 -18
  46. package/test-drizzle-out/0000_cultured_freak.sql +0 -7
  47. package/test-drizzle-out/0001_tiresome_professor_monster.sql +0 -1
  48. package/test-drizzle-out/meta/0000_snapshot.json +0 -55
  49. package/test-drizzle-out/meta/0001_snapshot.json +0 -63
  50. package/test-drizzle-out/meta/_journal.json +0 -20
  51. package/test-drizzle-prompt.sh +0 -2
  52. package/test-policy-prompt.sh +0 -3
  53. package/test-programmatic.ts +0 -30
  54. package/test-programmatic2.ts +0 -59
  55. package/test-schema-no-policies.ts +0 -12
  56. package/test_drizzle_mock.js +0 -3
  57. package/test_find_changed.mjs +0 -32
  58. package/test_hash.js +0 -14
  59. package/test_output.txt +0 -3145
package/dist/index.es.js CHANGED
@@ -124,7 +124,8 @@ function getDataSourceCapabilities(driver) {
124
124
  }
125
125
  const DEFAULT_ONE_OF_TYPE = "type";
126
126
  const DEFAULT_ONE_OF_VALUE = "value";
127
- const snakeCaseRegex = /[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g;
127
+ const tokenizeRegex = /[A-Z]{2,}(?=[A-Z][a-z]|\b)|[A-Z]?[a-z]+|[0-9]+(?:[a-z](?![a-z]))?|[A-Z]/g;
128
+ const snakeCaseRegex = tokenizeRegex;
128
129
  const toSnakeCase = (str) => {
129
130
  const regExpMatchArray = str.match(snakeCaseRegex);
130
131
  if (!regExpMatchArray) return "";
@@ -1021,6 +1022,80 @@ function generateForeignKeyName(name) {
1021
1022
  const singularName = snakeCaseName.endsWith("s") ? snakeCaseName.slice(0, -1) : snakeCaseName;
1022
1023
  return `${singularName}_id`;
1023
1024
  }
1025
+ function updateDateAutoValues({
1026
+ inputValues,
1027
+ properties,
1028
+ status,
1029
+ timestampNowValue
1030
+ }) {
1031
+ return traverseValuesProperties(inputValues, properties, (inputValue, property) => {
1032
+ if (property.type === "date") {
1033
+ if (status === "existing" && property.autoValue === "on_update") {
1034
+ return timestampNowValue;
1035
+ } else if ((status === "new" || status === "copy") && (property.autoValue === "on_update" || property.autoValue === "on_create")) {
1036
+ return timestampNowValue;
1037
+ } else {
1038
+ return inputValue;
1039
+ }
1040
+ } else {
1041
+ return inputValue;
1042
+ }
1043
+ }) ?? {};
1044
+ }
1045
+ function traverseValuesProperties(inputValues, properties, operation) {
1046
+ const safeInputValues = inputValues ?? {};
1047
+ const updatedValues = Object.entries(properties).map(([key, property]) => {
1048
+ const inputValue = safeInputValues && safeInputValues[key];
1049
+ const updatedValue = traverseValueProperty(inputValue, property, operation);
1050
+ if (updatedValue === null) return null;
1051
+ if (updatedValue === void 0) return void 0;
1052
+ return {
1053
+ [key]: updatedValue
1054
+ };
1055
+ }).reduce((a, b) => ({
1056
+ ...a,
1057
+ ...b
1058
+ }), {});
1059
+ const result = mergeDeep(safeInputValues, updatedValues);
1060
+ if (!result || Object.keys(result).length === 0) return void 0;
1061
+ return result;
1062
+ }
1063
+ function traverseValueProperty(inputValue, property, operation) {
1064
+ let value;
1065
+ if (property.type === "map" && property.properties) {
1066
+ value = traverseValuesProperties(inputValue, property.properties, operation);
1067
+ } else if (property.type === "array") {
1068
+ const of = property.of;
1069
+ if (of && Array.isArray(inputValue) && !Array.isArray(of)) {
1070
+ value = inputValue.map((e) => traverseValueProperty(e, of, operation));
1071
+ } else if (of && Array.isArray(inputValue) && Array.isArray(of)) {
1072
+ value = inputValue.map((e, i) => {
1073
+ if (i < of.length) return traverseValueProperty(e, of[i], operation);
1074
+ return null;
1075
+ }).filter(Boolean);
1076
+ } else if (property.oneOf && Array.isArray(inputValue)) {
1077
+ const typeField = property.oneOf?.typeField ?? DEFAULT_ONE_OF_TYPE;
1078
+ const valueField = property.oneOf?.valueField ?? DEFAULT_ONE_OF_VALUE;
1079
+ value = inputValue.map((e) => {
1080
+ if (e === null) return null;
1081
+ if (typeof e !== "object") return e;
1082
+ const rec = e;
1083
+ const type = rec[typeField];
1084
+ const childProperty = property.oneOf?.properties[type];
1085
+ if (!type || !childProperty) return e;
1086
+ return {
1087
+ [typeField]: type,
1088
+ [valueField]: traverseValueProperty(rec[valueField], childProperty, operation)
1089
+ };
1090
+ });
1091
+ } else {
1092
+ value = inputValue;
1093
+ }
1094
+ } else {
1095
+ value = operation(inputValue, property);
1096
+ }
1097
+ return value;
1098
+ }
1024
1099
  function createRelationRef(id, path2) {
1025
1100
  return {
1026
1101
  id,
@@ -2608,8 +2683,8 @@ isBuffer$2.exports;
2608
2683
  var freeExports = exports$1 && !exports$1.nodeType && exports$1;
2609
2684
  var freeModule = freeExports && true && module && !module.nodeType && module;
2610
2685
  var moduleExports = freeModule && freeModule.exports === freeExports;
2611
- var Buffer = moduleExports ? root2.Buffer : void 0;
2612
- var nativeIsBuffer = Buffer ? Buffer.isBuffer : void 0;
2686
+ var Buffer2 = moduleExports ? root2.Buffer : void 0;
2687
+ var nativeIsBuffer = Buffer2 ? Buffer2.isBuffer : void 0;
2613
2688
  var isBuffer2 = nativeIsBuffer || stubFalse2;
2614
2689
  module.exports = isBuffer2;
2615
2690
  })(isBuffer$2, isBuffer$2.exports);
@@ -2774,7 +2849,7 @@ _cloneBuffer.exports;
2774
2849
  var freeExports = exports$1 && !exports$1.nodeType && exports$1;
2775
2850
  var freeModule = freeExports && true && module && !module.nodeType && module;
2776
2851
  var moduleExports = freeModule && freeModule.exports === freeExports;
2777
- var Buffer = moduleExports ? root2.Buffer : void 0, allocUnsafe = Buffer ? Buffer.allocUnsafe : void 0;
2852
+ var Buffer2 = moduleExports ? root2.Buffer : void 0, allocUnsafe = Buffer2 ? Buffer2.allocUnsafe : void 0;
2778
2853
  function cloneBuffer2(buffer, isDeep) {
2779
2854
  if (isDeep) {
2780
2855
  return buffer.slice();
@@ -4477,7 +4552,25 @@ function serializePropertyToServer(value, property) {
4477
4552
  return result;
4478
4553
  }
4479
4554
  return value;
4555
+ case "string":
4556
+ if (typeof value === "string") {
4557
+ if (value.startsWith("data:application/octet-stream;base64,")) {
4558
+ const base64Data = value.split(",")[1];
4559
+ if (base64Data) {
4560
+ return Buffer.from(base64Data, "base64");
4561
+ }
4562
+ }
4563
+ }
4564
+ return value;
4480
4565
  default:
4566
+ if (typeof value === "string") {
4567
+ if (value.startsWith("data:application/octet-stream;base64,")) {
4568
+ const base64Data = value.split(",")[1];
4569
+ if (base64Data) {
4570
+ return Buffer.from(base64Data, "base64");
4571
+ }
4572
+ }
4573
+ }
4481
4574
  return value;
4482
4575
  }
4483
4576
  }
@@ -4603,6 +4696,37 @@ function parsePropertyFromServer(value, property, collection, propertyKey) {
4603
4696
  return value;
4604
4697
  }
4605
4698
  switch (property.type) {
4699
+ case "string": {
4700
+ if (typeof value === "string") return value;
4701
+ let isBuffer2 = false;
4702
+ let buf = null;
4703
+ if (Buffer.isBuffer(value)) {
4704
+ isBuffer2 = true;
4705
+ buf = value;
4706
+ } else if (typeof value === "object" && value !== null && value.type === "Buffer" && Array.isArray(value.data)) {
4707
+ isBuffer2 = true;
4708
+ buf = Buffer.from(value.data);
4709
+ }
4710
+ if (isBuffer2 && buf) {
4711
+ let isPrintable = true;
4712
+ for (let i = 0; i < buf.length; i++) {
4713
+ const b = buf[i];
4714
+ if ((b < 32 || b > 126) && b !== 9 && b !== 10 && b !== 13) {
4715
+ isPrintable = false;
4716
+ break;
4717
+ }
4718
+ }
4719
+ return isPrintable ? buf.toString("utf8") : `data:application/octet-stream;base64,${buf.toString("base64")}`;
4720
+ }
4721
+ if (typeof value === "object" && value !== null) {
4722
+ try {
4723
+ return JSON.stringify(value);
4724
+ } catch {
4725
+ return String(value);
4726
+ }
4727
+ }
4728
+ return String(value);
4729
+ }
4606
4730
  case "relation":
4607
4731
  if (typeof value === "string" || typeof value === "number") {
4608
4732
  let relationDef = property.relation;
@@ -4686,8 +4810,29 @@ function parsePropertyFromServer(value, property, collection, propertyKey) {
4686
4810
  }
4687
4811
  return null;
4688
4812
  }
4689
- default:
4813
+ default: {
4814
+ let isBuffer2 = false;
4815
+ let buf = null;
4816
+ if (Buffer.isBuffer(value)) {
4817
+ isBuffer2 = true;
4818
+ buf = value;
4819
+ } else if (typeof value === "object" && value !== null && value.type === "Buffer" && Array.isArray(value.data)) {
4820
+ isBuffer2 = true;
4821
+ buf = Buffer.from(value.data);
4822
+ }
4823
+ if (isBuffer2 && buf) {
4824
+ let isPrintable = true;
4825
+ for (let i = 0; i < buf.length; i++) {
4826
+ const b = buf[i];
4827
+ if ((b < 32 || b > 126) && b !== 9 && b !== 10 && b !== 13) {
4828
+ isPrintable = false;
4829
+ break;
4830
+ }
4831
+ }
4832
+ return isPrintable ? buf.toString("utf8") : `data:application/octet-stream;base64,${buf.toString("base64")}`;
4833
+ }
4690
4834
  return value;
4835
+ }
4691
4836
  }
4692
4837
  }
4693
4838
  function normalizeScalarValues(data, properties, collection, resolvedRelations, options) {
@@ -5955,6 +6100,10 @@ class EntityFetchService {
5955
6100
  await this.resolveJoinPathRelations(entity, collection, collectionPath, parsedId, databaseId);
5956
6101
  return entity;
5957
6102
  } catch (e) {
6103
+ if (e instanceof Error && e.message.includes("not enough information to infer relation")) {
6104
+ console.error(`[EntityFetchService] Relation inference error for collection '${collectionPath}': ${e.message}`);
6105
+ 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.`);
6106
+ }
5958
6107
  console.warn(`[EntityFetchService] db.query.findFirst failed for ${collectionPath}, falling back to db.select:`, e);
5959
6108
  }
5960
6109
  }
@@ -6015,6 +6164,10 @@ class EntityFetchService {
6015
6164
  const entities = results2.map((row) => this.drizzleResultToEntity(row, collection, collectionPath, idInfo, options.databaseId, idInfoArray));
6016
6165
  return entities;
6017
6166
  } catch (e) {
6167
+ if (e instanceof Error && e.message.includes("not enough information to infer relation")) {
6168
+ console.error(`[EntityFetchService] Relation inference error for collection '${collectionPath}': ${e.message}`);
6169
+ 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.`);
6170
+ }
6018
6171
  console.warn(`[EntityFetchService] db.query.findMany failed for ${collectionPath}, falling back to db.select:`, e);
6019
6172
  }
6020
6173
  }
@@ -6284,6 +6437,10 @@ class EntityFetchService {
6284
6437
  await this.resolveJoinPathRelationsBatchRest(restRows, collection, collectionPath, idInfoArray, include);
6285
6438
  return restRows;
6286
6439
  } catch (e) {
6440
+ if (e instanceof Error && e.message.includes("not enough information to infer relation")) {
6441
+ console.error(`[EntityFetchService] Relation inference error for collection '${collectionPath}': ${e.message}`);
6442
+ 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.`);
6443
+ }
6287
6444
  console.warn(`[fetchCollectionForRest] db.query.findMany failed for ${collectionPath}, falling back:`, e);
6288
6445
  }
6289
6446
  }
@@ -6364,6 +6521,10 @@ class EntityFetchService {
6364
6521
  await this.resolveJoinPathRelationsBatchRest([restRow], collection, collectionPath, idInfoArray, include);
6365
6522
  return restRow;
6366
6523
  } catch (e) {
6524
+ if (e instanceof Error && e.message.includes("not enough information to infer relation")) {
6525
+ console.error(`[EntityFetchService] Relation inference error for collection '${collectionPath}': ${e.message}`);
6526
+ 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.`);
6527
+ }
6367
6528
  console.warn(`[fetchEntityForRest] db.query.findFirst failed for ${collectionPath}, falling back:`, e);
6368
6529
  }
6369
6530
  }
@@ -6757,22 +6918,78 @@ class EntityPersistService {
6757
6918
  const pgError = this.extractPgError(error);
6758
6919
  if (pgError) {
6759
6920
  const detail = pgError.detail;
6921
+ const hint = pgError.hint;
6760
6922
  const constraint = pgError.constraint;
6761
6923
  const column = pgError.column;
6762
6924
  const table = pgError.table;
6925
+ const dataType = pgError.dataType;
6926
+ const pgMessage = pgError.message || "Unknown database error";
6927
+ const suffix = hint ? ` Hint: ${hint}` : "";
6928
+ const tableRef = table ?? collectionSlug;
6763
6929
  switch (pgError.code) {
6764
6930
  case "23503":
6765
- return new Error(detail ? `Foreign key constraint violated: ${detail}` : `Cannot save: a foreign key constraint${constraint ? ` (${constraint})` : ""} was violated in "${collectionSlug}".`);
6931
+ return new Error(detail ? `Foreign key constraint violated: ${detail}${suffix}` : `Cannot save: a foreign key constraint${constraint ? ` (${constraint})` : ""} was violated in "${collectionSlug}".${suffix}`);
6766
6932
  case "23505":
6767
- return new Error(detail ? `Duplicate value: ${detail}` : `Cannot save: a unique constraint${constraint ? ` (${constraint})` : ""} was violated in "${collectionSlug}".`);
6933
+ return new Error(detail ? `Duplicate value: ${detail}${suffix}` : `Cannot save: a unique constraint${constraint ? ` (${constraint})` : ""} was violated in "${collectionSlug}".${suffix}`);
6768
6934
  case "23502":
6769
- return new Error(`Missing required field: "${column ?? "unknown"}" in "${table ?? collectionSlug}" cannot be empty.`);
6935
+ return new Error(`Missing required field: "${column ?? "unknown"}" in "${tableRef}" cannot be empty.${suffix}`);
6770
6936
  case "23514":
6771
- return new Error(`Validation failed: a check constraint${constraint ? ` (${constraint})` : ""} was violated in "${collectionSlug}".`);
6937
+ return new Error(`Validation failed: a check constraint${constraint ? ` (${constraint})` : ""} was violated in "${collectionSlug}".${suffix}`);
6938
+ case "22P02":
6939
+ return new Error(`Invalid data format in "${collectionSlug}": ${pgMessage}${suffix}`);
6940
+ case "22001":
6941
+ return new Error(`Value too long for column "${column ?? "unknown"}" in "${tableRef}": ${pgMessage}${suffix}`);
6942
+ case "22003":
6943
+ return new Error(`Numeric value out of range for column "${column ?? "unknown"}" in "${tableRef}": ${pgMessage}${suffix}`);
6944
+ case "42703":
6945
+ return new Error(`Unknown column in "${tableRef}": ${pgMessage}. Check if your schema is up to date (run migrations).${suffix}`);
6946
+ case "42P01":
6947
+ return new Error(`Table not found for "${collectionSlug}": ${pgMessage}. Check if your schema is up to date (run migrations).${suffix}`);
6948
+ default: {
6949
+ const parts = [`Database error in "${collectionSlug}" [${pgError.code}]: ${pgMessage}`];
6950
+ if (detail) parts.push(`Detail: ${detail}`);
6951
+ if (column) parts.push(`Column: ${column}`);
6952
+ if (dataType) parts.push(`Data type: ${dataType}`);
6953
+ if (constraint) parts.push(`Constraint: ${constraint}`);
6954
+ if (hint) parts.push(`Hint: ${hint}`);
6955
+ return new Error(parts.join(". "));
6956
+ }
6957
+ }
6958
+ }
6959
+ const causeMessage = this.extractCauseMessage(error);
6960
+ if (causeMessage) {
6961
+ return new Error(`Database error in "${collectionSlug}": ${causeMessage}`);
6962
+ }
6963
+ if (error instanceof Error) {
6964
+ const cleaned = this.stripSqlFromMessage(error.message, collectionSlug);
6965
+ return new Error(cleaned);
6966
+ }
6967
+ return new Error(`Database error in "${collectionSlug}": ${String(error)}`);
6968
+ }
6969
+ /**
6970
+ * Walk the error cause chain and return the deepest meaningful message.
6971
+ */
6972
+ extractCauseMessage(error) {
6973
+ if (!error || typeof error !== "object") return null;
6974
+ const err = error;
6975
+ if (err.cause && typeof err.cause === "object") {
6976
+ const deeper = this.extractCauseMessage(err.cause);
6977
+ if (deeper) return deeper;
6978
+ if (err.cause instanceof Error && err.cause.message) {
6979
+ return err.cause.message;
6772
6980
  }
6773
6981
  }
6774
- if (error instanceof Error) return error;
6775
- return new Error(String(error));
6982
+ return null;
6983
+ }
6984
+ /**
6985
+ * Strip the raw SQL query from a Drizzle "Failed query: ..." message,
6986
+ * keeping only the error description.
6987
+ */
6988
+ stripSqlFromMessage(message, collectionSlug) {
6989
+ if (message.startsWith("Failed query:")) {
6990
+ return `Failed to save entity in "${collectionSlug}". Check server logs for details.`;
6991
+ }
6992
+ return message;
6776
6993
  }
6777
6994
  /**
6778
6995
  * Extract the underlying PostgreSQL error from a Drizzle wrapper.
@@ -6781,7 +6998,7 @@ class EntityPersistService {
6781
6998
  extractPgError(error) {
6782
6999
  if (!error || typeof error !== "object") return null;
6783
7000
  const err = error;
6784
- if (err.code && /^[0-9]{5}$/.test(err.code)) {
7001
+ if (err.code && /^[0-9A-Z]{5}$/.test(err.code)) {
6785
7002
  return err;
6786
7003
  }
6787
7004
  if (err.cause && typeof err.cause === "object") {
@@ -7069,6 +7286,7 @@ class PostgresBackendDriver {
7069
7286
  branchService;
7070
7287
  user;
7071
7288
  data;
7289
+ client;
7072
7290
  /**
7073
7291
  * When true, realtime notifications are deferred until after the
7074
7292
  * wrapping transaction commits. Set by `withAuth` → `withTransaction`.
@@ -7158,7 +7376,8 @@ class PostgresBackendDriver {
7158
7376
  const contextForCallback = {
7159
7377
  user: this.user,
7160
7378
  driver: this,
7161
- data: this.data
7379
+ data: this.data,
7380
+ client: this.client
7162
7381
  };
7163
7382
  return Promise.all(entities.map(async (entity) => {
7164
7383
  let fetched = entity;
@@ -7252,7 +7471,8 @@ class PostgresBackendDriver {
7252
7471
  const contextForCallback = {
7253
7472
  user: this.user,
7254
7473
  driver: this,
7255
- data: this.data
7474
+ data: this.data,
7475
+ client: this.client
7256
7476
  };
7257
7477
  if (callbacks?.afterRead) {
7258
7478
  entity = await callbacks.afterRead({
@@ -7321,7 +7541,8 @@ class PostgresBackendDriver {
7321
7541
  const contextForCallback = {
7322
7542
  user: this.user,
7323
7543
  driver: this,
7324
- data: this.data
7544
+ data: this.data,
7545
+ client: this.client
7325
7546
  };
7326
7547
  let previousValuesForHistory;
7327
7548
  if (status === "existing" && entityId) {
@@ -7356,6 +7577,14 @@ class PostgresBackendDriver {
7356
7577
  if (result) updatedValues = mergeDeep(updatedValues, result);
7357
7578
  }
7358
7579
  }
7580
+ if (resolvedCollection?.properties) {
7581
+ updatedValues = updateDateAutoValues({
7582
+ inputValues: updatedValues,
7583
+ properties: resolvedCollection.properties,
7584
+ status: status ?? "new",
7585
+ timestampNowValue: /* @__PURE__ */ new Date()
7586
+ });
7587
+ }
7359
7588
  try {
7360
7589
  let savedEntity = await this.entityService.saveEntity(path2, updatedValues, entityId, resolvedCollection?.databaseId);
7361
7590
  if (savedEntity && (callbacks?.afterRead || propertyCallbacks?.afterRead)) {
@@ -7461,7 +7690,8 @@ class PostgresBackendDriver {
7461
7690
  const contextForCallback = {
7462
7691
  user: this.user,
7463
7692
  driver: this,
7464
- data: this.data
7693
+ data: this.data,
7694
+ client: this.client
7465
7695
  };
7466
7696
  if (callbacks?.beforeDelete || propertyCallbacks?.beforeDelete) {
7467
7697
  if (callbacks?.beforeDelete) {
@@ -7788,6 +8018,7 @@ class AuthenticatedPostgresBackendDriver {
7788
8018
  txDelegate.entityService = txEntityService;
7789
8019
  txDelegate._deferNotifications = true;
7790
8020
  txDelegate._pendingNotifications = pendingNotifications;
8021
+ txDelegate.client = this.delegate.client;
7791
8022
  return await operation(txDelegate);
7792
8023
  });
7793
8024
  for (const notification of pendingNotifications) {
@@ -8069,6 +8300,12 @@ const userIdentitiesRelations = relations(userIdentities, ({
8069
8300
  references: [users.id]
8070
8301
  })
8071
8302
  }));
8303
+ const resolveColumnName = (propName, prop) => {
8304
+ if (prop && "columnName" in prop && typeof prop.columnName === "string") {
8305
+ return prop.columnName;
8306
+ }
8307
+ return toSnakeCase(propName);
8308
+ };
8072
8309
  const getPrimaryKeyProp = (collection) => {
8073
8310
  if (collection.properties) {
8074
8311
  const idPropEntry = Object.entries(collection.properties).find(([_, prop]) => "isId" in prop && Boolean(prop.isId));
@@ -8109,7 +8346,7 @@ const isIdProperty = (propName, prop, collection) => {
8109
8346
  return !hasExplicitId && propName === "id";
8110
8347
  };
8111
8348
  const getDrizzleColumn = (propName, prop, collection, collections) => {
8112
- const colName = toSnakeCase(propName);
8349
+ const colName = resolveColumnName(propName, prop);
8113
8350
  let columnDefinition;
8114
8351
  switch (prop.type) {
8115
8352
  case "string": {
@@ -8181,6 +8418,9 @@ const getDrizzleColumn = (propName, prop, collection, collections) => {
8181
8418
  } else {
8182
8419
  columnDefinition = `timestamp("${colName}", { withTimezone: true, mode: 'string' })`;
8183
8420
  }
8421
+ if (dateProp.autoValue === "on_create" || dateProp.autoValue === "on_update") {
8422
+ columnDefinition += `.default(sql\`now()\`)`;
8423
+ }
8184
8424
  break;
8185
8425
  }
8186
8426
  case "map":
@@ -8213,7 +8453,7 @@ const getDrizzleColumn = (propName, prop, collection, collections) => {
8213
8453
  } catch {
8214
8454
  return null;
8215
8455
  }
8216
- const fkColumnName = toSnakeCase(relation.localKey);
8456
+ const fkColumnName = relation.localKey;
8217
8457
  const targetTableVar = getTableVarName(getTableName(targetCollection));
8218
8458
  const pkProp = getPrimaryKeyProp(targetCollection);
8219
8459
  const targetIdField = pkProp.name;
@@ -8401,7 +8641,7 @@ const generateSchema = async (collections, stripPolicies = false) => {
8401
8641
  Object.entries(collection.properties ?? {}).forEach(([propName, prop]) => {
8402
8642
  if ("enum" in prop && (prop.type === "string" || prop.type === "number") && prop.enum) {
8403
8643
  const enumVarName = getEnumVarName(collectionPath, propName);
8404
- const enumDbName = `${collectionPath}_${toSnakeCase(propName)}`;
8644
+ const enumDbName = `${collectionPath}_${resolveColumnName(propName, prop)}`;
8405
8645
  const values = Array.isArray(prop.enum) ? prop.enum.map((v) => String(v.id ?? v)) : Object.keys(prop.enum);
8406
8646
  if (values.length > 0) {
8407
8647
  schemaContent += `export const ${enumVarName} = pgEnum("${enumDbName}", [${values.map((v) => `'${v}'`).join(", ")}]);
@@ -8458,9 +8698,9 @@ const generateSchema = async (collections, stripPolicies = false) => {
8458
8698
  const targetId = getPrimaryKeyName(targetCollection);
8459
8699
  schemaContent += `export const ${tableVarName} = pgTable("${tableName}", {
8460
8700
  `;
8461
- schemaContent += ` ${sourceColumn}: ${sourceColType}("${toSnakeCase(sourceColumn)}").notNull().references(() => ${getTableVarName(getTableName(sourceCollection))}.${sourceId}, ${refOptions}),
8701
+ schemaContent += ` ${sourceColumn}: ${sourceColType}("${sourceColumn}").notNull().references(() => ${getTableVarName(getTableName(sourceCollection))}.${sourceId}, ${refOptions}),
8462
8702
  `;
8463
- schemaContent += ` ${targetColumn}: ${targetColType}("${toSnakeCase(targetColumn)}").notNull().references(() => ${getTableVarName(getTableName(targetCollection))}.${targetId}, ${refOptions}),
8703
+ schemaContent += ` ${targetColumn}: ${targetColType}("${targetColumn}").notNull().references(() => ${getTableVarName(getTableName(targetCollection))}.${targetId}, ${refOptions}),
8464
8704
  `;
8465
8705
  schemaContent += "}, (table) => ({\n";
8466
8706
  schemaContent += ` pk: primaryKey({ columns: [table.${sourceColumn}, table.${targetColumn}] })
@@ -8584,6 +8824,32 @@ const generateSchema = async (collections, stripPolicies = false) => {
8584
8824
  console.warn(`Could not generate relation ${relationKey} for ${collection.name}:`, e);
8585
8825
  }
8586
8826
  }
8827
+ for (const otherCollection of collections) {
8828
+ if (otherCollection.slug === collection.slug) continue;
8829
+ const otherRelations = resolveCollectionRelations(otherCollection);
8830
+ for (const [otherKey, otherRel] of Object.entries(otherRelations)) {
8831
+ if (otherRel.direction === "inverse" && otherRel.foreignKeyOnTarget) {
8832
+ try {
8833
+ const otherTarget = otherRel.target();
8834
+ if (otherTarget.slug === collection.slug) {
8835
+ const drizzleRelationName = computeSharedRelationName(otherRel, otherCollection, collections);
8836
+ const deduplicationKey = `${drizzleRelationName}::owning`;
8837
+ if (!emittedRelationNames.has(deduplicationKey)) {
8838
+ const otherTableVar = getTableVarName(getTableName(otherCollection));
8839
+ const synthKey = `_synth_${otherTableVar}_${otherRel.foreignKeyOnTarget}`;
8840
+ tableRelations.push(` "${synthKey}": one(${otherTableVar}, {
8841
+ fields: [${tableVarName}.${otherRel.foreignKeyOnTarget}],
8842
+ references: [${otherTableVar}.${getPrimaryKeyName(otherCollection)}],
8843
+ relationName: "${drizzleRelationName}"
8844
+ })`);
8845
+ emittedRelationNames.add(deduplicationKey);
8846
+ }
8847
+ }
8848
+ } catch (e) {
8849
+ }
8850
+ }
8851
+ }
8852
+ }
8587
8853
  }
8588
8854
  if (tableRelations.length > 0) {
8589
8855
  const relVarName = `${tableVarName}Relations`;