velocious 1.0.475 → 1.0.477
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 +3 -0
- package/build/configuration-types.js +4 -0
- package/build/configuration.js +29 -0
- package/build/database/drivers/base.js +16 -0
- package/build/database/drivers/mssql/index.js +13 -0
- package/build/database/drivers/mssql/sql/remove-index.js +6 -0
- package/build/database/drivers/mysql/index.js +13 -0
- package/build/database/drivers/mysql/sql/remove-index.js +6 -0
- package/build/database/drivers/pgsql/index.js +13 -0
- package/build/database/drivers/pgsql/sql/remove-index.js +6 -0
- package/build/database/drivers/sqlite/base.js +13 -0
- package/build/database/drivers/sqlite/sql/remove-index.js +6 -0
- package/build/database/migration/index.js +44 -0
- package/build/database/pool/base-methods-forward.js +1 -0
- package/build/database/query/remove-index-base.js +39 -0
- package/build/database/record/index.js +25 -9
- package/build/environment-handlers/node/cli/commands/generate/base-models.js +36 -0
- package/build/frontend-model-controller.js +632 -1
- package/build/frontend-models/base.js +5 -1
- package/build/frontend-models/resource-definition.js +20 -3
- package/build/http-server/client/websocket-session.js +93 -0
- package/build/routes/hooks/frontend-model-command-route-hook.js +24 -0
- package/build/src/configuration-types.d.ts +20 -0
- package/build/src/configuration-types.d.ts.map +1 -1
- package/build/src/configuration-types.js +5 -1
- package/build/src/configuration.d.ts +13 -0
- package/build/src/configuration.d.ts.map +1 -1
- package/build/src/configuration.js +28 -1
- package/build/src/database/drivers/base.d.ts +20 -0
- package/build/src/database/drivers/base.d.ts.map +1 -1
- package/build/src/database/drivers/base.js +16 -1
- package/build/src/database/drivers/mssql/index.d.ts.map +1 -1
- package/build/src/database/drivers/mssql/index.js +12 -1
- package/build/src/database/drivers/mssql/sql/remove-index.d.ts +4 -0
- package/build/src/database/drivers/mssql/sql/remove-index.d.ts.map +1 -0
- package/build/src/database/drivers/mssql/sql/remove-index.js +5 -0
- package/build/src/database/drivers/mysql/index.d.ts.map +1 -1
- package/build/src/database/drivers/mysql/index.js +12 -1
- package/build/src/database/drivers/mysql/sql/remove-index.d.ts +4 -0
- package/build/src/database/drivers/mysql/sql/remove-index.d.ts.map +1 -0
- package/build/src/database/drivers/mysql/sql/remove-index.js +5 -0
- package/build/src/database/drivers/pgsql/index.d.ts.map +1 -1
- package/build/src/database/drivers/pgsql/index.js +12 -1
- package/build/src/database/drivers/pgsql/sql/remove-index.d.ts +4 -0
- package/build/src/database/drivers/pgsql/sql/remove-index.d.ts.map +1 -0
- package/build/src/database/drivers/pgsql/sql/remove-index.js +5 -0
- package/build/src/database/drivers/sqlite/base.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/base.js +12 -1
- package/build/src/database/drivers/sqlite/sql/remove-index.d.ts +4 -0
- package/build/src/database/drivers/sqlite/sql/remove-index.d.ts.map +1 -0
- package/build/src/database/drivers/sqlite/sql/remove-index.js +5 -0
- package/build/src/database/migration/index.d.ts +25 -0
- package/build/src/database/migration/index.d.ts.map +1 -1
- package/build/src/database/migration/index.js +39 -1
- package/build/src/database/pool/base-methods-forward.d.ts.map +1 -1
- package/build/src/database/pool/base-methods-forward.js +2 -1
- package/build/src/database/query/remove-index-base.d.ts +35 -0
- package/build/src/database/query/remove-index-base.d.ts.map +1 -0
- package/build/src/database/query/remove-index-base.js +34 -0
- package/build/src/database/record/index.d.ts +11 -0
- package/build/src/database/record/index.d.ts.map +1 -1
- package/build/src/database/record/index.js +26 -11
- package/build/src/environment-handlers/node/cli/commands/generate/base-models.d.ts +15 -0
- package/build/src/environment-handlers/node/cli/commands/generate/base-models.d.ts.map +1 -1
- package/build/src/environment-handlers/node/cli/commands/generate/base-models.js +33 -1
- package/build/src/frontend-model-controller.d.ts +156 -0
- package/build/src/frontend-model-controller.d.ts.map +1 -1
- package/build/src/frontend-model-controller.js +555 -2
- package/build/src/frontend-models/base.d.ts +5 -0
- package/build/src/frontend-models/base.d.ts.map +1 -1
- package/build/src/frontend-models/base.js +6 -2
- package/build/src/frontend-models/resource-definition.d.ts.map +1 -1
- package/build/src/frontend-models/resource-definition.js +20 -4
- package/build/src/http-server/client/websocket-session.d.ts +39 -0
- package/build/src/http-server/client/websocket-session.d.ts.map +1 -1
- package/build/src/http-server/client/websocket-session.js +85 -1
- package/build/src/routes/hooks/frontend-model-command-route-hook.d.ts.map +1 -1
- package/build/src/routes/hooks/frontend-model-command-route-hook.js +22 -1
- package/build/src/sync/conflict-strategy.d.ts +83 -0
- package/build/src/sync/conflict-strategy.d.ts.map +1 -0
- package/build/src/sync/conflict-strategy.js +215 -0
- package/build/src/sync/peer-mutation-bundle.d.ts +97 -0
- package/build/src/sync/peer-mutation-bundle.d.ts.map +1 -0
- package/build/src/sync/peer-mutation-bundle.js +215 -0
- package/build/src/sync/server-change-feed.d.ts +265 -0
- package/build/src/sync/server-change-feed.d.ts.map +1 -0
- package/build/src/sync/server-change-feed.js +475 -0
- package/build/sync/conflict-strategy.js +225 -0
- package/build/sync/peer-mutation-bundle.js +232 -0
- package/build/sync/server-change-feed.js +524 -0
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/configuration-types.js +4 -0
- package/src/configuration.js +29 -0
- package/src/database/drivers/base.js +16 -0
- package/src/database/drivers/mssql/index.js +13 -0
- package/src/database/drivers/mssql/sql/remove-index.js +6 -0
- package/src/database/drivers/mysql/index.js +13 -0
- package/src/database/drivers/mysql/sql/remove-index.js +6 -0
- package/src/database/drivers/pgsql/index.js +13 -0
- package/src/database/drivers/pgsql/sql/remove-index.js +6 -0
- package/src/database/drivers/sqlite/base.js +13 -0
- package/src/database/drivers/sqlite/sql/remove-index.js +6 -0
- package/src/database/migration/index.js +44 -0
- package/src/database/pool/base-methods-forward.js +1 -0
- package/src/database/query/remove-index-base.js +39 -0
- package/src/database/record/index.js +25 -9
- package/src/environment-handlers/node/cli/commands/generate/base-models.js +36 -0
- package/src/frontend-model-controller.js +632 -1
- package/src/frontend-models/base.js +5 -1
- package/src/frontend-models/resource-definition.js +20 -3
- package/src/http-server/client/websocket-session.js +93 -0
- package/src/routes/hooks/frontend-model-command-route-hook.js +24 -0
- package/src/sync/conflict-strategy.js +225 -0
- package/src/sync/peer-mutation-bundle.js +232 -0
- package/src/sync/server-change-feed.js +524 -0
package/README.md
CHANGED
|
@@ -723,6 +723,8 @@ Task.validates("name", {presence: true, uniqueness: true})
|
|
|
723
723
|
export default Task
|
|
724
724
|
```
|
|
725
725
|
|
|
726
|
+
Generated belongs-to setters synchronize the loaded relationship and foreign key before save, so `task.setProject(project)` updates `task.projectId()`, `task.changes()`, callbacks, and scoped features such as `actsAsList`. Custom relationship primary keys are also used after autosaving an assigned new or dirty related record. Generated backend write attributes accept belongs-to relationship names for `create` and `update` payloads. See [docs/relationships.md](docs/relationships.md#assigning-belongs-to-relationships).
|
|
727
|
+
|
|
726
728
|
Translated models also get a `currentTranslation` `hasOne` relationship scoped to the first available row in the current locale fallback order. See [docs/translations.md](docs/translations.md) for preloading and frontend-model sorting behavior.
|
|
727
729
|
|
|
728
730
|
Async class APIs initialize record metadata on first use when a model has not
|
|
@@ -1076,6 +1078,7 @@ export default class CreateEvents extends Migration {
|
|
|
1076
1078
|
}
|
|
1077
1079
|
|
|
1078
1080
|
async down() {
|
|
1081
|
+
await this.removeIndex("task_translations", ["task_id", "locale"])
|
|
1079
1082
|
await this.dropTable("task_translations")
|
|
1080
1083
|
await this.dropTable("examples")
|
|
1081
1084
|
await this.dropTable("tasks")
|
|
@@ -348,6 +348,7 @@
|
|
|
348
348
|
* frontends and peers; `policy` is hashed but intentionally omitted from
|
|
349
349
|
* frontend-safe manifests.
|
|
350
350
|
* @typedef {object} FrontendModelResourceSyncConfiguration
|
|
351
|
+
* @property {"optimisticVersion" | "serverWins" | "lastWriterWins" | "fieldThreeWay" | "appendOnly"} [conflictStrategy] - Strategy used when replay detects server/client divergence. Defaults to `optimisticVersion`.
|
|
351
352
|
* @property {boolean} [enabled] - Whether the resource is sync-enabled. Defaults to true when `sync` is configured.
|
|
352
353
|
* @property {string[]} [operations] - Sync operation names such as `index`, `find`, `create`, `update`, custom domain commands, etc.
|
|
353
354
|
* @property {string | number} [policyVersion] - App-controlled policy version used as a stable change signal.
|
|
@@ -358,6 +359,8 @@
|
|
|
358
359
|
/**
|
|
359
360
|
* Velocious sync configuration.
|
|
360
361
|
* @typedef {object} VelociousSyncConfiguration
|
|
362
|
+
* @property {import("./sync/device-identity.js").SyncJsonWebKey | null} [deviceCertificateBackendPublicKey] - Public backend key used to verify offline device certificates for sync replay.
|
|
363
|
+
* @property {number} [changeFeedRetentionSize] - Number of accepted server changes retained before clients must refresh from snapshot.
|
|
361
364
|
* @property {Array<import("./sync/offline-grant.js").OfflineGrantSigningKey>} offlineGrantSigningKeys - Signing keys used to issue and verify offline grants. Secrets must never be exposed to clients.
|
|
362
365
|
* @property {number} [offlineGrantTtlMs] - Default offline grant TTL in milliseconds. Defaults to 24 hours.
|
|
363
366
|
*/
|
|
@@ -365,6 +368,7 @@
|
|
|
365
368
|
/**
|
|
366
369
|
* Frontend-safe normalized sync metadata.
|
|
367
370
|
* @typedef {object} NormalizedFrontendModelResourceSyncConfiguration
|
|
371
|
+
* @property {"optimisticVersion" | "serverWins" | "lastWriterWins" | "fieldThreeWay" | "appendOnly"} conflictStrategy - Normalized replay conflict strategy.
|
|
368
372
|
* @property {boolean} enabled - Whether the resource is sync-enabled.
|
|
369
373
|
* @property {string[]} operations - Sorted, duplicate-free sync operation names.
|
|
370
374
|
* @property {string | null} policyVersion - App-controlled policy version, or null.
|
package/build/configuration.js
CHANGED
|
@@ -232,6 +232,9 @@ export default class VelociousConfiguration {
|
|
|
232
232
|
/** Grace period for paused WebSocket sessions before permanent teardown. */
|
|
233
233
|
this._websocketSessionGraceSeconds = 300
|
|
234
234
|
|
|
235
|
+
/** Interval (seconds) between server→client heartbeat pings; 0 disables reaping of silent sockets. */
|
|
236
|
+
this._websocketSessionHeartbeatSeconds = 30
|
|
237
|
+
|
|
235
238
|
/**
|
|
236
239
|
* Optional wrapper called around every WebSocket-borne request /
|
|
237
240
|
* connection message / channel dispatch. Apps register it here
|
|
@@ -406,15 +409,25 @@ export default class VelociousConfiguration {
|
|
|
406
409
|
* @returns {import("./configuration-types.js").VelociousSyncConfiguration} - Normalized sync configuration.
|
|
407
410
|
*/
|
|
408
411
|
_normalizeSyncConfiguration(sync) {
|
|
412
|
+
const deviceCertificateBackendPublicKey = sync?.deviceCertificateBackendPublicKey || null
|
|
413
|
+
const changeFeedRetentionSize = sync?.changeFeedRetentionSize
|
|
409
414
|
const offlineGrantSigningKeys = sync?.offlineGrantSigningKeys || []
|
|
410
415
|
const offlineGrantTtlMs = sync?.offlineGrantTtlMs
|
|
411
416
|
|
|
417
|
+
if (deviceCertificateBackendPublicKey !== null && (typeof deviceCertificateBackendPublicKey !== "object" || Array.isArray(deviceCertificateBackendPublicKey))) {
|
|
418
|
+
throw new Error("sync.deviceCertificateBackendPublicKey must be a public JSON Web Key object")
|
|
419
|
+
}
|
|
420
|
+
if (changeFeedRetentionSize !== undefined && (!Number.isInteger(changeFeedRetentionSize) || changeFeedRetentionSize <= 0)) {
|
|
421
|
+
throw new Error("sync.changeFeedRetentionSize must be a positive integer")
|
|
422
|
+
}
|
|
412
423
|
if (!Array.isArray(offlineGrantSigningKeys)) throw new Error("sync.offlineGrantSigningKeys must be an array")
|
|
413
424
|
if (offlineGrantTtlMs !== undefined && (!Number.isInteger(offlineGrantTtlMs) || offlineGrantTtlMs <= 0)) {
|
|
414
425
|
throw new Error("sync.offlineGrantTtlMs must be a positive integer number of milliseconds")
|
|
415
426
|
}
|
|
416
427
|
|
|
417
428
|
return {
|
|
429
|
+
changeFeedRetentionSize: changeFeedRetentionSize || 10000,
|
|
430
|
+
deviceCertificateBackendPublicKey,
|
|
418
431
|
offlineGrantSigningKeys: offlineGrantSigningKeys.map((key) => normalizeOfflineGrantSigningKey(key)),
|
|
419
432
|
offlineGrantTtlMs: offlineGrantTtlMs || 24 * 60 * 60 * 1000
|
|
420
433
|
}
|
|
@@ -2042,6 +2055,12 @@ export default class VelociousConfiguration {
|
|
|
2042
2055
|
*/
|
|
2043
2056
|
getWebsocketSessionGraceSeconds() { return this._websocketSessionGraceSeconds }
|
|
2044
2057
|
|
|
2058
|
+
/**
|
|
2059
|
+
* Runs get websocket session heartbeat seconds.
|
|
2060
|
+
* @returns {number} - Interval (seconds) between server→client heartbeat pings; 0 disables reaping.
|
|
2061
|
+
*/
|
|
2062
|
+
getWebsocketSessionHeartbeatSeconds() { return this._websocketSessionHeartbeatSeconds }
|
|
2063
|
+
|
|
2045
2064
|
/**
|
|
2046
2065
|
* Registers a wrapper invoked around every WS-borne request /
|
|
2047
2066
|
* connection message / channel dispatch. The wrapper receives the
|
|
@@ -2119,6 +2138,16 @@ export default class VelociousConfiguration {
|
|
|
2119
2138
|
this._websocketSessionGraceSeconds = seconds
|
|
2120
2139
|
}
|
|
2121
2140
|
|
|
2141
|
+
/**
|
|
2142
|
+
* Runs set websocket session heartbeat seconds.
|
|
2143
|
+
* @param {number} seconds
|
|
2144
|
+
* @returns {void}
|
|
2145
|
+
*/
|
|
2146
|
+
setWebsocketSessionHeartbeatSeconds(seconds) {
|
|
2147
|
+
if (!Number.isFinite(seconds) || seconds < 0) throw new Error(`Invalid heartbeat seconds: ${seconds}`)
|
|
2148
|
+
this._websocketSessionHeartbeatSeconds = seconds
|
|
2149
|
+
}
|
|
2150
|
+
|
|
2122
2151
|
/**
|
|
2123
2152
|
* Moves a session into the paused registry and starts the grace
|
|
2124
2153
|
* timer. When the timer fires, the session's permanent teardown
|
|
@@ -9,6 +9,12 @@
|
|
|
9
9
|
* @property {boolean} [unique] - Whether the index should enforce uniqueness.
|
|
10
10
|
* @property {string} tableName - Name of the table to add the index to.
|
|
11
11
|
*/
|
|
12
|
+
/**
|
|
13
|
+
* RemoveIndexSqlArgs type.
|
|
14
|
+
* @typedef {object} RemoveIndexSqlArgs
|
|
15
|
+
* @property {string} name - Index name to drop.
|
|
16
|
+
* @property {string} tableName - Name of the table the index belongs to.
|
|
17
|
+
*/
|
|
12
18
|
/**
|
|
13
19
|
* DropTableSqlArgsType type.
|
|
14
20
|
* @typedef {object} DropTableSqlArgsType
|
|
@@ -307,6 +313,16 @@ export default class VelociousDatabaseDriversBase {
|
|
|
307
313
|
throw new Error("'createIndexSQLs' not implemented")
|
|
308
314
|
}
|
|
309
315
|
|
|
316
|
+
/**
|
|
317
|
+
* Runs remove index sqls.
|
|
318
|
+
* @abstract
|
|
319
|
+
* @param {RemoveIndexSqlArgs} indexData - Index data.
|
|
320
|
+
* @returns {Promise<string[]>} - Resolves with SQL statements.
|
|
321
|
+
*/
|
|
322
|
+
async removeIndexSQLs(indexData) { // eslint-disable-line no-unused-vars
|
|
323
|
+
throw new Error("'removeIndexSQLs' not implemented")
|
|
324
|
+
}
|
|
325
|
+
|
|
310
326
|
/**
|
|
311
327
|
* Runs create table.
|
|
312
328
|
* @param {import("../table-data/index.js").default} tableData - Table data.
|
|
@@ -15,6 +15,7 @@ import Options from "./options.js"
|
|
|
15
15
|
import mssql from "mssql"
|
|
16
16
|
import net from "node:net"
|
|
17
17
|
import QueryParser from "./query-parser.js"
|
|
18
|
+
import RemoveIndex from "./sql/remove-index.js"
|
|
18
19
|
import Table from "./table.js"
|
|
19
20
|
import StructureSql from "./structure-sql.js"
|
|
20
21
|
import timeout from "awaitery/build/timeout.js"
|
|
@@ -113,6 +114,18 @@ export default class VelociousDatabaseDriversMssql extends Base{
|
|
|
113
114
|
return await createIndex.toSQLs()
|
|
114
115
|
}
|
|
115
116
|
|
|
117
|
+
/**
|
|
118
|
+
* Runs remove index sqls.
|
|
119
|
+
* @param {import("../base.js").RemoveIndexSqlArgs} indexData - Index data.
|
|
120
|
+
* @returns {Promise<string[]>} - Resolves with SQL statements.
|
|
121
|
+
*/
|
|
122
|
+
async removeIndexSQLs(indexData) {
|
|
123
|
+
const removeArgs = Object.assign({driver: this}, indexData)
|
|
124
|
+
const removeIndex = new RemoveIndex(removeArgs)
|
|
125
|
+
|
|
126
|
+
return await removeIndex.toSQLs()
|
|
127
|
+
}
|
|
128
|
+
|
|
116
129
|
/**
|
|
117
130
|
* Runs create table sql.
|
|
118
131
|
* @param {import("../../table-data/index.js").default} tableData - Table data.
|
|
@@ -14,6 +14,7 @@ import Options from "./options.js"
|
|
|
14
14
|
import mysql from "mysql"
|
|
15
15
|
import query from "./query.js"
|
|
16
16
|
import QueryParser from "./query-parser.js"
|
|
17
|
+
import RemoveIndex from "./sql/remove-index.js"
|
|
17
18
|
import Table from "./table.js"
|
|
18
19
|
import StructureSql from "./structure-sql.js"
|
|
19
20
|
import Upsert from "./sql/upsert.js"
|
|
@@ -227,6 +228,18 @@ export default class VelociousDatabaseDriversMysql extends Base{
|
|
|
227
228
|
return await createIndex.toSQLs()
|
|
228
229
|
}
|
|
229
230
|
|
|
231
|
+
/**
|
|
232
|
+
* Runs remove index sqls.
|
|
233
|
+
* @param {import("../base.js").RemoveIndexSqlArgs} indexData - Index data.
|
|
234
|
+
* @returns {Promise<string[]>} - Resolves with SQL statements.
|
|
235
|
+
*/
|
|
236
|
+
async removeIndexSQLs(indexData) {
|
|
237
|
+
const removeArgs = Object.assign({driver: this}, indexData)
|
|
238
|
+
const removeIndex = new RemoveIndex(removeArgs)
|
|
239
|
+
|
|
240
|
+
return await removeIndex.toSQLs()
|
|
241
|
+
}
|
|
242
|
+
|
|
230
243
|
/**
|
|
231
244
|
* Runs create table sql.
|
|
232
245
|
* @param {import("../../table-data/index.js").default} tableData - Table data.
|
|
@@ -14,6 +14,7 @@ import DropTable from "./sql/drop-table.js"
|
|
|
14
14
|
import Insert from "./sql/insert.js"
|
|
15
15
|
import Options from "./options.js"
|
|
16
16
|
import QueryParser from "./query-parser.js"
|
|
17
|
+
import RemoveIndex from "./sql/remove-index.js"
|
|
17
18
|
import Table from "./table.js"
|
|
18
19
|
import StructureSql from "./structure-sql.js"
|
|
19
20
|
import Upsert from "./sql/upsert.js"
|
|
@@ -149,6 +150,18 @@ export default class VelociousDatabaseDriversPgsql extends Base{
|
|
|
149
150
|
return await createIndex.toSQLs()
|
|
150
151
|
}
|
|
151
152
|
|
|
153
|
+
/**
|
|
154
|
+
* Runs remove index sqls.
|
|
155
|
+
* @param {import("../base.js").RemoveIndexSqlArgs} indexData - Index data.
|
|
156
|
+
* @returns {Promise<string[]>} - Resolves with SQL statements.
|
|
157
|
+
*/
|
|
158
|
+
async removeIndexSQLs(indexData) {
|
|
159
|
+
const removeArgs = Object.assign({driver: this}, indexData)
|
|
160
|
+
const removeIndex = new RemoveIndex(removeArgs)
|
|
161
|
+
|
|
162
|
+
return await removeIndex.toSQLs()
|
|
163
|
+
}
|
|
164
|
+
|
|
152
165
|
/**
|
|
153
166
|
* Runs create table sql.
|
|
154
167
|
* @param {import("../../table-data/index.js").default} tableData - Table data.
|
|
@@ -12,6 +12,7 @@ import escapeString from "sql-escape-string"
|
|
|
12
12
|
import Insert from "./sql/insert.js"
|
|
13
13
|
import Options from "./options.js"
|
|
14
14
|
import QueryParser from "./query-parser.js"
|
|
15
|
+
import RemoveIndex from "./sql/remove-index.js"
|
|
15
16
|
import Table from "./table.js"
|
|
16
17
|
import StructureSql from "./structure-sql.js"
|
|
17
18
|
import Upsert from "./sql/upsert.js"
|
|
@@ -55,6 +56,18 @@ export default class VelociousDatabaseDriversSqliteBase extends Base {
|
|
|
55
56
|
return await createIndex.toSQLs()
|
|
56
57
|
}
|
|
57
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Runs remove index sqls.
|
|
61
|
+
* @param {import("../base.js").RemoveIndexSqlArgs} indexData - Index data.
|
|
62
|
+
* @returns {Promise<string[]>} - Resolves with SQL statements.
|
|
63
|
+
*/
|
|
64
|
+
async removeIndexSQLs(indexData) {
|
|
65
|
+
const removeArgs = Object.assign({driver: this}, indexData)
|
|
66
|
+
const removeIndex = new RemoveIndex(removeArgs)
|
|
67
|
+
|
|
68
|
+
return await removeIndex.toSQLs()
|
|
69
|
+
}
|
|
70
|
+
|
|
58
71
|
/**
|
|
59
72
|
* Runs create table sql.
|
|
60
73
|
* @abstract
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
import { convertLegacyDateValueToUtcStorage } from "../datetime-storage.js"
|
|
40
40
|
import * as inflection from "inflection"
|
|
41
41
|
import restArgsError from "../../utils/rest-args-error.js"
|
|
42
|
+
import CreateIndexBase from "../query/create-index-base.js"
|
|
42
43
|
import TableData from "../table-data/index.js"
|
|
43
44
|
class NotImplementedError extends Error {}
|
|
44
45
|
|
|
@@ -184,6 +185,49 @@ export default class VelociousDatabaseMigration {
|
|
|
184
185
|
}
|
|
185
186
|
}
|
|
186
187
|
|
|
188
|
+
/**
|
|
189
|
+
* RemoveIndexArgsType type.
|
|
190
|
+
* @typedef {object} RemoveIndexArgsType
|
|
191
|
+
* @property {string} [name] - Explicit index name to remove.
|
|
192
|
+
*/
|
|
193
|
+
/**
|
|
194
|
+
* Runs remove index.
|
|
195
|
+
* @param {string} tableName - Table name.
|
|
196
|
+
* @param {string | Array<string | import("../table-data/table-column.js").default>} nameOrColumns - Index name or columns whose default addIndex name should be removed.
|
|
197
|
+
* @param {RemoveIndexArgsType} [args] - Options object.
|
|
198
|
+
* @returns {Promise<void>} - Resolves when complete.
|
|
199
|
+
*/
|
|
200
|
+
async removeIndex(tableName, nameOrColumns, args = {}) {
|
|
201
|
+
const {name, ...restArgs} = args
|
|
202
|
+
|
|
203
|
+
restArgsError(restArgs)
|
|
204
|
+
|
|
205
|
+
const removeIndexName = name || this._removeIndexName(tableName, nameOrColumns)
|
|
206
|
+
const sqls = await this.getDriver().removeIndexSQLs({name: removeIndexName, tableName})
|
|
207
|
+
|
|
208
|
+
for (const sql of sqls) {
|
|
209
|
+
await this.getDriver().query(sql)
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Runs remove index name.
|
|
215
|
+
* @param {string} tableName - Table name.
|
|
216
|
+
* @param {string | Array<string | import("../table-data/table-column.js").default>} nameOrColumns - Index name or columns.
|
|
217
|
+
* @returns {string} - The index name.
|
|
218
|
+
*/
|
|
219
|
+
_removeIndexName(tableName, nameOrColumns) {
|
|
220
|
+
if (typeof nameOrColumns === "string") return nameOrColumns
|
|
221
|
+
|
|
222
|
+
const createIndex = new CreateIndexBase({
|
|
223
|
+
columns: nameOrColumns,
|
|
224
|
+
driver: this.getDriver(),
|
|
225
|
+
tableName
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
return createIndex.generateIndexName()
|
|
229
|
+
}
|
|
230
|
+
|
|
187
231
|
/**
|
|
188
232
|
* AddForeignKeyArgsType type.
|
|
189
233
|
* @typedef {object} AddForeignKeyArgsType
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import QueryBase from "./base.js"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* RemoveIndexBaseArgsType type.
|
|
7
|
+
* @typedef {object} RemoveIndexBaseArgsType
|
|
8
|
+
* @property {import("../drivers/base.js").default} driver - Database driver used to generate SQL.
|
|
9
|
+
* @property {string} name - Index name to drop.
|
|
10
|
+
* @property {string} tableName - Name of the table the index belongs to.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export default class VelociousDatabaseQueryRemoveIndexBase extends QueryBase {
|
|
14
|
+
/**
|
|
15
|
+
* Runs constructor.
|
|
16
|
+
* @param {RemoveIndexBaseArgsType} args - Options object.
|
|
17
|
+
*/
|
|
18
|
+
constructor({driver, name, tableName}) {
|
|
19
|
+
super({driver})
|
|
20
|
+
this.name = name
|
|
21
|
+
this.tableName = tableName
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Runs to sqls.
|
|
26
|
+
* @returns {Promise<string[]>} - Resolves with SQL statements.
|
|
27
|
+
*/
|
|
28
|
+
async toSQLs() {
|
|
29
|
+
const databaseType = this.getDriver().getType()
|
|
30
|
+
const options = this.getOptions()
|
|
31
|
+
let sql = `DROP INDEX ${options.quoteIndexName(this.name)}`
|
|
32
|
+
|
|
33
|
+
if (databaseType == "mssql" || databaseType == "mysql") {
|
|
34
|
+
sql += ` ON ${options.quoteTableName(this.tableName)}`
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return [sql]
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -690,11 +690,14 @@ class VelociousDatabaseRecord {
|
|
|
690
690
|
return await this.relationshipOrLoad(relationshipName, {preloadTranslations: true})
|
|
691
691
|
}
|
|
692
692
|
|
|
693
|
-
prototype[`set${inflection.camelize(relationshipName)}`] = function(/** @type {
|
|
693
|
+
prototype[`set${inflection.camelize(relationshipName)}`] = function(/** @type {VelociousDatabaseRecord | null | undefined} */ model) {
|
|
694
694
|
const relationship = this.getRelationshipByName(relationshipName)
|
|
695
|
+
const foreignKeyValue = this._belongsToForeignKeyValue({model, relationship})
|
|
695
696
|
|
|
696
|
-
relationship.setLoaded(model)
|
|
697
|
+
relationship.setLoaded(model || undefined)
|
|
698
|
+
relationship.setPreloaded(true)
|
|
697
699
|
relationship.setDirty(true)
|
|
700
|
+
this._setColumnAttribute(relationship.getForeignKey(), foreignKeyValue)
|
|
698
701
|
}
|
|
699
702
|
} else if (actualData.type == "hasMany") {
|
|
700
703
|
relationship = new HasManyRelationship(actualData)
|
|
@@ -1783,6 +1786,20 @@ class VelociousDatabaseRecord {
|
|
|
1783
1786
|
return loaded.readColumn(relationship.getPrimaryKey()) == normalizedValue
|
|
1784
1787
|
}
|
|
1785
1788
|
|
|
1789
|
+
/**
|
|
1790
|
+
* Returns the foreign key value for a belongs-to relationship assignment.
|
|
1791
|
+
* @param {object} args - Relationship assignment arguments.
|
|
1792
|
+
* @param {VelociousDatabaseRecord | null | undefined} args.model - Assigned model.
|
|
1793
|
+
* @param {import("./instance-relationships/base.js").default} args.relationship - Belongs-to relationship instance.
|
|
1794
|
+
* @returns {string | number | null | undefined} - Foreign key value for the assignment.
|
|
1795
|
+
*/
|
|
1796
|
+
_belongsToForeignKeyValue({model, relationship}) {
|
|
1797
|
+
if (model == null) return null
|
|
1798
|
+
if (!(model instanceof VelociousDatabaseRecord)) throw new Error(`Unexpected model type: ${typeof model}`)
|
|
1799
|
+
|
|
1800
|
+
return /** @type {string | number | null | undefined} */ (model.readColumn(relationship.getPrimaryKey()))
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1786
1803
|
/**
|
|
1787
1804
|
* Runs clear loaded belongs to relationship.
|
|
1788
1805
|
* @param {?} relationship - Relationship instance.
|
|
@@ -2364,8 +2381,9 @@ class VelociousDatabaseRecord {
|
|
|
2364
2381
|
await model.save()
|
|
2365
2382
|
|
|
2366
2383
|
const foreignKey = this._relationshipForeignKeyAttribute(instanceRelationship)
|
|
2384
|
+
const foreignKeyValue = this._belongsToForeignKeyValue({model, relationship: instanceRelationship})
|
|
2367
2385
|
|
|
2368
|
-
this.setAttribute(foreignKey,
|
|
2386
|
+
this.setAttribute(foreignKey, foreignKeyValue)
|
|
2369
2387
|
|
|
2370
2388
|
instanceRelationship.setPreloaded(true)
|
|
2371
2389
|
instanceRelationship.setDirty(false)
|
|
@@ -3801,14 +3819,12 @@ class VelociousDatabaseRecord {
|
|
|
3801
3819
|
const relationship = this._instanceRelationships[relationshipName]
|
|
3802
3820
|
|
|
3803
3821
|
if (relationship.getType() == "belongsTo" && relationship.getDirty()) {
|
|
3804
|
-
const model = relationship.
|
|
3822
|
+
const model = relationship.getLoadedOrUndefined()
|
|
3805
3823
|
|
|
3806
3824
|
if (model) {
|
|
3807
|
-
if (model
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
throw new Error(`Unexpected model type: ${typeof model}`)
|
|
3811
|
-
}
|
|
3825
|
+
if (Array.isArray(model)) throw new Error("Unexpected belongs-to model array")
|
|
3826
|
+
|
|
3827
|
+
belongsToChanges[relationship.getForeignKey()] = this._belongsToForeignKeyValue({model, relationship})
|
|
3812
3828
|
}
|
|
3813
3829
|
}
|
|
3814
3830
|
}
|
|
@@ -120,6 +120,7 @@ export default class DbGenerateModel extends BaseCommand {
|
|
|
120
120
|
|
|
121
121
|
const columns = await table.getColumns()
|
|
122
122
|
const writeAttributeTypeName = `${modelNameCamelized}WriteAttributes`
|
|
123
|
+
const belongsToWriteAttributes = await this.belongsToWriteAttributesForModel({modelClass, modelsDir})
|
|
123
124
|
const nestedWriteAttributes = this.nestedWriteAttributesForModel({modelClass})
|
|
124
125
|
|
|
125
126
|
fileContent += `import DatabaseRecord from "${velociousPath}/database/record/index.js"\n\n`
|
|
@@ -135,6 +136,9 @@ export default class DbGenerateModel extends BaseCommand {
|
|
|
135
136
|
fileContent += ` * @property {${setterJsdocType}${column.getNull() ? " | null" : ""}} [${camelizedColumnName}] - Value for the ${camelizedColumnName} attribute.\n`
|
|
136
137
|
}
|
|
137
138
|
}
|
|
139
|
+
for (const belongsToWriteAttribute of belongsToWriteAttributes) {
|
|
140
|
+
fileContent += ` * @property {${belongsToWriteAttribute.propertyType}} [${belongsToWriteAttribute.propertyName}] - Related ${belongsToWriteAttribute.relationshipName} record.\n`
|
|
141
|
+
}
|
|
138
142
|
for (const nestedWriteAttribute of nestedWriteAttributes) {
|
|
139
143
|
fileContent += ` * @property {${nestedWriteAttribute.propertyType}} [${nestedWriteAttribute.propertyName}] - Nested ${nestedWriteAttribute.relationshipName} attributes.\n`
|
|
140
144
|
}
|
|
@@ -440,6 +444,38 @@ export default class DbGenerateModel extends BaseCommand {
|
|
|
440
444
|
return this.jsDocTypeFromColumn(column, modelClass)
|
|
441
445
|
}
|
|
442
446
|
|
|
447
|
+
/**
|
|
448
|
+
* Runs belongs to write attributes for model.
|
|
449
|
+
* @param {object} args - Arguments.
|
|
450
|
+
* @param {typeof import("../../../../../database/record/index.js").default} args.modelClass - Model class.
|
|
451
|
+
* @param {string} args.modelsDir - Source models directory.
|
|
452
|
+
* @returns {Promise<Array<{propertyName: string, propertyType: string, relationshipName: string}>>} - Belongs-to write attributes.
|
|
453
|
+
*/
|
|
454
|
+
async belongsToWriteAttributesForModel({modelClass, modelsDir}) {
|
|
455
|
+
const writeAttributes = []
|
|
456
|
+
|
|
457
|
+
for (const relationship of modelClass.getRelationships()) {
|
|
458
|
+
if (relationship.getType() !== "belongsTo") continue
|
|
459
|
+
if (relationship.getPolymorphic()) continue
|
|
460
|
+
|
|
461
|
+
const targetModelClass = relationship.getTargetModelClass()
|
|
462
|
+
|
|
463
|
+
if (!targetModelClass) throw new Error(`Relationship '${relationship.getRelationshipName()}' on '${modelClass.getModelName()}' has no target model class`)
|
|
464
|
+
|
|
465
|
+
const targetModelFileName = inflection.dasherize(inflection.underscore(targetModelClass.getModelName()))
|
|
466
|
+
const targetModelPath = `${modelsDir}/${targetModelFileName}.js`
|
|
467
|
+
const targetImportPath = await fileExists(targetModelPath) ? `../models/${targetModelFileName}.js` : `./${targetModelFileName}.js`
|
|
468
|
+
|
|
469
|
+
writeAttributes.push({
|
|
470
|
+
propertyName: relationship.getRelationshipName(),
|
|
471
|
+
propertyType: `import("${targetImportPath}").default`,
|
|
472
|
+
relationshipName: relationship.getRelationshipName()
|
|
473
|
+
})
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
return writeAttributes
|
|
477
|
+
}
|
|
478
|
+
|
|
443
479
|
/**
|
|
444
480
|
* Runs nested write attributes for model.
|
|
445
481
|
* @param {object} args - Arguments.
|