velocious 1.0.474 → 1.0.476
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 +10 -2
- package/build/configuration-types.js +39 -1
- package/build/configuration.js +46 -1
- package/build/database/drivers/base.js +67 -3
- 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 +87 -7
- 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/connection-sql-js.js +16 -5
- package/build/database/drivers/sqlite/index.web.js +29 -15
- package/build/database/drivers/sqlite/sql/remove-index.js +6 -0
- package/build/database/drivers/sqlite/web-persistence.js +521 -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/environment-handlers/node/cli/commands/generate/frontend-models.js +63 -0
- package/build/frontend-model-controller.js +401 -4
- package/build/frontend-model-resource/base-resource.js +5 -1
- package/build/frontend-models/base.js +146 -2
- package/build/frontend-models/query.js +3 -3
- package/build/frontend-models/resource-definition.js +304 -2
- package/build/routes/hooks/frontend-model-command-route-hook.js +16 -0
- package/build/src/configuration-types.d.ts +118 -2
- package/build/src/configuration-types.d.ts.map +1 -1
- package/build/src/configuration-types.js +36 -2
- package/build/src/configuration.d.ts +18 -1
- package/build/src/configuration.d.ts.map +1 -1
- package/build/src/configuration.js +42 -2
- package/build/src/database/drivers/base.d.ts +50 -0
- package/build/src/database/drivers/base.d.ts.map +1 -1
- package/build/src/database/drivers/base.js +62 -4
- 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 +32 -3
- package/build/src/database/drivers/mysql/index.d.ts.map +1 -1
- package/build/src/database/drivers/mysql/index.js +76 -7
- 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/connection-sql-js.d.ts +8 -1
- package/build/src/database/drivers/sqlite/connection-sql-js.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/connection-sql-js.js +15 -6
- package/build/src/database/drivers/sqlite/index.web.d.ts +5 -5
- package/build/src/database/drivers/sqlite/index.web.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/index.web.js +27 -13
- 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/drivers/sqlite/web-persistence.d.ts +77 -0
- package/build/src/database/drivers/sqlite/web-persistence.d.ts.map +1 -0
- package/build/src/database/drivers/sqlite/web-persistence.js +464 -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/environment-handlers/node/cli/commands/generate/frontend-models.d.ts +30 -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 +53 -1
- package/build/src/frontend-model-controller.d.ts +96 -0
- package/build/src/frontend-model-controller.d.ts.map +1 -1
- package/build/src/frontend-model-controller.js +359 -5
- package/build/src/frontend-model-resource/base-resource.d.ts +4 -2
- package/build/src/frontend-model-resource/base-resource.d.ts.map +1 -1
- package/build/src/frontend-model-resource/base-resource.js +7 -2
- package/build/src/frontend-models/base.d.ts +29 -0
- package/build/src/frontend-models/base.d.ts.map +1 -1
- package/build/src/frontend-models/base.js +131 -3
- package/build/src/frontend-models/query.js +4 -4
- package/build/src/frontend-models/resource-definition.d.ts +6 -0
- package/build/src/frontend-models/resource-definition.d.ts.map +1 -1
- package/build/src/frontend-models/resource-definition.js +272 -3
- 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 +15 -1
- package/build/src/sync/device-identity.d.ts +240 -0
- package/build/src/sync/device-identity.d.ts.map +1 -0
- package/build/src/sync/device-identity.js +454 -0
- package/build/src/sync/local-mutation-log.d.ts +212 -0
- package/build/src/sync/local-mutation-log.d.ts.map +1 -0
- package/build/src/sync/local-mutation-log.js +404 -0
- package/build/src/sync/offline-grant.d.ts +157 -0
- package/build/src/sync/offline-grant.d.ts.map +1 -0
- package/build/src/sync/offline-grant.js +292 -0
- package/build/sync/device-identity.js +503 -0
- package/build/sync/local-mutation-log.js +448 -0
- package/build/sync/offline-grant.js +327 -0
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/src/configuration-types.js +39 -1
- package/src/configuration.js +46 -1
- package/src/database/drivers/base.js +67 -3
- 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 +87 -7
- 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/connection-sql-js.js +16 -5
- package/src/database/drivers/sqlite/index.web.js +29 -15
- package/src/database/drivers/sqlite/sql/remove-index.js +6 -0
- package/src/database/drivers/sqlite/web-persistence.js +521 -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/environment-handlers/node/cli/commands/generate/frontend-models.js +63 -0
- package/src/frontend-model-controller.js +401 -4
- package/src/frontend-model-resource/base-resource.js +5 -1
- package/src/frontend-models/base.js +146 -2
- package/src/frontend-models/query.js +3 -3
- package/src/frontend-models/resource-definition.js +304 -2
- package/src/routes/hooks/frontend-model-command-route-hook.js +16 -0
- package/src/sync/device-identity.js +503 -0
- package/src/sync/local-mutation-log.js +448 -0
- package/src/sync/offline-grant.js +327 -0
package/README.md
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
* Migrations for schema changes and UTC datetime storage (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
|
+
* Client-side offline sync mutation logs and frontend-model optimistic queueing primitives (see [docs/offline-sync.md](docs/offline-sync.md))
|
|
12
|
+
* SQLite web persistence that automatically prefers OPFS, then IndexedDB, and migrates legacy persisted bytes when possible (see [docs/sqlite-web-persistence.md](docs/sqlite-web-persistence.md))
|
|
11
13
|
* Expo / Metro compatibility guidance and a real Expo export check (see [docs/expo-metro-compatibility.md](docs/expo-metro-compatibility.md))
|
|
12
14
|
* Gap-less positional lists with automatic reordering via `actsAsList` (see [docs/acts-as-list.md](docs/acts-as-list.md))
|
|
13
15
|
* Rails-style nested-attribute writes on frontend-model `save()` (see [docs/nested-attributes.md](docs/nested-attributes.md))
|
|
@@ -22,6 +24,7 @@
|
|
|
22
24
|
* EJS-backed mailers with delivery, queueing, and payload rendering support (see [docs/mailers.md](docs/mailers.md))
|
|
23
25
|
* Trusted reverse proxy handling for `request.remoteAddress()` (see [docs/trusted-proxies.md](docs/trusted-proxies.md))
|
|
24
26
|
* In-process driver schema metadata caching (see [docs/schema-metadata-cache.md](docs/schema-metadata-cache.md))
|
|
27
|
+
* Planned local-first shared-resource sync architecture (see [docs/offline-sync.md](docs/offline-sync.md))
|
|
25
28
|
* Named database connection checkouts, bounded pool waits, and debugging held connections (see [docs/database-connections.md](docs/database-connections.md))
|
|
26
29
|
* Optional built-in debug endpoint for inspecting server and database connection state (see [docs/debug-endpoint.md](docs/debug-endpoint.md))
|
|
27
30
|
|
|
@@ -580,7 +583,7 @@ Use `await FrontendModelBase.waitForIdle()` when a test harness or app lifecycle
|
|
|
580
583
|
Frontend-model HTTP requests always use `credentials: "include"` so shared custom commands can set session cookies without app-level transport overrides.
|
|
581
584
|
|
|
582
585
|
Unexpected frontend-model endpoint failures stay client-safe in production with `errorMessage: "Request failed."`.
|
|
583
|
-
Invalid client query descriptors, such as unknown `select`, `where`, `search`, `joins`, `group`, `sort`, `pluck`, or Ransack attributes, return the specific frontend-model query error message with `velocious.code: "frontend-model-query-error"` and are not emitted as framework errors.
|
|
586
|
+
Invalid client query descriptors, such as unknown `select`, `where`, `search`, `joins`, `preload`, `group`, `sort`, `pluck`, or Ransack attributes, return the specific frontend-model query error message with `velocious.code: "frontend-model-query-error"` and are not emitted as framework errors.
|
|
584
587
|
In `development` and `test`, Velocious also includes `debugErrorClass`, `debugErrorMessage`, and `debugBacktrace` fields so browser/system-test failures are easier to diagnose without exposing those details in production.
|
|
585
588
|
Other non-production environments, such as `staging`, keep the same client-safe default unless you explicitly opt in with `exposeInternalErrorsToClients: true`:
|
|
586
589
|
|
|
@@ -605,7 +608,9 @@ configuration.addClientErrorPayloadReporter(async ({error, requestDetails, conte
|
|
|
605
608
|
|
|
606
609
|
`requestDetails` includes `httpMethod`, `path`, and a parsed `body` snapshot when available. The body snapshot redacts common secret keys, truncates large strings and arrays, summarizes uploaded files and buffers without bytes, and compacts oversized frontend-model batches while preserving `requestId`, `model`, `commandType` / `customPath`, and payload shape.
|
|
607
610
|
|
|
608
|
-
For sqlite web databases, Velocious
|
|
611
|
+
For sqlite web databases, Velocious automatically picks the best browser persistence backend it can use: OPFS when a smoke test succeeds, then IndexedDB. Existing persisted bytes in a worse backend are migrated into the selected backend when possible. If neither OPFS nor IndexedDB is usable, Velocious keeps the legacy localStorage-style backend as a compatibility fallback. See [docs/sqlite-web-persistence.md](docs/sqlite-web-persistence.md) for the backend selection details.
|
|
612
|
+
|
|
613
|
+
Velocious defaults to `https://sql.js.org/dist/<file>` for `sql.js` wasm loading. You can override wasm resolution per database config with `locateFile`:
|
|
609
614
|
|
|
610
615
|
```js
|
|
611
616
|
import SqliteDriver from "velocious/build/src/database/drivers/sqlite/index.web.js"
|
|
@@ -718,6 +723,8 @@ Task.validates("name", {presence: true, uniqueness: true})
|
|
|
718
723
|
export default Task
|
|
719
724
|
```
|
|
720
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
|
+
|
|
721
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.
|
|
722
729
|
|
|
723
730
|
Async class APIs initialize record metadata on first use when a model has not
|
|
@@ -1071,6 +1078,7 @@ export default class CreateEvents extends Migration {
|
|
|
1071
1078
|
}
|
|
1072
1079
|
|
|
1073
1080
|
async down() {
|
|
1081
|
+
await this.removeIndex("task_translations", ["task_id", "locale"])
|
|
1074
1082
|
await this.dropTable("task_translations")
|
|
1075
1083
|
await this.dropTable("examples")
|
|
1076
1084
|
await this.dropTable("tasks")
|
|
@@ -320,6 +320,7 @@
|
|
|
320
320
|
* @property {string[]} [relationships] - Relationship names to expose in frontend models. Type and target model are inferred from the backend model's registered relationships.
|
|
321
321
|
* @property {string} [primaryKey] - Primary key attribute name.
|
|
322
322
|
* @property {FrontendModelResourceServerConfiguration} [server] - Optional legacy backend behavior overrides for built-in frontend actions.
|
|
323
|
+
* @property {FrontendModelResourceSyncConfiguration | boolean} [sync] - Optional safe local/offline sync policy metadata. `policy` participates in the hash but is not exposed to generated frontend config/manifest.
|
|
323
324
|
*/
|
|
324
325
|
|
|
325
326
|
/**
|
|
@@ -338,13 +339,49 @@
|
|
|
338
339
|
*/
|
|
339
340
|
|
|
340
341
|
/**
|
|
341
|
-
*
|
|
342
|
+
* JSON value accepted by sync policy metadata/hash inputs.
|
|
343
|
+
* @typedef {null | string | number | boolean | unknown[] | Record<string, unknown>} FrontendModelSyncJsonValue
|
|
344
|
+
*/
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Frontend-model local/offline sync policy config. `metadata` is exposed to
|
|
348
|
+
* frontends and peers; `policy` is hashed but intentionally omitted from
|
|
349
|
+
* frontend-safe manifests.
|
|
350
|
+
* @typedef {object} FrontendModelResourceSyncConfiguration
|
|
351
|
+
* @property {boolean} [enabled] - Whether the resource is sync-enabled. Defaults to true when `sync` is configured.
|
|
352
|
+
* @property {string[]} [operations] - Sync operation names such as `index`, `find`, `create`, `update`, custom domain commands, etc.
|
|
353
|
+
* @property {string | number} [policyVersion] - App-controlled policy version used as a stable change signal.
|
|
354
|
+
* @property {Record<string, FrontendModelSyncJsonValue>} [metadata] - Safe frontend-visible metadata.
|
|
355
|
+
* @property {Record<string, FrontendModelSyncJsonValue>} [policy] - Deterministic non-secret policy inputs included in the policy hash only.
|
|
356
|
+
*/
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Velocious sync configuration.
|
|
360
|
+
* @typedef {object} VelociousSyncConfiguration
|
|
361
|
+
* @property {import("./sync/device-identity.js").SyncJsonWebKey | null} [deviceCertificateBackendPublicKey] - Public backend key used to verify offline device certificates for sync replay.
|
|
362
|
+
* @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.
|
|
363
|
+
* @property {number} [offlineGrantTtlMs] - Default offline grant TTL in milliseconds. Defaults to 24 hours.
|
|
364
|
+
*/
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Frontend-safe normalized sync metadata.
|
|
368
|
+
* @typedef {object} NormalizedFrontendModelResourceSyncConfiguration
|
|
369
|
+
* @property {boolean} enabled - Whether the resource is sync-enabled.
|
|
370
|
+
* @property {string[]} operations - Sorted, duplicate-free sync operation names.
|
|
371
|
+
* @property {string | null} policyVersion - App-controlled policy version, or null.
|
|
372
|
+
* @property {string} policyHash - Deterministic sha256 hash of safe policy inputs.
|
|
373
|
+
* @property {Record<string, FrontendModelSyncJsonValue>} [metadata] - Safe frontend-visible metadata.
|
|
374
|
+
*/
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* @typedef {Omit<FrontendModelResourceConfiguration, "abilities" | "builtInCollectionCommands" | "builtInMemberCommands" | "collectionCommands" | "commands" | "memberCommands" | "sync"> & {
|
|
342
378
|
* abilities: FrontendModelResourceAbilitiesConfiguration
|
|
343
379
|
* builtInCollectionCommands: Record<string, string>
|
|
344
380
|
* builtInMemberCommands: Record<string, string>
|
|
345
381
|
* collectionCommands: Record<string, string>
|
|
346
382
|
* commandMetadata: Record<string, {args: Array<{name: string, type: string}>, returnType: string | null}>
|
|
347
383
|
* memberCommands: Record<string, string>
|
|
384
|
+
* sync?: NormalizedFrontendModelResourceSyncConfiguration
|
|
348
385
|
* }} NormalizedFrontendModelResourceConfiguration
|
|
349
386
|
*/
|
|
350
387
|
|
|
@@ -471,6 +508,7 @@
|
|
|
471
508
|
* @property {string[]} locales - Supported locales.
|
|
472
509
|
* @property {LocaleFallbacksType} localeFallbacks - Locale fallback map.
|
|
473
510
|
* @property {StructureSqlConfiguration} [structureSql] - Structure SQL generation configuration.
|
|
511
|
+
* @property {VelociousSyncConfiguration} [sync] - Local/offline sync framework configuration.
|
|
474
512
|
* @property {TenantResolverType} [tenantResolver] - Resolver for creating request-scoped tenant context objects.
|
|
475
513
|
* @property {TenantDatabaseResolverType} [tenantDatabaseResolver] - Resolver for deriving tenant-specific database config overrides.
|
|
476
514
|
* @property {Record<string, TenantDatabaseProviderType>} [tenantDatabaseProviders] - Tenant database lifecycle providers keyed by database identifier.
|
package/build/configuration.js
CHANGED
|
@@ -20,6 +20,7 @@ import VelociousWebsocketChannelSubscribers from "./http-server/websocket-channe
|
|
|
20
20
|
import {CurrentConfigurationNotSetError, currentConfiguration, setCurrentConfiguration} from "./current-configuration.js"
|
|
21
21
|
import {requestDetails} from "./error-reporting/request-details.js"
|
|
22
22
|
import {frontendModelResourceClassFromDefinition, frontendModelResourceConfigurationFromDefinition, frontendModelResourcesForBackendProject} from "./frontend-models/resource-definition.js"
|
|
23
|
+
import {currentOfflineGrantSigningKey, normalizeOfflineGrantSigningKey} from "./sync/offline-grant.js"
|
|
23
24
|
import PluginRoutes from "./routes/plugin-routes.js"
|
|
24
25
|
import restArgsError from "./utils/rest-args-error.js"
|
|
25
26
|
import {validateTimeZone} from "./time-zone.js"
|
|
@@ -126,7 +127,7 @@ export default class VelociousConfiguration {
|
|
|
126
127
|
* Runs constructor.
|
|
127
128
|
* @param {import("./configuration-types.js").ConfigurationArgsType} args - Configuration arguments.
|
|
128
129
|
*/
|
|
129
|
-
constructor({abilityResolver, abilityResources, attachments, autoload = true, backgroundJobs, backendProjects, beacon, cookieSecret, cors, database, debug = false, debugEndpoint = false, directory, enforceTenantDatabaseScopes = true, environment, environmentHandler, exposeInternalErrorsToClients = false, httpServer, initializeModels, initializers, locale, localeFallbacks, locales, logging, mailerBackend, requestTimeoutMs, routeResolverHooks, scheduledBackgroundJobs, structureSql, tenantDatabaseProviders, tenantDatabaseResolver, tenantResolver, testing, timeZone, timezoneOffsetMinutes, trustedProxies, websocketChannelResolver, websocketMessageHandlerResolver, ...restArgs}) {
|
|
130
|
+
constructor({abilityResolver, abilityResources, attachments, autoload = true, backgroundJobs, backendProjects, beacon, cookieSecret, cors, database, debug = false, debugEndpoint = false, directory, enforceTenantDatabaseScopes = true, environment, environmentHandler, exposeInternalErrorsToClients = false, httpServer, initializeModels, initializers, locale, localeFallbacks, locales, logging, mailerBackend, requestTimeoutMs, routeResolverHooks, scheduledBackgroundJobs, structureSql, sync, tenantDatabaseProviders, tenantDatabaseResolver, tenantResolver, testing, timeZone, timezoneOffsetMinutes, trustedProxies, websocketChannelResolver, websocketMessageHandlerResolver, ...restArgs}) {
|
|
130
131
|
restArgsError(restArgs)
|
|
131
132
|
|
|
132
133
|
this._abilityResolver = abilityResolver
|
|
@@ -189,6 +190,7 @@ export default class VelociousConfiguration {
|
|
|
189
190
|
this._trustedProxies = trustedProxies
|
|
190
191
|
this._requestTimeoutMs = requestTimeoutMs
|
|
191
192
|
this._structureSql = structureSql
|
|
193
|
+
this._sync = this._normalizeSyncConfiguration(sync)
|
|
192
194
|
this._tenantDatabaseProviders = tenantDatabaseProviders || {}
|
|
193
195
|
this._tenantDatabaseResolver = tenantDatabaseResolver
|
|
194
196
|
this._tenantResolver = tenantResolver
|
|
@@ -380,6 +382,49 @@ export default class VelociousConfiguration {
|
|
|
380
382
|
return this._cookieSecret
|
|
381
383
|
}
|
|
382
384
|
|
|
385
|
+
/**
|
|
386
|
+
* Runs get sync configuration.
|
|
387
|
+
* @returns {import("./configuration-types.js").VelociousSyncConfiguration} - Sync configuration.
|
|
388
|
+
*/
|
|
389
|
+
getSyncConfiguration() {
|
|
390
|
+
return this._sync
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Runs current offline grant signing key.
|
|
395
|
+
* @returns {import("./sync/offline-grant.js").OfflineGrantSigningKey} - Current signing key.
|
|
396
|
+
*/
|
|
397
|
+
currentOfflineGrantSigningKey() {
|
|
398
|
+
const signingKeys = this.getSyncConfiguration().offlineGrantSigningKeys
|
|
399
|
+
|
|
400
|
+
return currentOfflineGrantSigningKey(signingKeys)
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Normalizes sync configuration.
|
|
405
|
+
* @param {import("./configuration-types.js").VelociousSyncConfiguration | undefined} sync - Sync configuration.
|
|
406
|
+
* @returns {import("./configuration-types.js").VelociousSyncConfiguration} - Normalized sync configuration.
|
|
407
|
+
*/
|
|
408
|
+
_normalizeSyncConfiguration(sync) {
|
|
409
|
+
const deviceCertificateBackendPublicKey = sync?.deviceCertificateBackendPublicKey || null
|
|
410
|
+
const offlineGrantSigningKeys = sync?.offlineGrantSigningKeys || []
|
|
411
|
+
const offlineGrantTtlMs = sync?.offlineGrantTtlMs
|
|
412
|
+
|
|
413
|
+
if (deviceCertificateBackendPublicKey !== null && (typeof deviceCertificateBackendPublicKey !== "object" || Array.isArray(deviceCertificateBackendPublicKey))) {
|
|
414
|
+
throw new Error("sync.deviceCertificateBackendPublicKey must be a public JSON Web Key object")
|
|
415
|
+
}
|
|
416
|
+
if (!Array.isArray(offlineGrantSigningKeys)) throw new Error("sync.offlineGrantSigningKeys must be an array")
|
|
417
|
+
if (offlineGrantTtlMs !== undefined && (!Number.isInteger(offlineGrantTtlMs) || offlineGrantTtlMs <= 0)) {
|
|
418
|
+
throw new Error("sync.offlineGrantTtlMs must be a positive integer number of milliseconds")
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
return {
|
|
422
|
+
deviceCertificateBackendPublicKey,
|
|
423
|
+
offlineGrantSigningKeys: offlineGrantSigningKeys.map((key) => normalizeOfflineGrantSigningKey(key)),
|
|
424
|
+
offlineGrantTtlMs: offlineGrantTtlMs || 24 * 60 * 60 * 1000
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
383
428
|
/**
|
|
384
429
|
* Runs get database configuration.
|
|
385
430
|
* @returns {Record<string, import("./configuration-types.js").DatabaseConfigurationType>} - The database configuration.
|
|
@@ -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
|
|
@@ -50,6 +56,7 @@
|
|
|
50
56
|
* @property {string} [logName] - Query log subject.
|
|
51
57
|
* @property {boolean} [logQuery] - Whether to log the query.
|
|
52
58
|
* @property {boolean} [processListComment] - Whether to add process-list comments to the query.
|
|
59
|
+
* @property {boolean} [sessionTimeZone] - Whether to ensure the configured database session time zone before the query.
|
|
53
60
|
* @property {string} [sourceStack] - Stack captured at the caller boundary.
|
|
54
61
|
*/
|
|
55
62
|
|
|
@@ -237,6 +244,14 @@ export default class VelociousDatabaseDriversBase {
|
|
|
237
244
|
// No-op by default
|
|
238
245
|
}
|
|
239
246
|
|
|
247
|
+
/**
|
|
248
|
+
* Flushes pending writes that the driver delayed for persistence.
|
|
249
|
+
* @returns {Promise<void>} - Resolves when pending writes are durable.
|
|
250
|
+
*/
|
|
251
|
+
async flushPendingWrites() {
|
|
252
|
+
// No-op by default
|
|
253
|
+
}
|
|
254
|
+
|
|
240
255
|
/**
|
|
241
256
|
* Runs set connection checkout name.
|
|
242
257
|
* @param {string | undefined} name - Human-readable name for this active checkout.
|
|
@@ -298,6 +313,16 @@ export default class VelociousDatabaseDriversBase {
|
|
|
298
313
|
throw new Error("'createIndexSQLs' not implemented")
|
|
299
314
|
}
|
|
300
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
|
+
|
|
301
326
|
/**
|
|
302
327
|
* Runs create table.
|
|
303
328
|
* @param {import("../table-data/index.js").default} tableData - Table data.
|
|
@@ -1041,12 +1066,14 @@ export default class VelociousDatabaseDriversBase {
|
|
|
1041
1066
|
let result
|
|
1042
1067
|
|
|
1043
1068
|
try {
|
|
1069
|
+
const runQueryActualWithHooks = async () => await this._queryActualWithHooks(querySql, options)
|
|
1070
|
+
|
|
1044
1071
|
if (requestTiming && tries === 1) {
|
|
1045
|
-
result = await requestTiming.measureDbQuery(
|
|
1072
|
+
result = await requestTiming.measureDbQuery(runQueryActualWithHooks)
|
|
1046
1073
|
} else if (requestTiming) {
|
|
1047
|
-
result = await requestTiming.measure("db",
|
|
1074
|
+
result = await requestTiming.measure("db", runQueryActualWithHooks)
|
|
1048
1075
|
} else {
|
|
1049
|
-
result = await
|
|
1076
|
+
result = await runQueryActualWithHooks()
|
|
1050
1077
|
}
|
|
1051
1078
|
} finally {
|
|
1052
1079
|
this._activeQuery = previousActiveQuery
|
|
@@ -1070,6 +1097,42 @@ export default class VelociousDatabaseDriversBase {
|
|
|
1070
1097
|
return result
|
|
1071
1098
|
}
|
|
1072
1099
|
|
|
1100
|
+
/**
|
|
1101
|
+
* Runs query actual with before/after hooks.
|
|
1102
|
+
* @param {string} sql - SQL string.
|
|
1103
|
+
* @param {QueryOptions} options - Query options.
|
|
1104
|
+
* @returns {Promise<QueryResultType>} - Resolves with the query.
|
|
1105
|
+
*/
|
|
1106
|
+
async _queryActualWithHooks(sql, options) {
|
|
1107
|
+
await this.beforeQuery(sql, options)
|
|
1108
|
+
|
|
1109
|
+
try {
|
|
1110
|
+
return await this._queryActual(sql)
|
|
1111
|
+
} finally {
|
|
1112
|
+
await this.afterQuery(sql, options)
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
/**
|
|
1117
|
+
* Hook that runs immediately before a SQL query is sent to the driver.
|
|
1118
|
+
* @param {string} _sql - SQL string.
|
|
1119
|
+
* @param {QueryOptions} _options - Query options.
|
|
1120
|
+
* @returns {Promise<void>} - Resolves when complete.
|
|
1121
|
+
*/
|
|
1122
|
+
async beforeQuery(_sql, _options) {
|
|
1123
|
+
// No-op by default
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
/**
|
|
1127
|
+
* Hook that runs immediately after a SQL query has completed or failed.
|
|
1128
|
+
* @param {string} _sql - SQL string.
|
|
1129
|
+
* @param {QueryOptions} _options - Query options.
|
|
1130
|
+
* @returns {Promise<void>} - Resolves when complete.
|
|
1131
|
+
*/
|
|
1132
|
+
async afterQuery(_sql, _options) {
|
|
1133
|
+
// No-op by default
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1073
1136
|
/**
|
|
1074
1137
|
* Runs get debug snapshot.
|
|
1075
1138
|
* @returns {DatabaseConnectionDebugSnapshot} - Diagnostic snapshot for this connection.
|
|
@@ -1504,6 +1567,7 @@ export default class VelociousDatabaseDriversBase {
|
|
|
1504
1567
|
}
|
|
1505
1568
|
}
|
|
1506
1569
|
})
|
|
1570
|
+
await this.flushPendingWrites()
|
|
1507
1571
|
}
|
|
1508
1572
|
|
|
1509
1573
|
/**
|
|
@@ -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"
|
|
@@ -30,15 +31,20 @@ import Update from "./sql/update.js"
|
|
|
30
31
|
const MYSQL_INDEFINITE_LOCK_TIMEOUT_SECONDS = 60 * 60 * 24 * 365
|
|
31
32
|
|
|
32
33
|
export default class VelociousDatabaseDriversMysql extends Base{
|
|
34
|
+
/** @type {string | null} */
|
|
35
|
+
_desiredSessionTimeZone = "+00:00"
|
|
36
|
+
|
|
37
|
+
/** @type {string | null} */
|
|
38
|
+
_currentSessionTimeZone = null
|
|
39
|
+
|
|
33
40
|
/**
|
|
34
41
|
* Runs connect.
|
|
35
42
|
* @returns {Promise<void>} - Resolves when complete.
|
|
36
43
|
*/
|
|
37
44
|
async connect() {
|
|
45
|
+
this.resetCurrentSessionTimeZone()
|
|
38
46
|
this.pool = mysql.createPool(Object.assign({connectionLimit: 1}, this.connectArgs()))
|
|
39
47
|
this.pool.on("error", this.onPoolError)
|
|
40
|
-
|
|
41
|
-
await this.setSessionTimezoneToUtc()
|
|
42
48
|
}
|
|
43
49
|
|
|
44
50
|
/**
|
|
@@ -56,6 +62,7 @@ export default class VelociousDatabaseDriversMysql extends Base{
|
|
|
56
62
|
async close() {
|
|
57
63
|
await this.pool?.end()
|
|
58
64
|
this.pool = undefined
|
|
65
|
+
this.resetCurrentSessionTimeZone()
|
|
59
66
|
}
|
|
60
67
|
|
|
61
68
|
/**
|
|
@@ -64,7 +71,7 @@ export default class VelociousDatabaseDriversMysql extends Base{
|
|
|
64
71
|
* @returns {Promise<void>} - Resolves when complete.
|
|
65
72
|
*/
|
|
66
73
|
async setConnectionCheckoutName(name) {
|
|
67
|
-
await this.query(`SET @velocious_connection_checkout_name = ${name === undefined ? "NULL" : this.quote(name)}`, {logName: "Set Connection Checkout Name", processListComment: false})
|
|
74
|
+
await this.query(`SET @velocious_connection_checkout_name = ${name === undefined ? "NULL" : this.quote(name)}`, {logName: "Set Connection Checkout Name", processListComment: false, sessionTimeZone: false})
|
|
68
75
|
await super.setConnectionCheckoutName(name)
|
|
69
76
|
}
|
|
70
77
|
|
|
@@ -73,16 +80,77 @@ export default class VelociousDatabaseDriversMysql extends Base{
|
|
|
73
80
|
* @returns {Promise<void>} - Resolves when complete.
|
|
74
81
|
*/
|
|
75
82
|
async clearConnectionCheckoutName() {
|
|
76
|
-
await this.query("SET @velocious_connection_checkout_name = NULL", {logName: "Clear Connection Checkout Name", processListComment: false})
|
|
83
|
+
await this.query("SET @velocious_connection_checkout_name = NULL", {logName: "Clear Connection Checkout Name", processListComment: false, sessionTimeZone: false})
|
|
77
84
|
await super.clearConnectionCheckoutName()
|
|
78
85
|
}
|
|
79
86
|
|
|
80
87
|
/**
|
|
81
|
-
*
|
|
88
|
+
* Hook before every query.
|
|
89
|
+
* @param {string} _sql - SQL string.
|
|
90
|
+
* @param {import("../base.js").QueryOptions} options - Query options.
|
|
82
91
|
* @returns {Promise<void>} - Resolves when complete.
|
|
83
92
|
*/
|
|
84
|
-
async
|
|
85
|
-
|
|
93
|
+
async beforeQuery(_sql, options) {
|
|
94
|
+
if (options.sessionTimeZone !== false) await this.ensureSessionTimeZone()
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Gets the desired database session time zone for this connection context.
|
|
99
|
+
* @returns {string | null} - Desired session time zone.
|
|
100
|
+
*/
|
|
101
|
+
getDesiredSessionTimeZone() {
|
|
102
|
+
return this._desiredSessionTimeZone
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Sets the desired database session time zone without querying MySQL immediately.
|
|
107
|
+
* @param {string | null} timeZone - Desired session time zone.
|
|
108
|
+
*/
|
|
109
|
+
setDesiredSessionTimeZone(timeZone) {
|
|
110
|
+
this._desiredSessionTimeZone = timeZone
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Gets the database session time zone last confirmed through SET time_zone.
|
|
115
|
+
* @returns {string | null} - Current known session time zone.
|
|
116
|
+
*/
|
|
117
|
+
getCurrentSessionTimeZone() {
|
|
118
|
+
return this._currentSessionTimeZone
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Clears the current known database session time zone when the physical connection changes.
|
|
123
|
+
*/
|
|
124
|
+
resetCurrentSessionTimeZone() {
|
|
125
|
+
this._currentSessionTimeZone = null
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Ensures MySQL has the desired session time zone before user SQL runs.
|
|
130
|
+
* @returns {Promise<boolean>} - True when SET time_zone was executed.
|
|
131
|
+
*/
|
|
132
|
+
async ensureSessionTimeZone() {
|
|
133
|
+
const desiredSessionTimeZone = this.getDesiredSessionTimeZone()
|
|
134
|
+
|
|
135
|
+
if (desiredSessionTimeZone === null || this.getCurrentSessionTimeZone() === desiredSessionTimeZone) return false
|
|
136
|
+
|
|
137
|
+
await this.setSessionTimeZone(desiredSessionTimeZone)
|
|
138
|
+
|
|
139
|
+
return true
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Sets the database session time zone if it changed from the last confirmed value.
|
|
144
|
+
* @param {string} timeZone - Session time zone value accepted by MySQL.
|
|
145
|
+
* @returns {Promise<boolean>} - True when SET time_zone was executed.
|
|
146
|
+
*/
|
|
147
|
+
async setSessionTimeZone(timeZone) {
|
|
148
|
+
if (this.getCurrentSessionTimeZone() === timeZone) return false
|
|
149
|
+
|
|
150
|
+
await this._queryActual(`SET time_zone = ${this.quote(timeZone)}`)
|
|
151
|
+
this._currentSessionTimeZone = timeZone
|
|
152
|
+
|
|
153
|
+
return true
|
|
86
154
|
}
|
|
87
155
|
|
|
88
156
|
/**
|
|
@@ -160,6 +228,18 @@ export default class VelociousDatabaseDriversMysql extends Base{
|
|
|
160
228
|
return await createIndex.toSQLs()
|
|
161
229
|
}
|
|
162
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
|
+
|
|
163
243
|
/**
|
|
164
244
|
* Runs create table sql.
|
|
165
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
|
|
@@ -8,16 +8,28 @@ export default class VelociousDatabaseDriversSqliteConnectionSqlJs {
|
|
|
8
8
|
* Runs constructor.
|
|
9
9
|
* @param {import("../base.js").default} driver - Database driver instance.
|
|
10
10
|
* @param {import("sql.js").Database} connection - Connection.
|
|
11
|
+
* @param {import("./web-persistence.js").SqliteWebPersistence} persistence - Database persistence adapter.
|
|
11
12
|
*/
|
|
12
|
-
constructor(driver, connection) {
|
|
13
|
+
constructor(driver, connection, persistence) {
|
|
13
14
|
this.connection = connection
|
|
14
15
|
this.driver = driver
|
|
16
|
+
this.persistence = persistence
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
async close() {
|
|
18
|
-
await this.
|
|
20
|
+
await this.flushDatabaseSave()
|
|
19
21
|
await this.connection.close()
|
|
20
22
|
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Flushes any debounced database save and waits until persistence is complete.
|
|
26
|
+
* @returns {Promise<void>} - Resolves when the current database bytes are stored.
|
|
27
|
+
*/
|
|
28
|
+
async flushDatabaseSave() {
|
|
29
|
+
this.saveDatabaseDebounce.clear()
|
|
30
|
+
await this.saveDatabase()
|
|
31
|
+
}
|
|
32
|
+
|
|
21
33
|
/**
|
|
22
34
|
* Runs query.
|
|
23
35
|
* @param {string} sql - SQL string.
|
|
@@ -36,10 +48,9 @@ export default class VelociousDatabaseDriversSqliteConnectionSqlJs {
|
|
|
36
48
|
}
|
|
37
49
|
|
|
38
50
|
saveDatabase = async () => {
|
|
39
|
-
const
|
|
51
|
+
const databaseContent = this.connection.export()
|
|
40
52
|
|
|
41
|
-
|
|
42
|
-
await this.driver.betterLocalStorage.set(this.driver.localStorageName(), localStorageContent)
|
|
53
|
+
await this.persistence.save(databaseContent)
|
|
43
54
|
}
|
|
44
55
|
|
|
45
56
|
saveDatabaseDebounce = debounce(this.saveDatabase, 500)
|