velocious 1.0.453 → 1.0.455
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/README.md +17 -3
- package/build/authorization/ability.js +3 -6
- package/build/authorization/base-resource.js +7 -9
- package/build/configuration-types.js +4 -3
- package/build/configuration.js +12 -17
- package/build/database/drivers/base.js +11 -3
- package/build/database/drivers/mssql/index.js +0 -6
- package/build/database/drivers/mysql/index.js +0 -6
- package/build/database/drivers/pgsql/index.js +0 -1
- package/build/database/drivers/sqlite/base.js +0 -6
- package/build/database/migration/index.js +6 -6
- package/build/database/pool/base.js +2 -1
- package/build/database/query/preloader/ensure-model-class-initialized.js +1 -6
- package/build/database/record/attachments/handle.js +32 -0
- package/build/database/table-data/index.js +2 -1
- package/build/database/table-data/table-column.js +7 -1
- package/build/environment-handlers/node/cli/commands/generate/frontend-models.js +25 -26
- package/build/frontend-model-controller.js +167 -88
- package/build/frontend-model-resource/base-resource.js +133 -31
- package/build/frontend-models/base.js +30 -6
- package/build/frontend-models/model-registry.js +1 -1
- package/build/frontend-models/query.js +3 -9
- package/build/frontend-models/resource-definition.js +1 -0
- package/build/frontend-models/transport-serialization.js +2 -3
- package/build/frontend-models/websocket-channel.js +7 -12
- package/build/frontend-models/websocket-publishers.js +11 -67
- package/build/routes/hooks/frontend-model-command-route-hook.js +1 -1
- package/build/src/authorization/ability.d.ts.map +1 -1
- package/build/src/authorization/ability.js +4 -8
- package/build/src/authorization/base-resource.d.ts +2 -2
- package/build/src/authorization/base-resource.d.ts.map +1 -1
- package/build/src/authorization/base-resource.js +7 -9
- package/build/src/configuration-types.d.ts +11 -6
- package/build/src/configuration-types.d.ts.map +1 -1
- package/build/src/configuration-types.js +5 -4
- package/build/src/configuration.d.ts.map +1 -1
- package/build/src/configuration.js +13 -18
- package/build/src/database/drivers/base.d.ts +5 -0
- package/build/src/database/drivers/base.d.ts.map +1 -1
- package/build/src/database/drivers/base.js +11 -4
- package/build/src/database/drivers/mssql/index.d.ts +0 -5
- package/build/src/database/drivers/mssql/index.d.ts.map +1 -1
- package/build/src/database/drivers/mssql/index.js +1 -6
- package/build/src/database/drivers/mysql/index.d.ts +0 -5
- package/build/src/database/drivers/mysql/index.d.ts.map +1 -1
- package/build/src/database/drivers/mysql/index.js +1 -6
- package/build/src/database/drivers/pgsql/index.d.ts +0 -1
- package/build/src/database/drivers/pgsql/index.d.ts.map +1 -1
- package/build/src/database/drivers/pgsql/index.js +1 -2
- package/build/src/database/drivers/sqlite/base.d.ts +0 -5
- package/build/src/database/drivers/sqlite/base.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/base.js +1 -6
- package/build/src/database/migration/index.js +7 -7
- package/build/src/database/pool/base.d.ts +2 -1
- package/build/src/database/pool/base.d.ts.map +1 -1
- package/build/src/database/pool/base.js +3 -2
- package/build/src/database/query/preloader/ensure-model-class-initialized.d.ts.map +1 -1
- package/build/src/database/query/preloader/ensure-model-class-initialized.js +2 -6
- package/build/src/database/record/attachments/handle.d.ts +13 -0
- package/build/src/database/record/attachments/handle.d.ts.map +1 -1
- package/build/src/database/record/attachments/handle.js +29 -1
- package/build/src/database/table-data/index.d.ts +5 -0
- package/build/src/database/table-data/index.d.ts.map +1 -1
- package/build/src/database/table-data/index.js +3 -2
- package/build/src/database/table-data/table-column.d.ts.map +1 -1
- package/build/src/database/table-data/table-column.js +10 -2
- package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.d.ts +6 -0
- package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.d.ts.map +1 -1
- package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.js +25 -24
- package/build/src/frontend-model-controller.d.ts +70 -26
- package/build/src/frontend-model-controller.d.ts.map +1 -1
- package/build/src/frontend-model-controller.js +144 -87
- package/build/src/frontend-model-resource/base-resource.d.ts +192 -16
- package/build/src/frontend-model-resource/base-resource.d.ts.map +1 -1
- package/build/src/frontend-model-resource/base-resource.js +124 -33
- package/build/src/frontend-models/base.d.ts +14 -1
- package/build/src/frontend-models/base.d.ts.map +1 -1
- package/build/src/frontend-models/base.js +28 -7
- package/build/src/frontend-models/model-registry.js +2 -2
- package/build/src/frontend-models/query.d.ts.map +1 -1
- package/build/src/frontend-models/query.js +4 -10
- package/build/src/frontend-models/resource-definition.d.ts.map +1 -1
- package/build/src/frontend-models/resource-definition.js +2 -1
- package/build/src/frontend-models/transport-serialization.d.ts +2 -4
- package/build/src/frontend-models/transport-serialization.d.ts.map +1 -1
- package/build/src/frontend-models/transport-serialization.js +3 -4
- package/build/src/frontend-models/websocket-channel.d.ts.map +1 -1
- package/build/src/frontend-models/websocket-channel.js +8 -13
- package/build/src/frontend-models/websocket-publishers.d.ts.map +1 -1
- package/build/src/frontend-models/websocket-publishers.js +12 -60
- package/build/src/routes/hooks/frontend-model-command-route-hook.js +2 -2
- package/package.json +1 -1
- package/scripts/test-browser.js +2 -2
- package/src/authorization/ability.js +3 -6
- package/src/authorization/base-resource.js +7 -9
- package/src/configuration-types.js +4 -3
- package/src/configuration.js +12 -17
- package/src/database/drivers/base.js +11 -3
- package/src/database/drivers/mssql/index.js +0 -6
- package/src/database/drivers/mysql/index.js +0 -6
- package/src/database/drivers/pgsql/index.js +0 -1
- package/src/database/drivers/sqlite/base.js +0 -6
- package/src/database/migration/index.js +6 -6
- package/src/database/pool/base.js +2 -1
- package/src/database/query/preloader/ensure-model-class-initialized.js +1 -6
- package/src/database/record/attachments/handle.js +32 -0
- package/src/database/table-data/index.js +2 -1
- package/src/database/table-data/table-column.js +7 -1
- package/src/environment-handlers/node/cli/commands/generate/frontend-models.js +25 -26
- package/src/frontend-model-controller.js +167 -88
- package/src/frontend-model-resource/base-resource.js +133 -31
- package/src/frontend-models/base.js +30 -6
- package/src/frontend-models/model-registry.js +1 -1
- package/src/frontend-models/query.js +3 -9
- package/src/frontend-models/resource-definition.js +1 -0
- package/src/frontend-models/transport-serialization.js +2 -3
- package/src/frontend-models/websocket-channel.js +7 -12
- package/src/frontend-models/websocket-publishers.js +11 -67
- package/src/routes/hooks/frontend-model-command-route-hook.js +1 -1
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Database models with migrations and validations
|
|
6
6
|
* Database models that work almost the same in frontend and backend
|
|
7
7
|
* Declarative state machines for models (see [docs/state-machine.md](docs/state-machine.md))
|
|
8
|
-
* Migrations for schema changes
|
|
8
|
+
* Migrations for schema changes (see [docs/database-migrations.md](docs/database-migrations.md))
|
|
9
9
|
* Controllers and views for HTTP endpoints
|
|
10
10
|
* Frontend-model transport for creating, updating, querying, and subscribing to query-filtered lifecycle events over HTTP/WebSocket, with structured per-attribute validation error responses (see [docs/frontend-models.md](docs/frontend-models.md))
|
|
11
11
|
* Expo / Metro compatibility guidance and a real Expo export check (see [docs/expo-metro-compatibility.md](docs/expo-metro-compatibility.md))
|
|
@@ -376,6 +376,7 @@ export default new Configuration({
|
|
|
376
376
|
```
|
|
377
377
|
|
|
378
378
|
`frontendModels` entries must be `FrontendModelBaseResource` subclasses. Built-in CRUD/find/index/serialize behavior lives in the base class, and app resources override only the pieces they actually need.
|
|
379
|
+
Resource-level index customization should prefer `indexQuery()` or the pagination/search/sort hooks over replacing `records()`, so built-in pluck and aggregate count support can keep using the same query. See [`docs/frontend-model-resources.md`](docs/frontend-model-resources.md) for the resource extension points.
|
|
379
380
|
|
|
380
381
|
Resources expose the full CRUD ability set (`create`, `destroy`, `read`, `update`) by default. To restrict the API surface — for example to a read-only resource — declare an explicit subset:
|
|
381
382
|
|
|
@@ -446,7 +447,7 @@ Frontend-model `group(...)` is attribute/path based and does not accept raw SQL
|
|
|
446
447
|
Frontend-model `where(...)` supports nested relationship descriptors (for example `Task.where({project: {creatingUser: {reference: "owner-b"}}})`) and does not accept raw SQL fragments.
|
|
447
448
|
Frontend-model `joins(...)` supports relationship-object descriptors only (for example `Task.joins({project: {creatingUser: true}})`) and rejects raw SQL join strings.
|
|
448
449
|
Frontend-model `distinct(...)` only accepts booleans (`true` by default) and is applied server-side through the backend query API.
|
|
449
|
-
Frontend-model `pluck(...)` validates attribute/path descriptors against configured model metadata and does not accept SQL fragments.
|
|
450
|
+
Frontend-model `pluck(...)` validates attribute/path descriptors against configured resource/model metadata and does not accept SQL fragments or hidden raw model columns when the resource declares an explicit attribute list.
|
|
450
451
|
Frontend-model query fields are limited to attributes exposed by the backend resource. Use `{name: "attributeName", selectedByDefault: false}` for fields that may be selected or filtered explicitly but should stay out of default payloads.
|
|
451
452
|
|
|
452
453
|
When backend payloads include `__preloadedRelationships`, nested frontend-model relationships are hydrated recursively. Relationship methods can use `getRelationshipByName("relationship").loaded()` and will throw when a relationship was not preloaded.
|
|
@@ -1006,7 +1007,20 @@ npx velocious g:migration create-tasks
|
|
|
1006
1007
|
```
|
|
1007
1008
|
|
|
1008
1009
|
## Write a migration
|
|
1009
|
-
Implicit `id` primary keys and `references(...)` columns use UUIDs by default.
|
|
1010
|
+
Implicit `id` primary keys and `references(...)` columns use UUIDs by default. Set `primaryKeyType` on a database config to change the implicit type for that database, or pass an explicit `id` / reference `type` for legacy schemas and external compatibility.
|
|
1011
|
+
|
|
1012
|
+
```js
|
|
1013
|
+
export default new Configuration({
|
|
1014
|
+
database: {
|
|
1015
|
+
production: {
|
|
1016
|
+
default: {
|
|
1017
|
+
type: "pgsql",
|
|
1018
|
+
primaryKeyType: "bigint"
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
})
|
|
1023
|
+
```
|
|
1010
1024
|
|
|
1011
1025
|
```js
|
|
1012
1026
|
import Migration from "velocious/build/src/database/migration/index.js"
|
|
@@ -72,7 +72,7 @@ export default class VelociousAuthorizationAbility {
|
|
|
72
72
|
_resolveResourcesFromConfiguration() {
|
|
73
73
|
const configuration = this.context?.configuration
|
|
74
74
|
|
|
75
|
-
if (!configuration
|
|
75
|
+
if (!configuration) {
|
|
76
76
|
return []
|
|
77
77
|
}
|
|
78
78
|
|
|
@@ -85,12 +85,10 @@ export default class VelociousAuthorizationAbility {
|
|
|
85
85
|
for (const backendProject of backendProjects) {
|
|
86
86
|
const frontendModels = backendProject.frontendModels
|
|
87
87
|
|
|
88
|
-
if (!frontendModels
|
|
88
|
+
if (!frontendModels) continue
|
|
89
89
|
|
|
90
90
|
for (const resourceDefinition of Object.values(frontendModels)) {
|
|
91
|
-
|
|
92
|
-
resolved.push(resourceDefinition)
|
|
93
|
-
}
|
|
91
|
+
resolved.push(resourceDefinition)
|
|
94
92
|
}
|
|
95
93
|
}
|
|
96
94
|
|
|
@@ -173,7 +171,6 @@ export default class VelociousAuthorizationAbility {
|
|
|
173
171
|
for (const ResourceClass of this.resources) {
|
|
174
172
|
const resourceModelClass = ResourceClass.modelClass()
|
|
175
173
|
|
|
176
|
-
if (!resourceModelClass) continue
|
|
177
174
|
if (resourceModelClass !== modelClass) continue
|
|
178
175
|
|
|
179
176
|
const resourceInstance = new ResourceClass({
|
|
@@ -22,9 +22,13 @@ export default class AuthorizationBaseResource {
|
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* Runs model class.
|
|
25
|
-
* @returns {typeof import("../database/record/index.js").default
|
|
25
|
+
* @returns {typeof import("../database/record/index.js").default} - Model class handled by this resource.
|
|
26
26
|
*/
|
|
27
27
|
static modelClass() {
|
|
28
|
+
if (!this.ModelClass) {
|
|
29
|
+
throw new Error(`${this.name} must define static ModelClass before calling ability helpers.`)
|
|
30
|
+
}
|
|
31
|
+
|
|
28
32
|
return this.ModelClass
|
|
29
33
|
}
|
|
30
34
|
|
|
@@ -69,15 +73,9 @@ export default class AuthorizationBaseResource {
|
|
|
69
73
|
* @returns {typeof import("../database/record/index.js").default} - Model class handled by this resource.
|
|
70
74
|
*/
|
|
71
75
|
requiredModelClass() {
|
|
72
|
-
const
|
|
73
|
-
* Narrows the runtime value to the documented type.
|
|
74
|
-
* @type {typeof AuthorizationBaseResource} */ (this.constructor).modelClass()
|
|
75
|
-
|
|
76
|
-
if (!modelClass) {
|
|
77
|
-
throw new Error(`${this.constructor.name} must define static ModelClass before calling ability helpers.`)
|
|
78
|
-
}
|
|
76
|
+
const ResourceClass = /** @type {typeof AuthorizationBaseResource} */ (this.constructor)
|
|
79
77
|
|
|
80
|
-
return modelClass
|
|
78
|
+
return ResourceClass.modelClass()
|
|
81
79
|
}
|
|
82
80
|
|
|
83
81
|
/**
|
|
@@ -71,6 +71,7 @@
|
|
|
71
71
|
* @property {boolean} [migrations] - Whether migrations are enabled for this database.
|
|
72
72
|
* @property {string} [password] - Password for the database user.
|
|
73
73
|
* @property {number} [port] - Database port.
|
|
74
|
+
* @property {string} [primaryKeyType] - Default type for implicit migration primary keys and references. Defaults to `uuid`.
|
|
74
75
|
* @property {DatabasePoolConfiguration} [pool] - Velocious database pool lifecycle configuration.
|
|
75
76
|
* @property {string} [name] - Friendly name for the configuration.
|
|
76
77
|
* @property {(file: string) => string} [locateFile] - Optional sqlite-web sql.js wasm resolver (`initSqlJs({locateFile})`).
|
|
@@ -215,7 +216,7 @@
|
|
|
215
216
|
* @typedef {object} ClientErrorPayloadContext
|
|
216
217
|
* @property {string} controller - Controller class name.
|
|
217
218
|
* @property {string} [action] - Controller action or endpoint label.
|
|
218
|
-
* @property {"index" | "find" | "create" | "update" | "destroy" | "attach" | "download" | "url" | "custom-command"} [commandType] - Frontend-model command type.
|
|
219
|
+
* @property {"index" | "find" | "create" | "update" | "destroy" | "attach" | "attachmentList" | "download" | "url" | "custom-command"} [commandType] - Frontend-model command type.
|
|
219
220
|
* @property {boolean} [expectedError] - Whether the error is an expected user-flow failure.
|
|
220
221
|
* @property {boolean} [frontendModelEndpoint] - Whether the error came from the frontend-model endpoint.
|
|
221
222
|
* @property {string} [model] - Frontend-model name from the failed request.
|
|
@@ -341,10 +342,10 @@
|
|
|
341
342
|
|
|
342
343
|
/**
|
|
343
344
|
* @typedef {object} FrontendModelResourceServerConfiguration
|
|
344
|
-
* @property {function({action: "index" | "find" | "create" | "update" | "destroy" | "attach" | "download" | "url", controller: import("./controller.js").default, params: Record<string, ?>, modelClass: typeof import("./database/record/index.js").default}) : (boolean | void | Promise<boolean | void>)} [beforeAction] - Optional callback run before built-in frontend actions.
|
|
345
|
+
* @property {function({action: "index" | "find" | "create" | "update" | "destroy" | "attach" | "attachmentList" | "download" | "url", controller: import("./controller.js").default, params: Record<string, ?>, modelClass: typeof import("./database/record/index.js").default}) : (boolean | void | Promise<boolean | void>)} [beforeAction] - Optional callback run before built-in frontend actions.
|
|
345
346
|
* @property {function({action: "index", controller: import("./controller.js").default, params: Record<string, ?>, modelClass: typeof import("./database/record/index.js").default}) : Promise<import("./database/record/index.js").default[]>} [records] - Records loader for frontendIndex.
|
|
346
347
|
* @property {function({action: "index" | "find" | "create" | "update", controller: import("./controller.js").default, params: Record<string, ?>, modelClass: typeof import("./database/record/index.js").default, model: import("./database/record/index.js").default}) : Record<string, ?> | Promise<Record<string, ?>>} [serialize] - Record serializer for response payloads.
|
|
347
|
-
* @property {function({action: "find" | "update" | "destroy" | "attach" | "download" | "url", controller: import("./controller.js").default, params: Record<string, ?>, modelClass: typeof import("./database/record/index.js").default, id: string | number}) : Promise<import("./database/record/index.js").default | null>} [find] - Record loader for find/update/destroy/attach/download/url actions.
|
|
348
|
+
* @property {function({action: "find" | "update" | "destroy" | "attach" | "attachmentList" | "download" | "url", controller: import("./controller.js").default, params: Record<string, ?>, modelClass: typeof import("./database/record/index.js").default, id: string | number}) : Promise<import("./database/record/index.js").default | null>} [find] - Record loader for find/update/destroy/attach/download/url actions.
|
|
348
349
|
* @property {function({action: "create", controller: import("./controller.js").default, params: Record<string, ?>, modelClass: typeof import("./database/record/index.js").default, attributes: Record<string, ?>}) : Promise<import("./database/record/index.js").default>} [create] - Custom create callback.
|
|
349
350
|
* @property {function({action: "update", controller: import("./controller.js").default, params: Record<string, ?>, modelClass: typeof import("./database/record/index.js").default, model: import("./database/record/index.js").default, attributes: Record<string, ?>}) : Promise<import("./database/record/index.js").default | void>} [update] - Custom update callback.
|
|
350
351
|
* @property {function({action: "destroy", controller: import("./controller.js").default, params: Record<string, ?>, modelClass: typeof import("./database/record/index.js").default, model: import("./database/record/index.js").default}) : Promise<void>} [destroy] - Custom destroy callback.
|
package/build/configuration.js
CHANGED
|
@@ -18,7 +18,7 @@ import Ability from "./authorization/ability.js"
|
|
|
18
18
|
import EventEmitter from "./utils/event-emitter.js"
|
|
19
19
|
import VelociousWebsocketChannelSubscribers from "./http-server/websocket-channel-subscribers.js"
|
|
20
20
|
import {CurrentConfigurationNotSetError, currentConfiguration, setCurrentConfiguration} from "./current-configuration.js"
|
|
21
|
-
import {frontendModelResourceConfigurationFromDefinition, frontendModelResourcesForBackendProject} from "./frontend-models/resource-definition.js"
|
|
21
|
+
import {frontendModelResourceClassFromDefinition, frontendModelResourceConfigurationFromDefinition, frontendModelResourcesForBackendProject} from "./frontend-models/resource-definition.js"
|
|
22
22
|
import PluginRoutes from "./routes/plugin-routes.js"
|
|
23
23
|
import restArgsError from "./utils/rest-args-error.js"
|
|
24
24
|
import {withTrackedStack} from "./utils/with-tracked-stack.js"
|
|
@@ -1673,12 +1673,13 @@ export default class VelociousConfiguration {
|
|
|
1673
1673
|
throw new Error(`Resource for ${modelName} defines relationships as an object. Use an array instead: static relationships = ${JSON.stringify(Object.keys(resourceConfig.relationships))}`)
|
|
1674
1674
|
}
|
|
1675
1675
|
|
|
1676
|
-
const
|
|
1677
|
-
* Types the following value.
|
|
1678
|
-
* @type {typeof import("./database/record/index.js").default | undefined} */ (this.modelClasses[modelName])
|
|
1676
|
+
const resourceClass = frontendModelResourceClassFromDefinition(resourceDefinition)
|
|
1679
1677
|
|
|
1680
|
-
if (!
|
|
1678
|
+
if (!resourceClass) {
|
|
1679
|
+
throw new Error(`Frontend model resource for ${modelName} must be a FrontendModelBaseResource subclass.`)
|
|
1680
|
+
}
|
|
1681
1681
|
|
|
1682
|
+
const modelClass = resourceClass.modelClass()
|
|
1682
1683
|
const existingRelationships = modelClass.getRelationshipsMap()
|
|
1683
1684
|
|
|
1684
1685
|
for (const relationshipName of resourceConfig.relationships) {
|
|
@@ -2567,27 +2568,21 @@ export default class VelociousConfiguration {
|
|
|
2567
2568
|
return
|
|
2568
2569
|
}
|
|
2569
2570
|
|
|
2571
|
+
/** @type {Set<typeof import("./database/pool/base.js").default>} */
|
|
2570
2572
|
const constructors = new Set()
|
|
2571
2573
|
|
|
2572
2574
|
this._closeDatabaseConnectionsPromise = (async () => {
|
|
2573
2575
|
for (const pool of Object.values(this.databasePools)) {
|
|
2574
2576
|
if (!pool) continue
|
|
2575
2577
|
|
|
2576
|
-
|
|
2577
|
-
await pool.closeAll()
|
|
2578
|
-
}
|
|
2579
|
-
|
|
2580
|
-
const poolConstructor = /**
|
|
2581
|
-
* Types the following value.
|
|
2582
|
-
* @type {{clearGlobalConnections?: (configuration: VelociousConfiguration) => void}} */ (pool.constructor)
|
|
2578
|
+
await pool.closeAll()
|
|
2583
2579
|
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
}
|
|
2580
|
+
const PoolClass = /** @type {typeof import("./database/pool/base.js").default} */ (pool.constructor)
|
|
2581
|
+
constructors.add(PoolClass)
|
|
2587
2582
|
}
|
|
2588
2583
|
|
|
2589
|
-
for (const
|
|
2590
|
-
|
|
2584
|
+
for (const PoolClass of constructors) {
|
|
2585
|
+
PoolClass.clearGlobalConnections(this)
|
|
2591
2586
|
}
|
|
2592
2587
|
|
|
2593
2588
|
// Allow models to be re-initialized after connections are closed.
|
|
@@ -406,6 +406,14 @@ export default class VelociousDatabaseDriversBase {
|
|
|
406
406
|
return this.idSeq
|
|
407
407
|
}
|
|
408
408
|
|
|
409
|
+
/**
|
|
410
|
+
* Runs primary key type.
|
|
411
|
+
* @returns {string} - Configured primary key type, defaulting to UUID.
|
|
412
|
+
*/
|
|
413
|
+
primaryKeyType() {
|
|
414
|
+
return this.getArgs().primaryKeyType || "uuid"
|
|
415
|
+
}
|
|
416
|
+
|
|
409
417
|
/**
|
|
410
418
|
* Clears cached schema metadata for this driver instance.
|
|
411
419
|
* @returns {void} - No return value.
|
|
@@ -1170,7 +1178,7 @@ export default class VelociousDatabaseDriversBase {
|
|
|
1170
1178
|
* @returns {boolean} - Whether query logging is enabled for this driver.
|
|
1171
1179
|
*/
|
|
1172
1180
|
_queryLoggingEnabled() {
|
|
1173
|
-
if (
|
|
1181
|
+
if (!this.configuration) return true
|
|
1174
1182
|
if (!this.configuration.getQueryLoggingEnabled()) return false
|
|
1175
1183
|
|
|
1176
1184
|
const logger = new Logger("SQL", {configuration: this.configuration})
|
|
@@ -1205,9 +1213,9 @@ export default class VelociousDatabaseDriversBase {
|
|
|
1205
1213
|
_querySourceLine(sourceStack) {
|
|
1206
1214
|
if (!sourceStack) return undefined
|
|
1207
1215
|
|
|
1208
|
-
const applicationDirectory =
|
|
1216
|
+
const applicationDirectory = this.configuration
|
|
1209
1217
|
? this.configuration.getDirectoryIfAvailable()
|
|
1210
|
-
:
|
|
1218
|
+
: undefined
|
|
1211
1219
|
|
|
1212
1220
|
if (!applicationDirectory) return undefined
|
|
1213
1221
|
|
|
@@ -199,12 +199,6 @@ export default class VelociousDatabaseDriversMssql extends Base{
|
|
|
199
199
|
*/
|
|
200
200
|
getType() { return "mssql" }
|
|
201
201
|
|
|
202
|
-
/**
|
|
203
|
-
* Runs primary key type.
|
|
204
|
-
* @returns {string} - The primary key type.
|
|
205
|
-
*/
|
|
206
|
-
primaryKeyType() { return "uuid" }
|
|
207
|
-
|
|
208
202
|
/**
|
|
209
203
|
* Runs query actual.
|
|
210
204
|
* @param {string} sql - SQL string.
|
|
@@ -207,12 +207,6 @@ export default class VelociousDatabaseDriversMysql extends Base{
|
|
|
207
207
|
*/
|
|
208
208
|
getType() { return "mysql" }
|
|
209
209
|
|
|
210
|
-
/**
|
|
211
|
-
* Runs primary key type.
|
|
212
|
-
* @returns {string} - The primary key type.
|
|
213
|
-
*/
|
|
214
|
-
primaryKeyType() { return "uuid" }
|
|
215
|
-
|
|
216
210
|
/**
|
|
217
211
|
* Runs retryable database error.
|
|
218
212
|
* @param {Error} error - Error instance.
|
|
@@ -265,12 +265,6 @@ export default class VelociousDatabaseDriversSqliteBase extends Base {
|
|
|
265
265
|
return this._options
|
|
266
266
|
}
|
|
267
267
|
|
|
268
|
-
/**
|
|
269
|
-
* Runs primary key type.
|
|
270
|
-
* @returns {string} - The type of the primary key for this driver.
|
|
271
|
-
*/
|
|
272
|
-
primaryKeyType() { return "uuid" }
|
|
273
|
-
|
|
274
268
|
/**
|
|
275
269
|
* Runs query to sql.
|
|
276
270
|
* @param {import("../../query/index.js").default} query - Query instance.
|
|
@@ -351,8 +351,8 @@ export default class VelociousDatabaseMigration {
|
|
|
351
351
|
}
|
|
352
352
|
|
|
353
353
|
const {id = {}, ifNotExists = false, ...restArgs} = args
|
|
354
|
-
const
|
|
355
|
-
const
|
|
354
|
+
const driver = this.getDriver()
|
|
355
|
+
const defaultPrimaryKeyType = driver.primaryKeyType()
|
|
356
356
|
let idDefault, idType, restArgsId
|
|
357
357
|
|
|
358
358
|
if (id !== false) {
|
|
@@ -362,9 +362,9 @@ export default class VelociousDatabaseMigration {
|
|
|
362
362
|
}
|
|
363
363
|
|
|
364
364
|
if (!idType) {
|
|
365
|
-
idType =
|
|
365
|
+
idType = defaultPrimaryKeyType
|
|
366
366
|
}
|
|
367
|
-
const driverSupportsDefaultUUID =
|
|
367
|
+
const driverSupportsDefaultUUID = driver.supportsDefaultPrimaryKeyUUID?.()
|
|
368
368
|
const lowerIdType = idType?.toLowerCase()
|
|
369
369
|
const isUUIDPrimaryKey = lowerIdType == "uuid"
|
|
370
370
|
const numericAutoIncrementTypes = ["int", "integer", "bigint", "smallint", "tinyint"]
|
|
@@ -384,7 +384,7 @@ export default class VelociousDatabaseMigration {
|
|
|
384
384
|
// If driver doesn't support UUID() but the caller explicitly set a default, respect it.
|
|
385
385
|
}
|
|
386
386
|
|
|
387
|
-
const tableData = new TableData(tableName, {ifNotExists})
|
|
387
|
+
const tableData = new TableData(tableName, {ifNotExists, primaryKeyType: defaultPrimaryKeyType})
|
|
388
388
|
|
|
389
389
|
restArgsError(restArgs)
|
|
390
390
|
|
|
@@ -398,7 +398,7 @@ export default class VelociousDatabaseMigration {
|
|
|
398
398
|
callback(tableData)
|
|
399
399
|
}
|
|
400
400
|
|
|
401
|
-
const sqls = await
|
|
401
|
+
const sqls = await driver.createTableSql(tableData)
|
|
402
402
|
|
|
403
403
|
for (const sql of sqls) {
|
|
404
404
|
await this._db.query(sql)
|
|
@@ -90,9 +90,10 @@ class VelociousDatabasePoolBase {
|
|
|
90
90
|
|
|
91
91
|
/**
|
|
92
92
|
* Clears any global connections for the given configuration.
|
|
93
|
+
* @param {import("../../configuration.js").default} configuration - Configuration owning the pool.
|
|
93
94
|
* @returns {void} - No return value.
|
|
94
95
|
*/
|
|
95
|
-
static clearGlobalConnections() {}
|
|
96
|
+
static clearGlobalConnections(configuration) { void configuration }
|
|
96
97
|
|
|
97
98
|
/**
|
|
98
99
|
* Runs constructor.
|
|
@@ -9,10 +9,5 @@
|
|
|
9
9
|
export default async function ensureModelClassInitialized(modelClass, configuration) {
|
|
10
10
|
if (modelClass.isInitialized()) return
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
await modelClass.ensureInitialized({configuration})
|
|
14
|
-
return
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
await modelClass.initializeRecord({configuration})
|
|
12
|
+
await modelClass.ensureInitialized({configuration})
|
|
18
13
|
}
|
|
@@ -170,6 +170,38 @@ export default class RecordAttachmentHandle {
|
|
|
170
170
|
return downloads
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
+
/**
|
|
174
|
+
* Runs list metadata. Returns metadata (no content bytes) for every attachment
|
|
175
|
+
* under this (record, name), so callers can enumerate has-many attachments
|
|
176
|
+
* without downloading their content.
|
|
177
|
+
* @returns {Promise<Array<{byteSize: number, contentType: string | null, filename: string, id: string, url: string | null}>>} - Attachment metadata entries.
|
|
178
|
+
*/
|
|
179
|
+
async listMetadata() {
|
|
180
|
+
if (!this.model.isPersisted()) return []
|
|
181
|
+
|
|
182
|
+
const store = recordAttachmentsStoreForModel(this.model)
|
|
183
|
+
const rows = await store.findMany({model: this.model, name: this.name})
|
|
184
|
+
/**
|
|
185
|
+
* Metadata entries.
|
|
186
|
+
* @type {Array<{byteSize: number, contentType: string | null, filename: string, id: string, url: string | null}>} */
|
|
187
|
+
const entries = []
|
|
188
|
+
|
|
189
|
+
for (const row of rows) {
|
|
190
|
+
const url = await store.attachmentRowUrl({model: this.model, name: this.name, row})
|
|
191
|
+
const byteSize = Number(row.byte_size)
|
|
192
|
+
|
|
193
|
+
entries.push({
|
|
194
|
+
byteSize: Number.isFinite(byteSize) ? byteSize : 0,
|
|
195
|
+
contentType: row.content_type || null,
|
|
196
|
+
filename: row.filename || "attachment.bin",
|
|
197
|
+
id: row.id,
|
|
198
|
+
url
|
|
199
|
+
})
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return entries
|
|
203
|
+
}
|
|
204
|
+
|
|
173
205
|
/**
|
|
174
206
|
* Runs url.
|
|
175
207
|
* @param {string} [id] - Optional attachment id for has-many attachments.
|
|
@@ -8,6 +8,7 @@ import TableReference from "./table-reference.js"
|
|
|
8
8
|
* TableDataArgsType type.
|
|
9
9
|
* @typedef {object} TableDataArgsType
|
|
10
10
|
* @property {boolean} ifNotExists - Whether to create the table only if it does not exist.
|
|
11
|
+
* @property {string} [primaryKeyType] - Default type for implicit primary-key references.
|
|
11
12
|
*/
|
|
12
13
|
|
|
13
14
|
export default class TableData {
|
|
@@ -188,7 +189,7 @@ export default class TableData {
|
|
|
188
189
|
const referenceArgs = args || {}
|
|
189
190
|
const reference = new TableReference(name, referenceArgs)
|
|
190
191
|
const {index, polymorphic, ...restArgs} = referenceArgs
|
|
191
|
-
const columnArgs = Object.assign({isNewColumn: true, type: "uuid"}, restArgs)
|
|
192
|
+
const columnArgs = Object.assign({isNewColumn: true, type: this.args?.primaryKeyType || "uuid"}, restArgs)
|
|
192
193
|
const column = new TableColumn(columnName, columnArgs)
|
|
193
194
|
const indexArgs = typeof index == "object" ? {unique: index.unique === true} : undefined
|
|
194
195
|
const tableIndex = new TableIndex([column], indexArgs)
|
|
@@ -326,7 +326,13 @@ export default class TableColumn {
|
|
|
326
326
|
}
|
|
327
327
|
|
|
328
328
|
if (databaseType == "pgsql" && this.getAutoIncrement() && this.getPrimaryKey()) {
|
|
329
|
-
type
|
|
329
|
+
if (type == "BIGINT") {
|
|
330
|
+
type = "BIGSERIAL"
|
|
331
|
+
} else if (type == "SMALLINT") {
|
|
332
|
+
type = "SMALLSERIAL"
|
|
333
|
+
} else {
|
|
334
|
+
type = "SERIAL"
|
|
335
|
+
}
|
|
330
336
|
}
|
|
331
337
|
|
|
332
338
|
let sql = `${options.quoteColumnName(this.getActualName())} `
|
|
@@ -123,7 +123,7 @@ export default class DbGenerateFrontendModels extends BaseCommand {
|
|
|
123
123
|
const fileContent = await this.buildModelFileContent({
|
|
124
124
|
className,
|
|
125
125
|
importPath,
|
|
126
|
-
modelClass: resourceClass
|
|
126
|
+
modelClass: resourceClass ? resourceClass.modelClass() : configuration.getModelClasses()[className],
|
|
127
127
|
modelConfig,
|
|
128
128
|
resourceClass
|
|
129
129
|
})
|
|
@@ -817,22 +817,17 @@ export default class DbGenerateFrontendModels extends BaseCommand {
|
|
|
817
817
|
* @returns {FrontendModelGeneratorPermitSpec} - Permitted params spec.
|
|
818
818
|
*/
|
|
819
819
|
permittedParamsForGenerator(resourceClass, action) {
|
|
820
|
-
if (!resourceClass
|
|
821
|
-
|
|
822
|
-
const prototypeWithMethod = /**
|
|
823
|
-
* Resource prototype.
|
|
824
|
-
* @type {{permittedParams?: (arg?: object) => FrontendModelGeneratorPermitSpec}}
|
|
825
|
-
*/ (resourceClass.prototype)
|
|
826
|
-
|
|
827
|
-
if (typeof prototypeWithMethod?.permittedParams !== "function") return []
|
|
820
|
+
if (!resourceClass) return []
|
|
828
821
|
|
|
829
822
|
try {
|
|
823
|
+
const modelClass = resourceClass.modelClass()
|
|
824
|
+
|
|
830
825
|
const instance = new resourceClass({
|
|
831
826
|
ability: undefined,
|
|
832
827
|
context: {},
|
|
833
828
|
locals: {},
|
|
834
|
-
modelClass
|
|
835
|
-
modelName:
|
|
829
|
+
modelClass,
|
|
830
|
+
modelName: modelClass.getModelName(),
|
|
836
831
|
params: {},
|
|
837
832
|
resourceConfiguration: /**
|
|
838
833
|
* Resource configuration.
|
|
@@ -860,24 +855,19 @@ export default class DbGenerateFrontendModels extends BaseCommand {
|
|
|
860
855
|
* @returns {string[]} - Relationship names that accept nested writes (empty when none).
|
|
861
856
|
*/
|
|
862
857
|
nestedRelationshipNamesForGenerator(resourceClass) {
|
|
863
|
-
if (!resourceClass
|
|
864
|
-
|
|
865
|
-
const prototypeWithMethod = /**
|
|
866
|
-
* Resource prototype.
|
|
867
|
-
* @type {{permittedParams?: (arg?: object) => FrontendModelGeneratorPermitSpec}}
|
|
868
|
-
*/ (resourceClass.prototype)
|
|
869
|
-
|
|
870
|
-
if (typeof prototypeWithMethod?.permittedParams !== "function") return []
|
|
858
|
+
if (!resourceClass) return []
|
|
871
859
|
|
|
872
860
|
let spec
|
|
873
861
|
|
|
874
862
|
try {
|
|
863
|
+
const modelClass = resourceClass.modelClass()
|
|
864
|
+
|
|
875
865
|
const instance = new resourceClass({
|
|
876
866
|
ability: undefined,
|
|
877
867
|
context: {},
|
|
878
868
|
locals: {},
|
|
879
|
-
modelClass
|
|
880
|
-
modelName:
|
|
869
|
+
modelClass,
|
|
870
|
+
modelName: modelClass.getModelName(),
|
|
881
871
|
params: {},
|
|
882
872
|
resourceConfiguration: /**
|
|
883
873
|
* Resource configuration.
|
|
@@ -1073,6 +1063,7 @@ export default class DbGenerateFrontendModels extends BaseCommand {
|
|
|
1073
1063
|
*/
|
|
1074
1064
|
frontendAttributeConfigForGeneratedAttribute({attributeConfig, attributeName, modelClass}) {
|
|
1075
1065
|
if (!this.frontendAttributeIsModelPrimaryKey({attributeName, modelClass})) return attributeConfig
|
|
1066
|
+
if (this.frontendAttributeConfigHasNullability(attributeConfig)) return attributeConfig
|
|
1076
1067
|
|
|
1077
1068
|
return {...attributeConfig, null: false}
|
|
1078
1069
|
}
|
|
@@ -1141,6 +1132,18 @@ export default class DbGenerateFrontendModels extends BaseCommand {
|
|
|
1141
1132
|
|| typeof attributeConfig?.jsDocType == "string"
|
|
1142
1133
|
}
|
|
1143
1134
|
|
|
1135
|
+
/**
|
|
1136
|
+
* Runs frontend attribute config has nullability.
|
|
1137
|
+
* @param {FrontendAttributeConfig | null | undefined} attributeConfig - Attribute config.
|
|
1138
|
+
* @returns {boolean} - Whether the config declares nullability.
|
|
1139
|
+
*/
|
|
1140
|
+
frontendAttributeConfigHasNullability(attributeConfig) {
|
|
1141
|
+
if (!attributeConfig || typeof attributeConfig !== "object") return false
|
|
1142
|
+
if (Object.prototype.hasOwnProperty.call(attributeConfig, "null")) return true
|
|
1143
|
+
|
|
1144
|
+
return typeof attributeConfig.getNull == "function"
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1144
1147
|
/**
|
|
1145
1148
|
* Runs js doc type for frontend attribute.
|
|
1146
1149
|
* @param {object} args - Arguments.
|
|
@@ -1832,11 +1835,7 @@ export default class DbGenerateFrontendModels extends BaseCommand {
|
|
|
1832
1835
|
* @returns {{autoload: boolean, relationshipName: string, targetClassName: string, targetFileName: string, type: "belongsTo" | "hasOne" | "hasMany"}} Inferred relationship definition.
|
|
1833
1836
|
*/
|
|
1834
1837
|
inferredRelationshipDefinition({className, relationshipName, resourceClass}) {
|
|
1835
|
-
const modelClass = resourceClass
|
|
1836
|
-
|
|
1837
|
-
if (!modelClass) {
|
|
1838
|
-
throw new Error(`Could not find backend model class '${className}' for relationship '${relationshipName}'`)
|
|
1839
|
-
}
|
|
1838
|
+
const modelClass = resourceClass ? resourceClass.modelClass() : this.getConfiguration().getModelClass(className)
|
|
1840
1839
|
|
|
1841
1840
|
const relationship = modelClass.getRelationshipByName(relationshipName)
|
|
1842
1841
|
const relationshipType = relationship.getType()
|