velocious 1.0.510 → 1.0.512
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 +1 -0
- package/build/database/advisory-lock-runner.js +192 -0
- package/build/database/drivers/mysql/index.js +3 -0
- package/build/database/live-query.js +244 -0
- package/build/database/record/index.js +48 -111
- package/build/database/record-changes.js +135 -0
- package/build/database/use-live-query.js +118 -0
- package/build/src/configuration-types.d.ts +1 -1
- package/build/src/configuration.d.ts +1 -1
- package/build/src/database/advisory-lock-runner.d.ts +124 -0
- package/build/src/database/advisory-lock-runner.d.ts.map +1 -0
- package/build/src/database/advisory-lock-runner.js +176 -0
- package/build/src/database/drivers/mysql/index.d.ts +2 -2
- package/build/src/database/drivers/mysql/index.d.ts.map +1 -1
- package/build/src/database/drivers/mysql/index.js +3 -1
- package/build/src/database/live-query.d.ts +168 -0
- package/build/src/database/live-query.d.ts.map +1 -0
- package/build/src/database/live-query.js +211 -0
- package/build/src/database/record/attachments/handle.d.ts +1 -1
- package/build/src/database/record/index.d.ts +24 -61
- package/build/src/database/record/index.d.ts.map +1 -1
- package/build/src/database/record/index.js +46 -108
- package/build/src/database/record-changes.d.ts +111 -0
- package/build/src/database/record-changes.d.ts.map +1 -0
- package/build/src/database/record-changes.js +115 -0
- package/build/src/database/use-live-query.d.ts +44 -0
- package/build/src/database/use-live-query.d.ts.map +1 -0
- package/build/src/database/use-live-query.js +97 -0
- package/build/src/sync/sync-api-client.d.ts.map +1 -1
- package/build/src/sync/sync-api-client.js +17 -10
- package/build/src/sync/sync-envelope-replay-service.d.ts +1 -1
- package/build/src/sync/sync-realtime-bridge.d.ts.map +1 -1
- package/build/src/sync/sync-realtime-bridge.js +9 -5
- package/build/src/sync/sync-replay-upsert-applier.d.ts +2 -2
- package/build/sync/sync-api-client.js +19 -11
- package/build/sync/sync-realtime-bridge.js +9 -4
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/database/advisory-lock-runner.js +192 -0
- package/src/database/drivers/mysql/index.js +3 -0
- package/src/database/live-query.js +244 -0
- package/src/database/record/index.js +48 -111
- package/src/database/record-changes.js +135 -0
- package/src/database/use-live-query.js +118 -0
- package/src/sync/sync-api-client.js +19 -11
- package/src/sync/sync-realtime-bridge.js +9 -4
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import EventEmitter from "../utils/event-emitter.js"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* RecordModelClass type.
|
|
7
|
+
* @typedef {typeof import("./record/index.js").default} RecordModelClass */
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* RecordChangeOperation type.
|
|
11
|
+
* @typedef {"create" | "update" | "destroy"} RecordChangeOperation */
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* RecordChangeEvent type.
|
|
15
|
+
* @typedef {object} RecordChangeEvent
|
|
16
|
+
* @property {RecordModelClass} modelClass - Model class whose row changed.
|
|
17
|
+
* @property {RecordChangeOperation} operation - The committed operation.
|
|
18
|
+
* @property {InstanceType<RecordModelClass>} record - The committed record instance.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* RecordChangeListener type.
|
|
23
|
+
* @typedef {(event: RecordChangeEvent) => void} RecordChangeListener */
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Framework-level bus for committed local model changes. Records emit here once
|
|
27
|
+
* per commit (see `VelociousDatabaseRecord.save`/`destroy`), so local writes,
|
|
28
|
+
* pull applies, and realtime applies converge on one uniform signal that live
|
|
29
|
+
* queries subscribe to. Emission is keyed by model name; a `batch(...)` window
|
|
30
|
+
* coalesces a burst of commits into a single event per model class.
|
|
31
|
+
*/
|
|
32
|
+
class RecordChanges {
|
|
33
|
+
/**
|
|
34
|
+
* Underlying event bus keyed by model name.
|
|
35
|
+
* @type {import("eventemitter3").EventEmitter} */
|
|
36
|
+
_emitter = new EventEmitter()
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Number of open batch windows; while positive, emits buffer instead of dispatching.
|
|
40
|
+
* @type {number} */
|
|
41
|
+
_batchDepth = 0
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Latest buffered event per model name, dispatched once when the outermost batch ends.
|
|
45
|
+
* @type {Map<string, RecordChangeEvent>} */
|
|
46
|
+
_bufferedEvents = new Map()
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Subscribes a listener to committed changes of a model class.
|
|
50
|
+
* @param {RecordModelClass} modelClass - Model class to observe.
|
|
51
|
+
* @param {RecordChangeListener} listener - Listener called with each change event.
|
|
52
|
+
* @returns {() => void} Unsubscribe callback.
|
|
53
|
+
*/
|
|
54
|
+
subscribe(modelClass, listener) {
|
|
55
|
+
const eventName = modelClass.getModelName()
|
|
56
|
+
|
|
57
|
+
this._emitter.on(eventName, listener)
|
|
58
|
+
|
|
59
|
+
return () => {
|
|
60
|
+
this._emitter.off(eventName, listener)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Whether any listener is currently observing the given model class. Callers on
|
|
66
|
+
* the write path use this to skip emitting entirely when nothing is watching,
|
|
67
|
+
* keeping server-side saves free of live-query overhead.
|
|
68
|
+
* @param {RecordModelClass} modelClass - Model class to check.
|
|
69
|
+
* @returns {boolean} Whether listeners exist for the model class.
|
|
70
|
+
*/
|
|
71
|
+
hasListeners(modelClass) {
|
|
72
|
+
return this._emitter.listenerCount(modelClass.getModelName()) > 0
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Emits a committed change. While a batch window is open the event is buffered
|
|
77
|
+
* and deduplicated by model class, so a batch of N commits dispatches a single
|
|
78
|
+
* event per model class when the outermost batch ends.
|
|
79
|
+
* @param {RecordChangeEvent} event - Change event to dispatch.
|
|
80
|
+
* @returns {void}
|
|
81
|
+
*/
|
|
82
|
+
emit(event) {
|
|
83
|
+
if (this._batchDepth > 0) {
|
|
84
|
+
this._bufferedEvents.set(event.modelClass.getModelName(), event)
|
|
85
|
+
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
this._emitter.emit(event.modelClass.getModelName(), event)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Runs a callback with change dispatch coalesced: every change committed while
|
|
94
|
+
* the callback runs buffers, and the outermost batch flushes a single event per
|
|
95
|
+
* changed model class after it resolves. Nested batches share one flush. Sync
|
|
96
|
+
* appliers wrap their per-row apply loop in this so a large pull or realtime
|
|
97
|
+
* push triggers one re-run per live query instead of one per applied row.
|
|
98
|
+
* @template T
|
|
99
|
+
* @param {() => Promise<T> | T} callback - Work whose committed changes should coalesce.
|
|
100
|
+
* @returns {Promise<T>} The callback result.
|
|
101
|
+
*/
|
|
102
|
+
async batch(callback) {
|
|
103
|
+
this._batchDepth++
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
return await callback()
|
|
107
|
+
} finally {
|
|
108
|
+
this._batchDepth--
|
|
109
|
+
|
|
110
|
+
if (this._batchDepth === 0) this._flushBufferedEvents()
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Dispatches and clears the buffered per-model events collected during a batch.
|
|
116
|
+
* @returns {void}
|
|
117
|
+
*/
|
|
118
|
+
_flushBufferedEvents() {
|
|
119
|
+
const bufferedEvents = this._bufferedEvents
|
|
120
|
+
|
|
121
|
+
this._bufferedEvents = new Map()
|
|
122
|
+
|
|
123
|
+
for (const event of bufferedEvents.values()) {
|
|
124
|
+
this._emitter.emit(event.modelClass.getModelName(), event)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Shared singleton so record commits and live queries meet on one bus.
|
|
131
|
+
* @type {RecordChanges} */
|
|
132
|
+
const recordChanges = new RecordChanges()
|
|
133
|
+
|
|
134
|
+
export default recordChanges
|
|
135
|
+
export {RecordChanges}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import {useCallback, useEffect, useMemo, useRef, useSyncExternalStore} from "react"
|
|
4
|
+
|
|
5
|
+
import LiveQuery from "./live-query.js"
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* RecordModelClass type.
|
|
9
|
+
* @typedef {typeof import("./record/index.js").default} RecordModelClass */
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* LiveQuerySource type.
|
|
13
|
+
* @template T
|
|
14
|
+
* @typedef {import("./live-query.js").LiveQuerySource<T>} LiveQuerySource */
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* LiveQueryState type.
|
|
18
|
+
* @template T
|
|
19
|
+
* @typedef {import("./live-query.js").LiveQueryState<T>} LiveQueryState */
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* UseLiveQueryOptions type.
|
|
23
|
+
* @typedef {object} UseLiveQueryOptions
|
|
24
|
+
* @property {boolean} [active] - Whether the query is active. Default true; pass false to pause and return the empty state.
|
|
25
|
+
* @property {number} [debounce] - Trailing debounce in ms for re-runs. Defaults to microtask coalescing.
|
|
26
|
+
* @property {RecordModelClass[]} [models] - Model classes to observe. Defaults to the query's model class; pass this to also react to joined models.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Stable empty state returned while there is no active query, so a paused hook
|
|
31
|
+
* keeps a referentially stable snapshot for `useSyncExternalStore`.
|
|
32
|
+
* @type {LiveQueryState<?>} */
|
|
33
|
+
const EMPTY_STATE = {error: null, loading: false, results: []}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Assigns and stores a stable identity key for query sources without a `toSql`.
|
|
37
|
+
* @type {WeakMap<object, string>} */
|
|
38
|
+
const queryIdentityKeys = new WeakMap()
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Monotonic counter backing the queryIdentityKeys registry.
|
|
42
|
+
* @type {number} */
|
|
43
|
+
let nextQueryIdentity = 0
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Builds a dependency key identifying a query's semantics so the underlying
|
|
47
|
+
* controller is rebuilt when they change. Model-class queries expose `toSql`, so
|
|
48
|
+
* distinct conditions yield distinct keys; other sources fall back to a stable
|
|
49
|
+
* per-object identity (such sources must be memoized by the caller).
|
|
50
|
+
* @param {LiveQuerySource<?> & {toSql?: () => ?}} query - Query source.
|
|
51
|
+
* @param {RecordModelClass[] | undefined} models - Explicit model classes to observe.
|
|
52
|
+
* @returns {string} Dependency key.
|
|
53
|
+
*/
|
|
54
|
+
function liveQueryDependencyKey(query, models) {
|
|
55
|
+
const modelNames = (models ?? [query.getModelClass()]).map((modelClass) => modelClass.getModelName()).join(",")
|
|
56
|
+
|
|
57
|
+
if (typeof query.toSql === "function") return `${modelNames}::${String(query.toSql())}`
|
|
58
|
+
|
|
59
|
+
let identityKey = queryIdentityKeys.get(query)
|
|
60
|
+
|
|
61
|
+
if (identityKey === undefined) {
|
|
62
|
+
identityKey = `#${++nextQueryIdentity}`
|
|
63
|
+
queryIdentityKeys.set(query, identityKey)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return `${modelNames}::${identityKey}`
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* React hook declaring what a screen shows and keeping it current from committed
|
|
71
|
+
* local model changes. Runs `query.toArray()` once, subscribes to the query's
|
|
72
|
+
* model class(es) on the record-change bus, and re-runs (coalesced, stale-safe)
|
|
73
|
+
* whenever a watched model commits — so local writes, pull applies, and realtime
|
|
74
|
+
* applies all refresh the results without any manual refresh plumbing.
|
|
75
|
+
* @template T
|
|
76
|
+
* @param {(LiveQuerySource<T> & {toSql?: () => ?}) | null | undefined} query - Query source, e.g. `Model.where({...})`.
|
|
77
|
+
* @param {UseLiveQueryOptions} [options] - Hook options.
|
|
78
|
+
* @returns {LiveQueryState<T>} Current results, loading, and last error.
|
|
79
|
+
*/
|
|
80
|
+
export default function useLiveQuery(query, options = {}) {
|
|
81
|
+
const {active = true, debounce, models} = options
|
|
82
|
+
const enabled = active && Boolean(query)
|
|
83
|
+
const dependencyKey = enabled && query ? liveQueryDependencyKey(query, models) : "disabled"
|
|
84
|
+
|
|
85
|
+
const queryRef = useRef(query)
|
|
86
|
+
const modelsRef = useRef(models)
|
|
87
|
+
|
|
88
|
+
queryRef.current = query
|
|
89
|
+
modelsRef.current = models
|
|
90
|
+
|
|
91
|
+
const liveQuery = useMemo(() => {
|
|
92
|
+
if (!enabled || !queryRef.current) return null
|
|
93
|
+
|
|
94
|
+
return new LiveQuery({debounce, models: modelsRef.current, query: queryRef.current})
|
|
95
|
+
}, [dependencyKey, debounce, enabled])
|
|
96
|
+
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
if (!liveQuery) return undefined
|
|
99
|
+
|
|
100
|
+
liveQuery.start()
|
|
101
|
+
|
|
102
|
+
return () => liveQuery.close()
|
|
103
|
+
}, [liveQuery])
|
|
104
|
+
|
|
105
|
+
const subscribe = useCallback((/** @type {() => void} */ listener) => {
|
|
106
|
+
if (!liveQuery) return () => {}
|
|
107
|
+
|
|
108
|
+
return liveQuery.subscribe(listener)
|
|
109
|
+
}, [liveQuery])
|
|
110
|
+
|
|
111
|
+
const getSnapshot = useCallback(() => {
|
|
112
|
+
if (!liveQuery) return /** @type {LiveQueryState<T>} */ (EMPTY_STATE)
|
|
113
|
+
|
|
114
|
+
return liveQuery.getState()
|
|
115
|
+
}, [liveQuery])
|
|
116
|
+
|
|
117
|
+
return useSyncExternalStore(subscribe, getSnapshot, getSnapshot)
|
|
118
|
+
}
|
|
@@ -775,7 +775,7 @@ export type DatabaseConfigurationType = {
|
|
|
775
775
|
/**
|
|
776
776
|
* - Database type identifier.
|
|
777
777
|
*/
|
|
778
|
-
type?: "sqlite" | "
|
|
778
|
+
type?: "sqlite" | "mysql" | "pgsql" | "mssql" | undefined;
|
|
779
779
|
/**
|
|
780
780
|
* - Database to switch to after connecting.
|
|
781
781
|
*/
|
|
@@ -383,7 +383,7 @@ export default class VelociousConfiguration {
|
|
|
383
383
|
* @returns {typeof import("./database/pool/base.js").default} - The database pool type.
|
|
384
384
|
*/
|
|
385
385
|
getDatabasePoolType(identifier?: string): typeof import("./database/pool/base.js").default;
|
|
386
|
-
getDatabaseType(identifier?: string): "sqlite" | "
|
|
386
|
+
getDatabaseType(identifier?: string): "sqlite" | "mysql" | "pgsql" | "mssql";
|
|
387
387
|
/**
|
|
388
388
|
* Runs get directory.
|
|
389
389
|
* @returns {string} - The directory.
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runs advisory locks on the caller connection by default, using a dedicated
|
|
3
|
+
* lock connection only when a positive hold timeout needs separate ownership.
|
|
4
|
+
*/
|
|
5
|
+
export default class AdvisoryLockRunner {
|
|
6
|
+
/**
|
|
7
|
+
* Runs `callback`, rejecting with `AdvisoryLockHoldTimeoutError` if it has
|
|
8
|
+
* not settled within `holdTimeoutMs`. The callback is not cancelled; callers
|
|
9
|
+
* use a dedicated advisory-lock connection so the lock can still be released.
|
|
10
|
+
* @template T
|
|
11
|
+
* @param {string} name - Lock name (for the error message).
|
|
12
|
+
* @param {() => Promise<T>} callback - Callback holding the lock.
|
|
13
|
+
* @param {number | null} [holdTimeoutMs] - Max hold time; falsy disables the timeout.
|
|
14
|
+
* @returns {Promise<T>} - Resolves with the callback result.
|
|
15
|
+
*/
|
|
16
|
+
static runWithAdvisoryLockHoldTimeout<T>(name: string, callback: () => Promise<T>, holdTimeoutMs?: number | null): Promise<T>;
|
|
17
|
+
/**
|
|
18
|
+
* Creates an advisory-lock runner for one model database identifier.
|
|
19
|
+
* @param {{configuration: import("../configuration.js").default, connectionProvider: () => import("./drivers/base.js").default, databaseIdentifier: string}} args - Runner dependencies.
|
|
20
|
+
*/
|
|
21
|
+
constructor({ configuration, connectionProvider, databaseIdentifier }: {
|
|
22
|
+
configuration: import("../configuration.js").default;
|
|
23
|
+
connectionProvider: () => import("./drivers/base.js").default;
|
|
24
|
+
databaseIdentifier: string;
|
|
25
|
+
});
|
|
26
|
+
configuration: import("../configuration.js").default;
|
|
27
|
+
connectionProvider: () => import("./drivers/base.js").default;
|
|
28
|
+
databaseIdentifier: string;
|
|
29
|
+
/**
|
|
30
|
+
* Runs a callback after acquiring the advisory lock, waiting up to `timeoutMs`.
|
|
31
|
+
* @template T
|
|
32
|
+
* @param {string} name - Lock name.
|
|
33
|
+
* @param {() => Promise<T>} callback - Callback to invoke while the lock is held.
|
|
34
|
+
* @param {{timeoutMs?: number | null, holdTimeoutMs?: number | null}} [args] - Lock and hold timeout options.
|
|
35
|
+
* @returns {Promise<T>} - Resolves with the callback result.
|
|
36
|
+
*/
|
|
37
|
+
withAdvisoryLock<T>(name: string, callback: () => Promise<T>, args?: {
|
|
38
|
+
timeoutMs?: number | null;
|
|
39
|
+
holdTimeoutMs?: number | null;
|
|
40
|
+
}): Promise<T>;
|
|
41
|
+
/**
|
|
42
|
+
* Runs a callback only if the advisory lock can be acquired immediately.
|
|
43
|
+
* @template T
|
|
44
|
+
* @param {string} name - Lock name.
|
|
45
|
+
* @param {() => Promise<T>} callback - Callback to invoke while the lock is held.
|
|
46
|
+
* @param {{holdTimeoutMs?: number | null}} [args] - Hold timeout options.
|
|
47
|
+
* @returns {Promise<T>} - Resolves with the callback result.
|
|
48
|
+
*/
|
|
49
|
+
withAdvisoryLockOrFail<T>(name: string, callback: () => Promise<T>, args?: {
|
|
50
|
+
holdTimeoutMs?: number | null;
|
|
51
|
+
}): Promise<T>;
|
|
52
|
+
/**
|
|
53
|
+
* Runs the lock holder callback and releases the lock from its owning connection.
|
|
54
|
+
* @template T
|
|
55
|
+
* @param {{callback: () => Promise<T>, connection: import("./drivers/base.js").default, holdTimeoutMs?: number | null, name: string}} args - Locked callback args.
|
|
56
|
+
* @returns {Promise<T>} - Resolves with the callback result.
|
|
57
|
+
*/
|
|
58
|
+
runLockedCallback<T>({ callback, connection, holdTimeoutMs, name }: {
|
|
59
|
+
callback: () => Promise<T>;
|
|
60
|
+
connection: import("./drivers/base.js").default;
|
|
61
|
+
holdTimeoutMs?: number | null;
|
|
62
|
+
name: string;
|
|
63
|
+
}): Promise<T>;
|
|
64
|
+
/**
|
|
65
|
+
* Runs lock work on the caller connection unless a positive hold timeout needs
|
|
66
|
+
* its own lock connection.
|
|
67
|
+
* @template T
|
|
68
|
+
* @param {number | null | undefined} holdTimeoutMs - Max hold time; positive values use a dedicated lock connection.
|
|
69
|
+
* @param {(connection: import("./drivers/base.js").default) => Promise<T>} callback - Callback receiving the connection that owns the advisory lock.
|
|
70
|
+
* @returns {Promise<T>} - Resolves with the callback result.
|
|
71
|
+
*/
|
|
72
|
+
withLockConnection<T>(holdTimeoutMs: number | null | undefined, callback: (connection: import("./drivers/base.js").default) => Promise<T>): Promise<T>;
|
|
73
|
+
/**
|
|
74
|
+
* Spawns a hold-timeout lock connection and closes it after lock work completes when
|
|
75
|
+
* the spawned driver owns the underlying physical connection.
|
|
76
|
+
* @template T
|
|
77
|
+
* @param {(connection: import("./drivers/base.js").default) => Promise<T>} callback - Callback that receives the dedicated lock connection.
|
|
78
|
+
* @returns {Promise<T>} - Resolves with the callback result.
|
|
79
|
+
*/
|
|
80
|
+
withDedicatedConnection<T>(callback: (connection: import("./drivers/base.js").default) => Promise<T>): Promise<T>;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Thrown when `withAdvisoryLockOrFail` finds the lock already held.
|
|
84
|
+
*/
|
|
85
|
+
export class AdvisoryLockBusyError extends Error {
|
|
86
|
+
/**
|
|
87
|
+
* Runs constructor.
|
|
88
|
+
* @param {string} message - Error message.
|
|
89
|
+
* @param {{name: string}} args - The advisory lock name that was already held.
|
|
90
|
+
*/
|
|
91
|
+
constructor(message: string, { name }: {
|
|
92
|
+
name: string;
|
|
93
|
+
});
|
|
94
|
+
lockName: string;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Thrown when a callback holds an advisory lock longer than `holdTimeoutMs`.
|
|
98
|
+
*/
|
|
99
|
+
export class AdvisoryLockHoldTimeoutError extends Error {
|
|
100
|
+
/**
|
|
101
|
+
* Runs constructor.
|
|
102
|
+
* @param {string} message - Error message.
|
|
103
|
+
* @param {{name: string}} args - The advisory lock name whose hold timed out.
|
|
104
|
+
*/
|
|
105
|
+
constructor(message: string, { name }: {
|
|
106
|
+
name: string;
|
|
107
|
+
});
|
|
108
|
+
lockName: string;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Thrown when an advisory lock could not be acquired before `timeoutMs` elapsed.
|
|
112
|
+
*/
|
|
113
|
+
export class AdvisoryLockTimeoutError extends Error {
|
|
114
|
+
/**
|
|
115
|
+
* Runs constructor.
|
|
116
|
+
* @param {string} message - Error message.
|
|
117
|
+
* @param {{name: string}} args - The advisory lock name that timed out.
|
|
118
|
+
*/
|
|
119
|
+
constructor(message: string, { name }: {
|
|
120
|
+
name: string;
|
|
121
|
+
});
|
|
122
|
+
lockName: string;
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=advisory-lock-runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"advisory-lock-runner.d.ts","sourceRoot":"","sources":["../../../src/database/advisory-lock-runner.js"],"names":[],"mappings":"AAoDA;;;GAGG;AACH;IAoGE;;;;;;;;;OASG;IACH,sCANa,CAAC,QACH,MAAM,YACN,MAAM,OAAO,CAAC,CAAC,CAAC,kBAChB,MAAM,GAAG,IAAI,GACX,OAAO,CAAC,CAAC,CAAC,CAwBtB;IAnID;;;OAGG;IACH,uEAFW;QAAC,aAAa,EAAE,OAAO,qBAAqB,EAAE,OAAO,CAAC;QAAC,kBAAkB,EAAE,MAAM,OAAO,mBAAmB,EAAE,OAAO,CAAC;QAAC,kBAAkB,EAAE,MAAM,CAAA;KAAC,EAM3J;IAHC,qDAAkC;IAClC,0BAJ0F,OAAO,mBAAmB,EAAE,OAAO,CAIjF;IAC5C,2BAA4C;IAG9C;;;;;;;OAOG;IACH,iBANa,CAAC,QACH,MAAM,YACN,MAAM,OAAO,CAAC,CAAC,CAAC,SAChB;QAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAC,GACxD,OAAO,CAAC,CAAC,CAAC,CAYtB;IAED;;;;;;;OAOG;IACH,uBANa,CAAC,QACH,MAAM,YACN,MAAM,OAAO,CAAC,CAAC,CAAC,SAChB;QAAC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAC,GAC7B,OAAO,CAAC,CAAC,CAAC,CAYtB;IAED;;;;;OAKG;IACH,kBAJa,CAAC,iDACH;QAAC,QAAQ,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC;QAAC,UAAU,EAAE,OAAO,mBAAmB,EAAE,OAAO,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAC,GACxH,OAAO,CAAC,CAAC,CAAC,CAQtB;IAED;;;;;;;OAOG;IACH,mBALa,CAAC,iBACH,MAAM,GAAG,IAAI,GAAG,SAAS,YACzB,CAAC,UAAU,EAAE,OAAO,mBAAmB,EAAE,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,GAC7D,OAAO,CAAC,CAAC,CAAC,CAQtB;IAED;;;;;;OAMG;IACH,wBAJa,CAAC,YACH,CAAC,UAAU,EAAE,OAAO,mBAAmB,EAAE,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,GAC7D,OAAO,CAAC,CAAC,CAAC,CAYtB;CAmCF;AAzKD;;GAEG;AACH;IACE;;;;OAIG;IACH,qBAHW,MAAM,YACN;QAAC,IAAI,EAAE,MAAM,CAAA;KAAC,EAMxB;IADC,iBAAoB;CAEvB;AAED;;GAEG;AACH;IACE;;;;OAIG;IACH,qBAHW,MAAM,YACN;QAAC,IAAI,EAAE,MAAM,CAAA;KAAC,EAMxB;IADC,iBAAoB;CAEvB;AA9CD;;GAEG;AACH;IACE;;;;OAIG;IACH,qBAHW,MAAM,YACN;QAAC,IAAI,EAAE,MAAM,CAAA;KAAC,EAMxB;IADC,iBAAoB;CAEvB"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import timeout from "awaitery/build/timeout.js";
|
|
3
|
+
/**
|
|
4
|
+
* Thrown when an advisory lock could not be acquired before `timeoutMs` elapsed.
|
|
5
|
+
*/
|
|
6
|
+
class AdvisoryLockTimeoutError extends Error {
|
|
7
|
+
/**
|
|
8
|
+
* Runs constructor.
|
|
9
|
+
* @param {string} message - Error message.
|
|
10
|
+
* @param {{name: string}} args - The advisory lock name that timed out.
|
|
11
|
+
*/
|
|
12
|
+
constructor(message, { name }) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = "AdvisoryLockTimeoutError";
|
|
15
|
+
this.lockName = name;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Thrown when `withAdvisoryLockOrFail` finds the lock already held.
|
|
20
|
+
*/
|
|
21
|
+
class AdvisoryLockBusyError extends Error {
|
|
22
|
+
/**
|
|
23
|
+
* Runs constructor.
|
|
24
|
+
* @param {string} message - Error message.
|
|
25
|
+
* @param {{name: string}} args - The advisory lock name that was already held.
|
|
26
|
+
*/
|
|
27
|
+
constructor(message, { name }) {
|
|
28
|
+
super(message);
|
|
29
|
+
this.name = "AdvisoryLockBusyError";
|
|
30
|
+
this.lockName = name;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Thrown when a callback holds an advisory lock longer than `holdTimeoutMs`.
|
|
35
|
+
*/
|
|
36
|
+
class AdvisoryLockHoldTimeoutError extends Error {
|
|
37
|
+
/**
|
|
38
|
+
* Runs constructor.
|
|
39
|
+
* @param {string} message - Error message.
|
|
40
|
+
* @param {{name: string}} args - The advisory lock name whose hold timed out.
|
|
41
|
+
*/
|
|
42
|
+
constructor(message, { name }) {
|
|
43
|
+
super(message);
|
|
44
|
+
this.name = "AdvisoryLockHoldTimeoutError";
|
|
45
|
+
this.lockName = name;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Runs advisory locks on the caller connection by default, using a dedicated
|
|
50
|
+
* lock connection only when a positive hold timeout needs separate ownership.
|
|
51
|
+
*/
|
|
52
|
+
export default class AdvisoryLockRunner {
|
|
53
|
+
/**
|
|
54
|
+
* Creates an advisory-lock runner for one model database identifier.
|
|
55
|
+
* @param {{configuration: import("../configuration.js").default, connectionProvider: () => import("./drivers/base.js").default, databaseIdentifier: string}} args - Runner dependencies.
|
|
56
|
+
*/
|
|
57
|
+
constructor({ configuration, connectionProvider, databaseIdentifier }) {
|
|
58
|
+
this.configuration = configuration;
|
|
59
|
+
this.connectionProvider = connectionProvider;
|
|
60
|
+
this.databaseIdentifier = databaseIdentifier;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Runs a callback after acquiring the advisory lock, waiting up to `timeoutMs`.
|
|
64
|
+
* @template T
|
|
65
|
+
* @param {string} name - Lock name.
|
|
66
|
+
* @param {() => Promise<T>} callback - Callback to invoke while the lock is held.
|
|
67
|
+
* @param {{timeoutMs?: number | null, holdTimeoutMs?: number | null}} [args] - Lock and hold timeout options.
|
|
68
|
+
* @returns {Promise<T>} - Resolves with the callback result.
|
|
69
|
+
*/
|
|
70
|
+
async withAdvisoryLock(name, callback, args = {}) {
|
|
71
|
+
return await this.withLockConnection(args.holdTimeoutMs, async (connection) => {
|
|
72
|
+
const acquired = await connection.acquireAdvisoryLock(name, args);
|
|
73
|
+
if (!acquired) {
|
|
74
|
+
throw new AdvisoryLockTimeoutError(`Timed out waiting for advisory lock ${JSON.stringify(name)}`, { name });
|
|
75
|
+
}
|
|
76
|
+
return await this.runLockedCallback({ callback, connection, holdTimeoutMs: args.holdTimeoutMs, name });
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Runs a callback only if the advisory lock can be acquired immediately.
|
|
81
|
+
* @template T
|
|
82
|
+
* @param {string} name - Lock name.
|
|
83
|
+
* @param {() => Promise<T>} callback - Callback to invoke while the lock is held.
|
|
84
|
+
* @param {{holdTimeoutMs?: number | null}} [args] - Hold timeout options.
|
|
85
|
+
* @returns {Promise<T>} - Resolves with the callback result.
|
|
86
|
+
*/
|
|
87
|
+
async withAdvisoryLockOrFail(name, callback, args = {}) {
|
|
88
|
+
return await this.withLockConnection(args.holdTimeoutMs, async (connection) => {
|
|
89
|
+
const acquired = await connection.tryAcquireAdvisoryLock(name);
|
|
90
|
+
if (!acquired) {
|
|
91
|
+
throw new AdvisoryLockBusyError(`Advisory lock ${JSON.stringify(name)} is already held`, { name });
|
|
92
|
+
}
|
|
93
|
+
return await this.runLockedCallback({ callback, connection, holdTimeoutMs: args.holdTimeoutMs, name });
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Runs the lock holder callback and releases the lock from its owning connection.
|
|
98
|
+
* @template T
|
|
99
|
+
* @param {{callback: () => Promise<T>, connection: import("./drivers/base.js").default, holdTimeoutMs?: number | null, name: string}} args - Locked callback args.
|
|
100
|
+
* @returns {Promise<T>} - Resolves with the callback result.
|
|
101
|
+
*/
|
|
102
|
+
async runLockedCallback({ callback, connection, holdTimeoutMs, name }) {
|
|
103
|
+
try {
|
|
104
|
+
return await AdvisoryLockRunner.runWithAdvisoryLockHoldTimeout(name, callback, holdTimeoutMs);
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
await connection.releaseAdvisoryLock(name);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Runs lock work on the caller connection unless a positive hold timeout needs
|
|
112
|
+
* its own lock connection.
|
|
113
|
+
* @template T
|
|
114
|
+
* @param {number | null | undefined} holdTimeoutMs - Max hold time; positive values use a dedicated lock connection.
|
|
115
|
+
* @param {(connection: import("./drivers/base.js").default) => Promise<T>} callback - Callback receiving the connection that owns the advisory lock.
|
|
116
|
+
* @returns {Promise<T>} - Resolves with the callback result.
|
|
117
|
+
*/
|
|
118
|
+
async withLockConnection(holdTimeoutMs, callback) {
|
|
119
|
+
if (holdTimeoutMs && holdTimeoutMs > 0) {
|
|
120
|
+
return await this.withDedicatedConnection(callback);
|
|
121
|
+
}
|
|
122
|
+
return await callback(this.connectionProvider());
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Spawns a hold-timeout lock connection and closes it after lock work completes when
|
|
126
|
+
* the spawned driver owns the underlying physical connection.
|
|
127
|
+
* @template T
|
|
128
|
+
* @param {(connection: import("./drivers/base.js").default) => Promise<T>} callback - Callback that receives the dedicated lock connection.
|
|
129
|
+
* @returns {Promise<T>} - Resolves with the callback result.
|
|
130
|
+
*/
|
|
131
|
+
async withDedicatedConnection(callback) {
|
|
132
|
+
const connection = await this.configuration.getDatabasePool(this.databaseIdentifier).spawnConnection();
|
|
133
|
+
try {
|
|
134
|
+
return await callback(connection);
|
|
135
|
+
}
|
|
136
|
+
finally {
|
|
137
|
+
if (!connection.getArgs().getConnection) {
|
|
138
|
+
await connection.close();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Runs `callback`, rejecting with `AdvisoryLockHoldTimeoutError` if it has
|
|
144
|
+
* not settled within `holdTimeoutMs`. The callback is not cancelled; callers
|
|
145
|
+
* use a dedicated advisory-lock connection so the lock can still be released.
|
|
146
|
+
* @template T
|
|
147
|
+
* @param {string} name - Lock name (for the error message).
|
|
148
|
+
* @param {() => Promise<T>} callback - Callback holding the lock.
|
|
149
|
+
* @param {number | null} [holdTimeoutMs] - Max hold time; falsy disables the timeout.
|
|
150
|
+
* @returns {Promise<T>} - Resolves with the callback result.
|
|
151
|
+
*/
|
|
152
|
+
static async runWithAdvisoryLockHoldTimeout(name, callback, holdTimeoutMs) {
|
|
153
|
+
if (!holdTimeoutMs || holdTimeoutMs <= 0) {
|
|
154
|
+
return await callback();
|
|
155
|
+
}
|
|
156
|
+
let callbackSettled = false;
|
|
157
|
+
try {
|
|
158
|
+
return await timeout({ timeout: holdTimeoutMs }, async () => {
|
|
159
|
+
try {
|
|
160
|
+
return await callback();
|
|
161
|
+
}
|
|
162
|
+
finally {
|
|
163
|
+
callbackSettled = true;
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
if (!callbackSettled) {
|
|
169
|
+
throw new AdvisoryLockHoldTimeoutError(`Advisory lock ${JSON.stringify(name)} held longer than ${holdTimeoutMs}ms`, { name });
|
|
170
|
+
}
|
|
171
|
+
throw error;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
export { AdvisoryLockBusyError, AdvisoryLockHoldTimeoutError, AdvisoryLockTimeoutError };
|
|
176
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWR2aXNvcnktbG9jay1ydW5uZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvZGF0YWJhc2UvYWR2aXNvcnktbG9jay1ydW5uZXIuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsWUFBWTtBQUVaLE9BQU8sT0FBTyxNQUFNLDJCQUEyQixDQUFBO0FBRS9DOztHQUVHO0FBQ0gsTUFBTSx3QkFBeUIsU0FBUSxLQUFLO0lBQzFDOzs7O09BSUc7SUFDSCxZQUFZLE9BQU8sRUFBRSxFQUFDLElBQUksRUFBQztRQUN6QixLQUFLLENBQUMsT0FBTyxDQUFDLENBQUE7UUFDZCxJQUFJLENBQUMsSUFBSSxHQUFHLDBCQUEwQixDQUFBO1FBQ3RDLElBQUksQ0FBQyxRQUFRLEdBQUcsSUFBSSxDQUFBO0lBQ3RCLENBQUM7Q0FDRjtBQUVEOztHQUVHO0FBQ0gsTUFBTSxxQkFBc0IsU0FBUSxLQUFLO0lBQ3ZDOzs7O09BSUc7SUFDSCxZQUFZLE9BQU8sRUFBRSxFQUFDLElBQUksRUFBQztRQUN6QixLQUFLLENBQUMsT0FBTyxDQUFDLENBQUE7UUFDZCxJQUFJLENBQUMsSUFBSSxHQUFHLHVCQUF1QixDQUFBO1FBQ25DLElBQUksQ0FBQyxRQUFRLEdBQUcsSUFBSSxDQUFBO0lBQ3RCLENBQUM7Q0FDRjtBQUVEOztHQUVHO0FBQ0gsTUFBTSw0QkFBNkIsU0FBUSxLQUFLO0lBQzlDOzs7O09BSUc7SUFDSCxZQUFZLE9BQU8sRUFBRSxFQUFDLElBQUksRUFBQztRQUN6QixLQUFLLENBQUMsT0FBTyxDQUFDLENBQUE7UUFDZCxJQUFJLENBQUMsSUFBSSxHQUFHLDhCQUE4QixDQUFBO1FBQzFDLElBQUksQ0FBQyxRQUFRLEdBQUcsSUFBSSxDQUFBO0lBQ3RCLENBQUM7Q0FDRjtBQUVEOzs7R0FHRztBQUNILE1BQU0sQ0FBQyxPQUFPLE9BQU8sa0JBQWtCO0lBQ3JDOzs7T0FHRztJQUNILFlBQVksRUFBQyxhQUFhLEVBQUUsa0JBQWtCLEVB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QkFBNEIsRUFBRSx3QkFBd0IsRUFBQyxDQUFBIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQHRzLWNoZWNrXG5cbmltcG9ydCB0aW1lb3V0IGZyb20gXCJhd2FpdGVyeS9idWlsZC90aW1lb3V0LmpzXCJcblxuLyoqXG4gKiBUaHJvd24gd2hlbiBhbiBhZHZpc29yeSBsb2NrIGNvdWxkIG5vdCBiZSBhY3F1aXJlZCBiZWZvcmUgYHRpbWVvdXRNc2AgZWxhcHNlZC5cbiAqL1xuY2xhc3MgQWR2aXNvcnlMb2NrVGltZW91dEVycm9yIGV4dGVuZHMgRXJyb3Ige1xuICAvKipcbiAgICogUnVucyBjb25zdHJ1Y3Rvci5cbiAgICogQHBhcmFtIHtzdHJpbmd9IG1lc3NhZ2UgLSBFcnJvciBtZXNzYWdlLlxuICAgKiBAcGFyYW0ge3tuYW1lOiBzdHJpbmd9fSBhcmdzIC0gVGhlIGFkdmlzb3J5IGxvY2sgbmFtZSB0aGF0IHRpbWVkIG91dC5cbiAgICovXG4gIGNvbnN0cnVjdG9yKG1lc3NhZ2UsIHtuYW1lfSkge1xuICAgIHN1cGVyKG1lc3NhZ2UpXG4gICAgdGhpcy5uYW1lID0gXCJBZHZpc29yeUxvY2tUaW1lb3V0RXJyb3JcIlxuICAgIHRoaXMubG9ja05hbWUgPSBuYW1lXG4gIH1cbn1cblxuLyoqXG4gKiBUaHJvd24gd2hlbiBgd2l0aEFkdmlzb3J5TG9ja09yRmFpbGAgZmluZHMgdGhlIGxvY2sgYWxyZWFkeSBoZWxkLlxuICovXG5jbGFzcyBBZHZpc29yeUxvY2tCdXN5RXJyb3IgZXh0ZW5kcyBFcnJvciB7XG4gIC8qKlxuICAgKiBSdW5zIGNvbnN0cnVjdG9yLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gbWVzc2FnZSAtIEVycm9yIG1lc3NhZ2UuXG4gICAqIEBwYXJhbSB7e25hbWU6IHN0cmluZ319IGFyZ3MgLSBUaGUgYWR2aXNvcnkgbG9jayBuYW1lIHRoYXQgd2FzIGFscmVhZHkgaGVsZC5cbiAgICovXG4gIGNvbnN0cnVjdG9yKG1lc3NhZ2UsIHtuYW1lfSkge1xuICAgIHN1cGVyKG1lc3NhZ2UpXG4gICAgdGhpcy5uYW1lID0gXCJBZHZpc29yeUxvY2tCdXN5RXJyb3JcIlxuICAgIHRoaXMubG9ja05hbWUgPSBuYW1lXG4gIH1cbn1cblxuLyoqXG4gKiBUaHJvd24gd2hlbiBhIGNhbGxiYWNrIGhvbGRzIGFuIGFkdmlzb3J5IGxvY2sgbG9uZ2VyIHRoYW4gYGhvbGRUaW1lb3V0TXNgLlxuICovXG5jbGFzcyBBZHZpc29yeUxvY2tIb2xkVGltZW91dEVycm9yIGV4dGVuZHMgRXJyb3Ige1xuICAvKipcbiAgICogUnVucyBjb25zdHJ1Y3Rvci5cbiAgICogQHBhcmFtIHtzdHJpbmd9IG1lc3NhZ2UgLSBFcnJvciBtZXNzYWdlLlxuICAgKiBAcGFyYW0ge3tuYW1lOiBzdHJpbmd9fSBhcmdzIC0gVGhlIGFkdmlzb3J5IGxvY2sgbmFtZSB3aG9zZSBob2xkIHRpbWVkIG91dC5cbiAgICovXG4gIGNvbnN0cnVjdG9yKG1lc3NhZ2UsIHtuYW1lfSkge1xuICAgIHN1cGVyKG1lc3NhZ2UpXG4gICAgdGhpcy5uYW1lID0gXCJBZHZpc29yeUxvY2tIb2xkVGltZW91dEVycm9yXCJcbiAgICB0aGlzLmxvY2tOYW1lID0gbmFtZVxuICB9XG59XG5cbi8qKlxuICogUnVucyBhZHZpc29yeSBsb2NrcyBvbiB0aGUgY2FsbGVyIGNvbm5lY3Rpb24gYnkgZGVmYXVsdCwgdXNpbmcgYSBkZWRpY2F0ZWRcbiAqIGxvY2sgY29ubmVjdGlvbiBvbmx5IHdoZW4gYSBwb3NpdGl2ZSBob2xkIHRpbWVvdXQgbmVlZHMgc2VwYXJhdGUgb3duZXJzaGlwLlxuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBBZHZpc29yeUxvY2tSdW5uZXIge1xuICAvKipcbiAgICogQ3JlYXRlcyBhbiBhZHZpc29yeS1sb2NrIHJ1bm5lciBmb3Igb25lIG1vZGVsIGRhdGFiYXNlIGlkZW50aWZpZXIuXG4gICAqIEBwYXJhbSB7e2NvbmZpZ3VyYXRpb246IGltcG9ydChcIi4uL2NvbmZpZ3VyYXRpb24uanNcIikuZGVmYXVsdCwgY29ubmVjdGlvblByb3ZpZGVyOiAoKSA9PiBpbXBvcnQoXCIuL2RyaXZlcnMvYmFzZS5qc1wiKS5kZWZhdWx0LCBkYXRhYmFzZUlkZW50aWZpZXI6IHN0cmluZ319IGFyZ3MgLSBSdW5uZXIgZGVwZW5kZW5jaWVzLlxuICAgKi9cbiAgY29uc3RydWN0b3Ioe2NvbmZpZ3VyYXRpb24sIGNvbm5lY3Rpb25Qcm92aWRlciwgZGF0YWJhc2VJZGVudGlmaWVyfSkge1xuICAgIHRoaXMuY29uZmlndXJhdGlvbiA9IGNvbmZpZ3VyYXRpb25cbiAgICB0aGlzLmNvbm5lY3Rpb25Qcm92aWRlciA9IGNvbm5lY3Rpb25Qcm92aWRlclxuICAgIHRoaXMuZGF0YWJhc2VJZGVudGlmaWVyID0gZGF0YWJhc2VJZGVudGlmaWVyXG4gIH1cblxuICAvKipcbiAgICogUnVucyBhIGNhbGxiYWNrIGFmdGVyIGFjcXVpcmluZyB0aGUgYWR2aXNvcnkgbG9jaywgd2FpdGluZyB1cCB0byBgdGltZW91dE1zYC5cbiAgICogQHRlbXBsYXRlIFRcbiAgICogQHBhcmFtIHtzdHJpbmd9IG5hbWUgLSBMb2NrIG5hbWUuXG4gICAqIEBwYXJhbSB7KCkgPT4gUHJvbWlzZTxUPn0gY2FsbGJhY2sgLSBDYWxsYmFjayB0byBpbnZva2Ugd2hpbGUgdGhlIGxvY2sgaXMgaGVsZC5cbiAgICogQHBhcmFtIHt7dGltZW91dE1zPzogbnVtYmVyIHwgbnVsbCwgaG9sZFRpbWVvdXRNcz86IG51bWJlciB8IG51bGx9fSBbYXJnc10gLSBMb2NrIGFuZCBob2xkIHRpbWVvdXQgb3B0aW9ucy5cbiAgICogQHJldHVybnMge1Byb21pc2U8VD59IC0gUmVzb2x2ZXMgd2l0aCB0aGUgY2FsbGJhY2sgcmVzdWx0LlxuICAgKi9cbiAgYXN5bmMgd2l0aEFkdmlzb3J5TG9jayhuYW1lLCBjYWxsYmFjaywgYXJncyA9IHt9KSB7XG4gICAgcmV0dXJuIGF3YWl0IHRoaXMud2l0aExvY2tDb25uZWN0aW9uKGFyZ3MuaG9sZFRpbWVvdXRNcywgYXN5bmMgKGNvbm5lY3Rpb24pID0+IHtcbiAgICAgIGNvbnN0IGFjcXVpcmVkID0gYXdhaXQgY29ubmVjdGlvbi5hY3F1aXJlQWR2aXNvcnlMb2NrKG5hbWUsIGFyZ3MpXG5cbiAgICAgIGlmICghYWNxdWlyZWQpIHtcbiAgICAgICAgdGhyb3cgbmV3IEFkdmlzb3J5TG9ja1RpbWVvdXRFcnJvcihgVGltZWQgb3V0IHdhaXRpbmcgZm9yIGFkdmlzb3J5IGxvY2sgJHtKU09OLnN0cmluZ2lmeShuYW1lKX1gLCB7bmFtZX0pXG4gICAgICB9XG5cbiAgICAgIHJldHVybiBhd2FpdCB0aGlzLnJ1bkxvY2tlZENhbGxiYWNrKHtjYWxsYmFjaywgY29ubmVjdGlvbiwgaG9sZFRpbWVvdXRNczogYXJncy5ob2xkVGltZW91dE1zLCBuYW1lfSlcbiAgICB9KVxuICB9XG5cbiAgLyoqXG4gICAqIFJ1bnMgYSBjYWxsYmFjayBvbmx5IGlmIHRoZSBhZHZpc29yeSBsb2NrIGNhbiBiZSBhY3F1aXJlZCBpbW1lZGlhdGVseS5cbiAgICogQHRlbXBsYXRlIFRcbiAgICogQHBhcmFtIHtzdHJpbmd9IG5hbWUgLSBMb2NrIG5hbWUuXG4gICAqIEBwYXJhbSB7KCkgPT4gUHJvbWlzZTxUPn0gY2FsbGJhY2sgLSBDYWxsYmFjayB0byBpbnZva2Ugd2hpbGUgdGhlIGxvY2sgaXMgaGVsZC5cbiAgICogQHBhcmFtIHt7aG9sZFRpbWVvdXRNcz86IG51bWJlciB8IG51bGx9fSBbYXJnc10gLSBIb2xkIHRpbWVvdXQgb3B0aW9ucy5cbiAgICogQHJldHVybnMge1Byb21pc2U8VD59IC0gUmVzb2x2ZXMgd2l0aCB0aGUgY2FsbGJhY2sgcmVzdWx0LlxuICAgKi9cbiAgYXN5bmMgd2l0aEFkdmlzb3J5TG9ja09yRmFpbChuYW1lLCBjYWxsYmFjaywgYXJncyA9IHt9KSB7XG4gICAgcmV0dXJuIGF3YWl0IHRoaXMud2l0aExvY2tDb25uZWN0aW9uKGFyZ3MuaG9sZFRpbWVvdXRNcywgYXN5bmMgKGNvbm5lY3Rpb24pID0+IHtcbiAgICAgIGNvbnN0IGFjcXVpcmVkID0gYXdhaXQgY29ubmVjdGlvbi50cnlBY3F1aXJlQWR2aXNvcnlMb2NrKG5hbWUpXG5cbiAgICAgIGlmICghYWNxdWlyZWQpIHtcbiAgICAgICAgdGhyb3cgbmV3IEFkdmlzb3J5TG9ja0J1c3lFcnJvcihgQWR2aXNvcnkgbG9jayAke0pTT04uc3RyaW5naWZ5KG5hbWUpfSBpcyBhbHJlYWR5IGhlbGRgLCB7bmFtZX0pXG4gICAgICB9XG5cbiAgICAgIHJldHVybiBhd2FpdCB0aGlzLnJ1bkxvY2tlZENhbGxiYWNrKHtjYWxsYmFjaywgY29ubmVjdGlvbiwgaG9sZFRpbWVvdXRNczogYXJncy5ob2xkVGltZW91dE1zLCBuYW1lfSlcbiAgICB9KVxuICB9XG5cbiAgLyoqXG4gICAqIFJ1bnMgdGhlIGxvY2sgaG9sZGVyIGNhbGxiYWNrIGFuZCByZWxlYXNlcyB0aGUgbG9jayBmcm9tIGl0cyBvd25pbmcgY29ubmVjdGlvbi5cbiAgICogQHRlbXBsYXRlIFRcbiAgICogQHBhcmFtIHt7Y2FsbGJhY2s6ICgpID0+IFByb21pc2U8VD4sIGNvbm5lY3Rpb246IGltcG9ydChcIi4vZHJpdmVycy9iYXNlLmpzXCIpLmRlZmF1bHQsIGhvbGRUaW1lb3V0TXM/OiBudW1iZXIgfCBudWxsLCBuYW1lOiBzdHJpbmd9fSBhcmdzIC0gTG9ja2VkIGNhbGxiYWNrIGFyZ3MuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPFQ+fSAtIFJlc29sdmVzIHdpdGggdGhlIGNhbGxiYWNrIHJlc3VsdC5cbiAgICovXG4gIGFzeW5jIHJ1bkxvY2tlZENhbGxiYWNrKHtjYWxsYmFjaywgY29ubmVjdGlvbiwgaG9sZFRpbWVvdXRNcywgbmFtZX0pIHtcbiAgICB0cnkge1xuICAgICAgcmV0dXJuIGF3YWl0IEFkdmlzb3J5TG9ja1J1bm5lci5ydW5XaXRoQWR2aXNvcnlMb2NrSG9sZFRpbWVvdXQobmFtZSwgY2FsbGJhY2ssIGhvbGRUaW1lb3V0TXMpXG4gICAgfSBmaW5hbGx5IHtcbiAgICAgIGF3YWl0IGNvbm5lY3Rpb24ucmVsZWFzZUFkdmlzb3J5TG9jayhuYW1lKVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBSdW5zIGxvY2sgd29yayBvbiB0aGUgY2FsbGVyIGNvbm5lY3Rpb24gdW5sZXNzIGEgcG9zaXRpdmUgaG9sZCB0aW1lb3V0IG5lZWRzXG4gICAqIGl0cyBvd24gbG9jayBjb25uZWN0aW9uLlxuICAgKiBAdGVtcGxhdGUgVFxuICAgKiBAcGFyYW0ge251bWJlciB8IG51bGwgfCB1bmRlZmluZWR9IGhvbGRUaW1lb3V0TXMgLSBNYXggaG9sZCB0aW1lOyBwb3NpdGl2ZSB2YWx1ZXMgdXNlIGEgZGVkaWNhdGVkIGxvY2sgY29ubmVjdGlvbi5cbiAgICogQHBhcmFtIHsoY29ubmVjdGlvbjogaW1wb3J0KFwiLi9kcml2ZXJzL2Jhc2UuanNcIikuZGVmYXVsdCkgPT4gUHJvbWlzZTxUPn0gY2FsbGJhY2sgLSBDYWxsYmFjayByZWNlaXZpbmcgdGhlIGNvbm5lY3Rpb24gdGhhdCBvd25zIHRoZSBhZHZpc29yeSBsb2NrLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxUPn0gLSBSZXNvbHZlcyB3aXRoIHRoZSBjYWxsYmFjayByZXN1bHQuXG4gICAqL1xuICBhc3luYyB3aXRoTG9ja0Nvbm5lY3Rpb24oaG9sZFRpbWVvdXRNcywgY2FsbGJhY2spIHtcbiAgICBpZiAoaG9sZFRpbWVvdXRNcyAmJiBob2xkVGltZW91dE1zID4gMCkge1xuICAgICAgcmV0dXJuIGF3YWl0IHRoaXMud2l0aERlZGljYXRlZENvbm5lY3Rpb24oY2FsbGJhY2spXG4gICAgfVxuXG4gICAgcmV0dXJuIGF3YWl0IGNhbGxiYWNrKHRoaXMuY29ubmVjdGlvblByb3ZpZGVyKCkpXG4gIH1cblxuICAvKipcbiAgICogU3Bhd25zIGEgaG9sZC10aW1lb3V0IGxvY2sgY29ubmVjdGlvbiBhbmQgY2xvc2VzIGl0IGFmdGVyIGxvY2sgd29yayBjb21wbGV0ZXMgd2hlblxuICAgKiB0aGUgc3Bhd25lZCBkcml2ZXIgb3ducyB0aGUgdW5kZXJseWluZyBwaHlzaWNhbCBjb25uZWN0aW9uLlxuICAgKiBAdGVtcGxhdGUgVFxuICAgKiBAcGFyYW0geyhjb25uZWN0aW9uOiBpbXBvcnQoXCIuL2RyaXZlcnMvYmFzZS5qc1wiKS5kZWZhdWx0KSA9PiBQcm9taXNlPFQ+fSBjYWxsYmFjayAtIENhbGxiYWNrIHRoYXQgcmVjZWl2ZXMgdGhlIGRlZGljYXRlZCBsb2NrIGNvbm5lY3Rpb24uXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPFQ+fSAtIFJlc29sdmVzIHdpdGggdGhlIGNhbGxiYWNrIHJlc3VsdC5cbiAgICovXG4gIGFzeW5jIHdpdGhEZWRpY2F0ZWRDb25uZWN0aW9uKGNhbGxiYWNrKSB7XG4gICAgY29uc3QgY29ubmVjdGlvbiA9IGF3YWl0IHRoaXMuY29uZmlndXJhdGlvbi5nZXREYXRhYmFzZVBvb2wodGhpcy5kYXRhYmFzZUlkZW50aWZpZXIpLnNwYXduQ29ubmVjdGlvbigpXG5cbiAgICB0cnkge1xuICAgICAgcmV0dXJuIGF3YWl0IGNhbGxiYWNrKGNvbm5lY3Rpb24pXG4gICAgfSBmaW5hbGx5IHtcbiAgICAgIGlmICghY29ubmVjdGlvbi5nZXRBcmdzKCkuZ2V0Q29ubmVjdGlvbikge1xuICAgICAgICBhd2FpdCBjb25uZWN0aW9uLmNsb3NlKClcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUnVucyBgY2FsbGJhY2tgLCByZWplY3Rpbmcgd2l0aCBgQWR2aXNvcnlMb2NrSG9sZFRpbWVvdXRFcnJvcmAgaWYgaXQgaGFzXG4gICAqIG5vdCBzZXR0bGVkIHdpdGhpbiBgaG9sZFRpbWVvdXRNc2AuIFRoZSBjYWxsYmFjayBpcyBub3QgY2FuY2VsbGVkOyBjYWxsZXJzXG4gICAqIHVzZSBhIGRlZGljYXRlZCBhZHZpc29yeS1sb2NrIGNvbm5lY3Rpb24gc28gdGhlIGxvY2sgY2FuIHN0aWxsIGJlIHJlbGVhc2VkLlxuICAgKiBAdGVtcGxhdGUgVFxuICAgKiBAcGFyYW0ge3N0cmluZ30gbmFtZSAtIExvY2sgbmFtZSAoZm9yIHRoZSBlcnJvciBtZXNzYWdlKS5cbiAgICogQHBhcmFtIHsoKSA9PiBQcm9taXNlPFQ+fSBjYWxsYmFjayAtIENhbGxiYWNrIGhvbGRpbmcgdGhlIGxvY2suXG4gICAqIEBwYXJhbSB7bnVtYmVyIHwgbnVsbH0gW2hvbGRUaW1lb3V0TXNdIC0gTWF4IGhvbGQgdGltZTsgZmFsc3kgZGlzYWJsZXMgdGhlIHRpbWVvdXQuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPFQ+fSAtIFJlc29sdmVzIHdpdGggdGhlIGNhbGxiYWNrIHJlc3VsdC5cbiAgICovXG4gIHN0YXRpYyBhc3luYyBydW5XaXRoQWR2aXNvcnlMb2NrSG9sZFRpbWVvdXQobmFtZSwgY2FsbGJhY2ssIGhvbGRUaW1lb3V0TXMpIHtcbiAgICBpZiAoIWhvbGRUaW1lb3V0TXMgfHwgaG9sZFRpbWVvdXRNcyA8PSAwKSB7XG4gICAgICByZXR1cm4gYXdhaXQgY2FsbGJhY2soKVxuICAgIH1cblxuICAgIGxldCBjYWxsYmFja1NldHRsZWQgPSBmYWxzZVxuXG4gICAgdHJ5IHtcbiAgICAgIHJldHVybiBhd2FpdCB0aW1lb3V0KHt0aW1lb3V0OiBob2xkVGltZW91dE1zfSwgYXN5bmMgKCkgPT4ge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIHJldHVybiBhd2FpdCBjYWxsYmFjaygpXG4gICAgICAgIH0gZmluYWxseSB7XG4gICAgICAgICAgY2FsbGJhY2tTZXR0bGVkID0gdHJ1ZVxuICAgICAgICB9XG4gICAgICB9KVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBpZiAoIWNhbGxiYWNrU2V0dGxlZCkge1xuICAgICAgICB0aHJvdyBuZXcgQWR2aXNvcnlMb2NrSG9sZFRpbWVvdXRFcnJvcihgQWR2aXNvcnkgbG9jayAke0pTT04uc3RyaW5naWZ5KG5hbWUpfSBoZWxkIGxvbmdlciB0aGFuICR7aG9sZFRpbWVvdXRNc31tc2AsIHtuYW1lfSlcbiAgICAgIH1cblxuICAgICAgdGhyb3cgZXJyb3JcbiAgICB9XG4gIH1cbn1cblxuZXhwb3J0IHtBZHZpc29yeUxvY2tCdXN5RXJyb3IsIEFkdmlzb3J5TG9ja0hvbGRUaW1lb3V0RXJyb3IsIEFkdmlzb3J5TG9ja1RpbWVvdXRFcnJvcn1cbiJdfQ==
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
export default class VelociousDatabaseDriversMysql extends Base {
|
|
2
|
+
/** @type {import("mysql").Pool | undefined} */
|
|
3
|
+
pool: import("mysql").Pool | undefined;
|
|
2
4
|
/** @type {string | null} */
|
|
3
5
|
_desiredSessionTimeZone: string | null;
|
|
4
6
|
/** @type {string | null} */
|
|
5
7
|
_currentSessionTimeZone: string | null;
|
|
6
|
-
pool: mysql.Pool | undefined;
|
|
7
8
|
/**
|
|
8
9
|
* On pool error.
|
|
9
10
|
* @param {Error} error - Error from the connection attempt.
|
|
@@ -68,6 +69,5 @@ export default class VelociousDatabaseDriversMysql extends Base {
|
|
|
68
69
|
_options: Options | undefined;
|
|
69
70
|
}
|
|
70
71
|
import Base from "../base.js";
|
|
71
|
-
import mysql from "mysql";
|
|
72
72
|
import Options from "./options.js";
|
|
73
73
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/database/drivers/mysql/index.js"],"names":[],"mappings":"AAgCA;IACE,4BAA4B;IAC5B,yBADW,MAAM,GAAG,IAAI,CACU;IAElC,4BAA4B;IAC5B,yBADW,MAAM,GAAG,IAAI,CACM;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/database/drivers/mysql/index.js"],"names":[],"mappings":"AAgCA;IACE,+CAA+C;IAC/C,MADW,OAAO,OAAO,EAAE,IAAI,GAAG,SAAS,CAC3B;IAEhB,4BAA4B;IAC5B,yBADW,MAAM,GAAG,IAAI,CACU;IAElC,4BAA4B;IAC5B,yBADW,MAAM,GAAG,IAAI,CACM;IAY9B;;;OAGG;IACH,cAAe,OAFJ,KAES,UAEnB;IAyCD;;;OAGG;IACH,6BAFa,MAAM,GAAG,IAAI,CAIzB;IAED;;;OAGG;IACH,oCAFW,MAAM,GAAG,IAAI,QAIvB;IAED;;;OAGG;IACH,6BAFa,MAAM,GAAG,IAAI,CAIzB;IAED;;OAEG;IACH,oCAEC;IAED;;;OAGG;IACH,yBAFa,OAAO,CAAC,OAAO,CAAC,CAU5B;IAED;;;;OAIG;IACH,6BAHW,MAAM,GACJ,OAAO,CAAC,OAAO,CAAC,CAS5B;IAED;;;OAGG;IACH,eAFa,MAAM,CAAC,MAAM,EAAE,OAAC,CAAC,CAmB7B;IAcD;;;;;;OAMG;IACH,gCALW,MAAM,SAEd;QAAuB,WAAW;KAClC,GAAU,MAAM,EAAE,CAOpB;IAoDD;;;OAGG;IACH,mBAFa,OAAO,CAAC,MAAM,CAAC,CAM3B;IA4GD;;;;OAIG;IACH,aAHW,MAAM,GACJ,MAAM,CAQlB;IAoEqB,8BAA2C;CAuGlE;iBAxiBgB,YAAY;oBAST,cAAc"}
|