@mosaic-code/prisma-deadlock-avoidance-tests 0.1.0

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.
Files changed (43) hide show
  1. package/LICENSE +207 -0
  2. package/README.md +248 -0
  3. package/dist/assertions/row-assertion.d.ts +18 -0
  4. package/dist/assertions/row-assertion.d.ts.map +1 -0
  5. package/dist/assertions/row-assertion.js +53 -0
  6. package/dist/assertions/row-assertion.js.map +1 -0
  7. package/dist/assertions/table-assertion.d.ts +17 -0
  8. package/dist/assertions/table-assertion.d.ts.map +1 -0
  9. package/dist/assertions/table-assertion.js +33 -0
  10. package/dist/assertions/table-assertion.js.map +1 -0
  11. package/dist/extension.d.ts +60 -0
  12. package/dist/extension.d.ts.map +1 -0
  13. package/dist/extension.js +322 -0
  14. package/dist/extension.js.map +1 -0
  15. package/dist/graphs/row-graph.d.ts +61 -0
  16. package/dist/graphs/row-graph.d.ts.map +1 -0
  17. package/dist/graphs/row-graph.js +231 -0
  18. package/dist/graphs/row-graph.js.map +1 -0
  19. package/dist/graphs/table-graph.d.ts +61 -0
  20. package/dist/graphs/table-graph.d.ts.map +1 -0
  21. package/dist/graphs/table-graph.js +123 -0
  22. package/dist/graphs/table-graph.js.map +1 -0
  23. package/dist/index.d.ts +6 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +8 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/types.d.ts +98 -0
  28. package/dist/types.d.ts.map +1 -0
  29. package/dist/types.js +2 -0
  30. package/dist/types.js.map +1 -0
  31. package/dist/utils/caller-extractor.d.ts +27 -0
  32. package/dist/utils/caller-extractor.d.ts.map +1 -0
  33. package/dist/utils/caller-extractor.js +144 -0
  34. package/dist/utils/caller-extractor.js.map +1 -0
  35. package/dist/utils/primary-key-extractor.d.ts +23 -0
  36. package/dist/utils/primary-key-extractor.d.ts.map +1 -0
  37. package/dist/utils/primary-key-extractor.js +128 -0
  38. package/dist/utils/primary-key-extractor.js.map +1 -0
  39. package/dist/utils/raw-query-parser.d.ts +17 -0
  40. package/dist/utils/raw-query-parser.d.ts.map +1 -0
  41. package/dist/utils/raw-query-parser.js +58 -0
  42. package/dist/utils/raw-query-parser.js.map +1 -0
  43. package/package.json +79 -0
@@ -0,0 +1,322 @@
1
+ import { Prisma } from '@prisma/client';
2
+ import { TableLockGraph } from './graphs/table-graph.js';
3
+ import { RowLockGraphs } from './graphs/row-graph.js';
4
+ import { extractCaller } from './utils/caller-extractor.js';
5
+ import { extractPrimaryKeys } from './utils/primary-key-extractor.js';
6
+ import { inferTableFromSql, isLockingSql } from './utils/raw-query-parser.js';
7
+ import { assertConsistentTableLocking as assertTableLocking, TableLockingAssertionError, } from './assertions/table-assertion.js';
8
+ import { assertConsistentRowLocking as assertRowLocking, RowLockingAssertionError, } from './assertions/row-assertion.js';
9
+ /**
10
+ * Operations that acquire locks on records
11
+ */
12
+ const LOCKING_OPERATIONS = new Set([
13
+ 'update',
14
+ 'updateMany',
15
+ 'delete',
16
+ 'deleteMany',
17
+ 'create',
18
+ 'createMany',
19
+ 'createManyAndReturn',
20
+ 'upsert',
21
+ ]);
22
+ let globalState = null;
23
+ /**
24
+ * Get or create the global state instance
25
+ */
26
+ function getGlobalState() {
27
+ if (!globalState) {
28
+ globalState = {
29
+ tableGraph: new TableLockGraph(),
30
+ rowGraphs: new RowLockGraphs(),
31
+ currentOperationTables: [],
32
+ currentLockedRecords: new Map(),
33
+ inBatch: false,
34
+ };
35
+ }
36
+ return globalState;
37
+ }
38
+ /**
39
+ * Start tracking a new transaction or batch of operations.
40
+ * Called automatically when $transaction is invoked.
41
+ */
42
+ function startOperationBatch() {
43
+ const state = getGlobalState();
44
+ state.currentOperationTables = [];
45
+ state.currentLockedRecords.clear();
46
+ state.inBatch = true;
47
+ }
48
+ /**
49
+ * End the current operation batch.
50
+ * Called automatically when $transaction completes.
51
+ */
52
+ function endOperationBatch() {
53
+ const state = getGlobalState();
54
+ state.currentOperationTables = [];
55
+ state.currentLockedRecords.clear();
56
+ state.inBatch = false;
57
+ }
58
+ /**
59
+ * Execute a function while tracking table ordering as a single batch.
60
+ * Called automatically by the $transaction wrapper.
61
+ */
62
+ async function withOperationTracking(fn) {
63
+ startOperationBatch();
64
+ try {
65
+ return await fn();
66
+ }
67
+ finally {
68
+ endOperationBatch();
69
+ }
70
+ }
71
+ /**
72
+ * Record a table lock, creating edges from previously locked tables in the current batch
73
+ */
74
+ function recordTableLock(table, state) {
75
+ const caller = extractCaller();
76
+ // Create edges from all previously locked tables to this one
77
+ for (const prevTable of state.currentOperationTables) {
78
+ if (prevTable !== table) {
79
+ state.tableGraph.addEdge(prevTable, table, caller);
80
+ }
81
+ }
82
+ // Add this table to the current operation's locked tables
83
+ if (!state.currentOperationTables.includes(table)) {
84
+ state.currentOperationTables.push(table);
85
+ }
86
+ }
87
+ /**
88
+ * Record row ordering from query results.
89
+ * Filters out records that have already been locked in the current transaction.
90
+ */
91
+ function recordRowOrdering(table, result, state) {
92
+ const allKeys = extractPrimaryKeys(table, result);
93
+ if (allKeys.length === 0) {
94
+ return;
95
+ }
96
+ // Get or create the set of locked records for this table
97
+ let lockedRecords = state.currentLockedRecords.get(table);
98
+ if (!lockedRecords) {
99
+ lockedRecords = new Set();
100
+ state.currentLockedRecords.set(table, lockedRecords);
101
+ }
102
+ // Filter out records that have already been locked in this transaction
103
+ const newKeys = state.inBatch
104
+ ? allKeys.filter((key) => !lockedRecords.has(key))
105
+ : allKeys;
106
+ // Mark the new records as locked
107
+ for (const key of newKeys) {
108
+ lockedRecords.add(key);
109
+ }
110
+ // Only add to graph if we have multiple new keys (need ordering)
111
+ if (newKeys.length > 1) {
112
+ const caller = extractCaller();
113
+ state.rowGraphs.addRowOrdering(table, newKeys, caller);
114
+ }
115
+ }
116
+ /**
117
+ * Extract SQL string from raw query args
118
+ */
119
+ function extractSqlFromArgs(args) {
120
+ if (!args)
121
+ return '';
122
+ if (typeof args === 'object' && args !== null) {
123
+ const argsObj = args;
124
+ if ('strings' in argsObj && Array.isArray(argsObj.strings)) {
125
+ return String(argsObj.strings[0] ?? '');
126
+ }
127
+ if ('0' in argsObj) {
128
+ return String(argsObj['0'] ?? '');
129
+ }
130
+ }
131
+ if (typeof args === 'string') {
132
+ return args;
133
+ }
134
+ return '';
135
+ }
136
+ /**
137
+ * Extract SQL from unsafe query args
138
+ */
139
+ function extractSqlFromUnsafeArgs(args) {
140
+ if (Array.isArray(args) && args.length > 0) {
141
+ return String(args[0] ?? '');
142
+ }
143
+ return '';
144
+ }
145
+ /**
146
+ * Handle raw query tracking - extracts SQL, infers table, and records table lock.
147
+ * Logs a warning if table inference fails.
148
+ */
149
+ function handleRawQuery(args, extractFn, enabled) {
150
+ if (!enabled)
151
+ return;
152
+ const sql = extractFn(args);
153
+ if (!sql)
154
+ return;
155
+ const table = inferTableFromSql(sql);
156
+ if (table && isLockingSql(sql)) {
157
+ const state = getGlobalState();
158
+ recordTableLock(table, state);
159
+ }
160
+ else if (isLockingSql(sql) && !table) {
161
+ // Log warning if this is a locking query but we couldn't infer the table
162
+ console.warn(`[prisma-deadlock-detection] Could not infer table name from raw query. ` +
163
+ `The query will not be tracked for deadlock detection. ` +
164
+ `Query: ${sql.slice(0, 100)}${sql.length > 100 ? '...' : ''}`);
165
+ }
166
+ }
167
+ /**
168
+ * Create the internal Prisma extension for query interception.
169
+ */
170
+ function createDeadlockExtension(enabled) {
171
+ return Prisma.defineExtension({
172
+ name: 'prisma-deadlock-detection',
173
+ query: {
174
+ $allModels: {
175
+ async $allOperations({ model, operation, args, query }) {
176
+ if (!enabled) {
177
+ return query(args);
178
+ }
179
+ const state = getGlobalState();
180
+ const isLocking = LOCKING_OPERATIONS.has(operation);
181
+ // Record table lock before query execution
182
+ if (isLocking) {
183
+ recordTableLock(model, state);
184
+ }
185
+ // Execute the query
186
+ const result = await query(args);
187
+ // Record row ordering from result
188
+ if (isLocking && result !== null && result !== undefined) {
189
+ recordRowOrdering(model, result, state);
190
+ }
191
+ return result;
192
+ },
193
+ },
194
+ async $queryRaw({ args, query }) {
195
+ handleRawQuery(args, extractSqlFromArgs, enabled);
196
+ return query(args);
197
+ },
198
+ async $queryRawUnsafe({ args, query }) {
199
+ handleRawQuery(args, extractSqlFromUnsafeArgs, enabled);
200
+ return query(args);
201
+ },
202
+ async $executeRaw({ args, query }) {
203
+ handleRawQuery(args, extractSqlFromArgs, enabled);
204
+ return query(args);
205
+ },
206
+ async $executeRawUnsafe({ args, query }) {
207
+ handleRawQuery(args, extractSqlFromUnsafeArgs, enabled);
208
+ return query(args);
209
+ },
210
+ },
211
+ });
212
+ }
213
+ /**
214
+ * Wrap a Prisma client with deadlock detection.
215
+ * Automatically tracks table and row locking order across all transactions.
216
+ *
217
+ * Usage:
218
+ * ```typescript
219
+ * const prisma = withDeadlockDetection(new PrismaClient())
220
+ * ```
221
+ *
222
+ * @param client The PrismaClient instance to wrap
223
+ * @param config Optional configuration
224
+ * @returns A wrapped client with automatic transaction tracking
225
+ */
226
+ export function withDeadlockDetection(client, config) {
227
+ if (process.env.NODE_ENV === 'production') {
228
+ console.warn('[@mosaic-code/prisma-deadlock-avoidance-tests] WARNING: This library is intended for test environments only. ' +
229
+ 'Running in production may impact performance.');
230
+ }
231
+ const enabled = config?.enabled !== false;
232
+ // Apply the query interception extension
233
+ const extended = client.$extends(createDeadlockExtension(enabled));
234
+ // Wrap with proxy to intercept $transaction calls
235
+ return new Proxy(extended, {
236
+ get(target, prop) {
237
+ if (prop === '$transaction') {
238
+ // Return a wrapped $transaction method
239
+ return function (args) {
240
+ const originalMethod = target.$transaction;
241
+ // Check if this is an interactive transaction (callback-based)
242
+ const isInteractive = typeof args === 'function';
243
+ if (isInteractive && enabled) {
244
+ // Automatically wrap the transaction callback with tracking
245
+ return withOperationTracking(() => originalMethod.call(target, args));
246
+ }
247
+ // Batch transaction, disabled, or raw transaction - pass through
248
+ return originalMethod.call(target, args);
249
+ };
250
+ }
251
+ // For all other properties, return the original value
252
+ const value = target[String(prop)];
253
+ // If it's a function, bind it to the target
254
+ return typeof value === 'function' ? value.bind(target) : value;
255
+ },
256
+ });
257
+ }
258
+ /**
259
+ * Assert that table locking has been consistent across all tracked operations.
260
+ * Throws TableLockingAssertionError if a cycle is detected.
261
+ */
262
+ export function assertConsistentTableLocking() {
263
+ const state = getGlobalState();
264
+ assertTableLocking(state.tableGraph);
265
+ }
266
+ /**
267
+ * Assert that row locking has been consistent within tables.
268
+ * Throws RowLockingAssertionError if violations are detected.
269
+ *
270
+ * @param options Options controlling which tables to check and strictness mode
271
+ */
272
+ export function assertConsistentRowLocking(options) {
273
+ const state = getGlobalState();
274
+ assertRowLocking(state.rowGraphs, options);
275
+ }
276
+ /**
277
+ * Assert no deadlock risk exists by checking both table and row locking consistency.
278
+ * This is a convenience function that calls both assertions.
279
+ *
280
+ * @param rowOptions Options for the row locking assertion
281
+ */
282
+ export function assertNoDeadlockRisk(rowOptions) {
283
+ assertConsistentTableLocking();
284
+ assertConsistentRowLocking(rowOptions);
285
+ }
286
+ /**
287
+ * Reset all deadlock detection state.
288
+ * Call this between test runs if needed.
289
+ */
290
+ export function resetDeadlockDetection() {
291
+ globalState = null;
292
+ }
293
+ /**
294
+ * Wrapper function for tracking operations from prisma-lock-for-update.
295
+ * Use this to wrap forUpdate calls so they're tracked in the deadlock detection graph.
296
+ *
297
+ * @param model The model name (e.g., 'User', 'Post')
298
+ * @param fn The async function that performs the forUpdate operation
299
+ * @returns The result of the operation
300
+ *
301
+ * @example
302
+ * ```typescript
303
+ * const user = await trackForUpdate('User', () =>
304
+ * tx.user.findUniqueForUpdate({ where: { id: 1 } })
305
+ * )
306
+ * ```
307
+ */
308
+ export async function trackForUpdate(model, fn) {
309
+ const state = getGlobalState();
310
+ // Record the table lock
311
+ recordTableLock(model, state);
312
+ // Execute the operation
313
+ const result = await fn();
314
+ // Extract and record row ordering from result
315
+ if (result !== null && result !== undefined) {
316
+ recordRowOrdering(model, result, state);
317
+ }
318
+ return result;
319
+ }
320
+ // Re-export error types for consumers
321
+ export { TableLockingAssertionError, RowLockingAssertionError };
322
+ //# sourceMappingURL=extension.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension.js","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAgB,MAAM,gBAAgB,CAAA;AAErD,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAA;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAA;AACrE,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAC7E,OAAO,EACL,4BAA4B,IAAI,kBAAkB,EAClD,0BAA0B,GAC3B,MAAM,iCAAiC,CAAA;AACxC,OAAO,EACL,0BAA0B,IAAI,gBAAgB,EAC9C,wBAAwB,GACzB,MAAM,+BAA+B,CAAA;AAEtC;;GAEG;AACH,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC;IACjC,QAAQ;IACR,YAAY;IACZ,QAAQ;IACR,YAAY;IACZ,QAAQ;IACR,YAAY;IACZ,qBAAqB;IACrB,QAAQ;CACT,CAAC,CAAA;AAiBF,IAAI,WAAW,GAA+B,IAAI,CAAA;AAElD;;GAEG;AACH,SAAS,cAAc;IACrB,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,WAAW,GAAG;YACZ,UAAU,EAAE,IAAI,cAAc,EAAE;YAChC,SAAS,EAAE,IAAI,aAAa,EAAE;YAC9B,sBAAsB,EAAE,EAAE;YAC1B,oBAAoB,EAAE,IAAI,GAAG,EAAE;YAC/B,OAAO,EAAE,KAAK;SACf,CAAA;IACH,CAAC;IACD,OAAO,WAAW,CAAA;AACpB,CAAC;AAED;;;GAGG;AACH,SAAS,mBAAmB;IAC1B,MAAM,KAAK,GAAG,cAAc,EAAE,CAAA;IAC9B,KAAK,CAAC,sBAAsB,GAAG,EAAE,CAAA;IACjC,KAAK,CAAC,oBAAoB,CAAC,KAAK,EAAE,CAAA;IAClC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAA;AACtB,CAAC;AAED;;;GAGG;AACH,SAAS,iBAAiB;IACxB,MAAM,KAAK,GAAG,cAAc,EAAE,CAAA;IAC9B,KAAK,CAAC,sBAAsB,GAAG,EAAE,CAAA;IACjC,KAAK,CAAC,oBAAoB,CAAC,KAAK,EAAE,CAAA;IAClC,KAAK,CAAC,OAAO,GAAG,KAAK,CAAA;AACvB,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,qBAAqB,CAAI,EAAoB;IAC1D,mBAAmB,EAAE,CAAA;IACrB,IAAI,CAAC;QACH,OAAO,MAAM,EAAE,EAAE,CAAA;IACnB,CAAC;YAAS,CAAC;QACT,iBAAiB,EAAE,CAAA;IACrB,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,eAAe,CAAC,KAAa,EAAE,KAA0B;IAChE,MAAM,MAAM,GAAG,aAAa,EAAE,CAAA;IAE9B,6DAA6D;IAC7D,KAAK,MAAM,SAAS,IAAI,KAAK,CAAC,sBAAsB,EAAE,CAAC;QACrD,IAAI,SAAS,KAAK,KAAK,EAAE,CAAC;YACxB,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,SAAS,EAAE,KAAK,EAAE,MAAM,CAAC,CAAA;QACpD,CAAC;IACH,CAAC;IAED,0DAA0D;IAC1D,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAClD,KAAK,CAAC,sBAAsB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAC1C,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,iBAAiB,CACxB,KAAa,EACb,MAAe,EACf,KAA0B;IAE1B,MAAM,OAAO,GAAG,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;IACjD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAM;IACR,CAAC;IAED,yDAAyD;IACzD,IAAI,aAAa,GAAG,KAAK,CAAC,oBAAoB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;IACzD,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,aAAa,GAAG,IAAI,GAAG,EAAE,CAAA;QACzB,KAAK,CAAC,oBAAoB,CAAC,GAAG,CAAC,KAAK,EAAE,aAAa,CAAC,CAAA;IACtD,CAAC;IAED,uEAAuE;IACvE,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO;QAC3B,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClD,CAAC,CAAC,OAAO,CAAA;IAEX,iCAAiC;IACjC,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IACxB,CAAC;IAED,iEAAiE;IACjE,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,MAAM,GAAG,aAAa,EAAE,CAAA;QAC9B,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,CAAA;IACxD,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,kBAAkB,CAAC,IAAa;IACvC,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAA;IAEpB,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAC9C,MAAM,OAAO,GAAG,IAA+B,CAAA;QAC/C,IAAI,SAAS,IAAI,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YAC3D,OAAO,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAA;QACzC,CAAC;QACD,IAAI,GAAG,IAAI,OAAO,EAAE,CAAC;YACnB,OAAO,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAA;QACnC,CAAC;IACH,CAAC;IAED,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,OAAO,IAAI,CAAA;IACb,CAAC;IAED,OAAO,EAAE,CAAA;AACX,CAAC;AAED;;GAEG;AACH,SAAS,wBAAwB,CAAC,IAAa;IAC7C,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3C,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAA;IAC9B,CAAC;IACD,OAAO,EAAE,CAAA;AACX,CAAC;AAED;;;GAGG;AACH,SAAS,cAAc,CACrB,IAAa,EACb,SAAoC,EACpC,OAAgB;IAEhB,IAAI,CAAC,OAAO;QAAE,OAAM;IAEpB,MAAM,GAAG,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;IAC3B,IAAI,CAAC,GAAG;QAAE,OAAM;IAEhB,MAAM,KAAK,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAA;IAEpC,IAAI,KAAK,IAAI,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,cAAc,EAAE,CAAA;QAC9B,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;IAC/B,CAAC;SAAM,IAAI,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;QACvC,yEAAyE;QACzE,OAAO,CAAC,IAAI,CACV,yEAAyE;YACvE,wDAAwD;YACxD,UAAU,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAChE,CAAA;IACH,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,uBAAuB,CAAC,OAAgB;IAC/C,OAAO,MAAM,CAAC,eAAe,CAAC;QAC5B,IAAI,EAAE,2BAA2B;QAEjC,KAAK,EAAE;YACL,UAAU,EAAE;gBACV,KAAK,CAAC,cAAc,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE;oBACpD,IAAI,CAAC,OAAO,EAAE,CAAC;wBACb,OAAO,KAAK,CAAC,IAAI,CAAC,CAAA;oBACpB,CAAC;oBAED,MAAM,KAAK,GAAG,cAAc,EAAE,CAAA;oBAC9B,MAAM,SAAS,GAAG,kBAAkB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;oBAEnD,2CAA2C;oBAC3C,IAAI,SAAS,EAAE,CAAC;wBACd,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;oBAC/B,CAAC;oBAED,oBAAoB;oBACpB,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,CAAA;oBAEhC,kCAAkC;oBAClC,IAAI,SAAS,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;wBACzD,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,CAAA;oBACzC,CAAC;oBAED,OAAO,MAAM,CAAA;gBACf,CAAC;aACF;YAED,KAAK,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE;gBAC7B,cAAc,CAAC,IAAI,EAAE,kBAAkB,EAAE,OAAO,CAAC,CAAA;gBACjD,OAAO,KAAK,CAAC,IAAI,CAAC,CAAA;YACpB,CAAC;YAED,KAAK,CAAC,eAAe,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE;gBACnC,cAAc,CAAC,IAAI,EAAE,wBAAwB,EAAE,OAAO,CAAC,CAAA;gBACvD,OAAO,KAAK,CAAC,IAAI,CAAC,CAAA;YACpB,CAAC;YAED,KAAK,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE;gBAC/B,cAAc,CAAC,IAAI,EAAE,kBAAkB,EAAE,OAAO,CAAC,CAAA;gBACjD,OAAO,KAAK,CAAC,IAAI,CAAC,CAAA;YACpB,CAAC;YAED,KAAK,CAAC,iBAAiB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE;gBACrC,cAAc,CAAC,IAAI,EAAE,wBAAwB,EAAE,OAAO,CAAC,CAAA;gBACvD,OAAO,KAAK,CAAC,IAAI,CAAC,CAAA;YACpB,CAAC;SACF;KACF,CAAC,CAAA;AACJ,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,qBAAqB,CACnC,MAAS,EACT,MAAgC;IAEhC,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,EAAE,CAAC;QAC1C,OAAO,CAAC,IAAI,CACV,+GAA+G;YAC7G,+CAA+C,CAClD,CAAA;IACH,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,EAAE,OAAO,KAAK,KAAK,CAAA;IAEzC,yCAAyC;IACzC,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,uBAAuB,CAAC,OAAO,CAAC,CAAiB,CAAA;IAElF,kDAAkD;IAClD,OAAO,IAAI,KAAK,CAAC,QAAQ,EAAE;QACzB,GAAG,CAAC,MAAM,EAAE,IAAI;YACd,IAAI,IAAI,KAAK,cAAc,EAAE,CAAC;gBAC5B,uCAAuC;gBACvC,OAAO,UAAU,IAAa;oBAC5B,MAAM,cAAc,GAAG,MAAM,CAAC,YAAmD,CAAA;oBAEjF,+DAA+D;oBAC/D,MAAM,aAAa,GAAG,OAAO,IAAI,KAAK,UAAU,CAAA;oBAEhD,IAAI,aAAa,IAAI,OAAO,EAAE,CAAC;wBAC7B,4DAA4D;wBAC5D,OAAO,qBAAqB,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAA;oBACvE,CAAC;oBAED,iEAAiE;oBACjE,OAAO,cAAc,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;gBAC1C,CAAC,CAAA;YACH,CAAC;YAED,sDAAsD;YACtD,MAAM,KAAK,GAAI,MAAkC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAA;YAE/D,4CAA4C;YAC5C,OAAO,OAAO,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;QACjE,CAAC;KACF,CAAC,CAAA;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,4BAA4B;IAC1C,MAAM,KAAK,GAAG,cAAc,EAAE,CAAA;IAC9B,kBAAkB,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;AACtC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,0BAA0B,CAAC,OAA2B;IACpE,MAAM,KAAK,GAAG,cAAc,EAAE,CAAA;IAC9B,gBAAgB,CAAC,KAAK,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;AAC5C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,UAA8B;IACjE,4BAA4B,EAAE,CAAA;IAC9B,0BAA0B,CAAC,UAAU,CAAC,CAAA;AACxC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB;IACpC,WAAW,GAAG,IAAI,CAAA;AACpB,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,KAAa,EACb,EAAoB;IAEpB,MAAM,KAAK,GAAG,cAAc,EAAE,CAAA;IAE9B,wBAAwB;IACxB,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;IAE7B,wBAAwB;IACxB,MAAM,MAAM,GAAG,MAAM,EAAE,EAAE,CAAA;IAEzB,8CAA8C;IAC9C,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QAC5C,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,CAAA;IACzC,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC;AAED,sCAAsC;AACtC,OAAO,EAAE,0BAA0B,EAAE,wBAAwB,EAAE,CAAA"}
@@ -0,0 +1,61 @@
1
+ import { Graph } from '@dagrejs/graphlib';
2
+ import type { CallerInfo, RowCycleInfo, StrictMode } from '../types.js';
3
+ /**
4
+ * Manages directed graphs of row lock ordering, one per table.
5
+ * Nodes represent primary keys, edges represent "row A was locked before row B".
6
+ */
7
+ export declare class RowLockGraphs {
8
+ private graphs;
9
+ /**
10
+ * Get or create the graph for a specific table
11
+ */
12
+ private getOrCreateGraph;
13
+ /**
14
+ * Add row ordering edges for a sequence of locked rows.
15
+ * Creates edges between consecutive rows: [a, b, c] -> a->b, b->c
16
+ */
17
+ addRowOrdering(table: string, orderedKeys: string[], caller: CallerInfo): void;
18
+ /**
19
+ * Merge row graphs from another instance into this one
20
+ */
21
+ mergeFrom(other: RowLockGraphs): void;
22
+ /**
23
+ * Find cycles in a specific table's row graph
24
+ */
25
+ findCycles(table: string): string[][];
26
+ /**
27
+ * Check if all edges in a table's graph follow the specified ordering direction.
28
+ * Only makes sense for numeric/comparable primary keys.
29
+ */
30
+ private checkOrdering;
31
+ /**
32
+ * Check if all edges in a table's graph follow ascending order.
33
+ * Only makes sense for numeric/comparable primary keys.
34
+ */
35
+ checkAscendingOrder(table: string): boolean;
36
+ /**
37
+ * Check if all edges in a table's graph follow descending order.
38
+ */
39
+ checkDescendingOrder(table: string): boolean;
40
+ /**
41
+ * Check if a table's graph satisfies the given strict mode
42
+ */
43
+ checkStrictOrdering(table: string, mode: StrictMode): RowCycleInfo | null;
44
+ /**
45
+ * Get all callers from a table's graph
46
+ */
47
+ private getAllCallers;
48
+ /**
49
+ * Get all tables that have row graphs
50
+ */
51
+ getAllTables(): string[];
52
+ /**
53
+ * Get the graph for a specific table (for testing)
54
+ */
55
+ getGraph(table: string): Graph | undefined;
56
+ /**
57
+ * Clear all data from all graphs
58
+ */
59
+ reset(): void;
60
+ }
61
+ //# sourceMappingURL=row-graph.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"row-graph.d.ts","sourceRoot":"","sources":["../../src/graphs/row-graph.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAO,MAAM,mBAAmB,CAAA;AAC9C,OAAO,KAAK,EAAE,UAAU,EAAgB,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAGrF;;;GAGG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAgC;IAE9C;;OAEG;IACH,OAAO,CAAC,gBAAgB;IASxB;;;OAGG;IACH,cAAc,CACZ,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EAAE,EACrB,MAAM,EAAE,UAAU,GACjB,IAAI;IAuCP;;OAEG;IACH,SAAS,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IAWrC;;OAEG;IACH,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE;IAMrC;;;OAGG;IACH,OAAO,CAAC,aAAa;IA8BrB;;;OAGG;IACH,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAI3C;;OAEG;IACH,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAI5C;;OAEG;IACH,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,YAAY,GAAG,IAAI;IAqEzE;;OAEG;IACH,OAAO,CAAC,aAAa;IAqBrB;;OAEG;IACH,YAAY,IAAI,MAAM,EAAE;IAIxB;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS;IAI1C;;OAEG;IACH,KAAK,IAAI,IAAI;CAGd"}
@@ -0,0 +1,231 @@
1
+ import { Graph, alg } from '@dagrejs/graphlib';
2
+ import { addCallerIfUnique, callerKey } from '../utils/caller-extractor.js';
3
+ /**
4
+ * Manages directed graphs of row lock ordering, one per table.
5
+ * Nodes represent primary keys, edges represent "row A was locked before row B".
6
+ */
7
+ export class RowLockGraphs {
8
+ graphs = new Map();
9
+ /**
10
+ * Get or create the graph for a specific table
11
+ */
12
+ getOrCreateGraph(table) {
13
+ let graph = this.graphs.get(table);
14
+ if (!graph) {
15
+ graph = new Graph({ directed: true });
16
+ this.graphs.set(table, graph);
17
+ }
18
+ return graph;
19
+ }
20
+ /**
21
+ * Add row ordering edges for a sequence of locked rows.
22
+ * Creates edges between consecutive rows: [a, b, c] -> a->b, b->c
23
+ */
24
+ addRowOrdering(table, orderedKeys, caller) {
25
+ if (orderedKeys.length < 2) {
26
+ // Need at least 2 rows to create an ordering edge
27
+ return;
28
+ }
29
+ const graph = this.getOrCreateGraph(table);
30
+ // Create edges between consecutive rows
31
+ for (let i = 0; i < orderedKeys.length - 1; i++) {
32
+ const from = orderedKeys[i];
33
+ const to = orderedKeys[i + 1];
34
+ // Skip self-edges (shouldn't happen with unique PKs, but be safe)
35
+ if (from === to)
36
+ continue;
37
+ // Ensure nodes exist
38
+ if (!graph.hasNode(from)) {
39
+ graph.setNode(from);
40
+ }
41
+ if (!graph.hasNode(to)) {
42
+ graph.setNode(to);
43
+ }
44
+ // Get or create edge label
45
+ const existingLabel = graph.edge(from, to);
46
+ if (existingLabel) {
47
+ // Deduplicate callers by file:line
48
+ addCallerIfUnique(existingLabel.callers, caller);
49
+ }
50
+ else {
51
+ graph.setEdge(from, to, {
52
+ callers: [caller],
53
+ table,
54
+ });
55
+ }
56
+ }
57
+ }
58
+ /**
59
+ * Merge row graphs from another instance into this one
60
+ */
61
+ mergeFrom(other) {
62
+ for (const [table, otherGraph] of other.graphs) {
63
+ for (const edge of otherGraph.edges()) {
64
+ const label = otherGraph.edge(edge);
65
+ for (const caller of label.callers) {
66
+ this.addRowOrdering(table, [edge.v, edge.w], caller);
67
+ }
68
+ }
69
+ }
70
+ }
71
+ /**
72
+ * Find cycles in a specific table's row graph
73
+ */
74
+ findCycles(table) {
75
+ const graph = this.graphs.get(table);
76
+ if (!graph)
77
+ return [];
78
+ return alg.findCycles(graph);
79
+ }
80
+ /**
81
+ * Check if all edges in a table's graph follow the specified ordering direction.
82
+ * Only makes sense for numeric/comparable primary keys.
83
+ */
84
+ checkOrdering(table, direction) {
85
+ const graph = this.graphs.get(table);
86
+ if (!graph)
87
+ return true;
88
+ for (const edge of graph.edges()) {
89
+ // Try to compare as numbers
90
+ const fromNum = Number(edge.v);
91
+ const toNum = Number(edge.w);
92
+ if (!isNaN(fromNum) && !isNaN(toNum)) {
93
+ if (direction === 'ASC' && fromNum > toNum) {
94
+ return false;
95
+ }
96
+ if (direction === 'DESC' && fromNum < toNum) {
97
+ return false;
98
+ }
99
+ }
100
+ else {
101
+ // String comparison
102
+ if (direction === 'ASC' && edge.v > edge.w) {
103
+ return false;
104
+ }
105
+ if (direction === 'DESC' && edge.v < edge.w) {
106
+ return false;
107
+ }
108
+ }
109
+ }
110
+ return true;
111
+ }
112
+ /**
113
+ * Check if all edges in a table's graph follow ascending order.
114
+ * Only makes sense for numeric/comparable primary keys.
115
+ */
116
+ checkAscendingOrder(table) {
117
+ return this.checkOrdering(table, 'ASC');
118
+ }
119
+ /**
120
+ * Check if all edges in a table's graph follow descending order.
121
+ */
122
+ checkDescendingOrder(table) {
123
+ return this.checkOrdering(table, 'DESC');
124
+ }
125
+ /**
126
+ * Check if a table's graph satisfies the given strict mode
127
+ */
128
+ checkStrictOrdering(table, mode) {
129
+ const cycles = this.findCycles(table);
130
+ // Cycles are always a violation
131
+ if (cycles.length > 0) {
132
+ const graph = this.graphs.get(table);
133
+ const cycle = cycles[0];
134
+ const callers = [];
135
+ // Collect callers from cycle edges
136
+ for (let i = 0; i < cycle.length; i++) {
137
+ const from = cycle[i];
138
+ const to = cycle[(i + 1) % cycle.length];
139
+ const label = graph.edge(from, to);
140
+ if (label) {
141
+ callers.push(...label.callers);
142
+ }
143
+ }
144
+ return {
145
+ table,
146
+ primaryKeys: cycle.map((k) => (isNaN(Number(k)) ? k : Number(k))),
147
+ callers,
148
+ message: `Cycle detected in row ordering`,
149
+ };
150
+ }
151
+ // No strict ordering required
152
+ if (mode === false) {
153
+ return null;
154
+ }
155
+ // Check specific ordering
156
+ if (mode === 'ASC') {
157
+ if (!this.checkAscendingOrder(table)) {
158
+ return {
159
+ table,
160
+ primaryKeys: [],
161
+ callers: this.getAllCallers(table),
162
+ message: `Row ordering is not consistently ascending`,
163
+ };
164
+ }
165
+ }
166
+ else if (mode === 'DESC') {
167
+ if (!this.checkDescendingOrder(table)) {
168
+ return {
169
+ table,
170
+ primaryKeys: [],
171
+ callers: this.getAllCallers(table),
172
+ message: `Row ordering is not consistently descending`,
173
+ };
174
+ }
175
+ }
176
+ else if (mode === true) {
177
+ // Must be either all ASC or all DESC
178
+ const isAsc = this.checkAscendingOrder(table);
179
+ const isDesc = this.checkDescendingOrder(table);
180
+ if (!isAsc && !isDesc) {
181
+ return {
182
+ table,
183
+ primaryKeys: [],
184
+ callers: this.getAllCallers(table),
185
+ message: `Row ordering is neither consistently ascending nor descending`,
186
+ };
187
+ }
188
+ }
189
+ return null;
190
+ }
191
+ /**
192
+ * Get all callers from a table's graph
193
+ */
194
+ getAllCallers(table) {
195
+ const graph = this.graphs.get(table);
196
+ if (!graph)
197
+ return [];
198
+ const callers = [];
199
+ const seen = new Set();
200
+ for (const edge of graph.edges()) {
201
+ const label = graph.edge(edge);
202
+ for (const caller of label.callers) {
203
+ const key = callerKey(caller);
204
+ if (!seen.has(key)) {
205
+ seen.add(key);
206
+ callers.push(caller);
207
+ }
208
+ }
209
+ }
210
+ return callers;
211
+ }
212
+ /**
213
+ * Get all tables that have row graphs
214
+ */
215
+ getAllTables() {
216
+ return Array.from(this.graphs.keys());
217
+ }
218
+ /**
219
+ * Get the graph for a specific table (for testing)
220
+ */
221
+ getGraph(table) {
222
+ return this.graphs.get(table);
223
+ }
224
+ /**
225
+ * Clear all data from all graphs
226
+ */
227
+ reset() {
228
+ this.graphs.clear();
229
+ }
230
+ }
231
+ //# sourceMappingURL=row-graph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"row-graph.js","sourceRoot":"","sources":["../../src/graphs/row-graph.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAA;AAE9C,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAA;AAE3E;;;GAGG;AACH,MAAM,OAAO,aAAa;IAChB,MAAM,GAAuB,IAAI,GAAG,EAAE,CAAA;IAE9C;;OAEG;IACK,gBAAgB,CAAC,KAAa;QACpC,IAAI,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QAClC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,KAAK,GAAG,IAAI,KAAK,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAA;YACrC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;QAC/B,CAAC;QACD,OAAO,KAAK,CAAA;IACd,CAAC;IAED;;;OAGG;IACH,cAAc,CACZ,KAAa,EACb,WAAqB,EACrB,MAAkB;QAElB,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC3B,kDAAkD;YAClD,OAAM;QACR,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAA;QAE1C,wCAAwC;QACxC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAChD,MAAM,IAAI,GAAG,WAAW,CAAC,CAAC,CAAC,CAAA;YAC3B,MAAM,EAAE,GAAG,WAAW,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;YAE7B,kEAAkE;YAClE,IAAI,IAAI,KAAK,EAAE;gBAAE,SAAQ;YAEzB,qBAAqB;YACrB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;gBACzB,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;YACrB,CAAC;YACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC;gBACvB,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;YACnB,CAAC;YAED,2BAA2B;YAC3B,MAAM,aAAa,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAA6B,CAAA;YAEtE,IAAI,aAAa,EAAE,CAAC;gBAClB,mCAAmC;gBACnC,iBAAiB,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;YAClD,CAAC;iBAAM,CAAC;gBACN,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,EAAE;oBACtB,OAAO,EAAE,CAAC,MAAM,CAAC;oBACjB,KAAK;iBACiB,CAAC,CAAA;YAC3B,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACH,SAAS,CAAC,KAAoB;QAC5B,KAAK,MAAM,CAAC,KAAK,EAAE,UAAU,CAAC,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YAC/C,KAAK,MAAM,IAAI,IAAI,UAAU,CAAC,KAAK,EAAE,EAAE,CAAC;gBACtC,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAiB,CAAA;gBACnD,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;oBACnC,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAA;gBACtD,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACH,UAAU,CAAC,KAAa;QACtB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QACpC,IAAI,CAAC,KAAK;YAAE,OAAO,EAAE,CAAA;QACrB,OAAO,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,CAAA;IAC9B,CAAC;IAED;;;OAGG;IACK,aAAa,CAAC,KAAa,EAAE,SAAyB;QAC5D,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QACpC,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAA;QAEvB,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;YACjC,4BAA4B;YAC5B,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;YAC9B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;YAE5B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;gBACrC,IAAI,SAAS,KAAK,KAAK,IAAI,OAAO,GAAG,KAAK,EAAE,CAAC;oBAC3C,OAAO,KAAK,CAAA;gBACd,CAAC;gBACD,IAAI,SAAS,KAAK,MAAM,IAAI,OAAO,GAAG,KAAK,EAAE,CAAC;oBAC5C,OAAO,KAAK,CAAA;gBACd,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,oBAAoB;gBACpB,IAAI,SAAS,KAAK,KAAK,IAAI,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC;oBAC3C,OAAO,KAAK,CAAA;gBACd,CAAC;gBACD,IAAI,SAAS,KAAK,MAAM,IAAI,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC;oBAC5C,OAAO,KAAK,CAAA;gBACd,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAA;IACb,CAAC;IAED;;;OAGG;IACH,mBAAmB,CAAC,KAAa;QAC/B,OAAO,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;IACzC,CAAC;IAED;;OAEG;IACH,oBAAoB,CAAC,KAAa;QAChC,OAAO,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;IAC1C,CAAC;IAED;;OAEG;IACH,mBAAmB,CAAC,KAAa,EAAE,IAAgB;QACjD,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAA;QAErC,gCAAgC;QAChC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAE,CAAA;YACrC,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAA;YACvB,MAAM,OAAO,GAAiB,EAAE,CAAA;YAEhC,mCAAmC;YACnC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;gBACrB,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,CAAA;gBACxC,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAA6B,CAAA;gBAC9D,IAAI,KAAK,EAAE,CAAC;oBACV,OAAO,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,CAAA;gBAChC,CAAC;YACH,CAAC;YAED,OAAO;gBACL,KAAK;gBACL,WAAW,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;gBACjE,OAAO;gBACP,OAAO,EAAE,gCAAgC;aAC1C,CAAA;QACH,CAAC;QAED,8BAA8B;QAC9B,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;YACnB,OAAO,IAAI,CAAA;QACb,CAAC;QAED,0BAA0B;QAC1B,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;YACnB,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,EAAE,CAAC;gBACrC,OAAO;oBACL,KAAK;oBACL,WAAW,EAAE,EAAE;oBACf,OAAO,EAAE,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC;oBAClC,OAAO,EAAE,4CAA4C;iBACtD,CAAA;YACH,CAAC;QACH,CAAC;aAAM,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;YAC3B,IAAI,CAAC,IAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC,EAAE,CAAC;gBACtC,OAAO;oBACL,KAAK;oBACL,WAAW,EAAE,EAAE;oBACf,OAAO,EAAE,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC;oBAClC,OAAO,EAAE,6CAA6C;iBACvD,CAAA;YACH,CAAC;QACH,CAAC;aAAM,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YACzB,qCAAqC;YACrC,MAAM,KAAK,GAAG,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAA;YAC7C,MAAM,MAAM,GAAG,IAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAA;YAE/C,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC;gBACtB,OAAO;oBACL,KAAK;oBACL,WAAW,EAAE,EAAE;oBACf,OAAO,EAAE,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC;oBAClC,OAAO,EAAE,+DAA+D;iBACzE,CAAA;YACH,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAA;IACb,CAAC;IAED;;OAEG;IACK,aAAa,CAAC,KAAa;QACjC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QACpC,IAAI,CAAC,KAAK;YAAE,OAAO,EAAE,CAAA;QAErB,MAAM,OAAO,GAAiB,EAAE,CAAA;QAChC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;QAE9B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;YACjC,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAiB,CAAA;YAC9C,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;gBACnC,MAAM,GAAG,GAAG,SAAS,CAAC,MAAM,CAAC,CAAA;gBAC7B,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;oBACnB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;oBACb,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;gBACtB,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,OAAO,CAAA;IAChB,CAAC;IAED;;OAEG;IACH,YAAY;QACV,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAA;IACvC,CAAC;IAED;;OAEG;IACH,QAAQ,CAAC,KAAa;QACpB,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;IAC/B,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAA;IACrB,CAAC;CACF"}