velocious 1.0.650 → 1.0.651
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 +4 -2
- package/build/database/record/counter-cache-magnitude.js +14 -3
- package/build/database/record/counter-cache-parent-updates.js +137 -0
- package/build/database/record/index.js +13 -3
- package/build/frontend-models/websocket-publishers.js +35 -12
- package/build/http-client/websocket-client.js +38 -6
- package/build/src/database/record/counter-cache-magnitude.d.ts.map +1 -1
- package/build/src/database/record/counter-cache-magnitude.js +14 -4
- package/build/src/database/record/counter-cache-parent-updates.d.ts +43 -0
- package/build/src/database/record/counter-cache-parent-updates.d.ts.map +1 -0
- package/build/src/database/record/counter-cache-parent-updates.js +125 -0
- package/build/src/database/record/index.d.ts.map +1 -1
- package/build/src/database/record/index.js +14 -4
- package/build/src/frontend-models/websocket-publishers.d.ts.map +1 -1
- package/build/src/frontend-models/websocket-publishers.js +32 -13
- package/build/src/http-client/websocket-client.d.ts.map +1 -1
- package/build/src/http-client/websocket-client.js +39 -7
- package/package.json +1 -1
- package/src/database/record/counter-cache-magnitude.js +14 -3
- package/src/database/record/counter-cache-parent-updates.js +137 -0
- package/src/database/record/index.js +13 -3
- package/src/frontend-models/websocket-publishers.js +35 -12
- package/src/http-client/websocket-client.js +38 -6
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* External packages (engines) that contribute data models, frontend-model resources and migrations to a consuming app (see [docs/packages.md](docs/packages.md))
|
|
14
14
|
* Optional Rampway-owned durable deployment control plane mounted through the standard routes DSL on Velocious 1.0.577 or newer (see [docs/rampway-integration.md](docs/rampway-integration.md))
|
|
15
15
|
* Controllers and views for HTTP endpoints
|
|
16
|
-
* Frontend-model transport for creating, updating, querying, and subscribing to query-filtered lifecycle events over HTTP/WebSocket,
|
|
16
|
+
* Frontend-model transport for creating, updating, querying, and subscribing to query-filtered lifecycle events over HTTP/WebSocket, including committed counter-cache parent updates, structured per-attribute validation error responses, immutable per-operation remote request context, registration-local tenant subscription partitioning, and one-budget WebSocket startup controls (see [docs/frontend-models.md](docs/frontend-models.md), [docs/remote-request-context.md](docs/remote-request-context.md), and [docs/websocket-channels.md](docs/websocket-channels.md))
|
|
17
17
|
* Client-side offline sync mutation logs and frontend-model optimistic queueing primitives (see the [shared-resource sync developer guide](docs/shared-resource-sync-guide.md) and [offline sync architecture](docs/offline-sync.md))
|
|
18
18
|
* Declarative client sync scopes with per-scope cursors, automatic mutation tracking, opt-in durable base-version conflict replay, realtime delivery, and immutable-handle project clients whose local database state plus remote pull/replay/realtime request context stay tenant-bound through reconnect (see [docs/sync-client.md](docs/sync-client.md), [docs/remote-request-context.md](docs/remote-request-context.md), and [docs/offline-sync.md](docs/offline-sync.md))
|
|
19
19
|
* Reactive `useLiveQuery(Model.where(...))` queries for default databases plus immutable-handle tenant live-query sources whose committed events and refreshes stay on the captured physical tenant (see [docs/live-queries.md](docs/live-queries.md))
|
|
@@ -2124,7 +2124,9 @@ await client.close()
|
|
|
2124
2124
|
|
|
2125
2125
|
For long-lived Node clients, the constructor also accepts opt-in liveness options (all default off, so browser/Expo usage is unchanged): `webSocketImplementation` (inject Node's `ws`, since the global/undici WebSocket exposes neither protocol ping nor an unref-able socket), `heartbeatIntervalMs` (a ping heartbeat that drops a socket whose peer stops ponging, so a client notices a vanished server), and `unref` (unref the underlying socket so an idle connection can't keep the process alive on its own). See [docs/websocket-channels.md](docs/websocket-channels.md).
|
|
2126
2126
|
|
|
2127
|
-
`await client.close()` is a final graceful shutdown that releases resumable server-session state
|
|
2127
|
+
`await client.close()` is a final graceful shutdown that releases resumable server-session state. Every channel subscription still tracked by the client, including a pending subscription, becomes closed and receives `onClose("client_close")`. A pending `subscription.ready` still rejects because no subscribe acknowledgement arrived, and its error identifies `client_close`. Throwing channel close callbacks do not stop the remaining handles or transport/base cleanup; after cleanup, `client.close()` rejects with the original error for one failed callback or teardown step, or with an `AggregateError` for multiple failures.
|
|
2128
|
+
|
|
2129
|
+
Unexpected transport drops first attempt to resume server-session state. On a multi-worker server, the client automatically puts the prior session identity in the reconnect upgrade URL so the host can route it to its owning worker; routing is session-based and never source-IP-based. A successful resume retains the existing server-side connection and channel instances. If the server instead rejects the old session with `session-gone`, SnapReq promotes the already-established fresh session, reopens still-live one-to-one connection handles, and re-subscribes still-live channel handles. Those public handles remain usable and channel readiness resolves on the fresh session; explicitly closed handles stay closed. A channel that the server permanently closes or rejects remains terminal and is not automatically reopened; register a new listener after changing the authorization or request context. See [the WebSocket channel lifecycle guarantees](docs/websocket-channels.md#lifecycle-guarantees-phase-1b).
|
|
2128
2130
|
|
|
2129
2131
|
## Subscribe to events
|
|
2130
2132
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
+
import {prepareCounterCacheParentUpdate, scheduleCounterCacheParentUpdate} from "./counter-cache-parent-updates.js"
|
|
4
|
+
|
|
3
5
|
/**
|
|
4
6
|
* Reactive counter-cache driven by a per-record magnitude.
|
|
5
7
|
*
|
|
@@ -206,17 +208,26 @@ async function incrementParentCounter(record, definition, parentId, amount) {
|
|
|
206
208
|
|
|
207
209
|
// Resolve through the record so an operation both owns the parent write and
|
|
208
210
|
// rejects a relationship whose parent routes to another database.
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
.driver
|
|
211
|
+
const parentQuery = record.queryForModel(parentModelClass)
|
|
212
|
+
const db = parentQuery.driver
|
|
212
213
|
const counterColumnSql = db.quoteColumn(definition.counterColumn)
|
|
213
214
|
const truncatedAmount = Math.trunc(amount)
|
|
215
|
+
const preparedParentUpdate = await prepareCounterCacheParentUpdate({
|
|
216
|
+
parentId,
|
|
217
|
+
parentModelClass,
|
|
218
|
+
parentPrimaryKey: relationship.getPrimaryKey(),
|
|
219
|
+
parentQuery
|
|
220
|
+
})
|
|
214
221
|
|
|
215
222
|
await db.query(
|
|
216
223
|
`UPDATE ${db.quoteTable(parentModelClass.tableName())} ` +
|
|
217
224
|
`SET ${counterColumnSql} = COALESCE(${counterColumnSql}, 0) + ${truncatedAmount} ` +
|
|
218
225
|
`WHERE ${db.quoteColumn(relationship.getPrimaryKey())} = ${db.quote(parentId)}`
|
|
219
226
|
)
|
|
227
|
+
await scheduleCounterCacheParentUpdate({
|
|
228
|
+
preparedUpdate: preparedParentUpdate,
|
|
229
|
+
sourceRecord: record
|
|
230
|
+
})
|
|
220
231
|
}
|
|
221
232
|
|
|
222
233
|
/**
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import Logger from "../../logger.js"
|
|
4
|
+
import {ensureError} from "typanic"
|
|
5
|
+
|
|
6
|
+
/** @typedef {(parent: import("./index.js").default, previousParent: import("./index.js").default | undefined) => void | Promise<void>} CounterCacheParentUpdateListener */
|
|
7
|
+
/** @typedef {{canonicalParentModelClass: typeof import("./index.js").default, listeners: CounterCacheParentUpdateListener[], parentId: ReturnType<typeof JSON.parse>, parentPrimaryKey: string, parentQuery: import("../query/model-class-query.js").default<typeof import("./index.js").default>, previousParent: import("./index.js").default | undefined}} PreparedCounterCacheParentUpdate */
|
|
8
|
+
|
|
9
|
+
/** @type {WeakMap<typeof import("./index.js").default, Set<CounterCacheParentUpdateListener>>} */
|
|
10
|
+
const listenersByParentModelClass = new WeakMap()
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Registers an internal listener for committed counter-cache parent updates.
|
|
14
|
+
* @param {typeof import("./index.js").default} parentModelClass - Parent model class.
|
|
15
|
+
* @param {CounterCacheParentUpdateListener} listener - Committed-parent listener.
|
|
16
|
+
* @returns {() => void} - Listener removal callback.
|
|
17
|
+
*/
|
|
18
|
+
export function registerCounterCacheParentUpdateListener(parentModelClass, listener) {
|
|
19
|
+
const canonicalParentModelClass = parentModelClass.canonicalRecordMetadataModelClass()
|
|
20
|
+
let listeners = listenersByParentModelClass.get(canonicalParentModelClass)
|
|
21
|
+
|
|
22
|
+
if (!listeners) {
|
|
23
|
+
listeners = new Set()
|
|
24
|
+
listenersByParentModelClass.set(canonicalParentModelClass, listeners)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
listeners.add(listener)
|
|
28
|
+
|
|
29
|
+
return () => listeners.delete(listener)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Captures one counter-cache parent's pre-mutation state when listeners are registered.
|
|
34
|
+
* @param {object} args - Parent update arguments.
|
|
35
|
+
* @param {ReturnType<typeof JSON.parse>} args.parentId - Parent relationship identity.
|
|
36
|
+
* @param {typeof import("./index.js").default} args.parentModelClass - Parent model class.
|
|
37
|
+
* @param {string} args.parentPrimaryKey - Parent relationship primary key.
|
|
38
|
+
* @param {import("../query/model-class-query.js").default<typeof import("./index.js").default>} args.parentQuery - Source-owned parent query.
|
|
39
|
+
* @returns {Promise<PreparedCounterCacheParentUpdate | undefined>} - Prepared delivery, or undefined when no listener is registered.
|
|
40
|
+
*/
|
|
41
|
+
export async function prepareCounterCacheParentUpdate({parentId, parentModelClass, parentPrimaryKey, parentQuery}) {
|
|
42
|
+
const canonicalParentModelClass = parentModelClass.canonicalRecordMetadataModelClass()
|
|
43
|
+
const registeredListeners = listenersByParentModelClass.get(canonicalParentModelClass)
|
|
44
|
+
|
|
45
|
+
if (!registeredListeners || registeredListeners.size == 0) return
|
|
46
|
+
const previousParent = await parentQuery.findBy({[parentPrimaryKey]: parentId}) || undefined
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
canonicalParentModelClass,
|
|
50
|
+
listeners: [...registeredListeners],
|
|
51
|
+
parentId,
|
|
52
|
+
parentPrimaryKey,
|
|
53
|
+
parentQuery,
|
|
54
|
+
previousParent
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Schedules one non-coalesced parent reload and notification on the source record's commit lifecycle.
|
|
60
|
+
* @param {object} args - Parent update arguments.
|
|
61
|
+
* @param {PreparedCounterCacheParentUpdate | undefined} args.preparedUpdate - Pre-mutation parent delivery state.
|
|
62
|
+
* @param {import("./index.js").default} args.sourceRecord - Source record that owns the transaction lifecycle.
|
|
63
|
+
* @returns {Promise<void>} - Resolves after registration or immediate delivery.
|
|
64
|
+
*/
|
|
65
|
+
export async function scheduleCounterCacheParentUpdate({preparedUpdate, sourceRecord}) {
|
|
66
|
+
if (!preparedUpdate) return
|
|
67
|
+
|
|
68
|
+
const {canonicalParentModelClass, listeners, parentId, parentPrimaryKey, parentQuery, previousParent} = preparedUpdate
|
|
69
|
+
|
|
70
|
+
await sourceRecord.connection().afterCommit(async () => {
|
|
71
|
+
let parent
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
parent = await parentQuery.findBy({[parentPrimaryKey]: parentId})
|
|
75
|
+
} catch (error) {
|
|
76
|
+
await reportCounterCacheParentUpdateError(canonicalParentModelClass._getConfiguration(), error)
|
|
77
|
+
return
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (!parent) return
|
|
81
|
+
|
|
82
|
+
for (const listener of listeners) {
|
|
83
|
+
try {
|
|
84
|
+
await listener(parent, previousParent)
|
|
85
|
+
} catch (error) {
|
|
86
|
+
await reportCounterCacheParentUpdateError(parent._getConfiguration(), error)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Reports a post-commit delivery failure without rejecting the durable source operation.
|
|
94
|
+
* @param {import("../../configuration.js").default} configuration - Owning configuration.
|
|
95
|
+
* @param {ReturnType<typeof JSON.parse>} caughtError - Reload or listener failure.
|
|
96
|
+
* @returns {Promise<void>} - Resolves after best-effort reporting.
|
|
97
|
+
*/
|
|
98
|
+
async function reportCounterCacheParentUpdateError(configuration, caughtError) {
|
|
99
|
+
const error = ensureError(caughtError)
|
|
100
|
+
const payload = {
|
|
101
|
+
context: {stage: "counter-cache-parent-update-after-commit"},
|
|
102
|
+
error
|
|
103
|
+
}
|
|
104
|
+
/** @type {ReturnType<typeof JSON.parse>[]} */
|
|
105
|
+
const reportingErrors = []
|
|
106
|
+
let errorEvents
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
errorEvents = configuration.getErrorEvents()
|
|
110
|
+
} catch (reportingError) {
|
|
111
|
+
reportingErrors.push(reportingError)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (errorEvents) {
|
|
115
|
+
try {
|
|
116
|
+
errorEvents.emit("framework-error", payload)
|
|
117
|
+
} catch (reportingError) {
|
|
118
|
+
reportingErrors.push(reportingError)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
errorEvents.emit("all-error", {...payload, errorType: "framework-error"})
|
|
123
|
+
} catch (reportingError) {
|
|
124
|
+
reportingErrors.push(reportingError)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (reportingErrors.length == 0) return
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
const logger = new Logger("CounterCacheParentUpdates", {configuration})
|
|
132
|
+
|
|
133
|
+
await logger.error("Counter-cache parent update error reporting failed", {error, reportingErrors})
|
|
134
|
+
} catch {
|
|
135
|
+
console.error("Counter-cache parent update error reporting failed")
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -56,6 +56,7 @@ import {formatValue} from "../../utils/format-value.js"
|
|
|
56
56
|
import {modelPrimaryKeyCacheKey, modelPrimaryKeyConditions, readModelPrimaryKeyValue, scalarModelPrimaryKey, scalarModelPrimaryKeyValue} from "../../utils/model-primary-key.js"
|
|
57
57
|
import {captureCreateAuditChanges, captureUpdateAuditChanges, createAudit, createCreateAudit, createDestroyAudit, createUpdateAudit, initializeAuditing, registerAuditCallback, registerAuditing, withoutAudit} from "./auditing.js"
|
|
58
58
|
import {registerMagnitudeCounterCache} from "./counter-cache-magnitude.js"
|
|
59
|
+
import {prepareCounterCacheParentUpdate, scheduleCounterCacheParentUpdate} from "./counter-cache-parent-updates.js"
|
|
59
60
|
import {stateMachine} from "./state-machine.js"
|
|
60
61
|
import ValidatorsFormat from "./validators/format.js"
|
|
61
62
|
import ValidatorsLength from "./validators/length.js"
|
|
@@ -999,14 +1000,23 @@ class VelociousDatabaseRecord {
|
|
|
999
1000
|
const parentTable = ParentModel.tableName()
|
|
1000
1001
|
const childTable = ChildModel.tableName()
|
|
1001
1002
|
const pkColumn = inflection.underscore(primaryKey)
|
|
1002
|
-
const
|
|
1003
|
-
|
|
1004
|
-
.driver
|
|
1003
|
+
const parentQuery = record.queryForModel(ParentModel)
|
|
1004
|
+
const connection = parentQuery.driver
|
|
1005
1005
|
const quoted = connection.quote(parentId)
|
|
1006
|
+
const preparedParentUpdate = await prepareCounterCacheParentUpdate({
|
|
1007
|
+
parentId,
|
|
1008
|
+
parentModelClass: ParentModel,
|
|
1009
|
+
parentPrimaryKey: primaryKey,
|
|
1010
|
+
parentQuery
|
|
1011
|
+
})
|
|
1006
1012
|
|
|
1007
1013
|
const sql = `UPDATE ${connection.quoteTable(parentTable)} SET ${connection.quoteColumn(counterColumn)} = (SELECT COUNT(*) FROM ${connection.quoteTable(childTable)} WHERE ${connection.quoteColumn(fk)} = ${quoted}) WHERE ${connection.quoteColumn(pkColumn)} = ${quoted}`
|
|
1008
1014
|
|
|
1009
1015
|
await connection.query(sql, {logName: `${ParentModel.name} Update`})
|
|
1016
|
+
await scheduleCounterCacheParentUpdate({
|
|
1017
|
+
preparedUpdate: preparedParentUpdate,
|
|
1018
|
+
sourceRecord: record
|
|
1019
|
+
})
|
|
1010
1020
|
}
|
|
1011
1021
|
|
|
1012
1022
|
/**
|
|
@@ -5,12 +5,14 @@ import {frontendModelResourcesWithBuiltInsForBackendProject} from "./built-in-re
|
|
|
5
5
|
import {frontendModelResourceDefinitionIsClass} from "./resource-definition.js"
|
|
6
6
|
import {serializeFrontendModelTransportValue} from "./transport-serialization.js"
|
|
7
7
|
import {modelPrimaryKeyCacheKey, readModelPrimaryKeyValue} from "../utils/model-primary-key.js"
|
|
8
|
+
import {registerCounterCacheParentUpdateListener} from "../database/record/counter-cache-parent-updates.js"
|
|
8
9
|
|
|
9
10
|
/** @typedef {{primaryKey: import("../utils/model-primary-key.js").ModelPrimaryKeyDefinition}} FrontendModelPublisherResource */
|
|
10
11
|
/** @typedef {Record<string, import("./query.js").FrontendModelTransportValue>} FrontendModelDestroyAuthorizationRecord */
|
|
11
12
|
/** @typedef {import("../database/record/index.js").default & {__frontendModelWebsocketAction?: "create" | "update", __frontendModelWebsocketDestroyAuthorizationRecord?: FrontendModelDestroyAuthorizationRecord, __frontendModelWebsocketPreviousIds?: Map<string, import("../utils/model-primary-key.js").ModelPrimaryKeyValue>}} FrontendModelWebsocketRecord */
|
|
12
13
|
|
|
13
14
|
const modelClassesWithRegisteredHooks = new WeakSet()
|
|
15
|
+
const modelClassesWithRegisteredCounterCacheParentListeners = new WeakSet()
|
|
14
16
|
const channelClassRegisteredConfigurations = new WeakSet()
|
|
15
17
|
/** @type {WeakMap<import("../configuration.js").default, WeakMap<typeof import("../database/record/index.js").default, Map<string, FrontendModelPublisherResource>>>} */
|
|
16
18
|
const publisherResourcesByConfiguration = new WeakMap()
|
|
@@ -127,6 +129,7 @@ export async function ensureFrontendModelWebsocketPublishersRegistered(configura
|
|
|
127
129
|
if (!resourceClass.ModelClass) continue
|
|
128
130
|
|
|
129
131
|
const modelClass = resourceClass.modelClass()
|
|
132
|
+
const canonicalModelClass = modelClass.canonicalRecordMetadataModelClass()
|
|
130
133
|
const resourceConfiguration = resourceClass.resourceConfig()
|
|
131
134
|
const configuredPrimaryKey = resourceConfiguration.primaryKey
|
|
132
135
|
const modelPrimaryKey = modelClass.primaryKey()
|
|
@@ -140,38 +143,47 @@ export async function ensureFrontendModelWebsocketPublishersRegistered(configura
|
|
|
140
143
|
publisherResourcesByConfiguration.set(configuration, publisherResourcesByModelClass)
|
|
141
144
|
}
|
|
142
145
|
|
|
143
|
-
let publisherResources = publisherResourcesByModelClass.get(
|
|
146
|
+
let publisherResources = publisherResourcesByModelClass.get(canonicalModelClass)
|
|
144
147
|
|
|
145
148
|
if (!publisherResources) {
|
|
146
149
|
publisherResources = new Map()
|
|
147
|
-
publisherResourcesByModelClass.set(
|
|
150
|
+
publisherResourcesByModelClass.set(canonicalModelClass, publisherResources)
|
|
148
151
|
}
|
|
149
152
|
|
|
150
153
|
publisherResources.set(modelName, {
|
|
151
154
|
primaryKey
|
|
152
155
|
})
|
|
153
156
|
|
|
157
|
+
if (!modelClassesWithRegisteredCounterCacheParentListeners.has(canonicalModelClass)) {
|
|
158
|
+
modelClassesWithRegisteredCounterCacheParentListeners.add(canonicalModelClass)
|
|
159
|
+
registerCounterCacheParentUpdateListener(canonicalModelClass, (parent, previousParent) => {
|
|
160
|
+
const previousIds = previousParent ? frontendModelResourceIdentities(previousParent) : undefined
|
|
161
|
+
|
|
162
|
+
broadcastFrontendModelEvents(parent, "update", previousIds)
|
|
163
|
+
})
|
|
164
|
+
}
|
|
165
|
+
|
|
154
166
|
// Register lifecycle hooks once per model class, not per configuration. A model class belongs to a
|
|
155
167
|
// single backend project/config in production, so per-config registration only differs in tests where
|
|
156
168
|
// the same model class is reachable from multiple configs — there it attaches duplicate beforeCreate/
|
|
157
169
|
// afterSave/afterDestroy hooks that double-fire broadcasts (and leak across specs). The hooks read the
|
|
158
170
|
// model's runtime configuration when broadcasting, so a single registration is sufficient.
|
|
159
|
-
if (modelClassesWithRegisteredHooks.has(
|
|
171
|
+
if (modelClassesWithRegisteredHooks.has(canonicalModelClass)) continue
|
|
160
172
|
|
|
161
|
-
modelClassesWithRegisteredHooks.add(
|
|
173
|
+
modelClassesWithRegisteredHooks.add(canonicalModelClass)
|
|
162
174
|
|
|
163
|
-
|
|
175
|
+
canonicalModelClass.beforeCreate((model) => {
|
|
164
176
|
/** @type {FrontendModelWebsocketRecord} */ (model).__frontendModelWebsocketAction = "create"
|
|
165
177
|
})
|
|
166
178
|
|
|
167
|
-
|
|
179
|
+
canonicalModelClass.beforeUpdate(async (model) => {
|
|
168
180
|
const websocketModel = /** @type {FrontendModelWebsocketRecord} */ (model)
|
|
169
181
|
|
|
170
182
|
websocketModel.__frontendModelWebsocketAction = "update"
|
|
171
183
|
websocketModel.__frontendModelWebsocketPreviousIds = await frontendModelPreviousResourceIdentities(model)
|
|
172
184
|
})
|
|
173
185
|
|
|
174
|
-
|
|
186
|
+
canonicalModelClass.beforeDestroy(async (model) => {
|
|
175
187
|
const websocketModel = /** @type {FrontendModelWebsocketRecord} */ (model)
|
|
176
188
|
const persistedModel = await model
|
|
177
189
|
.queryForModel(model.getModelClass())
|
|
@@ -183,7 +195,7 @@ export async function ensureFrontendModelWebsocketPublishersRegistered(configura
|
|
|
183
195
|
websocketModel.__frontendModelWebsocketDestroyAuthorizationRecord = frontendModelDestroyAuthorizationRecord(persistedModel)
|
|
184
196
|
})
|
|
185
197
|
|
|
186
|
-
|
|
198
|
+
canonicalModelClass.afterSave((model) => {
|
|
187
199
|
const modelWithWebsocketAction = /** @type {FrontendModelWebsocketRecord} */ (model)
|
|
188
200
|
const action = modelWithWebsocketAction.__frontendModelWebsocketAction
|
|
189
201
|
|
|
@@ -197,7 +209,7 @@ export async function ensureFrontendModelWebsocketPublishersRegistered(configura
|
|
|
197
209
|
delete modelWithWebsocketAction.__frontendModelWebsocketPreviousIds
|
|
198
210
|
})
|
|
199
211
|
|
|
200
|
-
|
|
212
|
+
canonicalModelClass.afterDestroy((model) => {
|
|
201
213
|
const websocketModel = /** @type {FrontendModelWebsocketRecord} */ (model)
|
|
202
214
|
const destroyAuthorizationRecord = websocketModel.__frontendModelWebsocketDestroyAuthorizationRecord
|
|
203
215
|
const previousIds = websocketModel.__frontendModelWebsocketPreviousIds
|
|
@@ -217,7 +229,7 @@ export async function ensureFrontendModelWebsocketPublishersRegistered(configura
|
|
|
217
229
|
* @returns {Promise<Map<string, import("../utils/model-primary-key.js").ModelPrimaryKeyValue>>} - Previous identities by resource name.
|
|
218
230
|
*/
|
|
219
231
|
async function frontendModelPreviousResourceIdentities(model) {
|
|
220
|
-
const publisherResources =
|
|
232
|
+
const publisherResources = publisherResourcesForModel(model)
|
|
221
233
|
/** @type {Map<string, import("../utils/model-primary-key.js").ModelPrimaryKeyValue>} */
|
|
222
234
|
const previousIds = new Map()
|
|
223
235
|
|
|
@@ -252,7 +264,7 @@ async function frontendModelPreviousResourceIdentities(model) {
|
|
|
252
264
|
* @returns {Map<string, import("../utils/model-primary-key.js").ModelPrimaryKeyValue>} - Identities by resource name.
|
|
253
265
|
*/
|
|
254
266
|
function frontendModelResourceIdentities(model) {
|
|
255
|
-
const publisherResources =
|
|
267
|
+
const publisherResources = publisherResourcesForModel(model)
|
|
256
268
|
/** @type {Map<string, import("../utils/model-primary-key.js").ModelPrimaryKeyValue>} */
|
|
257
269
|
const identities = new Map()
|
|
258
270
|
|
|
@@ -267,6 +279,17 @@ function frontendModelResourceIdentities(model) {
|
|
|
267
279
|
return identities
|
|
268
280
|
}
|
|
269
281
|
|
|
282
|
+
/**
|
|
283
|
+
* Returns publisher resources through the backing model's canonical registry owner.
|
|
284
|
+
* @param {import("../database/record/index.js").default} model - Backing model instance.
|
|
285
|
+
* @returns {Map<string, FrontendModelPublisherResource> | undefined} - Publisher resources for the model.
|
|
286
|
+
*/
|
|
287
|
+
function publisherResourcesForModel(model) {
|
|
288
|
+
const canonicalModelClass = model.getModelClass().canonicalRecordMetadataModelClass()
|
|
289
|
+
|
|
290
|
+
return publisherResourcesByConfiguration.get(model._getConfiguration())?.get(canonicalModelClass)
|
|
291
|
+
}
|
|
292
|
+
|
|
270
293
|
/**
|
|
271
294
|
* Serializes the persisted record for server-side destroy authorization. Binary values
|
|
272
295
|
* use a dedicated byte-array marker because the shared transport serializer otherwise
|
|
@@ -337,7 +360,7 @@ function frontendModelResourceIdentity({model, previous = false, primaryKey}) {
|
|
|
337
360
|
*/
|
|
338
361
|
function broadcastFrontendModelEvents(model, action, previousIds, destroyAuthorizationRecord) {
|
|
339
362
|
const configuration = model._getConfiguration()
|
|
340
|
-
const publisherResources =
|
|
363
|
+
const publisherResources = publisherResourcesForModel(model)
|
|
341
364
|
|
|
342
365
|
if (!publisherResources) return
|
|
343
366
|
|
|
@@ -118,19 +118,51 @@ export default class VelociousWebsocketClient extends SnapReqWebSocketClient {
|
|
|
118
118
|
if (this.gracefulClosePromise) return await this.gracefulClosePromise
|
|
119
119
|
|
|
120
120
|
this.autoReconnect = false
|
|
121
|
+
const channelSubscriptions = [...this._channelSubscriptions.values()]
|
|
121
122
|
const socket = this.socket
|
|
123
|
+
|
|
124
|
+
this._channelSubscriptions.clear()
|
|
125
|
+
const {promise: publishedClosePromise, reject: rejectPublishedClose, resolve: resolvePublishedClose} = Promise.withResolvers()
|
|
126
|
+
|
|
127
|
+
this.gracefulClosePromise = publishedClosePromise
|
|
128
|
+
// This internal bridge exists only for synchronous reentrancy. Its rejection
|
|
129
|
+
// duplicates closePromise, which remains the public error source below.
|
|
130
|
+
void publishedClosePromise.catch(() => {})
|
|
122
131
|
const closePromise = (async () => {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
132
|
+
/** @type {unknown[]} */
|
|
133
|
+
const closeErrors = []
|
|
134
|
+
|
|
135
|
+
for (const subscription of channelSubscriptions) {
|
|
136
|
+
try {
|
|
137
|
+
subscription._handleClosed("client_close")
|
|
138
|
+
} catch (error) {
|
|
139
|
+
closeErrors.push(error)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
if (socket && socket.readyState === socket.OPEN) {
|
|
145
|
+
await new Promise((resolve) => {
|
|
146
|
+
socket.addEventListener("close", () => resolve(undefined), {once: true})
|
|
147
|
+
socket.close(1000)
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
} catch (error) {
|
|
151
|
+
closeErrors.push(error)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
await super.close()
|
|
156
|
+
} catch (error) {
|
|
157
|
+
closeErrors.push(error)
|
|
128
158
|
}
|
|
129
159
|
|
|
130
|
-
|
|
160
|
+
if (closeErrors.length === 1) throw closeErrors[0]
|
|
161
|
+
if (closeErrors.length > 1) throw new AggregateError(closeErrors, "Failed to close WebSocket client")
|
|
131
162
|
})()
|
|
132
163
|
|
|
133
164
|
this.gracefulClosePromise = closePromise
|
|
165
|
+
void closePromise.then(resolvePublishedClose, rejectPublishedClose)
|
|
134
166
|
|
|
135
167
|
try {
|
|
136
168
|
await closePromise
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"counter-cache-magnitude.d.ts","sourceRoot":"","sources":["../../../../src/database/record/counter-cache-magnitude.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"counter-cache-magnitude.d.ts","sourceRoot":"","sources":["../../../../src/database/record/counter-cache-magnitude.js"],"names":[],"mappings":"AAoBG,YAKG,+BAA+B,GALxB;IACR,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,CAAC,WAAW,EAAE,UAAU,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,MAAM,CAAA;CAClE,CAAiC;AAKlC,YAAgJ,qBAAqB,GAA3J;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,UAAU,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC;IAAC,WAAW,EAAE,UAAU,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,CAAA;CAAC,CAAuB;AA1BxK;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH;;;GAGG;AAEH;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,UAAU,EAJ7C,cAAc,YAAY,EAAE,OAIiB,EAAE,UAAU,EAHzD,+BAGyD,GAFvD,IAAI,CAiDhB"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
+
import { prepareCounterCacheParentUpdate, scheduleCounterCacheParentUpdate } from "./counter-cache-parent-updates.js";
|
|
2
3
|
/**
|
|
3
4
|
* Reactive counter-cache driven by a per-record magnitude.
|
|
4
5
|
*
|
|
@@ -175,14 +176,23 @@ async function incrementParentCounter(record, definition, parentId, amount) {
|
|
|
175
176
|
}
|
|
176
177
|
// Resolve through the record so an operation both owns the parent write and
|
|
177
178
|
// rejects a relationship whose parent routes to another database.
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
.driver;
|
|
179
|
+
const parentQuery = record.queryForModel(parentModelClass);
|
|
180
|
+
const db = parentQuery.driver;
|
|
181
181
|
const counterColumnSql = db.quoteColumn(definition.counterColumn);
|
|
182
182
|
const truncatedAmount = Math.trunc(amount);
|
|
183
|
+
const preparedParentUpdate = await prepareCounterCacheParentUpdate({
|
|
184
|
+
parentId,
|
|
185
|
+
parentModelClass,
|
|
186
|
+
parentPrimaryKey: relationship.getPrimaryKey(),
|
|
187
|
+
parentQuery
|
|
188
|
+
});
|
|
183
189
|
await db.query(`UPDATE ${db.quoteTable(parentModelClass.tableName())} ` +
|
|
184
190
|
`SET ${counterColumnSql} = COALESCE(${counterColumnSql}, 0) + ${truncatedAmount} ` +
|
|
185
191
|
`WHERE ${db.quoteColumn(relationship.getPrimaryKey())} = ${db.quote(parentId)}`);
|
|
192
|
+
await scheduleCounterCacheParentUpdate({
|
|
193
|
+
preparedUpdate: preparedParentUpdate,
|
|
194
|
+
sourceRecord: record
|
|
195
|
+
});
|
|
186
196
|
}
|
|
187
197
|
/**
|
|
188
198
|
* Resolves the column name backing an attribute name.
|
|
@@ -203,4 +213,4 @@ function attributeNameForColumn(record, columnName) {
|
|
|
203
213
|
const map = record.getModelClass().getAttributeNameToColumnNameMap();
|
|
204
214
|
return Object.keys(map).find((attributeName) => map[attributeName] === columnName) || columnName;
|
|
205
215
|
}
|
|
206
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY291bnRlci1jYWNoZS1tYWduaXR1ZGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvZGF0YWJhc2UvcmVjb3JkL2NvdW50ZXItY2FjaGUtbWFnbml0dWRlLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLFlBQVk7QUFFWjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXNCRztBQUVIOzs7R0FHRztBQUVIOzs7OztHQUtHO0FBQ0gsTUFBTSxVQUFVLDZCQUE2QixDQUFDLFVBQVUsRUFBRSxVQUFVO0lBQ2xFOzsrQ0FFMkM7SUFDM0MsTUFBTSxZQUFZLEdBQUcsVUFBVSxDQUFBO0lBQy9CLE1BQU0sY0FBYyxHQUFHLG9DQUFvQyxVQUFVLENBQUMsYUFBYSxFQUFFLENBQUE7SUFFckYseUZBQXlGO0lBQ3pGLElBQUksTUFBTSxDQUFDLFNBQVMsQ0FBQyxjQUFjLENBQUMsSUFBSSxDQUFDLFlBQVksRUFBRSxjQUFjLENBQUMsSUFBSSxZQUFZLENBQUMsY0FBYyxDQUFDLEVBQUUsQ0FBQztRQUN2RyxPQUFNO0lBQ1IsQ0FBQztJQUVELFlBQVksQ0FBQyxjQUFjLENBQUMsR0FBRyxJQUFJLENBQUE7SUFFbkMsTUFBTSxVQUFVLEdBQUcsaUNBQWlDLFVBQVUsQ0FBQyxhQUFhLEVBQUUsQ0FBQTtJQUU5RSxVQUFVLENBQUMsVUFBVSxDQUFDLEtBQUssV0FBVyxNQUFNO1FBQzFDOzttREFFMkM7UUFDM0MsTUFBTSxhQUFhLEdBQUcsTUFBTSxDQUFBO1FBRTVCLGFBQWEsQ0FBQyxVQUFVLENBQUMsR0FBRyw0QkFBNEIsQ0FBQyxNQUFNLEVBQUUsVUFBVSxDQUFDLENBQUE7SUFDOUUsQ0FBQyxDQUFDLENBQUE7SUFFRixVQUFVLENBQUMsU0FBUyxDQUFDLEtBQUssV0FBVyxNQUFNO1FBQ3pDOzttREFFMkM7UUFDM0MsTUFBTSxhQUFhLEdBQUcsTUFBTSxDQUFBO1FBQzVCLE1BQU0sT0FBTyxHQUFHLDJDQUEyQyxDQUFDLENBQUMs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Q0FBQTtBQUNqRyxDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxTQUFTLHNCQUFzQixDQUFDLE1BQU0sRUFBRSxVQUFVO0lBQ2hELE1BQU0sR0FBRyxHQUFHLE1BQU0sQ0FBQyxhQUFhLEVBQUUsQ0FBQywrQkFBK0IsRUFBRSxDQUFBO0lBRXBFLE9BQU8sTUFBTSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxhQUFhLEVBQUUsRUFBRSxDQUFDLEdBQUcsQ0FBQyxhQUFhLENBQUMsS0FBSyxVQUFVLENBQUMsSUFBSSxVQUFVLENBQUE7QUFDbEcsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIEB0cy1jaGVja1xuXG4vKipcbiAqIFJlYWN0aXZlIGNvdW50ZXItY2FjaGUgZHJpdmVuIGJ5IGEgcGVyLXJlY29yZCBtYWduaXR1ZGUuXG4gKlxuICogTWFpbnRhaW5zIGEgY291bnRlciBjb2x1bW4gb24gYSBgYmVsb25nc1RvYCBwYXJlbnQgYXMgdGhlIHJ1bm5pbmcgc3VtIG9mIGVhY2hcbiAqIGNoaWxkJ3MgbWFnbml0dWRlIOKAlCBhIHNtYWxsIG51bWJlciBkZXJpdmVkIGZyb20gb25lIHNvdXJjZSBhdHRyaWJ1dGUgKGUuZy5cbiAqIGAxYCB3aGlsZSBhIGJ1aWxkJ3MgYHN0YXR1c2AgaXMgYHJ1bm5pbmdgLCBlbHNlIGAwYCkuIE9uIGV2ZXJ5IGNyZWF0ZSwgdXBkYXRlXG4gKiBhbmQgZGVzdHJveSB0aGUgY2hhbmdlIGluIG1hZ25pdHVkZSBpcyBhcHBsaWVkIHRvIHRoZSBwYXJlbnQgYXMgYSBzaW5nbGVcbiAqIGF0b21pYyBpbmNyZW1lbnQgKGBTRVQgY29sID0gY29sICsgZGVsdGFgKSwgYW5kIHdoZW4gdGhlIGZvcmVpZ24ga2V5IGNoYW5nZXNcbiAqIHRoZSBtYWduaXR1ZGUgaXMgbW92ZWQgZnJvbSB0aGUgb2xkIHBhcmVudCB0byB0aGUgbmV3IG9uZS5cbiAqXG4gKiBCZWNhdXNlIHRoZSBjb3VudGVyIGlzIGRlcml2ZWQgZnJvbSB0aGUgc291cmNlIGF0dHJpYnV0ZSBhbmQgZGlmZmVkIG9uIGV2ZXJ5XG4gKiB3cml0ZSwgaXQgZm9sbG93cyB0aGF0IGF0dHJpYnV0ZSBhdXRvbWF0aWNhbGx5IG5vIG1hdHRlciB3aGljaCBjb2RlIHBhdGggd3JvdGVcbiAqIGl0IOKAlCB0aGVyZSBpcyBubyBwZXItdHJhbnNpdGlvbiBpbmNyZW1lbnQvZGVjcmVtZW50IHRvIGZvcmdldC4gVGhlIG9sZCB2YWx1ZSBpc1xuICogY2FwdHVyZWQgaW4gYGJlZm9yZVNhdmVgIChWZWxvY2lvdXMgY2xlYXJzIGBjaGFuZ2VzKClgIGR1cmluZyB0aGUgcG9zdC11cGRhdGVcbiAqIHJlbG9hZCkgYW5kIGNvbnN1bWVkIGluIGBhZnRlclNhdmVgLCBzbyB0aGUgaW5jcmVtZW50IGNvbW1pdHMgYXRvbWljYWxseSB3aXRoXG4gKiB0aGUgcm93IGl0IHJlZmxlY3RzLlxuICogQHR5cGVkZWYge3tcbiAqICAgYmVsb25nc1RvOiBzdHJpbmcsXG4gKiAgIGNvdW50ZXJDb2x1bW46IHN0cmluZyxcbiAqICAgc291cmNlQXR0cmlidXRlOiBzdHJpbmcsXG4gKiAgIG1hZ25pdHVkZTogKHNvdXJjZVZhbHVlOiBSZXR1cm5UeXBlPHR5cGVvZiBKU09OLnBhcnNlPikgPT4gbnVtYmVyXG4gKiB9fSBNYWduaXR1ZGVDb3VudGVyQ2FjaGVEZWZpbml0aW9uXG4gKi9cblxuLyoqXG4gKiBDYXB0dXJlZCBwZW5kaW5nIG1hZ25pdHVkZSBjaGFuZ2Ugc3Rhc2hlZCBvbiBhIHJlY29yZCBiZXR3ZWVuIGJlZm9yZVNhdmUgYW5kIGFmdGVyU2F2ZS5cbiAqIEB0eXBlZGVmIHt7bmV3TWFnbml0dWRlOiBudW1iZXIsIG9sZE1hZ25pdHVkZTogbnVtYmVyLCBuZXdQYXJlbnRJZDogUmV0dXJuVHlwZTx0eXBlb2YgSlNPTi5wYXJzZT4sIG9sZFBhcmVudElkOiBSZXR1cm5UeXBlPHR5cGVvZiBKU09OLnBhcnNlPn19IFBlbmRpbmdNYWduaXR1ZGVEZWx0YVxuICovXG5cbi8qKlxuICogUmVnaXN0ZXJzIGEgcmVhY3RpdmUgbWFnbml0dWRlIGNvdW50ZXItY2FjaGUgb24gYSBtb2RlbCBjbGFzcy5cbiAqIEBwYXJhbSB7dHlwZW9mIGltcG9ydChcIi4vaW5kZXguanNcIikuZGVmYXVsdH0gbW9kZWxDbGFzcyAtIE1vZGVsIGNsYXNzIHRvIGFkZCB0aGUgY291bnRlciBjYWNoZSB0by5cbiAqIEBwYXJhbSB7TWFnbml0dWRlQ291bnRlckNhY2hlRGVmaW5pdGlvbn0gZGVmaW5pdGlvbiAtIENvdW50ZXIgY2FjaGUgZGVmaW5pdGlvbi5cbiAqIEByZXR1cm5zIHt2b2lkfVxuICovXG5leHBvcnQgZnVuY3Rpb24gcmVnaXN0ZXJNYWduaXR1ZGVDb3VudGVyQ2FjaGUobW9kZWxDbGFzcywgZGVmaW5pdGlvbikge1xuICAvKipcbiAgICogRHluYW1pYyBjbGFzcy5cbiAgICogQHR5cGUge1JldHVyblR5cGU8dHlwZW9mIEpTT04ucGFyc2U+fSAqL1xuICBjb25zdCBkeW5hbWljQ2xhc3MgPSBtb2RlbENsYXNzXG4gIGNvbnN0IHJlZ2lzdGVyZWRGbGFnID0gYF9tYWduaXR1ZGVDb3VudGVyQ2FjaGVSZWdpc3RlcmVkXyR7ZGVmaW5pdGlvbi5jb3VudGVyQ29sdW1ufWBcblxuICAvLyBJZGVtcG90ZW50IHBlciBjb3VudGVyIGNvbHVtbjogcmUtZGVjbGFyaW5nIChvciBzdWJjbGFzc2luZykgbXVzdCBub3QgZG91YmxlLXJlZ2lzdGVyLlxuICBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKGR5bmFtaWNDbGFzcywgcmVnaXN0ZXJlZEZsYWcpICYmIGR5bmFtaWNDbGFzc1tyZWdpc3RlcmVkRmxhZ10pIHtcbiAgICByZXR1cm5cbiAgfVxuXG4gIGR5bmFtaWNDbGFzc1tyZWdpc3RlcmVkRmxhZ10gPSB0cnVlXG5cbiAgY29uc3QgcGVuZGluZ0tleSA9IGBfbWFnbml0dWRlQ291bnRlckNhY2hlUGVuZGluZ18ke2RlZmluaXRpb24uY291bnRlckNvbHVtbn1gXG5cbiAgbW9kZWxDbGFzcy5iZWZvcmVTYXZlKGFzeW5jIGZ1bmN0aW9uIChyZWNvcmQpIHtcbiAgICAvKipcbiAgICAgKiBEeW5hbWljIHJlY29yZC5cbiAgICAgKiBAdHlwZSB7UmV0dXJuVHlwZTx0eXBlb2YgSlNPTi5wYXJzZT59ICovXG4gICAgY29uc3QgZHluYW1pY1JlY29yZCA9IHJlY29yZFxuXG4gICAgZHluYW1pY1JlY29yZFtwZW5kaW5nS2V5XSA9IGNvbXB1dGVQZW5kaW5nTWFnbml0dWRlRGVsdGEocmVjb3JkLCBkZWZpbml0aW9uKVxuICB9KVxuXG4gIG1vZGVsQ2xhc3MuYWZ0ZXJTYXZlKGFzeW5jIGZ1bmN0aW9uIChyZWNvcmQpIHtcbiAgICAvKipcbiAgICAgKiBEeW5hbWljIHJlY29yZC5cbiAgICAgKiBAdHlwZSB7UmV0dXJuVHlwZTx0eXBlb2YgSlNPTi5wYXJzZT59ICovXG4gICAgY29uc3QgZHluYW1pY1JlY29yZCA9IHJlY29yZFxuICAgIGNvbnN0IHBlbmRpbmcgPSAvKiogQHR5cGUge1BlbmRpbmdNYWduaXR1ZGVEZWx0YSB8IG51bGx9ICovIChkeW5hbWljUmVjb3JkW3BlbmRpbmdLZXldKVxuXG4gICAgZHluYW1pY1JlY29yZFtwZW5kaW5nS2V5XSA9IG51bGxcblxuICAgIGlmIChwZW5kaW5nKSB7XG4gICAgICBhd2FpdCBhcHBseVBlbmRpbmdNYWduaXR1ZGVEZWx0YShyZWNvcmQsIGRlZmluaXRpb24sIHBlbmRpbmcpXG4gICAgfVxuICB9KVxuXG4gIG1vZGVsQ2xhc3MuYWZ0ZXJEZXN0cm95KGFzeW5jIGZ1bmN0aW9uIChyZWNvcmQpIHtcbiAgICBjb25zdCBtYWduaXR1ZGUgPSBkZWZpbml0aW9uLm1hZ25pdHVkZShyZWNvcmQucmVhZEF0dHJpYnV0ZShkZWZpbml0aW9uLnNvdXJjZUF0dHJpYnV0ZSkpXG4gICAgY29uc3QgcGFyZW50SWQgPSBjdXJyZW50UGFyZW50SWQocmVjb3JkLCBkZWZpbml0aW9uKVxuXG4gICAgaWYgKHBhcmVudElkICYmIG1hZ25pdHVkZSAhPT0gMCkge1xuICAgICAgYXdhaXQgaW5jcmVtZW50UGFyZW50Q291bnRlcihyZWNvcmQsIGRlZmluaXRpb24sIHBhcmVudElkLCAtbWFnbml0dWRlKVxuICAgIH1cbiAgfSlcbn1cblxuLyoqXG4gKiBEaWZmcyB0aGUgc291cmNlIGF0dHJpYnV0ZSAoYW5kIGZvcmVpZ24ga2V5KSBhY3Jvc3MgdGhlIHBlbmRpbmcgc2F2ZSB0byBjYXB0dXJlXG4gKiBob3cgdGhlIHBhcmVudCBjb3VudGVyIHNob3VsZCBtb3ZlLiBSdW5zIGluIGJlZm9yZVNhdmUsIHdoZXJlIGBjaGFuZ2VzKClgIHN0aWxsXG4gKiBob2xkcyB0aGUgcHJlLXNhdmUgdmFsdWVzLlxuICogQHBhcmFtIHtpbXBvcnQoXCIuL2luZGV4LmpzXCIpLmRlZmF1bHR9IHJlY29yZCAtIFJlY29yZCBiZWluZyBzYXZlZC5cbiAqIEBwYXJhbSB7TWFnbml0dWRlQ291bnRlckNhY2hlRGVmaW5pdGlvbn0gZGVmaW5pdGlvbiAtIENvdW50ZXIgY2FjaGUgZGVmaW5pdGlvbi5cbiAqIEByZXR1cm5zIHtQZW5kaW5nTWFnbml0dWRlRGVsdGF9IFRoZSBjYXB0dXJlZCBtYWduaXR1ZGUgY2hhbmdlLlxuICovXG5mdW5jdGlvbiBjb21wdXRlUGVuZGluZ01hZ25pdHVkZURlbHRhKHJlY29yZCwgZGVmaW5pdGlvbikge1xuICBjb25zdCBjaGFuZ2VzID0gcmVjb3JkLmNoYW5nZXMoKVxuICBjb25zdCBzb3VyY2VDb2x1bW4gPSBjb2x1bW5OYW1lRm9yQXR0cmlidXRlKHJlY29yZCwgZGVmaW5pdGlvbi5zb3VyY2VBdHRyaWJ1dGUpXG4gIGNvbnN0IGZvcmVpZ25LZXlDb2x1bW4gPSByZWNvcmQuZ2V0TW9kZWxDbGFzcygpLmdldFJlbGF0aW9uc2hpcEJ5TmFtZShkZWZpbml0aW9uLmJlbG9uZ3NUbykuZ2V0Rm9yZWlnbktleSgpXG5cbiAgY29uc3QgbmV3U291cmNlID0gcmVjb3JkLnJlYWRBdHRyaWJ1dGUoZGVmaW5pdGlvbi5zb3VyY2VBdHRyaWJ1dGUpXG4gIGNvbnN0IG9sZFNvdXJjZSA9IG9sZFNvdXJjZVZhbHVlVGhyb3VnaFJlYWRDYXN0KHJlY29yZCwgZGVmaW5pdGlvbi5zb3VyY2VBdHRyaWJ1dGUsIHNvdXJjZUNvbHVtbiwgY2hhbmdlcywgbmV3U291cmNlKVxuXG4gIGNvbnN0IG5ld1BhcmVudElkID0gY3VycmVudFBhcmVudElkKHJlY29yZCwgZGVmaW5pdGlvbilcbiAgY29uc3Qgb2xkUGFyZW50SWQgPSBmb3JlaWduS2V5Q29sdW1uIGluIGNoYW5nZXMgPyBjaGFuZ2VzW2ZvcmVpZ25LZXlDb2x1bW5dWzBdIDogbmV3UGFyZW50SWRcblxuICByZXR1cm4ge1xuICAgIG5ld01hZ25pdHVkZTogZGVmaW5pdGlvbi5tYWduaXR1ZGUobmV3U291cmNlKSxcbiAgICBvbGRNYWduaXR1ZGU6IGRlZmluaXRpb24ubWFnbml0dWRlKG9sZFNvdXJjZSksXG4gICAgbmV3UGFyZW50SWQsXG4gICAgb2xkUGFyZW50SWRcbiAgfVxufVxuXG4vKipcbiAqIEFwcGxpZXMgYSBjYXB0dXJlZCBtYWduaXR1ZGUgY2hhbmdlIHRvIHRoZSBwYXJlbnQgY291bnRlcihzKSBhcyBhdG9taWMgaW5jcmVtZW50cy5cbiAqIEBwYXJhbSB7aW1wb3J0KFwiLi9pbmRleC5qc1wiKS5kZWZhdWx0fSByZWNvcmQgLSBSZWNvcmQgdGhhdCB3YXMgc2F2ZWQuXG4gKiBAcGFyYW0ge01hZ25pdHVkZUNvdW50ZXJDYWNoZURlZmluaXRpb259IGRlZmluaXRpb24gLSBDb3VudGVyIGNhY2hlIGRlZmluaXRpb24uXG4gKiBAcGFyYW0ge1BlbmRpbmdNYWduaXR1ZGVEZWx0YX0gcGVuZGluZyAtIFRoZSBjYXB0dXJlZCBtYWduaXR1ZGUgY2hhbmdlLlxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59XG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGFwcGx5UGVuZGluZ01hZ25pdHVkZURlbHRhKHJlY29yZCwgZGVmaW5pdGlvbiwgcGVuZGluZykge1xuICBjb25zdCB7bmV3TWFnbml0dWRlLCBvbGRNYWduaXR1ZGUsIG5ld1BhcmVudElkLCBvbGRQYXJlbnRJZH0gPSBwZW5kaW5nXG5cbiAgaWYgKG9sZFBhcmVudElkID09PSBuZXdQYXJlbnRJZCkge1xuICAgIGNvbnN0IGRlbHRhID0gbmV3TWFnbml0dWRlIC0gb2xkTWFnbml0dWRlXG5cbiAgICBpZiAobmV3UGFyZW50SWQgJiYgZGVsdGEgIT09IDApIHtcbiAgICAgIGF3YWl0IGluY3JlbWVudFBhcmVudENvdW50ZXIocmVjb3JkLCBkZWZpbml0aW9uLCBuZXdQYXJlbnRJZCwgZGVsdGEpXG4gICAgfVxuXG4gICAgcmV0dXJuXG4gIH1cblxuICAvLyBUaGUgZm9yZWlnbiBrZXkgbW92ZWQ6IHRha2UgdGhlIG9sZCBtYWduaXR1ZGUgb2ZmIHRoZSBvbGQgcGFyZW50IGFuZCBwdXQgdGhlXG4gIC8vIG5ldyBtYWduaXR1ZGUgb24gdGhlIG5ldyBwYXJlbnQuXG4gIGlmIChvbGRQYXJlbnRJZCAmJiBvbGRNYWduaXR1ZGUgIT09IDApIHtcbiAgICBhd2FpdCBpbmNyZW1lbnRQYXJlbnRDb3VudGVyKHJlY29yZCwgZGVmaW5pdGlvbiwgb2xkUGFyZW50SWQsIC1vbGRNYWduaXR1ZGUpXG4gIH1cblxuICBpZiAobmV3UGFyZW50SWQgJiYgbmV3TWFnbml0dWRlICE9PSAwKSB7XG4gICAgYXdhaXQgaW5jcmVtZW50UGFyZW50Q291bnRlcihyZWNvcmQsIGRlZmluaXRpb24sIG5ld1BhcmVudElkLCBuZXdNYWduaXR1ZGUpXG4gIH1cbn1cblxuLyoqXG4gKiBSZWFkcyB0aGUgcHJlLXNhdmUgdmFsdWUgb2YgdGhlIHNvdXJjZSBhdHRyaWJ1dGUgdGhyb3VnaCB0aGUgbm9ybWFsIHJlYWQvY2FzdFxuICogcGF0aCwgc28gYG1hZ25pdHVkZWAgcmVjZWl2ZXMgdGhlIG9sZCB2YWx1ZSBpbiB0aGUgc2FtZSBzaGFwZSBhcyB0aGUgbmV3IG9uZVxuICogKGUuZy4gYSBkZWNsYXJlZCBib29sZWFuIGFzIGB0cnVlYC9gZmFsc2VgLCBub3QgdGhlIHJhdyBzdG9yZWQgYDFgL2AwYCkuIERyb3BzXG4gKiB0aGUgcGVuZGluZyBjaGFuZ2Ugc28gYHJlYWRBdHRyaWJ1dGVgIGZhbGxzIGJhY2sgdG8gdGhlIGNvbW1pdHRlZCB2YWx1ZSBhbmRcbiAqIGFwcGxpZXMgdGhlIHNhbWUgY2FzdCBhIGZyZXNoIHJlYWQgd291bGQsIHRoZW4gcmVzdG9yZXMgdGhlIHBlbmRpbmcgY2hhbmdlLlxuICogQHBhcmFtIHtpbXBvcnQoXCIuL2luZGV4LmpzXCIpLmRlZmF1bHR9IHJlY29yZCAtIFJlY29yZCBiZWluZyBzYXZlZC5cbiAqIEBwYXJhbSB7c3RyaW5nfSBzb3VyY2VBdHRyaWJ1dGUgLSBTb3VyY2UgYXR0cmlidXRlIG5hbWUuXG4gKiBAcGFyYW0ge3N0cmluZ30gc291cmNlQ29sdW1uIC0gU291cmNlIGNvbHVtbiBuYW1lLlxuICogQHBhcmFtIHtSZWNvcmQ8c3RyaW5nLCBSZXR1cm5UeXBlPHR5cGVvZiBKU09OLnBhcnNlPj59IGNoYW5nZXMgLSBUaGUgcmVjb3JkJ3MgcHJlLXNhdmUgY2hhbmdlcyAoY29sdW1uLWtleWVkKS5cbiAqIEBwYXJhbSB7UmV0dXJuVHlwZTx0eXBlb2YgSlNPTi5wYXJzZT59IGN1cnJlbnRWYWx1ZSAtIFRoZSBjdXJyZW50IChuZXcpIHJlYWQgdmFsdWUsIHJldHVybmVkIHdoZW4gdGhlIHNvdXJjZSBkaWQgbm90IGNoYW5nZS5cbiAqIEByZXR1cm5zIHtSZXR1cm5UeXBlPHR5cGVvZiBKU09OLnBhcnNlPn0gVGhlIHJlYWQtY2FzdCBwcmUtc2F2ZSB2YWx1ZS5cbiAqL1xuZnVuY3Rpb24gb2xkU291cmNlVmFsdWVUaHJvdWdoUmVhZENhc3QocmVjb3JkLCBzb3VyY2VBdHRyaWJ1dGUsIHNvdXJjZUNvbHVtbiwgY2hhbmdlcywgY3VycmVudFZhbHVlKSB7XG4gIGlmICghKHNvdXJjZUNvbHVtbiBpbiBjaGFuZ2VzKSkge1xuICAgIHJldHVybiBjdXJyZW50VmFsdWVcbiAgfVxuXG4gIC8qKlxuICAgKiBEeW5hbWljIHJlY29yZC5cbiAgICogQHR5cGUge1JldHVyblR5cGU8dHlwZW9mIEpTT04ucGFyc2U+fSAqL1xuICBjb25zdCBkeW5hbWljUmVjb3JkID0gcmVjb3JkXG4gIGNvbnN0IHBlbmRpbmdDaGFuZ2UgPSBkeW5hbWljUmVjb3JkLl9jaGFuZ2VzW3NvdXJjZUNvbHVtbl1cblxuICBkZWxldGUgZHluYW1pY1JlY29yZC5fY2hhbmdlc1tzb3VyY2VDb2x1bW5dXG5cbiAgdHJ5IHtcbiAgICByZXR1cm4gcmVjb3JkLnJlYWRBdHRyaWJ1dGUoc291cmNlQXR0cmlidXRlKVxuICB9IGZpbmFsbHkge1xuICAgIGR5bmFtaWNSZWNvcmQuX2NoYW5nZXNbc291cmNlQ29sdW1uXSA9IHBlbmRpbmdDaGFuZ2VcbiAgfVxufVxuXG4vKipcbiAqIFJlYWRzIHRoZSByZWNvcmQncyBjdXJyZW50IGZvcmVpZ24ta2V5IHZhbHVlIGZvciB0aGUgY291bnRlci1jYWNoZSBwYXJlbnQuXG4gKiBAcGFyYW0ge2ltcG9ydChcIi4vaW5kZXguanNcIikuZGVmYXVsdH0gcmVjb3JkIC0gUmVjb3JkIHdob3NlIHBhcmVudCBpcyB0YXJnZXRlZC5cbiAqIEBwYXJhbSB7TWFnbml0dWRlQ291bnRlckNhY2hlRGVmaW5pdGlvbn0gZGVmaW5pdGlvbiAtIENvdW50ZXIgY2FjaGUgZGVmaW5pdGlvbi5cbiAqIEByZXR1cm5zIHtSZXR1cm5UeXBlPHR5cGVvZiBKU09OLnBhcnNlPn0gVGhlIGN1cnJlbnQgZm9yZWlnbi1rZXkgdmFsdWUuXG4gKi9cbmZ1bmN0aW9uIGN1cnJlbnRQYXJlbnRJZChyZWNvcmQsIGRlZmluaXRpb24pIHtcbiAgY29uc3QgZm9yZWlnbktleUNvbHVtbiA9IHJlY29yZC5nZXRNb2RlbENsYXNzKCkuZ2V0UmVsYXRpb25zaGlwQnlOYW1lKGRlZmluaXRpb24uYmVsb25nc1RvKS5nZXRGb3JlaWduS2V5KClcblxuICByZXR1cm4gcmVjb3JkLnJlYWRBdHRyaWJ1dGUoYXR0cmlidXRlTmFtZUZvckNvbHVtbihyZWNvcmQsIGZvcmVpZ25LZXlDb2x1bW4pKVxufVxuXG4vKipcbiAqIEF0b21pY2FsbHkgYWRkcyBgYW1vdW50YCB0byB0aGUgcGFyZW50J3MgY291bnRlciBjb2x1bW4gZm9yIG9uZSBwYXJlbnQgcm93LlxuICogQHBhcmFtIHtpbXBvcnQoXCIuL2luZGV4LmpzXCIpLmRlZmF1bHR9IHJlY29yZCAtIENoaWxkIHJlY29yZCAoZm9yIGNvbm5lY3Rpb24gKyByZWxhdGlvbnNoaXAgbWV0YWRhdGEpLlxuICogQHBhcmFtIHtNYWduaXR1ZGVDb3VudGVyQ2FjaGVEZWZpbml0aW9ufSBkZWZpbml0aW9uIC0gQ291bnRlciBjYWNoZSBkZWZpbml0aW9uLlxuICogQHBhcmFtIHtSZXR1cm5UeXBlPHR5cGVvZiBKU09OLnBhcnNlPn0gcGFyZW50SWQgLSBQYXJlbnQgcHJpbWFyeS1rZXkgdmFsdWUuXG4gKiBAcGFyYW0ge251bWJlcn0gYW1vdW50IC0gU2lnbmVkIGludGVnZXIgdG8gYWRkLlxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59XG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGluY3JlbWVudFBhcmVudENvdW50ZXIocmVjb3JkLCBkZWZpbml0aW9uLCBwYXJlbnRJZCwgYW1vdW50KSB7XG4gIGNvbnN0IG1vZGVsQ2xhc3MgPSByZWNvcmQuZ2V0TW9kZWxDbGFzcygpXG4gIGNvbnN0IHJlbGF0aW9uc2hpcCA9IG1vZGVsQ2xhc3MuZ2V0UmVsYXRpb25zaGlwQnlOYW1lKGRlZmluaXRpb24uYmVsb25nc1RvKVxuICBjb25zdCBwYXJlbnRNb2RlbENsYXNzID0gcmVsYXRpb25zaGlwLmdldFRhcmdldE1vZGVsQ2xhc3MoKVxuXG4gIGlmICghcGFyZW50TW9kZWxDbGFzcykge1xuICAgIHRocm93IG5ldyBFcnJvcihgbWFnbml0dWRlQ291bnRlckNhY2hlIG9uICR7bW9kZWxDbGFzcy5nZXRNb2RlbE5hbWUoKX0gY291bGQgbm90IHJlc29sdmUgdGhlIFwiJHtkZWZpbml0aW9uLmJlbG9uZ3NUb31cIiBwYXJlbnQgbW9kZWwgY2xhc3NgKVxuICB9XG5cbiAgLy8gUmVzb2x2ZSB0aHJvdWdoIHRoZSByZWNvcmQgc28gYW4gb3BlcmF0aW9uIGJvdGggb3ducyB0aGUgcGFyZW50IHdyaXRlIGFuZFxuICAvLyByZWplY3RzIGEgcmVsYXRpb25zaGlwIHdob3NlIHBhcmVudCByb3V0ZXMgdG8gYW5vdGhlciBkYXRhYmFzZS5cbiAgY29uc3QgZGIgPSByZWNvcmRcbiAgICAucXVlcnlGb3JNb2RlbChwYXJlbnRNb2RlbENsYXNzKVxuICAgIC5kcml2ZXJcbiAgY29uc3QgY291bnRlckNvbHVtblNxbCA9IGRiLnF1b3RlQ29sdW1uKGRlZmluaXRpb24uY291bnRlckNvbHVtbilcbiAgY29uc3QgdHJ1bmNhdGVkQW1vdW50ID0gTWF0aC50cnVuYyhhbW91bnQpXG5cbiAgYXdhaXQgZGIucXVlcnkoXG4gICAgYFVQREFURSAke2RiLnF1b3RlVGFibGUocGFyZW50TW9kZWxDbGFzcy50YWJsZU5hbWUoKSl9IGAgK1xuICAgIGBTRVQgJHtjb3VudGVyQ29sdW1uU3FsfSA9IENPQUxFU0NFKCR7Y291bnRlckNvbHVtblNxbH0sIDApICsgJHt0cnVuY2F0ZWRBbW91bnR9IGAgK1xuICAgIGBXSEVSRSAke2RiLnF1b3RlQ29sdW1uKHJlbGF0aW9uc2hpcC5nZXRQcmltYXJ5S2V5KCkpfSA9ICR7ZGIucXVvdGUocGFyZW50SWQpfWBcbiAgKVxufVxuXG4vKipcbiAqIFJlc29sdmVzIHRoZSBjb2x1bW4gbmFtZSBiYWNraW5nIGFuIGF0dHJpYnV0ZSBuYW1lLlxuICogQHBhcmFtIHtpbXBvcnQoXCIuL2luZGV4LmpzXCIpLmRlZmF1bHR9IHJlY29yZCAtIFJlY29yZC5cbiAqIEBwYXJhbSB7c3RyaW5nfSBhdHRyaWJ1dGVOYW1lIC0gQXR0cmlidXRlIG5hbWUuXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgY29sdW1uIG5hbWUgZm9yIHRoZSBhdHRyaWJ1dGUgKGZhbGxzIGJhY2sgdG8gdGhlIGF0dHJpYnV0ZSBuYW1lKS5cbiAqL1xuZnVuY3Rpb24gY29sdW1uTmFtZUZvckF0dHJpYnV0ZShyZWNvcmQsIGF0dHJpYnV0ZU5hbWUpIHtcbiAgcmV0dXJuIHJlY29yZC5nZXRNb2RlbENsYXNzKCkuZ2V0QXR0cmlidXRlTmFtZVRvQ29sdW1uTmFtZU1hcCgpW2F0dHJpYnV0ZU5hbWVdIHx8IGF0dHJpYnV0ZU5hbWVcbn1cblxuLyoqXG4gKiBSZXNvbHZlcyB0aGUgYXR0cmlidXRlIG5hbWUgYmFja2luZyBhIGNvbHVtbiBuYW1lLlxuICogQHBhcmFtIHtpbXBvcnQoXCIuL2luZGV4LmpzXCIpLmRlZmF1bHR9IHJlY29yZCAtIFJlY29yZC5cbiAqIEBwYXJhbSB7c3RyaW5nfSBjb2x1bW5OYW1lIC0gQ29sdW1uIG5hbWUuXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgYXR0cmlidXRlIG5hbWUgZm9yIHRoZSBjb2x1bW4gKGZhbGxzIGJhY2sgdG8gdGhlIGNvbHVtbiBuYW1lKS5cbiAqL1xuZnVuY3Rpb24gYXR0cmlidXRlTmFtZUZvckNvbHVtbihyZWNvcmQsIGNvbHVtbk5hbWUpIHtcbiAgY29uc3QgbWFwID0gcmVjb3JkLmdldE1vZGVsQ2xhc3MoKS5nZXRBdHRyaWJ1dGVOYW1lVG9Db2x1bW5OYW1lTWFwKClcblxuICByZXR1cm4gT2JqZWN0LmtleXMobWFwKS5maW5kKChhdHRyaWJ1dGVOYW1lKSA9PiBtYXBbYXR0cmlidXRlTmFtZV0gPT09IGNvbHVtbk5hbWUpIHx8IGNvbHVtbk5hbWVcbn1cbiJdfQ==
|
|
216
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY291bnRlci1jYWNoZS1tYWduaXR1ZGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvZGF0YWJhc2UvcmVjb3JkL2NvdW50ZXItY2FjaGUtbWFnbml0dWRlLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLFlBQVk7QUFFWixPQUFPLEVBQUMsK0JBQStCLEVBQUUsZ0NBQWdDLEVBQUMsTUFBTSxtQ0FBbUMsQ0FBQTtBQUVuSDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXNCRztBQUVIOzs7R0FHRztBQUVIOzs7OztHQUtHO0FBQ0gsTUFBTSxVQUFVLDZCQUE2QixDQUFDLFVBQVUsRUFBRSxVQUFVO0lBQ2xFOzsrQ0FFMkM7SUFDM0MsTUFBTSxZQUFZLEdBQUcsVUFBVSxDQUFBO0lBQy9CLE1BQU0sY0FBYyxHQUFHLG9DQUFvQyxVQUFVLENBQUMsYUFBYSxFQUFFLENBQUE7SUFFckYseUZBQXlGO0lBQ3pGLElBQUksTUFBTSxDQUFDLFNBQVMsQ0FBQyxjQUFjLENBQUMsSUFBSSxDQUFDLFlBQVksRUFBRSxjQUFjLENBQUMsSUFBSSxZQUFZLENBQUMsY0FBYyxDQUFDLEVBQUUsQ0FBQztRQUN2RyxPQUFNO0lBQ1IsQ0FBQztJQUVELFlBQVksQ0FBQyxjQUFjLENBQUMsR0FBRyxJQUFJLENBQUE7SUFFbkMsTUFBTSxVQUFVLEdBQUcsaUNBQWlDLFVBQVUsQ0FBQyxhQUFhLEVBQUUsQ0FBQTtJQUU5RSxVQUFVLENBQUMsVUFBVSxDQUFDLEtBQUssV0FBVyxNQUFNO1FBQzFDOzttREFFMkM7UUFDM0MsTUFBTSxhQUFhLEdBQUcsTUFBTSxDQUFBO1FBRTVCLGFBQWEsQ0FBQyxVQUFVLENBQUMsR0FBRyw0QkFBNEIsQ0FBQyxNQUFNLEVBQUUsVUFBVSxDQUFDLENBQUE7SUFDOUUsQ0FBQyxDQUFDLENBQUE7SUFFRixVQUFVLENBQUMsU0FBUyxDQUFDLEtBQUssV0FBVyxNQUFNO1FBQ3pDOzttREFFMkM7UUFDM0MsTUFBTSxhQUFhLEdBQUcsTUFBTSxDQUFBO1FBQzVCLE1BQU0sT0FBTyxHQUFHLDJDQUEyQyxDQUFDLENBQUMsYUFBYSxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUE7UUFFdkYsYUFBYSxDQUFDLFVBQVUsQ0FBQyxHQUFHLElBQUksQ0FBQTtRQUVoQyxJQUFJLE9BQU8sRUFBRSxDQUFDO1lBQ1osTUFBTSwwQkFBMEIsQ0FBQyxNQUFNLEVBQUUsVUFBVSxFQUFFLE9BQU8sQ0FBQyxDQUFBO1FBQy9ELENBQUM7SUFDSCxDQUFDLENBQUMsQ0FBQTtJQUVGLFVBQVUsQ0FBQyxZQUFZLENBQUMsS0FBSyxXQUFXLE1BQU07UUFDNUMsTUFBTSxTQUFTLEdBQUcsVUFBVSxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUMsYUFBYSxDQUFDLFVBQVUsQ0FBQyxlQUFlLENBQUMsQ0FBQyxDQUFBO1FBQ3hGLE1BQU0sUUFBUSxHQUFHLGVBQWUsQ0FBQyxNQUFNLEVBQUUsVUFBVSxDQUFDLENBQUE7UUFFcEQsSUFBSSxRQUFRLElBQUksU0FBUyxLQUFLLENBQUMsRUFBRSxDQUFDO1lBQ2hDLE1BQU0sc0JBQXNCLENBQUMsTUFBTSxFQUFFLFVBQVUsRUFBRSxRQUFRLEVBQUUsQ0FBQyxTQUFTLENBQUMsQ0FBQTtRQUN4RSxDQUFDO0lBQ0gsQ0FBQyxDQUFDLENBQUE7QUFDSixDQUFDO0FBRUQ7Ozs7Ozs7R0FPRztBQUNILFNBQVMsNEJBQTRCLENBQUMsTUFBTSxFQUFFLFVBQVU7SUFDdEQsTUFBTSxPQUFPLEdBQUcsTUFBTSxDQUFDLE9BQU8sRUFBRSxDQUFBO0lBQ2hDLE1BQU0sWUFBWSxHQUFHLHNCQUFzQixDQUFDLE1BQU0sRUFBRSxVQUFVLENBQUMsZUFBZSxDQUFDLENBQUE7SUFDL0UsTUFBTSxnQkFBZ0IsR0FBRyxNQUFNLENBQUMsYUFBYSxFQUFFLENBQUMscUJBQXFCLENBQUMsVUFBVSxDQUFDLFNBQVMsQ0FBQyxDQUFDLGFBQWEsRUFBRSxDQUFBO0lBRTNHLE1BQU0sU0FBUyxHQUFHLE1BQU0sQ0FBQyxhQUFhLENBQUMsVUFBVSxDQUFDLGVBQWUsQ0FBQyxDQUFBO0lBQ2xFLE1BQU0sU0FBUyxHQUFHLDZCQUE2QixDQUFDLE1BQU0sRUFBRSxVQUFVLENBQUMsZUFBZSxFQUFFLFlBQVksRUFBRSxPQUFPLEVBQUUsU0FBUyxDQUFDLENBQUE7SUFFckgsTUFBTSxXQUFXLEdBQUcsZUFBZSxDQUFDLE1BQU0sRUFBRSxVQUFVLENBQUMsQ0FBQTtJQUN2RCxNQUFNLFdBQVcsR0FBRyxnQkFBZ0IsSUFBSSxPQUFPLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxXQUFXLENBQUE7SUFFNUYsT0FBTztRQUNMLFlBQVksRUFBRSxVQUFVLENBQUMsU0FBUyxDQUFDLFNBQVMsQ0FBQztRQUM3QyxZQUFZLEVBQUUsVUFBVSxDQUFDLFNBQVMsQ0FBQyxTQUFTLENBQUM7UUFDN0MsV0FBVztRQUNYLFdBQVc7S0FDWixDQUFBO0FBQ0gsQ0FBQztBQUVEOzs7Ozs7R0FNRztBQUNILEtBQUssVUFBVSwwQkFBMEIsQ0FBQyxNQUFNLEVBQUUsVUFBVSxFQUFFLE9BQU87SUFDbkUsTUFBTSxFQUFDLFlBQVksRUFBRSxZQUFZLEVBQUUsV0FBVyxFQUFFLFdBQVcsRUFBQyxHQUFHLE9BQU8sQ0FBQTtJQUV0RSxJQUFJLFdBQVcsS0FBSyxXQUFXLEVBQUUsQ0FBQztRQUNoQyxNQUFNLEtBQUssR0FBRyxZQUFZLEdBQUcsWUFBWSxDQUFBO1FBRXpDLElBQUksV0FBVyxJQUFJLEtBQUssS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUMvQixNQUFNLHNCQUFzQixDQUFDLE1BQU0sRUFBRSxVQUFVLEVBQUUsV0FBVyxFQUFFLEtBQUssQ0FBQyxDQUFBO1FBQ3RFLENBQUM7UUFFRCxPQUFNO0lBQ1IsQ0FBQztJQUVELCtFQUErRTtJQUMvRSxtQ0FBbUM7SUFDbkMsSUFBSSxXQUFXLElBQUksWUFBWSxLQUFLLENBQUMsRUFBRSxDQUFDO1FBQ3RDLE1BQU0sc0JBQXNCLENBQUMsTUFBTSxFQUFFLFVBQVUsRUFBRSxXQUFXLEVBQUUsQ0FBQyxZQUFZLENBQUMsQ0FBQTtJQUM5RSxDQUFDO0lBRUQsSUFBSSxXQUFXLElBQUksWUFBWSxLQUFLLENBQUMsRUFBRSxDQUFDO1FBQ3RDLE1BQU0sc0JBQXNCLENBQUMsTUFBTSxFQUFFLFVBQVUsRUFBRSxXQUFXLEVBQUUsWUFBWSxDQUFDLENBQUE7SUFDN0UsQ0FBQztBQUNILENBQUM7QUFFRDs7Ozs7Ozs7Ozs7O0dBWUc7QUFDSCxTQUFTLDZCQUE2QixDQUFDLE1BQU0sRUFBRSxlQUFlLEVBQUUsWUFBWSxFQUFFLE9BQU8sRUFBRSxZQUFZO0lBQ2pHLElBQUksQ0FBQyxDQUFDLFlBQVksSUFBSSxPQUFPLENBQUMsRUFBRSxDQUFDO1FBQy9CLE9BQU8sWUFBWSxDQUFBO0lBQ3JCLENBQUM7SUFFRDs7K0NBRTJDO0lBQzNDLE1BQU0sYUFBYSxHQUFHLE1BQU0sQ0FBQTtJQUM1QixNQUFNLGFBQWEsR0FBRyxhQUFhLENBQUMsUUFBUSxDQUFDLFlBQVksQ0FBQyxDQUFBO0lBRTFELE9BQU8sYUFBYSxDQUFDLFFBQVEsQ0FBQyxZQUFZLENBQUMsQ0FBQTtJQUUzQyxJQUFJLENBQUM7UUFDSCxPQUFPLE1BQU0sQ0FBQyxhQUFhLENBQUMsZUFBZSxDQUFDLENBQUE7SUFDOUMsQ0FBQztZQUFTLENBQUM7UUFDVCxhQUFhLENBQUMsUUFBUSxDQUFDLFlBQVksQ0FBQyxHQUFHLGFBQWEsQ0FBQTtJQUN0RCxDQUFDO0FBQ0gsQ0FBQztBQUVEOzs7OztHQUtHO0FBQ0gsU0FBUyxlQUFlLENBQUMsTUFBTSxFQUFFLFVBQVU7SUFDekMsTUFBTSxnQkFBZ0IsR0FBRyxNQUFNLENBQUMsYUFBYSxFQUFFLENBQUMscUJBQXFCLENBQUMsVUFBVSxDQUFDLFNBQVMsQ0FBQyxDQUFDLGFBQWEsRUFBRSxDQUFBO0lBRTNHLE9BQU8sTUFBTSxDQUFDLGFBQWEsQ0FBQyxzQkFBc0IsQ0FBQyxNQUFNLEVBQUUsZ0JBQWdCLENBQUMsQ0FBQyxDQUFBO0FBQy9FLENBQUM7QUFFRDs7Ozs7OztHQU9HO0FBQ0gsS0FBSyxVQUFVLHNCQUFzQixDQUFDLE1BQU0sRUFBRSxVQUFVLEVBQUUsUUFBUSxFQUFFLE1BQU07SUFDeEUsTUFBTSxVQUFVLEdBQUcsTUFBTSxDQUFDLGFBQWEsRUFBRSxDQUFBO0lBQ3pDLE1BQU0sWUFBWSxHQUFHLFVBQVUsQ0FBQyxxQkFBcUIsQ0FBQyxVQUFVLENBQUMsU0FBUyxDQUFDLENBQUE7SUFDM0UsTUFBTSxnQkFBZ0IsR0FBRyxZQUFZLENBQUMsbUJBQW1CLEVBQUUsQ0FBQTtJQUUzRCxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQztRQUN0QixNQUFNLElBQUksS0FBSyxDQUFDLDRCQUE0QixVQUFVLENBQUMsWUFBWSxFQUFFLDJCQUEyQixVQUFVLENBQUMsU0FBUyxzQkFBc0IsQ0FBQyxDQUFBO0lBQzdJLENBQUM7SUFFRCw0RUFBNEU7SUFDNUUsa0VBQWtFO0lBQ2xFLE1BQU0sV0FBVyxHQUFHLE1BQU0sQ0FBQyxhQUFhLENBQUMsZ0JBQWdCLENBQUMsQ0FBQTtJQUMxRCxNQUFNLEVBQUUsR0FBRyxXQUFXLENBQUMsTUFBTSxDQUFBO0lBQzdCLE1BQU0sZ0JBQWdCLEdBQUcsRUFBRSxDQUFDLFdBQVcsQ0FBQyxVQUFVLENBQUMsYUFBYSxDQUFDLENBQUE7SUFDakUsTUFBTSxlQUFlLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsQ0FBQTtJQUMxQyxNQUFNLG9CQUFvQixHQUFHLE1BQU0sK0JBQStCLENBQUM7UUFDakUsUUFBUTtRQUNSLGdCQUFnQjtRQUNoQixnQkFBZ0IsRUFBRSxZQUFZLENBQUMsYUFBYSxFQUFFO1FBQzlDLFdBQVc7S0FDWixDQUFDLENBQUE7SUFFRixNQUFNLEVBQUUsQ0FBQyxLQUFLLENBQ1osVUFBVSxFQUFFLENBQUMsVUFBVSxDQUFDLGdCQUFnQixDQUFDLFNBQVMsRUFBRSxDQUFDLEdBQUc7UUFDeEQsT0FBTyxnQkFBZ0IsZUFBZSxnQkFBZ0IsVUFBVSxlQUFlLEdBQUc7UUFDbEYsU0FBUyxFQUFFLENBQUMsV0FBVyxDQUFDLFlBQVksQ0FBQyxhQUFhLEVBQUUsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxLQUFLLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FDaEYsQ0FBQTtJQUNELE1BQU0sZ0NBQWdDLENBQUM7UUFDckMsY0FBYyxFQUFFLG9CQUFvQjtRQUNwQyxZQUFZLEVBQUUsTUFBTTtLQUNyQixDQUFDLENBQUE7QUFDSixDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxTQUFTLHNCQUFzQixDQUFDLE1BQU0sRUFBRSxhQUFhO0lBQ25ELE9BQU8sTUFBTSxDQUFDLGFBQWEsRUFBRSxDQUFDLCtCQUErQixFQUFFLENBQUMsYUFBYSxDQUFDLElBQUksYUFBYSxDQUFBO0FBQ2pHLENBQUM7QUFFRDs7Ozs7R0FLRztBQUNILFNBQVMsc0JBQXNCLENBQUMsTUFBTSxFQUFFLFVBQVU7SUFDaEQsTUFBTSxHQUFHLEdBQUcsTUFBTSxDQUFDLGFBQWEsRUFBRSxDQUFDLCtCQUErQixFQUFFLENBQUE7SUFFcEUsT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLGFBQWEsRUFBRSxFQUFFLENBQUMsR0FBRyxDQUFDLGFBQWEsQ0FBQyxLQUFLLFVBQVUsQ0FBQyxJQUFJLFVBQVUsQ0FBQTtBQUNsRyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQHRzLWNoZWNrXG5cbmltcG9ydCB7cHJlcGFyZUNvdW50ZXJDYWNoZVBhcmVudFVwZGF0ZSwgc2NoZWR1bGVDb3VudGVyQ2FjaGVQYXJlbnRVcGRhdGV9IGZyb20gXCIuL2NvdW50ZXItY2FjaGUtcGFyZW50LXVwZGF0ZXMuanNcIlxuXG4vKipcbiAqIFJlYWN0aXZlIGNvdW50ZXItY2FjaGUgZHJpdmVuIGJ5IGEgcGVyLXJlY29yZCBtYWduaXR1ZGUuXG4gKlxuICogTWFpbnRhaW5zIGEgY291bnRlciBjb2x1bW4gb24gYSBgYmVsb25nc1RvYCBwYXJlbnQgYXMgdGhlIHJ1bm5pbmcgc3VtIG9mIGVhY2hcbiAqIGNoaWxkJ3MgbWFnbml0dWRlIOKAlCBhIHNtYWxsIG51bWJlciBkZXJpdmVkIGZyb20gb25lIHNvdXJjZSBhdHRyaWJ1dGUgKGUuZy5cbiAqIGAxYCB3aGlsZSBhIGJ1aWxkJ3MgYHN0YXR1c2AgaXMgYHJ1bm5pbmdgLCBlbHNlIGAwYCkuIE9uIGV2ZXJ5IGNyZWF0ZSwgdXBkYXRlXG4gKiBhbmQgZGVzdHJveSB0aGUgY2hhbmdlIGluIG1hZ25pdHVkZSBpcyBhcHBsaWVkIHRvIHRoZSBwYXJlbnQgYXMgYSBzaW5nbGVcbiAqIGF0b21pYyBpbmNyZW1lbnQgKGBTRVQgY29sID0gY29sICsgZGVsdGFgKSwgYW5kIHdoZW4gdGhlIGZvcmVpZ24ga2V5IGNoYW5nZXNcbiAqIHRoZSBtYWduaXR1ZGUgaXMgbW92ZWQgZnJvbSB0aGUgb2xkIHBhcmVudCB0byB0aGUgbmV3IG9uZS5cbiAqXG4gKiBCZWNhdXNlIHRoZSBjb3VudGVyIGlzIGRlcml2ZWQgZnJvbSB0aGUgc291cmNlIGF0dHJpYnV0ZSBhbmQgZGlmZmVkIG9uIGV2ZXJ5XG4gKiB3cml0ZSwgaXQgZm9sbG93cyB0aGF0IGF0dHJpYnV0ZSBhdXRvbWF0aWNhbGx5IG5vIG1hdHRlciB3aGljaCBjb2RlIHBhdGggd3JvdGVcbiAqIGl0IOKAlCB0aGVyZSBpcyBubyBwZXItdHJhbnNpdGlvbiBpbmNyZW1lbnQvZGVjcmVtZW50IHRvIGZvcmdldC4gVGhlIG9sZCB2YWx1ZSBpc1xuICogY2FwdHVyZWQgaW4gYGJlZm9yZVNhdmVgIChWZWxvY2lvdXMgY2xlYXJzIGBjaGFuZ2VzKClgIGR1cmluZyB0aGUgcG9zdC11cGRhdGVcbiAqIHJlbG9hZCkgYW5kIGNvbnN1bWVkIGluIGBhZnRlclNhdmVgLCBzbyB0aGUgaW5jcmVtZW50IGNvbW1pdHMgYXRvbWljYWxseSB3aXRoXG4gKiB0aGUgcm93IGl0IHJlZmxlY3RzLlxuICogQHR5cGVkZWYge3tcbiAqICAgYmVsb25nc1RvOiBzdHJpbmcsXG4gKiAgIGNvdW50ZXJDb2x1bW46IHN0cmluZyxcbiAqICAgc291cmNlQXR0cmlidXRlOiBzdHJpbmcsXG4gKiAgIG1hZ25pdHVkZTogKHNvdXJjZVZhbHVlOiBSZXR1cm5UeXBlPHR5cGVvZiBKU09OLnBhcnNlPikgPT4gbnVtYmVyXG4gKiB9fSBNYWduaXR1ZGVDb3VudGVyQ2FjaGVEZWZpbml0aW9uXG4gKi9cblxuLyoqXG4gKiBDYXB0dXJlZCBwZW5kaW5nIG1hZ25pdHVkZSBjaGFuZ2Ugc3Rhc2hlZCBvbiBhIHJlY29yZCBiZXR3ZWVuIGJlZm9yZVNhdmUgYW5kIGFmdGVyU2F2ZS5cbiAqIEB0eXBlZGVmIHt7bmV3TWFnbml0dWRlOiBudW1iZXIsIG9sZE1hZ25pdHVkZTogbnVtYmVyLCBuZXdQYXJlbnRJZDogUmV0dXJuVHlwZTx0eXBlb2YgSlNPTi5wYXJzZT4sIG9sZFBhcmVudElkOiBSZXR1cm5UeXBlPHR5cGVvZiBKU09OLnBhcnNlPn19IFBlbmRpbmdNYWduaXR1ZGVEZWx0YVxuICovXG5cbi8qKlxuICogUmVnaXN0ZXJzIGEgcmVhY3RpdmUgbWFnbml0dWRlIGNvdW50ZXItY2FjaGUgb24gYSBtb2RlbCBjbGFzcy5cbiAqIEBwYXJhbSB7dHlwZW9mIGltcG9ydChcIi4vaW5kZXguanNcIikuZGVmYXVsdH0gbW9kZWxDbGFzcyAtIE1vZGVsIGNsYXNzIHRvIGFkZCB0aGUgY291bnRlciBjYWNoZSB0by5cbiAqIEBwYXJhbSB7TWFnbml0dWRlQ291bnRlckNhY2hlRGVmaW5pdGlvbn0gZGVmaW5pdGlvbiAtIENvdW50ZXIgY2FjaGUgZGVmaW5pdGlvbi5cbiAqIEByZXR1cm5zIHt2b2lkfVxuICovXG5leHBvcnQgZnVuY3Rpb24gcmVnaXN0ZXJNYWduaXR1ZGVDb3VudGVyQ2FjaGUobW9kZWxDbGFzcywgZGVmaW5pdGlvbikge1xuICAvKipcbiAgICogRHluYW1pYyBjbGFzcy5cbiAgICogQHR5cGUge1JldHVyblR5cGU8dHlwZW9mIEpTT04ucGFyc2U+fSAqL1xuICBjb25zdCBkeW5hbWljQ2xhc3MgPSBtb2RlbENsYXNzXG4gIGNvbnN0IHJlZ2lzdGVyZWRGbGFnID0gYF9tYWduaXR1ZGVDb3VudGVyQ2FjaGVSZWdpc3RlcmVkXyR7ZGVmaW5pdGlvbi5jb3VudGVyQ29sdW1ufWBcblxuICAvLyBJZGVtcG90ZW50IHBlciBjb3VudGVyIGNvbHVtbjogcmUtZGVjbGFyaW5nIChvciBzdWJjbGFzc2luZykgbXVzdCBub3QgZG91YmxlLXJlZ2lzdGVyLlxuICBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKGR5bmFtaWNDbGFzcywgcmVnaXN0ZXJlZEZsYWcpICYmIGR5bmFtaWNDbGFzc1tyZWdpc3RlcmVkRmxhZ10pIHtcbiAgICByZXR1cm5cbiAgfVxuXG4gIGR5bmFtaWNDbGFzc1tyZWdpc3RlcmVkRmxhZ10gPSB0cnVlXG5cbiAgY29uc3QgcGVuZGluZ0tleSA9IGBfbWFnbml0dWRlQ291bnRlckNhY2hlUGVuZGluZ18ke2RlZmluaXRpb24uY291bnRlckNvbHVtbn1gXG5cbiAgbW9kZWxDbGFzcy5iZWZvcmVTYXZlKGFzeW5jIGZ1bmN0aW9uIChyZWNvcmQpIHtcbiAgICAvKipcbiAgICAgKiBEeW5hbWljIHJlY29yZC5cbiAgICAgKiBAdHlwZSB7UmV0dXJuVHlwZTx0eXBlb2YgSlNPTi5wYXJzZT59ICovXG4gICAgY29uc3QgZHluYW1pY1JlY29yZCA9IHJlY29yZFxuXG4gICAgZHluYW1pY1JlY29yZFtwZW5kaW5nS2V5XSA9IGNvbXB1dGVQZW5kaW5nTWFnbml0dWRlRGVsdGEocmVjb3JkLCBkZWZpbml0aW9uKVxuICB9KVxuXG4gIG1vZGVsQ2xhc3MuYWZ0ZXJTYXZlKGFzeW5jIGZ1bmN0aW9uIChyZWNvcmQpIHtcbiAgICAvKipcbiAgICAgKiBEeW5hbWljIHJlY29yZC5cbiAgICAgKiBAdHlwZSB7UmV0dXJuVHlwZTx0eXBlb2YgSlNPTi5wYXJzZT59ICovXG4gICAgY29uc3QgZHluYW1pY1JlY29yZCA9IHJlY29yZFxuICAgIGNvbnN0IHBlbmRpbmcgPSAvKiogQHR5cGUge1BlbmRpbmdNYWduaXR1ZGVEZWx0YSB8IG51bGx9ICovIChkeW5hbWljUmVjb3JkW3BlbmRpbmdLZXldKVxuXG4gICAgZHluYW1pY1JlY29yZFtwZW5kaW5nS2V5XSA9IG51bGxcblxuICAgIGlmIChwZW5kaW5nKSB7XG4gICAgICBhd2FpdCBhcHBseVBlbmRpbmdNYWduaXR1ZGVEZWx0YShyZWNvcmQsIGRlZmluaXRpb24sIHBlbmRpbmcpXG4gICAgfVxuICB9KVxuXG4gIG1vZGVsQ2xhc3MuYWZ0ZXJEZXN0cm95KGFzeW5jIGZ1bmN0aW9uIChyZWNvcmQpIHtcbiAgICBjb25zdCBtYWduaXR1ZGUgPSBkZWZpbml0aW9uLm1hZ25pdHVkZShyZWNvcmQucmVhZEF0dHJpYnV0ZShkZWZpbml0aW9uLnNvdXJjZUF0dHJpYnV0ZSkpXG4gICAgY29uc3QgcGFyZW50SWQgPSBjdXJyZW50UGFyZW50SWQocmVjb3JkLCBkZWZpbml0aW9uKVxuXG4gICAgaWYgKHBhcmVudElkICYmIG1hZ25pdHVkZSAhPT0gMCkge1xuICAgICAgYXdhaXQgaW5jcmVtZW50UGFyZW50Q291bnRlcihyZWNvcmQsIGRlZmluaXRpb24sIHBhcmVudElkLCAtbWFnbml0dWRlKVxuICAgIH1cbiAgfSlcbn1cblxuLyoqXG4gKiBEaWZmcyB0aGUgc291cmNlIGF0dHJpYnV0ZSAoYW5kIGZvcmVpZ24ga2V5KSBhY3Jvc3MgdGhlIHBlbmRpbmcgc2F2ZSB0byBjYXB0dXJlXG4gKiBob3cgdGhlIHBhcmVudCBjb3VudGVyIHNob3VsZCBtb3ZlLiBSdW5zIGluIGJlZm9yZVNhdmUsIHdoZXJlIGBjaGFuZ2VzKClgIHN0aWxsXG4gKiBob2xkcyB0aGUgcHJlLXNhdmUgdmFsdWVzLlxuICogQHBhcmFtIHtpbXBvcnQoXCIuL2luZGV4LmpzXCIpLmRlZmF1bHR9IHJlY29yZCAtIFJlY29yZCBiZWluZyBzYXZlZC5cbiAqIEBwYXJhbSB7TWFnbml0dWRlQ291bnRlckNhY2hlRGVmaW5pdGlvbn0gZGVmaW5pdGlvbiAtIENvdW50ZXIgY2FjaGUgZGVmaW5pdGlvbi5cbiAqIEByZXR1cm5zIHtQZW5kaW5nTWFnbml0dWRlRGVsdGF9IFRoZSBjYXB0dXJlZCBtYWduaXR1ZGUgY2hhbmdlLlxuICovXG5mdW5jdGlvbiBjb21wdXRlUGVuZGluZ01hZ25pdHVkZURlbHRhKHJlY29yZCwgZGVmaW5pdGlvbikge1xuICBjb25zdCBjaGFuZ2VzID0gcmVjb3JkLmNoYW5nZXMoKVxuICBjb25zdCBzb3VyY2VDb2x1bW4gPSBjb2x1bW5OYW1lRm9yQXR0cmlidXRlKHJlY29yZCwgZGVmaW5pdGlvbi5zb3VyY2VBdHRyaWJ1dGUpXG4gIGNvbnN0IGZvcmVpZ25LZXlDb2x1bW4gPSByZWNvcmQuZ2V0TW9kZWxDbGFzcygpLmdldFJlbGF0aW9uc2hpcEJ5TmFtZShkZWZpbml0aW9uLmJlbG9uZ3NUbykuZ2V0Rm9yZWlnbktleSgpXG5cbiAgY29uc3QgbmV3U291cmNlID0gcmVjb3JkLnJlYWRBdHRyaWJ1dGUoZGVmaW5pdGlvbi5zb3VyY2VBdHRyaWJ1dGUpXG4gIGNvbnN0IG9sZFNvdXJjZSA9IG9sZFNvdXJjZVZhbHVlVGhyb3VnaFJlYWRDYXN0KHJlY29yZCwgZGVmaW5pdGlvbi5zb3VyY2VBdHRyaWJ1dGUsIHNvdXJjZUNvbHVtbiwgY2hhbmdlcywgbmV3U291cmNlKVxuXG4gIGNvbnN0IG5ld1BhcmVudElkID0gY3VycmVudFBhcmVudElkKHJlY29yZCwgZGVmaW5pdGlvbilcbiAgY29uc3Qgb2xkUGFyZW50SWQgPSBmb3JlaWduS2V5Q29sdW1uIGluIGNoYW5nZXMgPyBjaGFuZ2VzW2ZvcmVpZ25LZXlDb2x1bW5dWzBdIDogbmV3UGFyZW50SWRcblxuICByZXR1cm4ge1xuICAgIG5ld01hZ25pdHVkZTogZGVmaW5pdGlvbi5tYWduaXR1ZGUobmV3U291cmNlKSxcbiAgICBvbGRNYWduaXR1ZGU6IGRlZmluaXRpb24ubWFnbml0dWRlKG9sZFNvdXJjZSksXG4gICAgbmV3UGFyZW50SWQsXG4gICAgb2xkUGFyZW50SWRcbiAgfVxufVxuXG4vKipcbiAqIEFwcGxpZXMgYSBjYXB0dXJlZCBtYWduaXR1ZGUgY2hhbmdlIHRvIHRoZSBwYXJlbnQgY291bnRlcihzKSBhcyBhdG9taWMgaW5jcmVtZW50cy5cbiAqIEBwYXJhbSB7aW1wb3J0KFwiLi9pbmRleC5qc1wiKS5kZWZhdWx0fSByZWNvcmQgLSBSZWNvcmQgdGhhdCB3YXMgc2F2ZWQuXG4gKiBAcGFyYW0ge01hZ25pdHVkZUNvdW50ZXJDYWNoZURlZmluaXRpb259IGRlZmluaXRpb24gLSBDb3VudGVyIGNhY2hlIGRlZmluaXRpb24uXG4gKiBAcGFyYW0ge1BlbmRpbmdNYWduaXR1ZGVEZWx0YX0gcGVuZGluZyAtIFRoZSBjYXB0dXJlZCBtYWduaXR1ZGUgY2hhbmdlLlxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59XG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGFwcGx5UGVuZGluZ01hZ25pdHVkZURlbHRhKHJlY29yZCwgZGVmaW5pdGlvbiwgcGVuZGluZykge1xuICBjb25zdCB7bmV3TWFnbml0dWRlLCBvbGRNYWduaXR1ZGUsIG5ld1BhcmVudElkLCBvbGRQYXJlbnRJZH0gPSBwZW5kaW5nXG5cbiAgaWYgKG9sZFBhcmVudElkID09PSBuZXdQYXJlbnRJZCkge1xuICAgIGNvbnN0IGRlbHRhID0gbmV3TWFnbml0dWRlIC0gb2xkTWFnbml0dWRlXG5cbiAgICBpZiAobmV3UGFyZW50SWQgJiYgZGVsdGEgIT09IDApIHtcbiAgICAgIGF3YWl0IGluY3JlbWVudFBhcmVudENvdW50ZXIocmVjb3JkLCBkZWZpbml0aW9uLCBuZXdQYXJlbnRJZCwgZGVsdGEpXG4gICAgfVxuXG4gICAgcmV0dXJuXG4gIH1cblxuICAvLyBUaGUgZm9yZWlnbiBrZXkgbW92ZWQ6IHRha2UgdGhlIG9sZCBtYWduaXR1ZGUgb2ZmIHRoZSBvbGQgcGFyZW50IGFuZCBwdXQgdGhlXG4gIC8vIG5ldyBtYWduaXR1ZGUgb24gdGhlIG5ldyBwYXJlbnQuXG4gIGlmIChvbGRQYXJlbnRJZCAmJiBvbGRNYWduaXR1ZGUgIT09IDApIHtcbiAgICBhd2FpdCBpbmNyZW1lbnRQYXJlbnRDb3VudGVyKHJlY29yZCwgZGVmaW5pdGlvbiwgb2xkUGFyZW50SWQsIC1vbGRNYWduaXR1ZGUpXG4gIH1cblxuICBpZiAobmV3UGFyZW50SWQgJiYgbmV3TWFnbml0dWRlICE9PSAwKSB7XG4gICAgYXdhaXQgaW5jcmVtZW50UGFyZW50Q291bnRlcihyZWNvcmQsIGRlZmluaXRpb24sIG5ld1BhcmVudElkLCBuZXdNYWduaXR1ZGUpXG4gIH1cbn1cblxuLyoqXG4gKiBSZWFkcyB0aGUgcHJlLXNhdmUgdmFsdWUgb2YgdGhlIHNvdXJjZSBhdHRyaWJ1dGUgdGhyb3VnaCB0aGUgbm9ybWFsIHJlYWQvY2FzdFxuICogcGF0aCwgc28gYG1hZ25pdHVkZWAgcmVjZWl2ZXMgdGhlIG9sZCB2YWx1ZSBpbiB0aGUgc2FtZSBzaGFwZSBhcyB0aGUgbmV3IG9uZVxuICogKGUuZy4gYSBkZWNsYXJlZCBib29sZWFuIGFzIGB0cnVlYC9gZmFsc2VgLCBub3QgdGhlIHJhdyBzdG9yZWQgYDFgL2AwYCkuIERyb3BzXG4gKiB0aGUgcGVuZGluZyBjaGFuZ2Ugc28gYHJlYWRBdHRyaWJ1dGVgIGZhbGxzIGJhY2sgdG8gdGhlIGNvbW1pdHRlZCB2YWx1ZSBhbmRcbiAqIGFwcGxpZXMgdGhlIHNhbWUgY2FzdCBhIGZyZXNoIHJlYWQgd291bGQsIHRoZW4gcmVzdG9yZXMgdGhlIHBlbmRpbmcgY2hhbmdlLlxuICogQHBhcmFtIHtpbXBvcnQoXCIuL2luZGV4LmpzXCIpLmRlZmF1bHR9IHJlY29yZCAtIFJlY29yZCBiZWluZyBzYXZlZC5cbiAqIEBwYXJhbSB7c3RyaW5nfSBzb3VyY2VBdHRyaWJ1dGUgLSBTb3VyY2UgYXR0cmlidXRlIG5hbWUuXG4gKiBAcGFyYW0ge3N0cmluZ30gc291cmNlQ29sdW1uIC0gU291cmNlIGNvbHVtbiBuYW1lLlxuICogQHBhcmFtIHtSZWNvcmQ8c3RyaW5nLCBSZXR1cm5UeXBlPHR5cGVvZiBKU09OLnBhcnNlPj59IGNoYW5nZXMgLSBUaGUgcmVjb3JkJ3MgcHJlLXNhdmUgY2hhbmdlcyAoY29sdW1uLWtleWVkKS5cbiAqIEBwYXJhbSB7UmV0dXJuVHlwZTx0eXBlb2YgSlNPTi5wYXJzZT59IGN1cnJlbnRWYWx1ZSAtIFRoZSBjdXJyZW50IChuZXcpIHJlYWQgdmFsdWUsIHJldHVybmVkIHdoZW4gdGhlIHNvdXJjZSBkaWQgbm90IGNoYW5nZS5cbiAqIEByZXR1cm5zIHtSZXR1cm5UeXBlPHR5cGVvZiBKU09OLnBhcnNlPn0gVGhlIHJlYWQtY2FzdCBwcmUtc2F2ZSB2YWx1ZS5cbiAqL1xuZnVuY3Rpb24gb2xkU291cmNlVmFsdWVUaHJvdWdoUmVhZENhc3QocmVjb3JkLCBzb3VyY2VBdHRyaWJ1dGUsIHNvdXJjZUNvbHVtbiwgY2hhbmdlcywgY3VycmVudFZhbHVlKSB7XG4gIGlmICghKHNvdXJjZUNvbHVtbiBpbiBjaGFuZ2VzKSkge1xuICAgIHJldHVybiBjdXJyZW50VmFsdWVcbiAgfVxuXG4gIC8qKlxuICAgKiBEeW5hbWljIHJlY29yZC5cbiAgICogQHR5cGUge1JldHVyblR5cGU8dHlwZW9mIEpTT04ucGFyc2U+fSAqL1xuICBjb25zdCBkeW5hbWljUmVjb3JkID0gcmVjb3JkXG4gIGNvbnN0IHBlbmRpbmdDaGFuZ2UgPSBkeW5hbWljUmVjb3JkLl9jaGFuZ2VzW3NvdXJjZUNvbHVtbl1cblxuICBkZWxldGUgZHluYW1pY1JlY29yZC5fY2hhbmdlc1tzb3VyY2VDb2x1bW5dXG5cbiAgdHJ5IHtcbiAgICByZXR1cm4gcmVjb3JkLnJlYWRBdHRyaWJ1dGUoc291cmNlQXR0cmlidXRlKVxuICB9IGZpbmFsbHkge1xuICAgIGR5bmFtaWNSZWNvcmQuX2NoYW5nZXNbc291cmNlQ29sdW1uXSA9IHBlbmRpbmdDaGFuZ2VcbiAgfVxufVxuXG4vKipcbiAqIFJlYWRzIHRoZSByZWNvcmQncyBjdXJyZW50IGZvcmVpZ24ta2V5IHZhbHVlIGZvciB0aGUgY291bnRlci1jYWNoZSBwYXJlbnQuXG4gKiBAcGFyYW0ge2ltcG9ydChcIi4vaW5kZXguanNcIikuZGVmYXVsdH0gcmVjb3JkIC0gUmVjb3JkIHdob3NlIHBhcmVudCBpcyB0YXJnZXRlZC5cbiAqIEBwYXJhbSB7TWFnbml0dWRlQ291bnRlckNhY2hlRGVmaW5pdGlvbn0gZGVmaW5pdGlvbiAtIENvdW50ZXIgY2FjaGUgZGVmaW5pdGlvbi5cbiAqIEByZXR1cm5zIHtSZXR1cm5UeXBlPHR5cGVvZiBKU09OLnBhcnNlPn0gVGhlIGN1cnJlbnQgZm9yZWlnbi1rZXkgdmFsdWUuXG4gKi9cbmZ1bmN0aW9uIGN1cnJlbnRQYXJlbnRJZChyZWNvcmQsIGRlZmluaXRpb24pIHtcbiAgY29uc3QgZm9yZWlnbktleUNvbHVtbiA9IHJlY29yZC5nZXRNb2RlbENsYXNzKCkuZ2V0UmVsYXRpb25zaGlwQnlOYW1lKGRlZmluaXRpb24uYmVsb25nc1RvKS5nZXRGb3JlaWduS2V5KClcblxuICByZXR1cm4gcmVjb3JkLnJlYWRBdHRyaWJ1dGUoYXR0cmlidXRlTmFtZUZvckNvbHVtbihyZWNvcmQsIGZvcmVpZ25LZXlDb2x1bW4pKVxufVxuXG4vKipcbiAqIEF0b21pY2FsbHkgYWRkcyBgYW1vdW50YCB0byB0aGUgcGFyZW50J3MgY291bnRlciBjb2x1bW4gZm9yIG9uZSBwYXJlbnQgcm93LlxuICogQHBhcmFtIHtpbXBvcnQoXCIuL2luZGV4LmpzXCIpLmRlZmF1bHR9IHJlY29yZCAtIENoaWxkIHJlY29yZCAoZm9yIGNvbm5lY3Rpb24gKyByZWxhdGlvbnNoaXAgbWV0YWRhdGEpLlxuICogQHBhcmFtIHtNYWduaXR1ZGVDb3VudGVyQ2FjaGVEZWZpbml0aW9ufSBkZWZpbml0aW9uIC0gQ291bnRlciBjYWNoZSBkZWZpbml0aW9uLlxuICogQHBhcmFtIHtSZXR1cm5UeXBlPHR5cGVvZiBKU09OLnBhcnNlPn0gcGFyZW50SWQgLSBQYXJlbnQgcHJpbWFyeS1rZXkgdmFsdWUuXG4gKiBAcGFyYW0ge251bWJlcn0gYW1vdW50IC0gU2lnbmVkIGludGVnZXIgdG8gYWRkLlxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59XG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGluY3JlbWVudFBhcmVudENvdW50ZXIocmVjb3JkLCBkZWZpbml0aW9uLCBwYXJlbnRJZCwgYW1vdW50KSB7XG4gIGNvbnN0IG1vZGVsQ2xhc3MgPSByZWNvcmQuZ2V0TW9kZWxDbGFzcygpXG4gIGNvbnN0IHJlbGF0aW9uc2hpcCA9IG1vZGVsQ2xhc3MuZ2V0UmVsYXRpb25zaGlwQnlOYW1lKGRlZmluaXRpb24uYmVsb25nc1RvKVxuICBjb25zdCBwYXJlbnRNb2RlbENsYXNzID0gcmVsYXRpb25zaGlwLmdldFRhcmdldE1vZGVsQ2xhc3MoKVxuXG4gIGlmICghcGFyZW50TW9kZWxDbGFzcykge1xuICAgIHRocm93IG5ldyBFcnJvcihgbWFnbml0dWRlQ291bnRlckNhY2hlIG9uICR7bW9kZWxDbGFzcy5nZXRNb2RlbE5hbWUoKX0gY291bGQgbm90IHJlc29sdmUgdGhlIFwiJHtkZWZpbml0aW9uLmJlbG9uZ3NUb31cIiBwYXJlbnQgbW9kZWwgY2xhc3NgKVxuICB9XG5cbiAgLy8gUmVzb2x2ZSB0aHJvdWdoIHRoZSByZWNvcmQgc28gYW4gb3BlcmF0aW9uIGJvdGggb3ducyB0aGUgcGFyZW50IHdyaXRlIGFuZFxuICAvLyByZWplY3RzIGEgcmVsYXRpb25zaGlwIHdob3NlIHBhcmVudCByb3V0ZXMgdG8gYW5vdGhlciBkYXRhYmFzZS5cbiAgY29uc3QgcGFyZW50UXVlcnkgPSByZWNvcmQucXVlcnlGb3JNb2RlbChwYXJlbnRNb2RlbENsYXNzKVxuICBjb25zdCBkYiA9IHBhcmVudFF1ZXJ5LmRyaXZlclxuICBjb25zdCBjb3VudGVyQ29sdW1uU3FsID0gZGIucXVvdGVDb2x1bW4oZGVmaW5pdGlvbi5jb3VudGVyQ29sdW1uKVxuICBjb25zdCB0cnVuY2F0ZWRBbW91bnQgPSBNYXRoLnRydW5jKGFtb3VudClcbiAgY29uc3QgcHJlcGFyZWRQYXJlbnRVcGRhdGUgPSBhd2FpdCBwcmVwYXJlQ291bnRlckNhY2hlUGFyZW50VXBkYXRlKHtcbiAgICBwYXJlbnRJZCxcbiAgICBwYXJlbnRNb2RlbENsYXNzLFxuICAgIHBhcmVudFByaW1hcnlLZXk6IHJlbGF0aW9uc2hpcC5nZXRQcmltYXJ5S2V5KCksXG4gICAgcGFyZW50UXVlcnlcbiAgfSlcblxuICBhd2FpdCBkYi5xdWVyeShcbiAgICBgVVBEQVRFICR7ZGIucXVvdGVUYWJsZShwYXJlbnRNb2RlbENsYXNzLnRhYmxlTmFtZSgpKX0gYCArXG4gICAgYFNFVCAke2NvdW50ZXJDb2x1bW5TcWx9ID0gQ09BTEVTQ0UoJHtjb3VudGVyQ29sdW1uU3FsfSwgMCkgKyAke3RydW5jYXRlZEFtb3VudH0gYCArXG4gICAgYFdIRVJFICR7ZGIucXVvdGVDb2x1bW4ocmVsYXRpb25zaGlwLmdldFByaW1hcnlLZXkoKSl9ID0gJHtkYi5xdW90ZShwYXJlbnRJZCl9YFxuICApXG4gIGF3YWl0IHNjaGVkdWxlQ291bnRlckNhY2hlUGFyZW50VXBkYXRlKHtcbiAgICBwcmVwYXJlZFVwZGF0ZTogcHJlcGFyZWRQYXJlbnRVcGRhdGUsXG4gICAgc291cmNlUmVjb3JkOiByZWNvcmRcbiAgfSlcbn1cblxuLyoqXG4gKiBSZXNvbHZlcyB0aGUgY29sdW1uIG5hbWUgYmFja2luZyBhbiBhdHRyaWJ1dGUgbmFtZS5cbiAqIEBwYXJhbSB7aW1wb3J0KFwiLi9pbmRleC5qc1wiKS5kZWZhdWx0fSByZWNvcmQgLSBSZWNvcmQuXG4gKiBAcGFyYW0ge3N0cmluZ30gYXR0cmlidXRlTmFtZSAtIEF0dHJpYnV0ZSBuYW1lLlxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGNvbHVtbiBuYW1lIGZvciB0aGUgYXR0cmlidXRlIChmYWxscyBiYWNrIHRvIHRoZSBhdHRyaWJ1dGUgbmFtZSkuXG4gKi9cbmZ1bmN0aW9uIGNvbHVtbk5hbWVGb3JBdHRyaWJ1dGUocmVjb3JkLCBhdHRyaWJ1dGVOYW1lKSB7XG4gIHJldHVybiByZWNvcmQuZ2V0TW9kZWxDbGFzcygpLmdldEF0dHJpYnV0ZU5hbWVUb0NvbHVtbk5hbWVNYXAoKVthdHRyaWJ1dGVOYW1lXSB8fCBhdHRyaWJ1dGVOYW1lXG59XG5cbi8qKlxuICogUmVzb2x2ZXMgdGhlIGF0dHJpYnV0ZSBuYW1lIGJhY2tpbmcgYSBjb2x1bW4gbmFtZS5cbiAqIEBwYXJhbSB7aW1wb3J0KFwiLi9pbmRleC5qc1wiKS5kZWZhdWx0fSByZWNvcmQgLSBSZWNvcmQuXG4gKiBAcGFyYW0ge3N0cmluZ30gY29sdW1uTmFtZSAtIENvbHVtbiBuYW1lLlxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGF0dHJpYnV0ZSBuYW1lIGZvciB0aGUgY29sdW1uIChmYWxscyBiYWNrIHRvIHRoZSBjb2x1bW4gbmFtZSkuXG4gKi9cbmZ1bmN0aW9uIGF0dHJpYnV0ZU5hbWVGb3JDb2x1bW4ocmVjb3JkLCBjb2x1bW5OYW1lKSB7XG4gIGNvbnN0IG1hcCA9IHJlY29yZC5nZXRNb2RlbENsYXNzKCkuZ2V0QXR0cmlidXRlTmFtZVRvQ29sdW1uTmFtZU1hcCgpXG5cbiAgcmV0dXJuIE9iamVjdC5rZXlzKG1hcCkuZmluZCgoYXR0cmlidXRlTmFtZSkgPT4gbWFwW2F0dHJpYnV0ZU5hbWVdID09PSBjb2x1bW5OYW1lKSB8fCBjb2x1bW5OYW1lXG59XG4iXX0=
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export type CounterCacheParentUpdateListener = (parent: import("./index.js").default, previousParent: import("./index.js").default | undefined) => void | Promise<void>;
|
|
2
|
+
export type PreparedCounterCacheParentUpdate = {
|
|
3
|
+
canonicalParentModelClass: typeof import("./index.js").default;
|
|
4
|
+
listeners: CounterCacheParentUpdateListener[];
|
|
5
|
+
parentId: ReturnType<typeof JSON.parse>;
|
|
6
|
+
parentPrimaryKey: string;
|
|
7
|
+
parentQuery: import("../query/model-class-query.js").default<typeof import("./index.js").default>;
|
|
8
|
+
previousParent: import("./index.js").default | undefined;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Registers an internal listener for committed counter-cache parent updates.
|
|
12
|
+
* @param {typeof import("./index.js").default} parentModelClass - Parent model class.
|
|
13
|
+
* @param {CounterCacheParentUpdateListener} listener - Committed-parent listener.
|
|
14
|
+
* @returns {() => void} - Listener removal callback.
|
|
15
|
+
*/
|
|
16
|
+
export declare function registerCounterCacheParentUpdateListener(parentModelClass: typeof import("./index.js").default, listener: CounterCacheParentUpdateListener): () => void;
|
|
17
|
+
/**
|
|
18
|
+
* Captures one counter-cache parent's pre-mutation state when listeners are registered.
|
|
19
|
+
* @param {object} args - Parent update arguments.
|
|
20
|
+
* @param {ReturnType<typeof JSON.parse>} args.parentId - Parent relationship identity.
|
|
21
|
+
* @param {typeof import("./index.js").default} args.parentModelClass - Parent model class.
|
|
22
|
+
* @param {string} args.parentPrimaryKey - Parent relationship primary key.
|
|
23
|
+
* @param {import("../query/model-class-query.js").default<typeof import("./index.js").default>} args.parentQuery - Source-owned parent query.
|
|
24
|
+
* @returns {Promise<PreparedCounterCacheParentUpdate | undefined>} - Prepared delivery, or undefined when no listener is registered.
|
|
25
|
+
*/
|
|
26
|
+
export declare function prepareCounterCacheParentUpdate({ parentId, parentModelClass, parentPrimaryKey, parentQuery }: {
|
|
27
|
+
parentId: ReturnType<typeof JSON.parse>;
|
|
28
|
+
parentModelClass: typeof import("./index.js").default;
|
|
29
|
+
parentPrimaryKey: string;
|
|
30
|
+
parentQuery: import("../query/model-class-query.js").default<typeof import("./index.js").default>;
|
|
31
|
+
}): Promise<PreparedCounterCacheParentUpdate | undefined>;
|
|
32
|
+
/**
|
|
33
|
+
* Schedules one non-coalesced parent reload and notification on the source record's commit lifecycle.
|
|
34
|
+
* @param {object} args - Parent update arguments.
|
|
35
|
+
* @param {PreparedCounterCacheParentUpdate | undefined} args.preparedUpdate - Pre-mutation parent delivery state.
|
|
36
|
+
* @param {import("./index.js").default} args.sourceRecord - Source record that owns the transaction lifecycle.
|
|
37
|
+
* @returns {Promise<void>} - Resolves after registration or immediate delivery.
|
|
38
|
+
*/
|
|
39
|
+
export declare function scheduleCounterCacheParentUpdate({ preparedUpdate, sourceRecord }: {
|
|
40
|
+
preparedUpdate: PreparedCounterCacheParentUpdate | undefined;
|
|
41
|
+
sourceRecord: import("./index.js").default;
|
|
42
|
+
}): Promise<void>;
|
|
43
|
+
//# sourceMappingURL=counter-cache-parent-updates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"counter-cache-parent-updates.d.ts","sourceRoot":"","sources":["../../../../src/database/record/counter-cache-parent-updates.js"],"names":[],"mappings":"AAKI,YAAoI,gCAAgC,GAA1J,CAAC,MAAM,EAAE,OAAO,YAAY,EAAE,OAAO,EAAE,cAAc,EAAE,OAAO,YAAY,EAAE,OAAO,GAAG,SAAS,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAkC;AACpK,YAA2V,gCAAgC,GAAjX;IAAC,yBAAyB,EAAE,cAAc,YAAY,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,gCAAgC,EAAE,CAAC;IAAC,QAAQ,EAAE,UAAU,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,OAAO,+BAA+B,EAAE,OAAO,CAAC,cAAc,YAAY,EAAE,OAAO,CAAC,CAAC;IAAC,cAAc,EAAE,OAAO,YAAY,EAAE,OAAO,GAAG,SAAS,CAAA;CAAC,CAAkC;AAK/X;;;;;GAKG;AACH,wBAAgB,wCAAwC,CAAC,gBAAgB,EAJ9D,cAAc,YAAY,EAAE,OAIkC,EAAE,QAAQ,EAHxE,gCAGwE,GAFtE,MAAM,IAAI,CActB;AAED;;;;;;;;GAQG;AACH,wBAAsB,+BAA+B,CAAC,EAAC,QAAQ,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,WAAW,EAAC,EAN9G;IAA4C,QAAQ,EAA5C,UAAU,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,CACrC;IAAkD,gBAAgB,EAA1D,cAAc,YAAY,EAAE,OAAO,CAC3C;IAAqB,gBAAgB,EAA7B,MAAM,CACd;IAAmG,WAAW,EAAtG,OAAO,+BAA+B,EAAE,OAAO,CAAC,cAAc,YAAY,EAAE,OAAO,CAAC,CAC5F;CAE8G,GAFpG,OAAO,CAAC,gCAAgC,GAAG,SAAS,CAAC,CAiBjE;AAED;;;;;;GAMG;AACH,wBAAsB,gCAAgC,CAAC,EAAC,cAAc,EAAE,YAAY,EAAC,EAJlF;IAA2D,cAAc,EAAjE,gCAAgC,GAAG,SAAS,CACpD;IAA2C,YAAY,EAA/C,OAAO,YAAY,EAAE,OAAO,CACpC;CAEkF,GAFxE,OAAO,CAAC,IAAI,CAAC,CA2BzB"}
|