velocious 1.0.437 → 1.0.439

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 (40) hide show
  1. package/README.md +10 -0
  2. package/build/configuration.js +31 -0
  3. package/build/database/query/preloader/has-many.js +29 -1
  4. package/build/database/record/index.js +4 -2
  5. package/build/database/record/instance-relationships/has-many.js +6 -1
  6. package/build/database/record/relationships/base.js +9 -0
  7. package/build/environment-handlers/node/cli/commands/generate/base-models.js +4 -2
  8. package/build/frontend-model-controller.js +14 -6
  9. package/build/src/configuration.d.ts +26 -0
  10. package/build/src/configuration.d.ts.map +1 -1
  11. package/build/src/configuration.js +27 -1
  12. package/build/src/database/query/preloader/has-many.d.ts +8 -0
  13. package/build/src/database/query/preloader/has-many.d.ts.map +1 -1
  14. package/build/src/database/query/preloader/has-many.js +26 -2
  15. package/build/src/database/record/index.d.ts.map +1 -1
  16. package/build/src/database/record/index.js +5 -3
  17. package/build/src/database/record/instance-relationships/has-many.d.ts.map +1 -1
  18. package/build/src/database/record/instance-relationships/has-many.js +4 -2
  19. package/build/src/database/record/relationships/base.d.ts +8 -0
  20. package/build/src/database/record/relationships/base.d.ts.map +1 -1
  21. package/build/src/database/record/relationships/base.js +9 -1
  22. package/build/src/environment-handlers/node/cli/commands/generate/base-models.d.ts.map +1 -1
  23. package/build/src/environment-handlers/node/cli/commands/generate/base-models.js +5 -3
  24. package/build/src/frontend-model-controller.d.ts +2 -2
  25. package/build/src/frontend-model-controller.d.ts.map +1 -1
  26. package/build/src/frontend-model-controller.js +15 -7
  27. package/build/src/utils/deburr-column-name.d.ts +11 -0
  28. package/build/src/utils/deburr-column-name.d.ts.map +1 -0
  29. package/build/src/utils/deburr-column-name.js +33 -0
  30. package/build/tsconfig.tsbuildinfo +1 -1
  31. package/build/utils/deburr-column-name.js +37 -0
  32. package/package.json +1 -1
  33. package/src/configuration.js +31 -0
  34. package/src/database/query/preloader/has-many.js +29 -1
  35. package/src/database/record/index.js +4 -2
  36. package/src/database/record/instance-relationships/has-many.js +6 -1
  37. package/src/database/record/relationships/base.js +9 -0
  38. package/src/environment-handlers/node/cli/commands/generate/base-models.js +4 -2
  39. package/src/frontend-model-controller.js +14 -6
  40. package/src/utils/deburr-column-name.js +37 -0
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Transliterates German umlauts (and ß) in a database column name to ASCII so generated and runtime
3
+ * attribute names stay ASCII regardless of whether the column uses the umlaut ("Plätze") or already
4
+ * transliterated ("Plaetze") spelling. Both then map to the same attribute (e.g. "plaetzeVerkauft"),
5
+ * which keeps generated model bases consistent with code that references the ASCII attribute names.
6
+ * The raw column name is still used for the actual SQL, so the underlying column is untouched.
7
+ * @param {string} columnName - Raw database column name.
8
+ * @returns {string} - ASCII-transliterated column name.
9
+ */
10
+ export default function deburrColumnName(columnName: string): string;
11
+ //# sourceMappingURL=deburr-column-name.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deburr-column-name.d.ts","sourceRoot":"","sources":["../../../src/utils/deburr-column-name.js"],"names":[],"mappings":"AAaA;;;;;;;;GAQG;AACH,qDAHW,MAAM,GACJ,MAAM,CAgBlB"}
@@ -0,0 +1,33 @@
1
+ // @ts-check
2
+ /** @type {Array<[RegExp, string]>} */
3
+ const UMLAUT_REPLACEMENTS = [
4
+ [/Ä/g, "Ae"],
5
+ [/Ö/g, "Oe"],
6
+ [/Ü/g, "Ue"],
7
+ [/ä/g, "ae"],
8
+ [/ö/g, "oe"],
9
+ [/ü/g, "ue"],
10
+ [/ß/g, "ss"]
11
+ ];
12
+ /**
13
+ * Transliterates German umlauts (and ß) in a database column name to ASCII so generated and runtime
14
+ * attribute names stay ASCII regardless of whether the column uses the umlaut ("Plätze") or already
15
+ * transliterated ("Plaetze") spelling. Both then map to the same attribute (e.g. "plaetzeVerkauft"),
16
+ * which keeps generated model bases consistent with code that references the ASCII attribute names.
17
+ * The raw column name is still used for the actual SQL, so the underlying column is untouched.
18
+ * @param {string} columnName - Raw database column name.
19
+ * @returns {string} - ASCII-transliterated column name.
20
+ */
21
+ export default function deburrColumnName(columnName) {
22
+ let result = columnName;
23
+ for (const [pattern, replacement] of UMLAUT_REPLACEMENTS) {
24
+ result = result.replace(pattern, replacement);
25
+ }
26
+ // An all-caps acronym column (e.g. "IP", "EA") would camelize to "iP"/"eA" because only the first
27
+ // letter is lowercased. Down-case columns that contain no lowercase letters so "IP" becomes "ip".
28
+ if (!/[a-z]/.test(result)) {
29
+ result = result.toLowerCase();
30
+ }
31
+ return result;
32
+ }
33
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVidXJyLWNvbHVtbi1uYW1lLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3V0aWxzL2RlYnVyci1jb2x1bW4tbmFtZS5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxZQUFZO0FBRVosc0NBQXNDO0FBQ3RDLE1BQU0sbUJBQW1CLEdBQUc7SUFDMUIsQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDO0lBQ1osQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDO0lBQ1osQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDO0lBQ1osQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDO0lBQ1osQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDO0lBQ1osQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDO0lBQ1osQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDO0NBQ2IsQ0FBQTtBQUVEOzs7Ozs7OztHQVFHO0FBQ0gsTUFBTSxDQUFDLE9BQU8sVUFBVSxnQkFBZ0IsQ0FBQyxVQUFVO0lBQ2pELElBQUksTUFBTSxHQUFHLFVBQVUsQ0FBQTtJQUV2QixLQUFLLE1BQU0sQ0FBQyxPQUFPLEVBQUUsV0FBVyxDQUFDLElBQUksbUJBQW1CLEVBQUUsQ0FBQztRQUN6RCxNQUFNLEdBQUcsTUFBTSxDQUFDLE9BQU8sQ0FBQyxPQUFPLEVBQUUsV0FBVyxDQUFDLENBQUE7SUFDL0MsQ0FBQztJQUVELGtHQUFrRztJQUNsRyxrR0FBa0c7SUFDbEcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQztRQUMxQixNQUFNLEdBQUcsTUFBTSxDQUFDLFdBQVcsRUFBRSxDQUFBO0lBQy9CLENBQUM7SUFFRCxPQUFPLE1BQU0sQ0FBQTtBQUNmLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBAdHMtY2hlY2tcblxuLyoqIEB0eXBlIHtBcnJheTxbUmVnRXhwLCBzdHJpbmddPn0gKi9cbmNvbnN0IFVNTEFVVF9SRVBMQUNFTUVOVFMgPSBbXG4gIFsvw4QvZywgXCJBZVwiXSxcbiAgWy/Dli9nLCBcIk9lXCJdLFxuICBbL8OcL2csIFwiVWVcIl0sXG4gIFsvw6QvZywgXCJhZVwiXSxcbiAgWy/Dti9nLCBcIm9lXCJdLFxuICBbL8O8L2csIFwidWVcIl0sXG4gIFsvw58vZywgXCJzc1wiXVxuXVxuXG4vKipcbiAqIFRyYW5zbGl0ZXJhdGVzIEdlcm1hbiB1bWxhdXRzIChhbmQgw58pIGluIGEgZGF0YWJhc2UgY29sdW1uIG5hbWUgdG8gQVNDSUkgc28gZ2VuZXJhdGVkIGFuZCBydW50aW1lXG4gKiBhdHRyaWJ1dGUgbmFtZXMgc3RheSBBU0NJSSByZWdhcmRsZXNzIG9mIHdoZXRoZXIgdGhlIGNvbHVtbiB1c2VzIHRoZSB1bWxhdXQgKFwiUGzDpHR6ZVwiKSBvciBhbHJlYWR5XG4gKiB0cmFuc2xpdGVyYXRlZCAoXCJQbGFldHplXCIpIHNwZWxsaW5nLiBCb3RoIHRoZW4gbWFwIHRvIHRoZSBzYW1lIGF0dHJpYnV0ZSAoZS5nLiBcInBsYWV0emVWZXJrYXVmdFwiKSxcbiAqIHdoaWNoIGtlZXBzIGdlbmVyYXRlZCBtb2RlbCBiYXNlcyBjb25zaXN0ZW50IHdpdGggY29kZSB0aGF0IHJlZmVyZW5jZXMgdGhlIEFTQ0lJIGF0dHJpYnV0ZSBuYW1lcy5cbiAqIFRoZSByYXcgY29sdW1uIG5hbWUgaXMgc3RpbGwgdXNlZCBmb3IgdGhlIGFjdHVhbCBTUUwsIHNvIHRoZSB1bmRlcmx5aW5nIGNvbHVtbiBpcyB1bnRvdWNoZWQuXG4gKiBAcGFyYW0ge3N0cmluZ30gY29sdW1uTmFtZSAtIFJhdyBkYXRhYmFzZSBjb2x1bW4gbmFtZS5cbiAqIEByZXR1cm5zIHtzdHJpbmd9IC0gQVNDSUktdHJhbnNsaXRlcmF0ZWQgY29sdW1uIG5hbWUuXG4gKi9cbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIGRlYnVyckNvbHVtbk5hbWUoY29sdW1uTmFtZSkge1xuICBsZXQgcmVzdWx0ID0gY29sdW1uTmFtZVxuXG4gIGZvciAoY29uc3QgW3BhdHRlcm4sIHJlcGxhY2VtZW50XSBvZiBVTUxBVVRfUkVQTEFDRU1FTlRTKSB7XG4gICAgcmVzdWx0ID0gcmVzdWx0LnJlcGxhY2UocGF0dGVybiwgcmVwbGFjZW1lbnQpXG4gIH1cblxuICAvLyBBbiBhbGwtY2FwcyBhY3JvbnltIGNvbHVtbiAoZS5nLiBcIklQXCIsIFwiRUFcIikgd291bGQgY2FtZWxpemUgdG8gXCJpUFwiL1wiZUFcIiBiZWNhdXNlIG9ubHkgdGhlIGZpcnN0XG4gIC8vIGxldHRlciBpcyBsb3dlcmNhc2VkLiBEb3duLWNhc2UgY29sdW1ucyB0aGF0IGNvbnRhaW4gbm8gbG93ZXJjYXNlIGxldHRlcnMgc28gXCJJUFwiIGJlY29tZXMgXCJpcFwiLlxuICBpZiAoIS9bYS16XS8udGVzdChyZXN1bHQpKSB7XG4gICAgcmVzdWx0ID0gcmVzdWx0LnRvTG93ZXJDYXNlKClcbiAgfVxuXG4gIHJldHVybiByZXN1bHRcbn1cbiJdfQ==
@@ -1 +1 @@
1
- {"root":["../index.js","../bin/velocious.js","../src/application.js","../src/configuration-resolver.js","../src/configuration-types.js","../src/configuration.js","../src/controller.js","../src/current-configuration.js","../src/current.js","../src/error-logger.js","../src/frontend-model-controller.js","../src/initializer.js","../src/logger.js","../src/mailer.js","../src/record-payload-values.js","../src/velocious-error.js","../src/authorization/ability.js","../src/authorization/base-resource.js","../src/background-jobs/client.js","../src/background-jobs/cron-expression.js","../src/background-jobs/forked-runner-child.js","../src/background-jobs/job-record.js","../src/background-jobs/job-registry.js","../src/background-jobs/job-runner.js","../src/background-jobs/job.js","../src/background-jobs/json-socket.js","../src/background-jobs/main.js","../src/background-jobs/normalize-error.js","../src/background-jobs/scheduler.js","../src/background-jobs/socket-request.js","../src/background-jobs/status-reporter.js","../src/background-jobs/store.js","../src/background-jobs/types.js","../src/background-jobs/worker.js","../src/background-jobs/web/authorization.js","../src/background-jobs/web/controller.js","../src/background-jobs/web/index.js","../src/background-jobs/web/path-matcher.js","../src/background-jobs/web/registry.js","../src/beacon/client.js","../src/beacon/in-process-broker.js","../src/beacon/in-process-client.js","../src/beacon/server.js","../src/beacon/types.js","../src/cli/base-command.js","../src/cli/browser-cli.js","../src/cli/index.js","../src/cli/tenant-database-command-helper.js","../src/cli/use-browser-cli.js","../src/cli/commands/background-jobs-main.js","../src/cli/commands/background-jobs-runner.js","../src/cli/commands/background-jobs-worker.js","../src/cli/commands/beacon.js","../src/cli/commands/console.js","../src/cli/commands/init.js","../src/cli/commands/routes.js","../src/cli/commands/run-script.js","../src/cli/commands/runner.js","../src/cli/commands/server.js","../src/cli/commands/test.js","../src/cli/commands/db/base-command.js","../src/cli/commands/db/create.js","../src/cli/commands/db/drop.js","../src/cli/commands/db/migrate.js","../src/cli/commands/db/reset.js","../src/cli/commands/db/rollback.js","../src/cli/commands/db/seed.js","../src/cli/commands/db/schema/dump.js","../src/cli/commands/db/schema/load.js","../src/cli/commands/db/tenants/check.js","../src/cli/commands/db/tenants/create.js","../src/cli/commands/db/tenants/migrate.js","../src/cli/commands/destroy/migration.js","../src/cli/commands/generate/base-models.js","../src/cli/commands/generate/frontend-models.js","../src/cli/commands/generate/migration.js","../src/cli/commands/generate/model.js","../src/database/annotations-async-hooks.js","../src/database/annotations.js","../src/database/handler.js","../src/database/initializer-from-require-context.js","../src/database/migrator.js","../src/database/use-database.js","../src/database/drivers/base-column.js","../src/database/drivers/base-columns-index.js","../src/database/drivers/base-foreign-key.js","../src/database/drivers/base-table.js","../src/database/drivers/base.js","../src/database/drivers/mssql/column.js","../src/database/drivers/mssql/columns-index.js","../src/database/drivers/mssql/connect-connection.js","../src/database/drivers/mssql/foreign-key.js","../src/database/drivers/mssql/index.js","../src/database/drivers/mssql/options.js","../src/database/drivers/mssql/query-parser.js","../src/database/drivers/mssql/structure-sql.js","../src/database/drivers/mssql/table.js","../src/database/drivers/mssql/sql/alter-table.js","../src/database/drivers/mssql/sql/create-database.js","../src/database/drivers/mssql/sql/create-index.js","../src/database/drivers/mssql/sql/create-table.js","../src/database/drivers/mssql/sql/delete.js","../src/database/drivers/mssql/sql/drop-database.js","../src/database/drivers/mssql/sql/drop-table.js","../src/database/drivers/mssql/sql/insert.js","../src/database/drivers/mssql/sql/update.js","../src/database/drivers/mssql/sql/upsert.js","../src/database/drivers/mysql/column.js","../src/database/drivers/mysql/columns-index.js","../src/database/drivers/mysql/foreign-key.js","../src/database/drivers/mysql/index.js","../src/database/drivers/mysql/options.js","../src/database/drivers/mysql/query-parser.js","../src/database/drivers/mysql/query.js","../src/database/drivers/mysql/structure-sql.js","../src/database/drivers/mysql/table.js","../src/database/drivers/mysql/sql/alter-table.js","../src/database/drivers/mysql/sql/create-database.js","../src/database/drivers/mysql/sql/create-index.js","../src/database/drivers/mysql/sql/create-table.js","../src/database/drivers/mysql/sql/delete.js","../src/database/drivers/mysql/sql/drop-database.js","../src/database/drivers/mysql/sql/drop-table.js","../src/database/drivers/mysql/sql/insert.js","../src/database/drivers/mysql/sql/update.js","../src/database/drivers/mysql/sql/upsert.js","../src/database/drivers/pgsql/column.js","../src/database/drivers/pgsql/columns-index.js","../src/database/drivers/pgsql/foreign-key.js","../src/database/drivers/pgsql/index.js","../src/database/drivers/pgsql/options.js","../src/database/drivers/pgsql/query-parser.js","../src/database/drivers/pgsql/structure-sql.js","../src/database/drivers/pgsql/table.js","../src/database/drivers/pgsql/sql/alter-table.js","../src/database/drivers/pgsql/sql/create-database.js","../src/database/drivers/pgsql/sql/create-index.js","../src/database/drivers/pgsql/sql/create-table.js","../src/database/drivers/pgsql/sql/delete.js","../src/database/drivers/pgsql/sql/drop-database.js","../src/database/drivers/pgsql/sql/drop-table.js","../src/database/drivers/pgsql/sql/insert.js","../src/database/drivers/pgsql/sql/update.js","../src/database/drivers/pgsql/sql/upsert.js","../src/database/drivers/sqlite/base.js","../src/database/drivers/sqlite/column.js","../src/database/drivers/sqlite/columns-index.js","../src/database/drivers/sqlite/connection-sql-js.js","../src/database/drivers/sqlite/foreign-key.js","../src/database/drivers/sqlite/index.js","../src/database/drivers/sqlite/index.native.js","../src/database/drivers/sqlite/index.web.js","../src/database/drivers/sqlite/options.js","../src/database/drivers/sqlite/query-parser.js","../src/database/drivers/sqlite/query.js","../src/database/drivers/sqlite/query.native.js","../src/database/drivers/sqlite/query.web.js","../src/database/drivers/sqlite/structure-sql.js","../src/database/drivers/sqlite/table-rebuilder.js","../src/database/drivers/sqlite/table.js","../src/database/drivers/sqlite/sql/alter-table.js","../src/database/drivers/sqlite/sql/create-index.js","../src/database/drivers/sqlite/sql/create-table.js","../src/database/drivers/sqlite/sql/delete.js","../src/database/drivers/sqlite/sql/drop-table.js","../src/database/drivers/sqlite/sql/insert.js","../src/database/drivers/sqlite/sql/update.js","../src/database/drivers/sqlite/sql/upsert.js","../src/database/drivers/structure-sql/utils.js","../src/database/migration/index.js","../src/database/migrator/files-finder.js","../src/database/migrator/types.js","../src/database/pool/async-tracked-multi-connection.js","../src/database/pool/base-methods-forward.js","../src/database/pool/base.js","../src/database/pool/single-multi-use.js","../src/database/query/alter-table-base.js","../src/database/query/base.js","../src/database/query/create-database-base.js","../src/database/query/create-index-base.js","../src/database/query/create-table-base.js","../src/database/query/delete-base.js","../src/database/query/drop-database-base.js","../src/database/query/drop-table-base.js","../src/database/query/from-base.js","../src/database/query/from-plain.js","../src/database/query/from-table.js","../src/database/query/index.js","../src/database/query/insert-base.js","../src/database/query/join-base.js","../src/database/query/join-object.js","../src/database/query/join-plain.js","../src/database/query/join-tracker.js","../src/database/query/model-class-query.js","../src/database/query/order-base.js","../src/database/query/order-column.js","../src/database/query/order-plain.js","../src/database/query/preloader.js","../src/database/query/query-data.js","../src/database/query/select-base.js","../src/database/query/select-plain.js","../src/database/query/select-table-and-column.js","../src/database/query/update-base.js","../src/database/query/upsert-base.js","../src/database/query/where-base.js","../src/database/query/where-combinator.js","../src/database/query/where-hash.js","../src/database/query/where-model-class-hash.js","../src/database/query/where-not.js","../src/database/query/where-plain.js","../src/database/query/with-count.js","../src/database/query/preloader/belongs-to.js","../src/database/query/preloader/ensure-model-class-initialized.js","../src/database/query/preloader/has-many.js","../src/database/query/preloader/has-one.js","../src/database/query/preloader/selection.js","../src/database/query-parser/base-query-parser.js","../src/database/query-parser/from-parser.js","../src/database/query-parser/group-parser.js","../src/database/query-parser/joins-parser.js","../src/database/query-parser/limit-parser.js","../src/database/query-parser/options.js","../src/database/query-parser/order-parser.js","../src/database/query-parser/select-parser.js","../src/database/query-parser/where-parser.js","../src/database/record/acts-as-list.js","../src/database/record/index.js","../src/database/record/record-not-found-error.js","../src/database/record/state-machine.js","../src/database/record/user-module.js","../src/database/record/attachments/download.js","../src/database/record/attachments/handle.js","../src/database/record/attachments/normalize-input.js","../src/database/record/attachments/store.js","../src/database/record/attachments/storage-drivers/filesystem.js","../src/database/record/attachments/storage-drivers/native.js","../src/database/record/attachments/storage-drivers/s3.js","../src/database/record/instance-relationships/base.js","../src/database/record/instance-relationships/belongs-to.js","../src/database/record/instance-relationships/has-many.js","../src/database/record/instance-relationships/has-one.js","../src/database/record/relationships/base.js","../src/database/record/relationships/belongs-to.js","../src/database/record/relationships/has-many.js","../src/database/record/relationships/has-one.js","../src/database/record/validators/base.js","../src/database/record/validators/format.js","../src/database/record/validators/presence.js","../src/database/record/validators/uniqueness.js","../src/database/table-data/index.js","../src/database/table-data/table-column.js","../src/database/table-data/table-foreign-key.js","../src/database/table-data/table-index.js","../src/database/table-data/table-reference.js","../src/environment-handlers/base.js","../src/environment-handlers/browser.js","../src/environment-handlers/node.js","../src/environment-handlers/node/cli/commands/background-jobs-main.js","../src/environment-handlers/node/cli/commands/background-jobs-runner.js","../src/environment-handlers/node/cli/commands/background-jobs-worker.js","../src/environment-handlers/node/cli/commands/beacon.js","../src/environment-handlers/node/cli/commands/cli-command-context.js","../src/environment-handlers/node/cli/commands/console.js","../src/environment-handlers/node/cli/commands/init.js","../src/environment-handlers/node/cli/commands/routes.js","../src/environment-handlers/node/cli/commands/run-script.js","../src/environment-handlers/node/cli/commands/runner.js","../src/environment-handlers/node/cli/commands/server.js","../src/environment-handlers/node/cli/commands/test.js","../src/environment-handlers/node/cli/commands/db/seed.js","../src/environment-handlers/node/cli/commands/db/schema/dump.js","../src/environment-handlers/node/cli/commands/db/schema/load.js","../src/environment-handlers/node/cli/commands/destroy/migration.js","../src/environment-handlers/node/cli/commands/generate/base-models.js","../src/environment-handlers/node/cli/commands/generate/frontend-models.js","../src/environment-handlers/node/cli/commands/generate/migration.js","../src/environment-handlers/node/cli/commands/generate/model.js","../src/frontend-model-resource/base-resource.js","../src/frontend-models/base.js","../src/frontend-models/clear-pending-debounced-callback.js","../src/frontend-models/event-hook-models.js","../src/frontend-models/model-registry.js","../src/frontend-models/outgoing-event-buffer.js","../src/frontend-models/preloader.js","../src/frontend-models/query.js","../src/frontend-models/resource-config-validation.js","../src/frontend-models/resource-definition.js","../src/frontend-models/transport-serialization.js","../src/frontend-models/use-created-event.js","../src/frontend-models/use-destroyed-event.js","../src/frontend-models/use-model-class-event.js","../src/frontend-models/use-updated-event.js","../src/frontend-models/websocket-channel.js","../src/frontend-models/websocket-publishers.js","../src/http-client/header.js","../src/http-client/index.js","../src/http-client/request.js","../src/http-client/response.js","../src/http-client/websocket-client.js","../src/http-server/cookie.js","../src/http-server/development-reloader.js","../src/http-server/index.js","../src/http-server/remote-address.js","../src/http-server/server-client.js","../src/http-server/server-lock.js","../src/http-server/websocket-channel-subscribers.js","../src/http-server/websocket-channel.js","../src/http-server/websocket-connection.js","../src/http-server/websocket-event-log-store.js","../src/http-server/websocket-events-host.js","../src/http-server/websocket-events.js","../src/http-server/client/index.js","../src/http-server/client/params-to-object.js","../src/http-server/client/request-parser.js","../src/http-server/client/request-runner.js","../src/http-server/client/request-timing.js","../src/http-server/client/request.js","../src/http-server/client/response.js","../src/http-server/client/websocket-request.js","../src/http-server/client/websocket-session.js","../src/http-server/client/request-buffer/form-data-part.js","../src/http-server/client/request-buffer/header.js","../src/http-server/client/request-buffer/index.js","../src/http-server/client/uploaded-file/memory-uploaded-file.js","../src/http-server/client/uploaded-file/temporary-uploaded-file.js","../src/http-server/client/uploaded-file/uploaded-file.js","../src/http-server/worker-handler/channel-subscriber-dispatch.js","../src/http-server/worker-handler/in-process.js","../src/http-server/worker-handler/index.js","../src/http-server/worker-handler/worker-script.js","../src/http-server/worker-handler/worker-thread.js","../src/jobs/mail-delivery.js","../src/logger/base-logger.js","../src/logger/console-logger.js","../src/logger/file-logger.js","../src/logger/outputs/array-output.js","../src/logger/outputs/console-output.js","../src/logger/outputs/file-output.js","../src/logger/outputs/stdout-output.js","../src/mailer/base.js","../src/mailer/delivery.js","../src/mailer/index.js","../src/mailer/backends/smtp.js","../src/plugins/sqljs-wasm-route-controller.js","../src/plugins/sqljs-wasm-route.js","../src/routes/app-routes.js","../src/routes/base-route.js","../src/routes/basic-route.js","../src/routes/get-route.js","../src/routes/index.js","../src/routes/namespace-route.js","../src/routes/plugin-routes.js","../src/routes/post-route.js","../src/routes/resolver.js","../src/routes/resource-route.js","../src/routes/root-route.js","../src/routes/built-in/debug/controller.js","../src/routes/built-in/errors/controller.js","../src/routes/hooks/frontend-model-command-route-hook.js","../src/testing/base-expect.js","../src/testing/browser-frontend-model-event-hook-scenarios.js","../src/testing/browser-test-app.js","../src/testing/expect-to-change.js","../src/testing/expect-utils.js","../src/testing/expect.js","../src/testing/request-client.js","../src/testing/test-files-finder.js","../src/testing/test-filter-parser.js","../src/testing/test-runner.js","../src/testing/test-suite-splitter.js","../src/testing/test.js","../src/types/external-modules.d.ts","../src/utils/backtrace-cleaner-node.js","../src/utils/backtrace-cleaner.js","../src/utils/ensure-error.js","../src/utils/event-emitter.js","../src/utils/file-exists.js","../src/utils/format-value.js","../src/utils/model-scope.js","../src/utils/nest-callbacks.js","../src/utils/plain-object.js","../src/utils/ransack.js","../src/utils/rest-args-error.js","../src/utils/singularize-model-name.js","../src/utils/split-sql-statements.js","../src/utils/to-import-specifier.js","../src/utils/with-tracked-stack-async-hooks.js","../src/utils/with-tracked-stack.js"],"version":"6.0.3"}
1
+ {"root":["../index.js","../bin/velocious.js","../src/application.js","../src/configuration-resolver.js","../src/configuration-types.js","../src/configuration.js","../src/controller.js","../src/current-configuration.js","../src/current.js","../src/error-logger.js","../src/frontend-model-controller.js","../src/initializer.js","../src/logger.js","../src/mailer.js","../src/record-payload-values.js","../src/velocious-error.js","../src/authorization/ability.js","../src/authorization/base-resource.js","../src/background-jobs/client.js","../src/background-jobs/cron-expression.js","../src/background-jobs/forked-runner-child.js","../src/background-jobs/job-record.js","../src/background-jobs/job-registry.js","../src/background-jobs/job-runner.js","../src/background-jobs/job.js","../src/background-jobs/json-socket.js","../src/background-jobs/main.js","../src/background-jobs/normalize-error.js","../src/background-jobs/scheduler.js","../src/background-jobs/socket-request.js","../src/background-jobs/status-reporter.js","../src/background-jobs/store.js","../src/background-jobs/types.js","../src/background-jobs/worker.js","../src/background-jobs/web/authorization.js","../src/background-jobs/web/controller.js","../src/background-jobs/web/index.js","../src/background-jobs/web/path-matcher.js","../src/background-jobs/web/registry.js","../src/beacon/client.js","../src/beacon/in-process-broker.js","../src/beacon/in-process-client.js","../src/beacon/server.js","../src/beacon/types.js","../src/cli/base-command.js","../src/cli/browser-cli.js","../src/cli/index.js","../src/cli/tenant-database-command-helper.js","../src/cli/use-browser-cli.js","../src/cli/commands/background-jobs-main.js","../src/cli/commands/background-jobs-runner.js","../src/cli/commands/background-jobs-worker.js","../src/cli/commands/beacon.js","../src/cli/commands/console.js","../src/cli/commands/init.js","../src/cli/commands/routes.js","../src/cli/commands/run-script.js","../src/cli/commands/runner.js","../src/cli/commands/server.js","../src/cli/commands/test.js","../src/cli/commands/db/base-command.js","../src/cli/commands/db/create.js","../src/cli/commands/db/drop.js","../src/cli/commands/db/migrate.js","../src/cli/commands/db/reset.js","../src/cli/commands/db/rollback.js","../src/cli/commands/db/seed.js","../src/cli/commands/db/schema/dump.js","../src/cli/commands/db/schema/load.js","../src/cli/commands/db/tenants/check.js","../src/cli/commands/db/tenants/create.js","../src/cli/commands/db/tenants/migrate.js","../src/cli/commands/destroy/migration.js","../src/cli/commands/generate/base-models.js","../src/cli/commands/generate/frontend-models.js","../src/cli/commands/generate/migration.js","../src/cli/commands/generate/model.js","../src/database/annotations-async-hooks.js","../src/database/annotations.js","../src/database/handler.js","../src/database/initializer-from-require-context.js","../src/database/migrator.js","../src/database/use-database.js","../src/database/drivers/base-column.js","../src/database/drivers/base-columns-index.js","../src/database/drivers/base-foreign-key.js","../src/database/drivers/base-table.js","../src/database/drivers/base.js","../src/database/drivers/mssql/column.js","../src/database/drivers/mssql/columns-index.js","../src/database/drivers/mssql/connect-connection.js","../src/database/drivers/mssql/foreign-key.js","../src/database/drivers/mssql/index.js","../src/database/drivers/mssql/options.js","../src/database/drivers/mssql/query-parser.js","../src/database/drivers/mssql/structure-sql.js","../src/database/drivers/mssql/table.js","../src/database/drivers/mssql/sql/alter-table.js","../src/database/drivers/mssql/sql/create-database.js","../src/database/drivers/mssql/sql/create-index.js","../src/database/drivers/mssql/sql/create-table.js","../src/database/drivers/mssql/sql/delete.js","../src/database/drivers/mssql/sql/drop-database.js","../src/database/drivers/mssql/sql/drop-table.js","../src/database/drivers/mssql/sql/insert.js","../src/database/drivers/mssql/sql/update.js","../src/database/drivers/mssql/sql/upsert.js","../src/database/drivers/mysql/column.js","../src/database/drivers/mysql/columns-index.js","../src/database/drivers/mysql/foreign-key.js","../src/database/drivers/mysql/index.js","../src/database/drivers/mysql/options.js","../src/database/drivers/mysql/query-parser.js","../src/database/drivers/mysql/query.js","../src/database/drivers/mysql/structure-sql.js","../src/database/drivers/mysql/table.js","../src/database/drivers/mysql/sql/alter-table.js","../src/database/drivers/mysql/sql/create-database.js","../src/database/drivers/mysql/sql/create-index.js","../src/database/drivers/mysql/sql/create-table.js","../src/database/drivers/mysql/sql/delete.js","../src/database/drivers/mysql/sql/drop-database.js","../src/database/drivers/mysql/sql/drop-table.js","../src/database/drivers/mysql/sql/insert.js","../src/database/drivers/mysql/sql/update.js","../src/database/drivers/mysql/sql/upsert.js","../src/database/drivers/pgsql/column.js","../src/database/drivers/pgsql/columns-index.js","../src/database/drivers/pgsql/foreign-key.js","../src/database/drivers/pgsql/index.js","../src/database/drivers/pgsql/options.js","../src/database/drivers/pgsql/query-parser.js","../src/database/drivers/pgsql/structure-sql.js","../src/database/drivers/pgsql/table.js","../src/database/drivers/pgsql/sql/alter-table.js","../src/database/drivers/pgsql/sql/create-database.js","../src/database/drivers/pgsql/sql/create-index.js","../src/database/drivers/pgsql/sql/create-table.js","../src/database/drivers/pgsql/sql/delete.js","../src/database/drivers/pgsql/sql/drop-database.js","../src/database/drivers/pgsql/sql/drop-table.js","../src/database/drivers/pgsql/sql/insert.js","../src/database/drivers/pgsql/sql/update.js","../src/database/drivers/pgsql/sql/upsert.js","../src/database/drivers/sqlite/base.js","../src/database/drivers/sqlite/column.js","../src/database/drivers/sqlite/columns-index.js","../src/database/drivers/sqlite/connection-sql-js.js","../src/database/drivers/sqlite/foreign-key.js","../src/database/drivers/sqlite/index.js","../src/database/drivers/sqlite/index.native.js","../src/database/drivers/sqlite/index.web.js","../src/database/drivers/sqlite/options.js","../src/database/drivers/sqlite/query-parser.js","../src/database/drivers/sqlite/query.js","../src/database/drivers/sqlite/query.native.js","../src/database/drivers/sqlite/query.web.js","../src/database/drivers/sqlite/structure-sql.js","../src/database/drivers/sqlite/table-rebuilder.js","../src/database/drivers/sqlite/table.js","../src/database/drivers/sqlite/sql/alter-table.js","../src/database/drivers/sqlite/sql/create-index.js","../src/database/drivers/sqlite/sql/create-table.js","../src/database/drivers/sqlite/sql/delete.js","../src/database/drivers/sqlite/sql/drop-table.js","../src/database/drivers/sqlite/sql/insert.js","../src/database/drivers/sqlite/sql/update.js","../src/database/drivers/sqlite/sql/upsert.js","../src/database/drivers/structure-sql/utils.js","../src/database/migration/index.js","../src/database/migrator/files-finder.js","../src/database/migrator/types.js","../src/database/pool/async-tracked-multi-connection.js","../src/database/pool/base-methods-forward.js","../src/database/pool/base.js","../src/database/pool/single-multi-use.js","../src/database/query/alter-table-base.js","../src/database/query/base.js","../src/database/query/create-database-base.js","../src/database/query/create-index-base.js","../src/database/query/create-table-base.js","../src/database/query/delete-base.js","../src/database/query/drop-database-base.js","../src/database/query/drop-table-base.js","../src/database/query/from-base.js","../src/database/query/from-plain.js","../src/database/query/from-table.js","../src/database/query/index.js","../src/database/query/insert-base.js","../src/database/query/join-base.js","../src/database/query/join-object.js","../src/database/query/join-plain.js","../src/database/query/join-tracker.js","../src/database/query/model-class-query.js","../src/database/query/order-base.js","../src/database/query/order-column.js","../src/database/query/order-plain.js","../src/database/query/preloader.js","../src/database/query/query-data.js","../src/database/query/select-base.js","../src/database/query/select-plain.js","../src/database/query/select-table-and-column.js","../src/database/query/update-base.js","../src/database/query/upsert-base.js","../src/database/query/where-base.js","../src/database/query/where-combinator.js","../src/database/query/where-hash.js","../src/database/query/where-model-class-hash.js","../src/database/query/where-not.js","../src/database/query/where-plain.js","../src/database/query/with-count.js","../src/database/query/preloader/belongs-to.js","../src/database/query/preloader/ensure-model-class-initialized.js","../src/database/query/preloader/has-many.js","../src/database/query/preloader/has-one.js","../src/database/query/preloader/selection.js","../src/database/query-parser/base-query-parser.js","../src/database/query-parser/from-parser.js","../src/database/query-parser/group-parser.js","../src/database/query-parser/joins-parser.js","../src/database/query-parser/limit-parser.js","../src/database/query-parser/options.js","../src/database/query-parser/order-parser.js","../src/database/query-parser/select-parser.js","../src/database/query-parser/where-parser.js","../src/database/record/acts-as-list.js","../src/database/record/index.js","../src/database/record/record-not-found-error.js","../src/database/record/state-machine.js","../src/database/record/user-module.js","../src/database/record/attachments/download.js","../src/database/record/attachments/handle.js","../src/database/record/attachments/normalize-input.js","../src/database/record/attachments/store.js","../src/database/record/attachments/storage-drivers/filesystem.js","../src/database/record/attachments/storage-drivers/native.js","../src/database/record/attachments/storage-drivers/s3.js","../src/database/record/instance-relationships/base.js","../src/database/record/instance-relationships/belongs-to.js","../src/database/record/instance-relationships/has-many.js","../src/database/record/instance-relationships/has-one.js","../src/database/record/relationships/base.js","../src/database/record/relationships/belongs-to.js","../src/database/record/relationships/has-many.js","../src/database/record/relationships/has-one.js","../src/database/record/validators/base.js","../src/database/record/validators/format.js","../src/database/record/validators/presence.js","../src/database/record/validators/uniqueness.js","../src/database/table-data/index.js","../src/database/table-data/table-column.js","../src/database/table-data/table-foreign-key.js","../src/database/table-data/table-index.js","../src/database/table-data/table-reference.js","../src/environment-handlers/base.js","../src/environment-handlers/browser.js","../src/environment-handlers/node.js","../src/environment-handlers/node/cli/commands/background-jobs-main.js","../src/environment-handlers/node/cli/commands/background-jobs-runner.js","../src/environment-handlers/node/cli/commands/background-jobs-worker.js","../src/environment-handlers/node/cli/commands/beacon.js","../src/environment-handlers/node/cli/commands/cli-command-context.js","../src/environment-handlers/node/cli/commands/console.js","../src/environment-handlers/node/cli/commands/init.js","../src/environment-handlers/node/cli/commands/routes.js","../src/environment-handlers/node/cli/commands/run-script.js","../src/environment-handlers/node/cli/commands/runner.js","../src/environment-handlers/node/cli/commands/server.js","../src/environment-handlers/node/cli/commands/test.js","../src/environment-handlers/node/cli/commands/db/seed.js","../src/environment-handlers/node/cli/commands/db/schema/dump.js","../src/environment-handlers/node/cli/commands/db/schema/load.js","../src/environment-handlers/node/cli/commands/destroy/migration.js","../src/environment-handlers/node/cli/commands/generate/base-models.js","../src/environment-handlers/node/cli/commands/generate/frontend-models.js","../src/environment-handlers/node/cli/commands/generate/migration.js","../src/environment-handlers/node/cli/commands/generate/model.js","../src/frontend-model-resource/base-resource.js","../src/frontend-models/base.js","../src/frontend-models/clear-pending-debounced-callback.js","../src/frontend-models/event-hook-models.js","../src/frontend-models/model-registry.js","../src/frontend-models/outgoing-event-buffer.js","../src/frontend-models/preloader.js","../src/frontend-models/query.js","../src/frontend-models/resource-config-validation.js","../src/frontend-models/resource-definition.js","../src/frontend-models/transport-serialization.js","../src/frontend-models/use-created-event.js","../src/frontend-models/use-destroyed-event.js","../src/frontend-models/use-model-class-event.js","../src/frontend-models/use-updated-event.js","../src/frontend-models/websocket-channel.js","../src/frontend-models/websocket-publishers.js","../src/http-client/header.js","../src/http-client/index.js","../src/http-client/request.js","../src/http-client/response.js","../src/http-client/websocket-client.js","../src/http-server/cookie.js","../src/http-server/development-reloader.js","../src/http-server/index.js","../src/http-server/remote-address.js","../src/http-server/server-client.js","../src/http-server/server-lock.js","../src/http-server/websocket-channel-subscribers.js","../src/http-server/websocket-channel.js","../src/http-server/websocket-connection.js","../src/http-server/websocket-event-log-store.js","../src/http-server/websocket-events-host.js","../src/http-server/websocket-events.js","../src/http-server/client/index.js","../src/http-server/client/params-to-object.js","../src/http-server/client/request-parser.js","../src/http-server/client/request-runner.js","../src/http-server/client/request-timing.js","../src/http-server/client/request.js","../src/http-server/client/response.js","../src/http-server/client/websocket-request.js","../src/http-server/client/websocket-session.js","../src/http-server/client/request-buffer/form-data-part.js","../src/http-server/client/request-buffer/header.js","../src/http-server/client/request-buffer/index.js","../src/http-server/client/uploaded-file/memory-uploaded-file.js","../src/http-server/client/uploaded-file/temporary-uploaded-file.js","../src/http-server/client/uploaded-file/uploaded-file.js","../src/http-server/worker-handler/channel-subscriber-dispatch.js","../src/http-server/worker-handler/in-process.js","../src/http-server/worker-handler/index.js","../src/http-server/worker-handler/worker-script.js","../src/http-server/worker-handler/worker-thread.js","../src/jobs/mail-delivery.js","../src/logger/base-logger.js","../src/logger/console-logger.js","../src/logger/file-logger.js","../src/logger/outputs/array-output.js","../src/logger/outputs/console-output.js","../src/logger/outputs/file-output.js","../src/logger/outputs/stdout-output.js","../src/mailer/base.js","../src/mailer/delivery.js","../src/mailer/index.js","../src/mailer/backends/smtp.js","../src/plugins/sqljs-wasm-route-controller.js","../src/plugins/sqljs-wasm-route.js","../src/routes/app-routes.js","../src/routes/base-route.js","../src/routes/basic-route.js","../src/routes/get-route.js","../src/routes/index.js","../src/routes/namespace-route.js","../src/routes/plugin-routes.js","../src/routes/post-route.js","../src/routes/resolver.js","../src/routes/resource-route.js","../src/routes/root-route.js","../src/routes/built-in/debug/controller.js","../src/routes/built-in/errors/controller.js","../src/routes/hooks/frontend-model-command-route-hook.js","../src/testing/base-expect.js","../src/testing/browser-frontend-model-event-hook-scenarios.js","../src/testing/browser-test-app.js","../src/testing/expect-to-change.js","../src/testing/expect-utils.js","../src/testing/expect.js","../src/testing/request-client.js","../src/testing/test-files-finder.js","../src/testing/test-filter-parser.js","../src/testing/test-runner.js","../src/testing/test-suite-splitter.js","../src/testing/test.js","../src/types/external-modules.d.ts","../src/utils/backtrace-cleaner-node.js","../src/utils/backtrace-cleaner.js","../src/utils/deburr-column-name.js","../src/utils/ensure-error.js","../src/utils/event-emitter.js","../src/utils/file-exists.js","../src/utils/format-value.js","../src/utils/model-scope.js","../src/utils/nest-callbacks.js","../src/utils/plain-object.js","../src/utils/ransack.js","../src/utils/rest-args-error.js","../src/utils/singularize-model-name.js","../src/utils/split-sql-statements.js","../src/utils/to-import-specifier.js","../src/utils/with-tracked-stack-async-hooks.js","../src/utils/with-tracked-stack.js"],"version":"6.0.3"}
@@ -0,0 +1,37 @@
1
+ // @ts-check
2
+
3
+ /** @type {Array<[RegExp, string]>} */
4
+ const UMLAUT_REPLACEMENTS = [
5
+ [/Ä/g, "Ae"],
6
+ [/Ö/g, "Oe"],
7
+ [/Ü/g, "Ue"],
8
+ [/ä/g, "ae"],
9
+ [/ö/g, "oe"],
10
+ [/ü/g, "ue"],
11
+ [/ß/g, "ss"]
12
+ ]
13
+
14
+ /**
15
+ * Transliterates German umlauts (and ß) in a database column name to ASCII so generated and runtime
16
+ * attribute names stay ASCII regardless of whether the column uses the umlaut ("Plätze") or already
17
+ * transliterated ("Plaetze") spelling. Both then map to the same attribute (e.g. "plaetzeVerkauft"),
18
+ * which keeps generated model bases consistent with code that references the ASCII attribute names.
19
+ * The raw column name is still used for the actual SQL, so the underlying column is untouched.
20
+ * @param {string} columnName - Raw database column name.
21
+ * @returns {string} - ASCII-transliterated column name.
22
+ */
23
+ export default function deburrColumnName(columnName) {
24
+ let result = columnName
25
+
26
+ for (const [pattern, replacement] of UMLAUT_REPLACEMENTS) {
27
+ result = result.replace(pattern, replacement)
28
+ }
29
+
30
+ // An all-caps acronym column (e.g. "IP", "EA") would camelize to "iP"/"eA" because only the first
31
+ // letter is lowercased. Down-case columns that contain no lowercase letters so "IP" becomes "ip".
32
+ if (!/[a-z]/.test(result)) {
33
+ result = result.toLowerCase()
34
+ }
35
+
36
+ return result
37
+ }
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "velocious": "build/bin/velocious.js"
4
4
  },
5
5
  "name": "velocious",
6
- "version": "1.0.437",
6
+ "version": "1.0.439",
7
7
  "main": "build/index.js",
8
8
  "types": "build/index.d.ts",
9
9
  "files": [
@@ -143,6 +143,8 @@ export default class VelociousConfiguration {
143
143
  this._scheduledBackgroundJobs = scheduledBackgroundJobs
144
144
  this._attachments = attachments || {}
145
145
  this._backendProjects = backendProjects || []
146
+ /** @type {Array<(args: {context: ?, error: Error, request: ?}) => Promise<Record<string, ?> | void> | Record<string, ?> | void>} */
147
+ this._clientErrorPayloadReporters = []
146
148
  this.cors = cors
147
149
  this._cookieSecret = cookieSecret
148
150
  this.database = database
@@ -2382,6 +2384,35 @@ export default class VelociousConfiguration {
2382
2384
  return this._errorEvents
2383
2385
  }
2384
2386
 
2387
+ /**
2388
+ * Registers a reporter that can add client-safe metadata to frontend-model error payloads.
2389
+ * @param {(args: {context: ?, error: Error, request: ?}) => Promise<Record<string, ?> | void> | Record<string, ?> | void} reporter - Reporter callback.
2390
+ * @returns {void}
2391
+ */
2392
+ addClientErrorPayloadReporter(reporter) {
2393
+ this._clientErrorPayloadReporters.push(reporter)
2394
+ }
2395
+
2396
+ /**
2397
+ * Runs registered client error payload reporters.
2398
+ * @param {{context: ?, error: Error, request: ?}} args - Reporter args.
2399
+ * @returns {Promise<Record<string, ?>>} - Merged client-safe reporter payload.
2400
+ */
2401
+ async clientErrorPayloadForError(args) {
2402
+ /** @type {Record<string, ?>} */
2403
+ const payload = {}
2404
+
2405
+ for (const reporter of this._clientErrorPayloadReporters) {
2406
+ const reporterPayload = await reporter(args)
2407
+
2408
+ if (reporterPayload && typeof reporterPayload === "object") {
2409
+ Object.assign(payload, reporterPayload)
2410
+ }
2411
+ }
2412
+
2413
+ return payload
2414
+ }
2415
+
2385
2416
  /**
2386
2417
  * Runs with connections.
2387
2418
  * @template T
@@ -4,6 +4,34 @@ import ensureModelClassInitialized from "./ensure-model-class-initialized.js"
4
4
  import PreloaderSelection from "./selection.js"
5
5
  import restArgsError from "../../../utils/rest-args-error.js"
6
6
 
7
+ /**
8
+ * Resolves the target column that references the through model.
9
+ * @param {import("../../record/relationships/has-many.js").default} relationship - Has-many through relationship.
10
+ * @param {typeof import("../../record/index.js").default} throughModelClass - Model used by the through relationship.
11
+ * @param {typeof import("../../record/index.js").default} targetModelClass - Model loaded by the through relationship.
12
+ * @returns {string} Target model foreign key column.
13
+ */
14
+ export function hasManyThroughTargetForeignKey(relationship, throughModelClass, targetModelClass) {
15
+ // An explicit foreign key on the has-many names the exact target column that references the
16
+ // through model — honor it. The target can have several belongs-to pointing at the through model
17
+ // (e.g. a default plus an alternate), so picking the first match would otherwise be ambiguous.
18
+ const explicitForeignKey = relationship.getExplicitForeignKey()
19
+
20
+ if (explicitForeignKey) return explicitForeignKey
21
+
22
+ for (const targetRelationship of targetModelClass.getRelationships()) {
23
+ if (targetRelationship.getType() != "belongsTo") continue
24
+
25
+ const relationshipTargetModelClass = targetRelationship.getTargetModelClass()
26
+
27
+ if (relationshipTargetModelClass === throughModelClass) {
28
+ return targetRelationship.getForeignKey()
29
+ }
30
+ }
31
+
32
+ return relationship.getForeignKey()
33
+ }
34
+
7
35
  export default class VelociousDatabaseQueryPreloaderHasMany {
8
36
  /**
9
37
  * Runs constructor.
@@ -90,7 +118,7 @@ export default class VelociousDatabaseQueryPreloaderHasMany {
90
118
 
91
119
  if (!targetModelClass) throw new Error("No target model class could be gotten from relationship")
92
120
 
93
- const targetForeignKey = this.relationship.getForeignKey()
121
+ const targetForeignKey = hasManyThroughTargetForeignKey(this.relationship, throughModelClass, targetModelClass)
94
122
  const {modelsToLoad, satisfiedTargets} = this._partition(targetModelClass, [targetForeignKey])
95
123
 
96
124
  if (modelsToLoad.length == 0) return satisfiedTargets
@@ -30,6 +30,7 @@ import HasOneInstanceRelationship from "./instance-relationships/has-one.js"
30
30
  import HasOneRelationship from "./relationships/has-one.js"
31
31
  import RecordAttachmentHandle from "./attachments/handle.js"
32
32
  import * as inflection from "inflection"
33
+ import deburrColumnName from "../../utils/deburr-column-name.js"
33
34
  import ModelClassQuery from "../query/model-class-query.js"
34
35
  import Preloader from "../query/preloader.js"
35
36
  import {readPayloadAssociationCount, readPayloadComputedAbility, readPayloadQueryData, setPayloadAssociationCount, setPayloadComputedAbility, setPayloadQueryData} from "../../record-payload-values.js"
@@ -1350,8 +1351,9 @@ class VelociousDatabaseRecord {
1350
1351
  for (const column of this._columns) {
1351
1352
  this._columnsAsHash[column.getName()] = column
1352
1353
 
1353
- const camelizedColumnName = inflection.camelize(column.getName(), true)
1354
- const camelizedColumnNameBigFirst = inflection.camelize(column.getName())
1354
+ const deburredColumnName = deburrColumnName(column.getName())
1355
+ const camelizedColumnName = inflection.camelize(deburredColumnName, true)
1356
+ const camelizedColumnNameBigFirst = inflection.camelize(deburredColumnName)
1355
1357
 
1356
1358
  attributeNameToColumnName[camelizedColumnName] = column.getName()
1357
1359
  columnNameToAttributeName[column.getName()] = camelizedColumnName
@@ -1,6 +1,7 @@
1
1
  // @ts-check
2
2
 
3
3
  import BaseInstanceRelationship from "./base.js"
4
+ import {hasManyThroughTargetForeignKey} from "../../query/preloader/has-many.js"
4
5
 
5
6
  /**
6
7
  * A generic query over some model type.
@@ -192,7 +193,11 @@ export default class VelociousDatabaseRecordHasManyInstanceRelationship extends
192
193
 
193
194
  const throughForeignKey = throughRelationship.getForeignKey()
194
195
  const throughPrimaryKey = throughRelationship.getPrimaryKey()
195
- const targetForeignKey = this.getForeignKey()
196
+ const targetForeignKey = hasManyThroughTargetForeignKey(
197
+ /** @type {import("../relationships/has-many.js").default} */ (this.getRelationship()),
198
+ throughModelClass,
199
+ TargetModelClass
200
+ )
196
201
  const targetTable = TargetModelClass.tableName()
197
202
  const throughTable = throughModelClass.tableName()
198
203
  const driver = TargetModelClass.connection()
@@ -46,6 +46,7 @@ export default class VelociousDatabaseRecordBaseRelationship {
46
46
  this._counterCache = counterCache || false
47
47
  this._dependent = dependent
48
48
  this.foreignKey = foreignKey
49
+ this._explicitForeignKey = foreignKey
49
50
  this._inverseOf = inverseOf
50
51
  this.klass = klass
51
52
  this.modelClass = modelClass
@@ -77,6 +78,14 @@ export default class VelociousDatabaseRecordBaseRelationship {
77
78
  */
78
79
  getDependent() { return this._dependent }
79
80
 
81
+ /**
82
+ * The foreign key explicitly passed when the relationship was declared, if any. Unlike
83
+ * `getForeignKey()` this never falls back to a computed default, so callers can tell whether the
84
+ * developer named a specific column (e.g. to disambiguate multiple belongs-to on a through target).
85
+ * @returns {string | undefined} - The explicitly declared foreign key, or undefined.
86
+ */
87
+ getExplicitForeignKey() { return this._explicitForeignKey }
88
+
80
89
  /**
81
90
  * Runs get foreign key.
82
91
  * @abstract
@@ -1,4 +1,5 @@
1
1
  import BaseCommand from "../../../../../cli/base-command.js"
2
+ import deburrColumnName from "../../../../../utils/deburr-column-name.js"
2
3
  import fileExists from "../../../../../utils/file-exists.js"
3
4
  import fs from "fs/promises"
4
5
  import * as inflection from "inflection"
@@ -141,8 +142,9 @@ export default class DbGenerateModel extends BaseCommand {
141
142
  let methodsCount = 0
142
143
 
143
144
  for (const column of columns) {
144
- const camelizedColumnName = inflection.camelize(column.getName(), true)
145
- const camelizedColumnNameBigFirst = inflection.camelize(column.getName())
145
+ const deburredColumnName = deburrColumnName(column.getName())
146
+ const camelizedColumnName = inflection.camelize(deburredColumnName, true)
147
+ const camelizedColumnNameBigFirst = inflection.camelize(deburredColumnName)
146
148
  const jsdocType = this.jsDocTypeFromColumn(column, modelClass)
147
149
 
148
150
  if (methodsCount > 0) {
@@ -2741,10 +2741,11 @@ export default class FrontendModelController extends Controller {
2741
2741
  /**
2742
2742
  * Runs frontend model client error payload for error.
2743
2743
  * @param {?} error - Caught error.
2744
- * @returns {Record<string, ?>} - Client payload for the current environment.
2744
+ * @returns {Promise<Record<string, ?>>} - Client payload for the current environment.
2745
2745
  */
2746
- frontendModelClientErrorPayloadForError(error) {
2746
+ async frontendModelClientErrorPayloadForError(error) {
2747
2747
  const velociousMetadata = frontendModelVelociousMetadataForError(error)
2748
+ const normalizedError = error instanceof Error ? error : new Error(String(error))
2748
2749
 
2749
2750
  let validationErrorsPayload = {}
2750
2751
 
@@ -2778,7 +2779,14 @@ export default class FrontendModelController extends Controller {
2778
2779
  error
2779
2780
  }),
2780
2781
  ...(velociousMetadata ? {velocious: velociousMetadata} : {}),
2781
- ...validationErrorsPayload
2782
+ ...validationErrorsPayload,
2783
+ ...(await this.getConfiguration().clientErrorPayloadForError({
2784
+ context: {
2785
+ controller: this.constructor.name
2786
+ },
2787
+ error: normalizedError,
2788
+ request: this.getRequest()
2789
+ }))
2782
2790
  }
2783
2791
  }
2784
2792
 
@@ -2845,7 +2853,7 @@ export default class FrontendModelController extends Controller {
2845
2853
  await this.render({
2846
2854
  json: /**
2847
2855
  * Types the following value.
2848
- @type {Record<string, ?>} */ (serializeFrontendModelTransportValue(this.frontendModelClientErrorPayloadForError(error)))
2856
+ @type {Record<string, ?>} */ (serializeFrontendModelTransportValue(await this.frontendModelClientErrorPayloadForError(error)))
2849
2857
  })
2850
2858
  }
2851
2859
  }
@@ -3122,7 +3130,7 @@ export default class FrontendModelController extends Controller {
3122
3130
 
3123
3131
  responses.push({
3124
3132
  requestId,
3125
- response: this.frontendModelClientErrorPayloadForError(error)
3133
+ response: await this.frontendModelClientErrorPayloadForError(error)
3126
3134
  })
3127
3135
  }
3128
3136
  }
@@ -3348,7 +3356,7 @@ export default class FrontendModelController extends Controller {
3348
3356
  await this.render({
3349
3357
  json: /**
3350
3358
  * Types the following value.
3351
- @type {Record<string, ?>} */ (serializeFrontendModelTransportValue(this.frontendModelClientErrorPayloadForError(error)))
3359
+ @type {Record<string, ?>} */ (serializeFrontendModelTransportValue(await this.frontendModelClientErrorPayloadForError(error)))
3352
3360
  })
3353
3361
  }
3354
3362
  }
@@ -0,0 +1,37 @@
1
+ // @ts-check
2
+
3
+ /** @type {Array<[RegExp, string]>} */
4
+ const UMLAUT_REPLACEMENTS = [
5
+ [/Ä/g, "Ae"],
6
+ [/Ö/g, "Oe"],
7
+ [/Ü/g, "Ue"],
8
+ [/ä/g, "ae"],
9
+ [/ö/g, "oe"],
10
+ [/ü/g, "ue"],
11
+ [/ß/g, "ss"]
12
+ ]
13
+
14
+ /**
15
+ * Transliterates German umlauts (and ß) in a database column name to ASCII so generated and runtime
16
+ * attribute names stay ASCII regardless of whether the column uses the umlaut ("Plätze") or already
17
+ * transliterated ("Plaetze") spelling. Both then map to the same attribute (e.g. "plaetzeVerkauft"),
18
+ * which keeps generated model bases consistent with code that references the ASCII attribute names.
19
+ * The raw column name is still used for the actual SQL, so the underlying column is untouched.
20
+ * @param {string} columnName - Raw database column name.
21
+ * @returns {string} - ASCII-transliterated column name.
22
+ */
23
+ export default function deburrColumnName(columnName) {
24
+ let result = columnName
25
+
26
+ for (const [pattern, replacement] of UMLAUT_REPLACEMENTS) {
27
+ result = result.replace(pattern, replacement)
28
+ }
29
+
30
+ // An all-caps acronym column (e.g. "IP", "EA") would camelize to "iP"/"eA" because only the first
31
+ // letter is lowercased. Down-case columns that contain no lowercase letters so "IP" becomes "ip".
32
+ if (!/[a-z]/.test(result)) {
33
+ result = result.toLowerCase()
34
+ }
35
+
36
+ return result
37
+ }