@strapi/database 5.57.0 → 5.59.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.
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +15 -2
- package/dist/connection.js.map +1 -1
- package/dist/connection.mjs +15 -2
- package/dist/connection.mjs.map +1 -1
- package/dist/dialects/sqlite/index.js +3 -3
- package/dist/dialects/sqlite/index.js.map +1 -1
- package/dist/dialects/sqlite/index.mjs +3 -3
- package/dist/dialects/sqlite/index.mjs.map +1 -1
- package/dist/entity-manager/regular-relations.js +1 -1
- package/dist/entity-manager/regular-relations.js.map +1 -1
- package/dist/entity-manager/regular-relations.mjs +1 -1
- package/dist/entity-manager/regular-relations.mjs.map +1 -1
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +55 -13
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +44 -3
- package/dist/index.mjs.map +1 -1
- package/dist/migrations/internal.d.ts.map +1 -1
- package/dist/migrations/internal.js +1 -0
- package/dist/migrations/internal.js.map +1 -1
- package/dist/migrations/internal.mjs +1 -0
- package/dist/migrations/internal.mjs.map +1 -1
- package/dist/migrations/runner.d.ts +2 -0
- package/dist/migrations/runner.d.ts.map +1 -1
- package/dist/migrations/runner.js +7 -2
- package/dist/migrations/runner.js.map +1 -1
- package/dist/migrations/runner.mjs +7 -2
- package/dist/migrations/runner.mjs.map +1 -1
- package/dist/migrations/users.d.ts.map +1 -1
- package/dist/migrations/users.js +1 -0
- package/dist/migrations/users.js.map +1 -1
- package/dist/migrations/users.mjs +1 -0
- package/dist/migrations/users.mjs.map +1 -1
- package/dist/pool-diagnostics/details.d.ts +58 -0
- package/dist/pool-diagnostics/details.d.ts.map +1 -0
- package/dist/pool-diagnostics/details.js +42 -0
- package/dist/pool-diagnostics/details.js.map +1 -0
- package/dist/pool-diagnostics/details.mjs +38 -0
- package/dist/pool-diagnostics/details.mjs.map +1 -0
- package/dist/pool-diagnostics/event-loop.d.ts +31 -0
- package/dist/pool-diagnostics/event-loop.d.ts.map +1 -0
- package/dist/pool-diagnostics/event-loop.js +59 -0
- package/dist/pool-diagnostics/event-loop.js.map +1 -0
- package/dist/pool-diagnostics/event-loop.mjs +54 -0
- package/dist/pool-diagnostics/event-loop.mjs.map +1 -0
- package/dist/pool-diagnostics/format.d.ts +9 -0
- package/dist/pool-diagnostics/format.d.ts.map +1 -0
- package/dist/pool-diagnostics/format.js +46 -0
- package/dist/pool-diagnostics/format.js.map +1 -0
- package/dist/pool-diagnostics/format.mjs +42 -0
- package/dist/pool-diagnostics/format.mjs.map +1 -0
- package/dist/pool-diagnostics/index.d.ts +33 -0
- package/dist/pool-diagnostics/index.d.ts.map +1 -0
- package/dist/pool-diagnostics/index.js +117 -0
- package/dist/pool-diagnostics/index.js.map +1 -0
- package/dist/pool-diagnostics/index.mjs +109 -0
- package/dist/pool-diagnostics/index.mjs.map +1 -0
- package/dist/pool-diagnostics/stack.d.ts +12 -0
- package/dist/pool-diagnostics/stack.d.ts.map +1 -0
- package/dist/pool-diagnostics/stack.js +46 -0
- package/dist/pool-diagnostics/stack.js.map +1 -0
- package/dist/pool-diagnostics/stack.mjs +44 -0
- package/dist/pool-diagnostics/stack.mjs.map +1 -0
- package/dist/query/helpers/populate/apply.d.ts.map +1 -1
- package/dist/query/helpers/populate/apply.js +21 -3
- package/dist/query/helpers/populate/apply.js.map +1 -1
- package/dist/query/helpers/populate/apply.mjs +21 -3
- package/dist/query/helpers/populate/apply.mjs.map +1 -1
- package/dist/query/helpers/streams/readable.d.ts +1 -1
- package/dist/query/helpers/streams/readable.d.ts.map +1 -1
- package/dist/query/helpers/streams/readable.js +2 -2
- package/dist/query/helpers/streams/readable.js.map +1 -1
- package/dist/query/helpers/streams/readable.mjs +1 -1
- package/dist/query/helpers/streams/readable.mjs.map +1 -1
- package/dist/schema/diff.d.ts.map +1 -1
- package/dist/schema/diff.js +3 -3
- package/dist/schema/diff.js.map +1 -1
- package/dist/schema/diff.mjs +3 -3
- package/dist/schema/diff.mjs.map +1 -1
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +20 -18
- package/dist/schema/index.js.map +1 -1
- package/dist/schema/index.mjs +20 -18
- package/dist/schema/index.mjs.map +1 -1
- package/dist/utils/knex-log.d.ts +19 -0
- package/dist/utils/knex-log.d.ts.map +1 -0
- package/dist/utils/knex-log.js +51 -0
- package/dist/utils/knex-log.js.map +1 -0
- package/dist/utils/knex-log.mjs +47 -0
- package/dist/utils/knex-log.mjs.map +1 -0
- package/dist/utils/throttle.d.ts +26 -0
- package/dist/utils/throttle.d.ts.map +1 -0
- package/dist/utils/throttle.js +34 -0
- package/dist/utils/throttle.js.map +1 -0
- package/dist/utils/throttle.mjs +32 -0
- package/dist/utils/throttle.mjs.map +1 -0
- package/dist/utils/types.d.ts.map +1 -1
- package/dist/utils/types.js +9 -9
- package/dist/utils/types.js.map +1 -1
- package/dist/utils/types.mjs +9 -9
- package/dist/utils/types.mjs.map +1 -1
- package/package.json +7 -7
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var os = require('node:os');
|
|
4
|
+
var node_perf_hooks = require('node:perf_hooks');
|
|
5
|
+
var throttle = require('../utils/throttle.js');
|
|
6
|
+
var details = require('./details.js');
|
|
7
|
+
var eventLoop = require('./event-loop.js');
|
|
8
|
+
var format = require('./format.js');
|
|
9
|
+
var stack = require('./stack.js');
|
|
10
|
+
|
|
11
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
|
+
|
|
13
|
+
var os__default = /*#__PURE__*/_interopDefault(os);
|
|
14
|
+
|
|
15
|
+
const ACQUIRE_TIMEOUT_PREFIX = 'Knex: Timeout acquiring a connection';
|
|
16
|
+
const WARNING_INTERVAL_MS = 30000;
|
|
17
|
+
/** knex also uses KnexTimeoutError for query timeouts; only pool acquire timeouts are described. */ const isPoolAcquireTimeout = (error)=>error instanceof Error && error.name === 'KnexTimeoutError' && error.message.startsWith(ACQUIRE_TIMEOUT_PREFIX);
|
|
18
|
+
/** An earlier install (for example another copy of this package) already described this error. */ const isDescribed = (error)=>error.details?.code === details.POOL_TIMEOUT_CODE;
|
|
19
|
+
const replaceMessage = (error, message)=>{
|
|
20
|
+
const previous = error.message;
|
|
21
|
+
error.message = message;
|
|
22
|
+
// V8 builds `stack` lazily; once something has read it, its first line keeps the old text
|
|
23
|
+
if (typeof error.stack === 'string' && !error.stack.includes(message)) {
|
|
24
|
+
error.stack = error.stack.replace(previous, ()=>message);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Wraps acquireConnection on the knex client Strapi created. On a pool acquire timeout, the same
|
|
29
|
+
* error object gets a deeper stack, a docs link and `details`; at most one warning block per
|
|
30
|
+
* interval goes to the logger. Facts are collected before the error is changed, so a failure while
|
|
31
|
+
* collecting leaves the error's class, name and message as knex made them (its stack may already be
|
|
32
|
+
* deeper); a failing logger only loses the warning. An error that already carries `details` from
|
|
33
|
+
* an earlier install is passed through.
|
|
34
|
+
*/ const installPoolDiagnostics = (knex, options)=>{
|
|
35
|
+
const client = knex.client;
|
|
36
|
+
// Monotonic: a wall clock step must not distort waitedMs or the throttle
|
|
37
|
+
const now = options.now ?? (()=>node_perf_hooks.performance.now());
|
|
38
|
+
const hostname = options.hostname ?? os__default.default.hostname();
|
|
39
|
+
const pid = options.pid ?? process.pid;
|
|
40
|
+
const throttle$1 = throttle.createThrottle({
|
|
41
|
+
intervalMs: options.intervalMs ?? WARNING_INTERVAL_MS,
|
|
42
|
+
now
|
|
43
|
+
});
|
|
44
|
+
const eventLoop$1 = options.eventLoopMonitor === undefined ? eventLoop.createEventLoopMonitor({
|
|
45
|
+
now
|
|
46
|
+
}) : options.eventLoopMonitor;
|
|
47
|
+
const safePhase = ()=>{
|
|
48
|
+
try {
|
|
49
|
+
return options.getPhase?.();
|
|
50
|
+
} catch {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const describeTimeout = (error, waitedMs)=>{
|
|
55
|
+
try {
|
|
56
|
+
const details$1 = details.collectPoolTimeoutDetails(client, {
|
|
57
|
+
waitedMs,
|
|
58
|
+
cause: error.cause,
|
|
59
|
+
eventLoopDelay: eventLoop$1?.read(),
|
|
60
|
+
phase: safePhase(),
|
|
61
|
+
hostname,
|
|
62
|
+
pid
|
|
63
|
+
});
|
|
64
|
+
const message = format.appendDocsLink(error.message);
|
|
65
|
+
const { emit, suppressed } = throttle$1.take();
|
|
66
|
+
const warning = emit ? format.formatPoolTimeoutWarning(details$1, {
|
|
67
|
+
suppressed
|
|
68
|
+
}) : undefined;
|
|
69
|
+
// Everything that can throw has run; only now change the error
|
|
70
|
+
replaceMessage(error, message);
|
|
71
|
+
Object.assign(error, {
|
|
72
|
+
details: details$1
|
|
73
|
+
});
|
|
74
|
+
if (warning) {
|
|
75
|
+
options.logger.warn(warning);
|
|
76
|
+
}
|
|
77
|
+
} catch {
|
|
78
|
+
// Diagnostics never change the failure itself
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
const hadOwn = Object.prototype.hasOwnProperty.call(client, 'acquireConnection');
|
|
82
|
+
const original = client.acquireConnection;
|
|
83
|
+
client.acquireConnection = async function acquireConnectionWithDiagnostics() {
|
|
84
|
+
const startedAt = now();
|
|
85
|
+
try {
|
|
86
|
+
return await original.call(this);
|
|
87
|
+
} catch (error) {
|
|
88
|
+
// An outer second install must not overwrite `details` or log a second block
|
|
89
|
+
if (isPoolAcquireTimeout(error) && !isDescribed(error)) {
|
|
90
|
+
try {
|
|
91
|
+
// Called here so this function is the first re-captured frame
|
|
92
|
+
stack.deepenStack(error);
|
|
93
|
+
} catch {
|
|
94
|
+
// keep knex's stack
|
|
95
|
+
}
|
|
96
|
+
describeTimeout(error, now() - startedAt);
|
|
97
|
+
}
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
return {
|
|
102
|
+
dispose () {
|
|
103
|
+
if (hadOwn) {
|
|
104
|
+
client.acquireConnection = original;
|
|
105
|
+
} else {
|
|
106
|
+
delete client.acquireConnection;
|
|
107
|
+
}
|
|
108
|
+
eventLoop$1?.stop();
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
exports.POOL_TIMEOUT_CODE = details.POOL_TIMEOUT_CODE;
|
|
114
|
+
exports.POOL_TIMEOUT_DOCS_URL = format.POOL_TIMEOUT_DOCS_URL;
|
|
115
|
+
exports.installPoolDiagnostics = installPoolDiagnostics;
|
|
116
|
+
exports.isPoolAcquireTimeout = isPoolAcquireTimeout;
|
|
117
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/pool-diagnostics/index.ts"],"sourcesContent":["import os from 'node:os';\nimport { performance } from 'node:perf_hooks';\nimport type { Knex } from 'knex';\n\nimport { createThrottle } from '../utils/throttle';\nimport { collectPoolTimeoutDetails, POOL_TIMEOUT_CODE, type KnexClientLike } from './details';\nimport { createEventLoopMonitor, type EventLoopMonitor } from './event-loop';\nimport { appendDocsLink, formatPoolTimeoutWarning } from './format';\nimport { deepenStack } from './stack';\n\nexport { POOL_TIMEOUT_CODE } from './details';\nexport type { PoolState, PoolTimeoutDetails } from './details';\nexport type { EventLoopDelay } from './event-loop';\nexport { POOL_TIMEOUT_DOCS_URL } from './format';\n\nconst ACQUIRE_TIMEOUT_PREFIX = 'Knex: Timeout acquiring a connection';\nconst WARNING_INTERVAL_MS = 30_000;\n\ninterface DiagnosableClient extends KnexClientLike {\n acquireConnection(): Promise<unknown>;\n}\n\nexport interface PoolDiagnosticsOptions {\n logger: { warn(message: string): void };\n getPhase?: () => string | undefined;\n now?: () => number;\n hostname?: string;\n pid?: number;\n intervalMs?: number;\n /** Injected in tests; null turns the event loop measurement off. */\n eventLoopMonitor?: EventLoopMonitor | null;\n}\n\nexport interface PoolDiagnostics {\n dispose(): void;\n}\n\n/** knex also uses KnexTimeoutError for query timeouts; only pool acquire timeouts are described. */\nexport const isPoolAcquireTimeout = (error: unknown): error is Error =>\n error instanceof Error &&\n error.name === 'KnexTimeoutError' &&\n error.message.startsWith(ACQUIRE_TIMEOUT_PREFIX);\n\n/** An earlier install (for example another copy of this package) already described this error. */\nconst isDescribed = (error: Error) =>\n (error as { details?: { code?: unknown } | null }).details?.code === POOL_TIMEOUT_CODE;\n\nconst replaceMessage = (error: Error, message: string) => {\n const previous = error.message;\n error.message = message;\n\n // V8 builds `stack` lazily; once something has read it, its first line keeps the old text\n if (typeof error.stack === 'string' && !error.stack.includes(message)) {\n error.stack = error.stack.replace(previous, () => message);\n }\n};\n\n/**\n * Wraps acquireConnection on the knex client Strapi created. On a pool acquire timeout, the same\n * error object gets a deeper stack, a docs link and `details`; at most one warning block per\n * interval goes to the logger. Facts are collected before the error is changed, so a failure while\n * collecting leaves the error's class, name and message as knex made them (its stack may already be\n * deeper); a failing logger only loses the warning. An error that already carries `details` from\n * an earlier install is passed through.\n */\nexport const installPoolDiagnostics = (\n knex: Knex,\n options: PoolDiagnosticsOptions\n): PoolDiagnostics => {\n const client = knex.client as unknown as DiagnosableClient;\n // Monotonic: a wall clock step must not distort waitedMs or the throttle\n const now = options.now ?? (() => performance.now());\n const hostname = options.hostname ?? os.hostname();\n const pid = options.pid ?? process.pid;\n const throttle = createThrottle({ intervalMs: options.intervalMs ?? WARNING_INTERVAL_MS, now });\n const eventLoop =\n options.eventLoopMonitor === undefined\n ? createEventLoopMonitor({ now })\n : options.eventLoopMonitor;\n\n const safePhase = () => {\n try {\n return options.getPhase?.();\n } catch {\n return undefined;\n }\n };\n\n const describeTimeout = (error: Error, waitedMs: number) => {\n try {\n const details = collectPoolTimeoutDetails(client, {\n waitedMs,\n cause: (error as { cause?: unknown }).cause,\n eventLoopDelay: eventLoop?.read(),\n phase: safePhase(),\n hostname,\n pid,\n });\n const message = appendDocsLink(error.message);\n const { emit, suppressed } = throttle.take();\n const warning = emit ? formatPoolTimeoutWarning(details, { suppressed }) : undefined;\n\n // Everything that can throw has run; only now change the error\n replaceMessage(error, message);\n Object.assign(error, { details });\n\n if (warning) {\n options.logger.warn(warning);\n }\n } catch {\n // Diagnostics never change the failure itself\n }\n };\n\n const hadOwn = Object.prototype.hasOwnProperty.call(client, 'acquireConnection');\n const original = client.acquireConnection;\n\n client.acquireConnection = async function acquireConnectionWithDiagnostics(\n this: DiagnosableClient\n ) {\n const startedAt = now();\n\n try {\n return await original.call(this);\n } catch (error) {\n // An outer second install must not overwrite `details` or log a second block\n if (isPoolAcquireTimeout(error) && !isDescribed(error)) {\n try {\n // Called here so this function is the first re-captured frame\n deepenStack(error);\n } catch {\n // keep knex's stack\n }\n describeTimeout(error, now() - startedAt);\n }\n\n throw error;\n }\n };\n\n return {\n dispose() {\n if (hadOwn) {\n client.acquireConnection = original;\n } else {\n delete (client as Partial<DiagnosableClient>).acquireConnection;\n }\n eventLoop?.stop();\n },\n };\n};\n"],"names":["ACQUIRE_TIMEOUT_PREFIX","WARNING_INTERVAL_MS","isPoolAcquireTimeout","error","Error","name","message","startsWith","isDescribed","details","code","POOL_TIMEOUT_CODE","replaceMessage","previous","stack","includes","replace","installPoolDiagnostics","knex","options","client","now","performance","hostname","os","pid","process","throttle","createThrottle","intervalMs","eventLoop","eventLoopMonitor","undefined","createEventLoopMonitor","safePhase","getPhase","describeTimeout","waitedMs","collectPoolTimeoutDetails","cause","eventLoopDelay","read","phase","appendDocsLink","emit","suppressed","take","warning","formatPoolTimeoutWarning","Object","assign","logger","warn","hadOwn","prototype","hasOwnProperty","call","original","acquireConnection","acquireConnectionWithDiagnostics","startedAt","deepenStack","dispose","stop"],"mappings":"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"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import { performance } from 'node:perf_hooks';
|
|
3
|
+
import { createThrottle } from '../utils/throttle.mjs';
|
|
4
|
+
import { POOL_TIMEOUT_CODE, collectPoolTimeoutDetails } from './details.mjs';
|
|
5
|
+
import { createEventLoopMonitor } from './event-loop.mjs';
|
|
6
|
+
import { appendDocsLink, formatPoolTimeoutWarning } from './format.mjs';
|
|
7
|
+
export { POOL_TIMEOUT_DOCS_URL } from './format.mjs';
|
|
8
|
+
import { deepenStack } from './stack.mjs';
|
|
9
|
+
|
|
10
|
+
const ACQUIRE_TIMEOUT_PREFIX = 'Knex: Timeout acquiring a connection';
|
|
11
|
+
const WARNING_INTERVAL_MS = 30000;
|
|
12
|
+
/** knex also uses KnexTimeoutError for query timeouts; only pool acquire timeouts are described. */ const isPoolAcquireTimeout = (error)=>error instanceof Error && error.name === 'KnexTimeoutError' && error.message.startsWith(ACQUIRE_TIMEOUT_PREFIX);
|
|
13
|
+
/** An earlier install (for example another copy of this package) already described this error. */ const isDescribed = (error)=>error.details?.code === POOL_TIMEOUT_CODE;
|
|
14
|
+
const replaceMessage = (error, message)=>{
|
|
15
|
+
const previous = error.message;
|
|
16
|
+
error.message = message;
|
|
17
|
+
// V8 builds `stack` lazily; once something has read it, its first line keeps the old text
|
|
18
|
+
if (typeof error.stack === 'string' && !error.stack.includes(message)) {
|
|
19
|
+
error.stack = error.stack.replace(previous, ()=>message);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Wraps acquireConnection on the knex client Strapi created. On a pool acquire timeout, the same
|
|
24
|
+
* error object gets a deeper stack, a docs link and `details`; at most one warning block per
|
|
25
|
+
* interval goes to the logger. Facts are collected before the error is changed, so a failure while
|
|
26
|
+
* collecting leaves the error's class, name and message as knex made them (its stack may already be
|
|
27
|
+
* deeper); a failing logger only loses the warning. An error that already carries `details` from
|
|
28
|
+
* an earlier install is passed through.
|
|
29
|
+
*/ const installPoolDiagnostics = (knex, options)=>{
|
|
30
|
+
const client = knex.client;
|
|
31
|
+
// Monotonic: a wall clock step must not distort waitedMs or the throttle
|
|
32
|
+
const now = options.now ?? (()=>performance.now());
|
|
33
|
+
const hostname = options.hostname ?? os.hostname();
|
|
34
|
+
const pid = options.pid ?? process.pid;
|
|
35
|
+
const throttle = createThrottle({
|
|
36
|
+
intervalMs: options.intervalMs ?? WARNING_INTERVAL_MS,
|
|
37
|
+
now
|
|
38
|
+
});
|
|
39
|
+
const eventLoop = options.eventLoopMonitor === undefined ? createEventLoopMonitor({
|
|
40
|
+
now
|
|
41
|
+
}) : options.eventLoopMonitor;
|
|
42
|
+
const safePhase = ()=>{
|
|
43
|
+
try {
|
|
44
|
+
return options.getPhase?.();
|
|
45
|
+
} catch {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
const describeTimeout = (error, waitedMs)=>{
|
|
50
|
+
try {
|
|
51
|
+
const details = collectPoolTimeoutDetails(client, {
|
|
52
|
+
waitedMs,
|
|
53
|
+
cause: error.cause,
|
|
54
|
+
eventLoopDelay: eventLoop?.read(),
|
|
55
|
+
phase: safePhase(),
|
|
56
|
+
hostname,
|
|
57
|
+
pid
|
|
58
|
+
});
|
|
59
|
+
const message = appendDocsLink(error.message);
|
|
60
|
+
const { emit, suppressed } = throttle.take();
|
|
61
|
+
const warning = emit ? formatPoolTimeoutWarning(details, {
|
|
62
|
+
suppressed
|
|
63
|
+
}) : undefined;
|
|
64
|
+
// Everything that can throw has run; only now change the error
|
|
65
|
+
replaceMessage(error, message);
|
|
66
|
+
Object.assign(error, {
|
|
67
|
+
details
|
|
68
|
+
});
|
|
69
|
+
if (warning) {
|
|
70
|
+
options.logger.warn(warning);
|
|
71
|
+
}
|
|
72
|
+
} catch {
|
|
73
|
+
// Diagnostics never change the failure itself
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
const hadOwn = Object.prototype.hasOwnProperty.call(client, 'acquireConnection');
|
|
77
|
+
const original = client.acquireConnection;
|
|
78
|
+
client.acquireConnection = async function acquireConnectionWithDiagnostics() {
|
|
79
|
+
const startedAt = now();
|
|
80
|
+
try {
|
|
81
|
+
return await original.call(this);
|
|
82
|
+
} catch (error) {
|
|
83
|
+
// An outer second install must not overwrite `details` or log a second block
|
|
84
|
+
if (isPoolAcquireTimeout(error) && !isDescribed(error)) {
|
|
85
|
+
try {
|
|
86
|
+
// Called here so this function is the first re-captured frame
|
|
87
|
+
deepenStack(error);
|
|
88
|
+
} catch {
|
|
89
|
+
// keep knex's stack
|
|
90
|
+
}
|
|
91
|
+
describeTimeout(error, now() - startedAt);
|
|
92
|
+
}
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
return {
|
|
97
|
+
dispose () {
|
|
98
|
+
if (hadOwn) {
|
|
99
|
+
client.acquireConnection = original;
|
|
100
|
+
} else {
|
|
101
|
+
delete client.acquireConnection;
|
|
102
|
+
}
|
|
103
|
+
eventLoop?.stop();
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export { POOL_TIMEOUT_CODE, installPoolDiagnostics, isPoolAcquireTimeout };
|
|
109
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../../src/pool-diagnostics/index.ts"],"sourcesContent":["import os from 'node:os';\nimport { performance } from 'node:perf_hooks';\nimport type { Knex } from 'knex';\n\nimport { createThrottle } from '../utils/throttle';\nimport { collectPoolTimeoutDetails, POOL_TIMEOUT_CODE, type KnexClientLike } from './details';\nimport { createEventLoopMonitor, type EventLoopMonitor } from './event-loop';\nimport { appendDocsLink, formatPoolTimeoutWarning } from './format';\nimport { deepenStack } from './stack';\n\nexport { POOL_TIMEOUT_CODE } from './details';\nexport type { PoolState, PoolTimeoutDetails } from './details';\nexport type { EventLoopDelay } from './event-loop';\nexport { POOL_TIMEOUT_DOCS_URL } from './format';\n\nconst ACQUIRE_TIMEOUT_PREFIX = 'Knex: Timeout acquiring a connection';\nconst WARNING_INTERVAL_MS = 30_000;\n\ninterface DiagnosableClient extends KnexClientLike {\n acquireConnection(): Promise<unknown>;\n}\n\nexport interface PoolDiagnosticsOptions {\n logger: { warn(message: string): void };\n getPhase?: () => string | undefined;\n now?: () => number;\n hostname?: string;\n pid?: number;\n intervalMs?: number;\n /** Injected in tests; null turns the event loop measurement off. */\n eventLoopMonitor?: EventLoopMonitor | null;\n}\n\nexport interface PoolDiagnostics {\n dispose(): void;\n}\n\n/** knex also uses KnexTimeoutError for query timeouts; only pool acquire timeouts are described. */\nexport const isPoolAcquireTimeout = (error: unknown): error is Error =>\n error instanceof Error &&\n error.name === 'KnexTimeoutError' &&\n error.message.startsWith(ACQUIRE_TIMEOUT_PREFIX);\n\n/** An earlier install (for example another copy of this package) already described this error. */\nconst isDescribed = (error: Error) =>\n (error as { details?: { code?: unknown } | null }).details?.code === POOL_TIMEOUT_CODE;\n\nconst replaceMessage = (error: Error, message: string) => {\n const previous = error.message;\n error.message = message;\n\n // V8 builds `stack` lazily; once something has read it, its first line keeps the old text\n if (typeof error.stack === 'string' && !error.stack.includes(message)) {\n error.stack = error.stack.replace(previous, () => message);\n }\n};\n\n/**\n * Wraps acquireConnection on the knex client Strapi created. On a pool acquire timeout, the same\n * error object gets a deeper stack, a docs link and `details`; at most one warning block per\n * interval goes to the logger. Facts are collected before the error is changed, so a failure while\n * collecting leaves the error's class, name and message as knex made them (its stack may already be\n * deeper); a failing logger only loses the warning. An error that already carries `details` from\n * an earlier install is passed through.\n */\nexport const installPoolDiagnostics = (\n knex: Knex,\n options: PoolDiagnosticsOptions\n): PoolDiagnostics => {\n const client = knex.client as unknown as DiagnosableClient;\n // Monotonic: a wall clock step must not distort waitedMs or the throttle\n const now = options.now ?? (() => performance.now());\n const hostname = options.hostname ?? os.hostname();\n const pid = options.pid ?? process.pid;\n const throttle = createThrottle({ intervalMs: options.intervalMs ?? WARNING_INTERVAL_MS, now });\n const eventLoop =\n options.eventLoopMonitor === undefined\n ? createEventLoopMonitor({ now })\n : options.eventLoopMonitor;\n\n const safePhase = () => {\n try {\n return options.getPhase?.();\n } catch {\n return undefined;\n }\n };\n\n const describeTimeout = (error: Error, waitedMs: number) => {\n try {\n const details = collectPoolTimeoutDetails(client, {\n waitedMs,\n cause: (error as { cause?: unknown }).cause,\n eventLoopDelay: eventLoop?.read(),\n phase: safePhase(),\n hostname,\n pid,\n });\n const message = appendDocsLink(error.message);\n const { emit, suppressed } = throttle.take();\n const warning = emit ? formatPoolTimeoutWarning(details, { suppressed }) : undefined;\n\n // Everything that can throw has run; only now change the error\n replaceMessage(error, message);\n Object.assign(error, { details });\n\n if (warning) {\n options.logger.warn(warning);\n }\n } catch {\n // Diagnostics never change the failure itself\n }\n };\n\n const hadOwn = Object.prototype.hasOwnProperty.call(client, 'acquireConnection');\n const original = client.acquireConnection;\n\n client.acquireConnection = async function acquireConnectionWithDiagnostics(\n this: DiagnosableClient\n ) {\n const startedAt = now();\n\n try {\n return await original.call(this);\n } catch (error) {\n // An outer second install must not overwrite `details` or log a second block\n if (isPoolAcquireTimeout(error) && !isDescribed(error)) {\n try {\n // Called here so this function is the first re-captured frame\n deepenStack(error);\n } catch {\n // keep knex's stack\n }\n describeTimeout(error, now() - startedAt);\n }\n\n throw error;\n }\n };\n\n return {\n dispose() {\n if (hadOwn) {\n client.acquireConnection = original;\n } else {\n delete (client as Partial<DiagnosableClient>).acquireConnection;\n }\n eventLoop?.stop();\n },\n };\n};\n"],"names":["ACQUIRE_TIMEOUT_PREFIX","WARNING_INTERVAL_MS","isPoolAcquireTimeout","error","Error","name","message","startsWith","isDescribed","details","code","POOL_TIMEOUT_CODE","replaceMessage","previous","stack","includes","replace","installPoolDiagnostics","knex","options","client","now","performance","hostname","os","pid","process","throttle","createThrottle","intervalMs","eventLoop","eventLoopMonitor","undefined","createEventLoopMonitor","safePhase","getPhase","describeTimeout","waitedMs","collectPoolTimeoutDetails","cause","eventLoopDelay","read","phase","appendDocsLink","emit","suppressed","take","warning","formatPoolTimeoutWarning","Object","assign","logger","warn","hadOwn","prototype","hasOwnProperty","call","original","acquireConnection","acquireConnectionWithDiagnostics","startedAt","deepenStack","dispose","stop"],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* knex creates the timeout error with Node's default of 10 frames, which on the query path ends
|
|
3
|
+
* inside the document service or a lifecycle hook. The caller of this function is awaited by the
|
|
4
|
+
* same async chain, so capture that chain again with a higher limit (only on this failure path) and
|
|
5
|
+
* keep knex's header and first frame. The error object itself is unchanged.
|
|
6
|
+
*
|
|
7
|
+
* A deeper stack is a convenience and must never replace the error being reported, so this leaves
|
|
8
|
+
* the stack alone when stacks are disabled or when another library (APM, source maps) installed a
|
|
9
|
+
* `prepareStackTrace` that returns something other than a string.
|
|
10
|
+
*/
|
|
11
|
+
export declare const deepenStack: (error: Error) => void;
|
|
12
|
+
//# sourceMappingURL=stack.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stack.d.ts","sourceRoot":"","sources":["../../src/pool-diagnostics/stack.ts"],"names":[],"mappings":"AAGA;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW,GAAI,OAAO,KAAK,KAAG,IAgC1C,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/** Lowest `Error.stackTraceLimit` used for the one re-capture below. Node's default is 10. */ const DEEP_STACK_LIMIT = 50;
|
|
4
|
+
/**
|
|
5
|
+
* knex creates the timeout error with Node's default of 10 frames, which on the query path ends
|
|
6
|
+
* inside the document service or a lifecycle hook. The caller of this function is awaited by the
|
|
7
|
+
* same async chain, so capture that chain again with a higher limit (only on this failure path) and
|
|
8
|
+
* keep knex's header and first frame. The error object itself is unchanged.
|
|
9
|
+
*
|
|
10
|
+
* A deeper stack is a convenience and must never replace the error being reported, so this leaves
|
|
11
|
+
* the stack alone when stacks are disabled or when another library (APM, source maps) installed a
|
|
12
|
+
* `prepareStackTrace` that returns something other than a string.
|
|
13
|
+
*/ const deepenStack = (error)=>{
|
|
14
|
+
if (typeof error.stack !== 'string' || !(Error.stackTraceLimit > 0)) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
const previousLimit = Error.stackTraceLimit;
|
|
19
|
+
Error.stackTraceLimit = Math.max(previousLimit, DEEP_STACK_LIMIT);
|
|
20
|
+
let capture;
|
|
21
|
+
try {
|
|
22
|
+
capture = new Error().stack;
|
|
23
|
+
} finally{
|
|
24
|
+
Error.stackTraceLimit = previousLimit;
|
|
25
|
+
}
|
|
26
|
+
if (typeof capture !== 'string') {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const lines = error.stack.split('\n');
|
|
30
|
+
const firstFrame = lines.findIndex((line)=>line.trimStart().startsWith('at '));
|
|
31
|
+
if (firstFrame === -1) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
// Header plus the frame where knex created the error, then the re-captured chain without its
|
|
35
|
+
// "Error" line and this function's own frame
|
|
36
|
+
error.stack = [
|
|
37
|
+
...lines.slice(0, firstFrame + 1),
|
|
38
|
+
...capture.split('\n').slice(2)
|
|
39
|
+
].join('\n');
|
|
40
|
+
} catch {
|
|
41
|
+
// Keep knex's stack: a deeper stack is a convenience and must never replace the error
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
exports.deepenStack = deepenStack;
|
|
46
|
+
//# sourceMappingURL=stack.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stack.js","sources":["../../src/pool-diagnostics/stack.ts"],"sourcesContent":["/** Lowest `Error.stackTraceLimit` used for the one re-capture below. Node's default is 10. */\nconst DEEP_STACK_LIMIT = 50;\n\n/**\n * knex creates the timeout error with Node's default of 10 frames, which on the query path ends\n * inside the document service or a lifecycle hook. The caller of this function is awaited by the\n * same async chain, so capture that chain again with a higher limit (only on this failure path) and\n * keep knex's header and first frame. The error object itself is unchanged.\n *\n * A deeper stack is a convenience and must never replace the error being reported, so this leaves\n * the stack alone when stacks are disabled or when another library (APM, source maps) installed a\n * `prepareStackTrace` that returns something other than a string.\n */\nexport const deepenStack = (error: Error): void => {\n if (typeof error.stack !== 'string' || !(Error.stackTraceLimit > 0)) {\n return;\n }\n\n try {\n const previousLimit = Error.stackTraceLimit;\n Error.stackTraceLimit = Math.max(previousLimit, DEEP_STACK_LIMIT);\n\n let capture: unknown;\n try {\n capture = new Error().stack;\n } finally {\n Error.stackTraceLimit = previousLimit;\n }\n\n if (typeof capture !== 'string') {\n return;\n }\n\n const lines = error.stack.split('\\n');\n const firstFrame = lines.findIndex((line) => line.trimStart().startsWith('at '));\n if (firstFrame === -1) {\n return;\n }\n\n // Header plus the frame where knex created the error, then the re-captured chain without its\n // \"Error\" line and this function's own frame\n error.stack = [...lines.slice(0, firstFrame + 1), ...capture.split('\\n').slice(2)].join('\\n');\n } catch {\n // Keep knex's stack: a deeper stack is a convenience and must never replace the error\n }\n};\n"],"names":["DEEP_STACK_LIMIT","deepenStack","error","stack","Error","stackTraceLimit","previousLimit","Math","max","capture","lines","split","firstFrame","findIndex","line","trimStart","startsWith","slice","join"],"mappings":";;AAAA,+FACA,MAAMA,gBAAAA,GAAmB,EAAA;AAEzB;;;;;;;;;IAUO,MAAMC,WAAAA,GAAc,CAACC,KAAAA,GAAAA;IAC1B,IAAI,OAAOA,KAAAA,CAAMC,KAAK,KAAK,QAAA,IAAY,EAAEC,KAAAA,CAAMC,eAAe,GAAG,CAAA,CAAA,EAAI;AACnE,QAAA;AACF,IAAA;IAEA,IAAI;QACF,MAAMC,aAAAA,GAAgBF,MAAMC,eAAe;AAC3CD,QAAAA,KAAAA,CAAMC,eAAe,GAAGE,IAAAA,CAAKC,GAAG,CAACF,aAAAA,EAAeN,gBAAAA,CAAAA;QAEhD,IAAIS,OAAAA;QACJ,IAAI;YACFA,OAAAA,GAAU,IAAIL,QAAQD,KAAK;QAC7B,CAAA,QAAU;AACRC,YAAAA,KAAAA,CAAMC,eAAe,GAAGC,aAAAA;AAC1B,QAAA;QAEA,IAAI,OAAOG,YAAY,QAAA,EAAU;AAC/B,YAAA;AACF,QAAA;AAEA,QAAA,MAAMC,KAAAA,GAAQR,KAAAA,CAAMC,KAAK,CAACQ,KAAK,CAAC,IAAA,CAAA;QAChC,MAAMC,UAAAA,GAAaF,KAAAA,CAAMG,SAAS,CAAC,CAACC,OAASA,IAAAA,CAAKC,SAAS,EAAA,CAAGC,UAAU,CAAC,KAAA,CAAA,CAAA;QACzE,IAAIJ,UAAAA,KAAe,CAAC,CAAA,EAAG;AACrB,YAAA;AACF,QAAA;;;AAIAV,QAAAA,KAAAA,CAAMC,KAAK,GAAG;eAAIO,KAAAA,CAAMO,KAAK,CAAC,CAAA,EAAGL,UAAAA,GAAa,CAAA,CAAA;AAAOH,YAAAA,GAAAA,OAAAA,CAAQE,KAAK,CAAC,IAAA,CAAA,CAAMM,KAAK,CAAC,CAAA;AAAG,SAAA,CAACC,IAAI,CAAC,IAAA,CAAA;AAC1F,IAAA,CAAA,CAAE,OAAM;;AAER,IAAA;AACF;;;;"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** Lowest `Error.stackTraceLimit` used for the one re-capture below. Node's default is 10. */ const DEEP_STACK_LIMIT = 50;
|
|
2
|
+
/**
|
|
3
|
+
* knex creates the timeout error with Node's default of 10 frames, which on the query path ends
|
|
4
|
+
* inside the document service or a lifecycle hook. The caller of this function is awaited by the
|
|
5
|
+
* same async chain, so capture that chain again with a higher limit (only on this failure path) and
|
|
6
|
+
* keep knex's header and first frame. The error object itself is unchanged.
|
|
7
|
+
*
|
|
8
|
+
* A deeper stack is a convenience and must never replace the error being reported, so this leaves
|
|
9
|
+
* the stack alone when stacks are disabled or when another library (APM, source maps) installed a
|
|
10
|
+
* `prepareStackTrace` that returns something other than a string.
|
|
11
|
+
*/ const deepenStack = (error)=>{
|
|
12
|
+
if (typeof error.stack !== 'string' || !(Error.stackTraceLimit > 0)) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const previousLimit = Error.stackTraceLimit;
|
|
17
|
+
Error.stackTraceLimit = Math.max(previousLimit, DEEP_STACK_LIMIT);
|
|
18
|
+
let capture;
|
|
19
|
+
try {
|
|
20
|
+
capture = new Error().stack;
|
|
21
|
+
} finally{
|
|
22
|
+
Error.stackTraceLimit = previousLimit;
|
|
23
|
+
}
|
|
24
|
+
if (typeof capture !== 'string') {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const lines = error.stack.split('\n');
|
|
28
|
+
const firstFrame = lines.findIndex((line)=>line.trimStart().startsWith('at '));
|
|
29
|
+
if (firstFrame === -1) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
// Header plus the frame where knex created the error, then the re-captured chain without its
|
|
33
|
+
// "Error" line and this function's own frame
|
|
34
|
+
error.stack = [
|
|
35
|
+
...lines.slice(0, firstFrame + 1),
|
|
36
|
+
...capture.split('\n').slice(2)
|
|
37
|
+
].join('\n');
|
|
38
|
+
} catch {
|
|
39
|
+
// Keep knex's stack: a deeper stack is a convenience and must never replace the error
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export { deepenStack };
|
|
44
|
+
//# sourceMappingURL=stack.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stack.mjs","sources":["../../src/pool-diagnostics/stack.ts"],"sourcesContent":["/** Lowest `Error.stackTraceLimit` used for the one re-capture below. Node's default is 10. */\nconst DEEP_STACK_LIMIT = 50;\n\n/**\n * knex creates the timeout error with Node's default of 10 frames, which on the query path ends\n * inside the document service or a lifecycle hook. The caller of this function is awaited by the\n * same async chain, so capture that chain again with a higher limit (only on this failure path) and\n * keep knex's header and first frame. The error object itself is unchanged.\n *\n * A deeper stack is a convenience and must never replace the error being reported, so this leaves\n * the stack alone when stacks are disabled or when another library (APM, source maps) installed a\n * `prepareStackTrace` that returns something other than a string.\n */\nexport const deepenStack = (error: Error): void => {\n if (typeof error.stack !== 'string' || !(Error.stackTraceLimit > 0)) {\n return;\n }\n\n try {\n const previousLimit = Error.stackTraceLimit;\n Error.stackTraceLimit = Math.max(previousLimit, DEEP_STACK_LIMIT);\n\n let capture: unknown;\n try {\n capture = new Error().stack;\n } finally {\n Error.stackTraceLimit = previousLimit;\n }\n\n if (typeof capture !== 'string') {\n return;\n }\n\n const lines = error.stack.split('\\n');\n const firstFrame = lines.findIndex((line) => line.trimStart().startsWith('at '));\n if (firstFrame === -1) {\n return;\n }\n\n // Header plus the frame where knex created the error, then the re-captured chain without its\n // \"Error\" line and this function's own frame\n error.stack = [...lines.slice(0, firstFrame + 1), ...capture.split('\\n').slice(2)].join('\\n');\n } catch {\n // Keep knex's stack: a deeper stack is a convenience and must never replace the error\n }\n};\n"],"names":["DEEP_STACK_LIMIT","deepenStack","error","stack","Error","stackTraceLimit","previousLimit","Math","max","capture","lines","split","firstFrame","findIndex","line","trimStart","startsWith","slice","join"],"mappings":"AAAA,+FACA,MAAMA,gBAAAA,GAAmB,EAAA;AAEzB;;;;;;;;;IAUO,MAAMC,WAAAA,GAAc,CAACC,KAAAA,GAAAA;IAC1B,IAAI,OAAOA,KAAAA,CAAMC,KAAK,KAAK,QAAA,IAAY,EAAEC,KAAAA,CAAMC,eAAe,GAAG,CAAA,CAAA,EAAI;AACnE,QAAA;AACF,IAAA;IAEA,IAAI;QACF,MAAMC,aAAAA,GAAgBF,MAAMC,eAAe;AAC3CD,QAAAA,KAAAA,CAAMC,eAAe,GAAGE,IAAAA,CAAKC,GAAG,CAACF,aAAAA,EAAeN,gBAAAA,CAAAA;QAEhD,IAAIS,OAAAA;QACJ,IAAI;YACFA,OAAAA,GAAU,IAAIL,QAAQD,KAAK;QAC7B,CAAA,QAAU;AACRC,YAAAA,KAAAA,CAAMC,eAAe,GAAGC,aAAAA;AAC1B,QAAA;QAEA,IAAI,OAAOG,YAAY,QAAA,EAAU;AAC/B,YAAA;AACF,QAAA;AAEA,QAAA,MAAMC,KAAAA,GAAQR,KAAAA,CAAMC,KAAK,CAACQ,KAAK,CAAC,IAAA,CAAA;QAChC,MAAMC,UAAAA,GAAaF,KAAAA,CAAMG,SAAS,CAAC,CAACC,OAASA,IAAAA,CAAKC,SAAS,EAAA,CAAGC,UAAU,CAAC,KAAA,CAAA,CAAA;QACzE,IAAIJ,UAAAA,KAAe,CAAC,CAAA,EAAG;AACrB,YAAA;AACF,QAAA;;;AAIAV,QAAAA,KAAAA,CAAMC,KAAK,GAAG;eAAIO,KAAAA,CAAMO,KAAK,CAAC,CAAA,EAAGL,UAAAA,GAAa,CAAA,CAAA;AAAOH,YAAAA,GAAAA,OAAAA,CAAQE,KAAK,CAAC,IAAA,CAAA,CAAMM,KAAK,CAAC,CAAA;AAAG,SAAA,CAACC,IAAI,CAAC,IAAA,CAAA;AAC1F,IAAA,CAAA,CAAE,OAAM;;AAER,IAAA;AACF;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../../../src/query/helpers/populate/apply.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../../../src/query/helpers/populate/apply.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAgDzC,KAAK,OAAO,GAAG;IACb,EAAE,EAAE,QAAQ,CAAC;IACb,EAAE,EAAE,YAAY,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAoBF,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAorBnC,QAAA,MAAM,aAAa,GAAU,SAAS,GAAG,EAAE,EAAE,UAAU,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,OAAO,+BA+DvF,CAAC;AAEF,eAAe,aAAa,CAAC"}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
var _ = require('lodash');
|
|
4
4
|
var utils = require('@strapi/utils');
|
|
5
5
|
var transform = require('../transform.js');
|
|
6
|
+
var transactionContext = require('../../../transaction-context.js');
|
|
6
7
|
|
|
7
8
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
8
9
|
|
|
@@ -12,6 +13,23 @@ var ___default = /*#__PURE__*/_interopDefault(_);
|
|
|
12
13
|
// Therefore, we will prefix with something unlikely to conflict with a user attribute
|
|
13
14
|
// TODO: ...and completely restrict the strapi_ prefix for an attribute name in the future
|
|
14
15
|
const joinColPrefix = '__strapi';
|
|
16
|
+
/**
|
|
17
|
+
* Runs one populate task per key.
|
|
18
|
+
*
|
|
19
|
+
* Inside a transaction every query is bound to the transaction's single connection, and
|
|
20
|
+
* node-postgres deprecated (pg@8.19) and removes (pg@9) calling `client.query()` on a client
|
|
21
|
+
* that is already executing a query. The tasks therefore run one after another there, which
|
|
22
|
+
* costs nothing: the driver was already serialising them on that one connection. Outside a
|
|
23
|
+
* transaction they fan out over the pool as before.
|
|
24
|
+
*/ const forEachPopulate = async (keys, task)=>{
|
|
25
|
+
if (transactionContext.transactionCtx.get()) {
|
|
26
|
+
for (const key of keys){
|
|
27
|
+
await task(key);
|
|
28
|
+
}
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
await Promise.all(keys.map(task));
|
|
32
|
+
};
|
|
15
33
|
/**
|
|
16
34
|
* Join-table `order` preserves connect order when no explicit populate sort is set.
|
|
17
35
|
* When `populateValue.orderBy` is present, join-table ordering must not take precedence
|
|
@@ -428,7 +446,7 @@ const morphToMany = async (input, ctx)=>{
|
|
|
428
446
|
}, {});
|
|
429
447
|
const map = {};
|
|
430
448
|
const { on, ...typePopulate } = populateValue;
|
|
431
|
-
await
|
|
449
|
+
await forEachPopulate(Object.keys(idsByType), async (type)=>{
|
|
432
450
|
const ids = idsByType[type];
|
|
433
451
|
// type was removed but still in morph relation
|
|
434
452
|
if (!db.metadata.get(type)) {
|
|
@@ -442,7 +460,7 @@ const morphToMany = async (input, ctx)=>{
|
|
|
442
460
|
mapResults: false
|
|
443
461
|
});
|
|
444
462
|
map[type] = ___default.default.groupBy(rows, idColumn.referencedColumn);
|
|
445
|
-
})
|
|
463
|
+
});
|
|
446
464
|
results.forEach((result)=>{
|
|
447
465
|
const joinResults = joinMap[result[joinColumn.referencedColumn]] || [];
|
|
448
466
|
const matchingRows = joinResults.flatMap((joinResult)=>{
|
|
@@ -658,7 +676,7 @@ const applyPopulate = async (results, populate, ctx)=>{
|
|
|
658
676
|
}
|
|
659
677
|
}
|
|
660
678
|
};
|
|
661
|
-
await
|
|
679
|
+
await forEachPopulate(Object.keys(populate), populateAttribute);
|
|
662
680
|
};
|
|
663
681
|
|
|
664
682
|
module.exports = applyPopulate;
|