@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.
Files changed (104) hide show
  1. package/dist/connection.d.ts.map +1 -1
  2. package/dist/connection.js +15 -2
  3. package/dist/connection.js.map +1 -1
  4. package/dist/connection.mjs +15 -2
  5. package/dist/connection.mjs.map +1 -1
  6. package/dist/dialects/sqlite/index.js +3 -3
  7. package/dist/dialects/sqlite/index.js.map +1 -1
  8. package/dist/dialects/sqlite/index.mjs +3 -3
  9. package/dist/dialects/sqlite/index.mjs.map +1 -1
  10. package/dist/entity-manager/regular-relations.js +1 -1
  11. package/dist/entity-manager/regular-relations.js.map +1 -1
  12. package/dist/entity-manager/regular-relations.mjs +1 -1
  13. package/dist/entity-manager/regular-relations.mjs.map +1 -1
  14. package/dist/index.d.ts +25 -0
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +55 -13
  17. package/dist/index.js.map +1 -1
  18. package/dist/index.mjs +44 -3
  19. package/dist/index.mjs.map +1 -1
  20. package/dist/migrations/internal.d.ts.map +1 -1
  21. package/dist/migrations/internal.js +1 -0
  22. package/dist/migrations/internal.js.map +1 -1
  23. package/dist/migrations/internal.mjs +1 -0
  24. package/dist/migrations/internal.mjs.map +1 -1
  25. package/dist/migrations/runner.d.ts +2 -0
  26. package/dist/migrations/runner.d.ts.map +1 -1
  27. package/dist/migrations/runner.js +7 -2
  28. package/dist/migrations/runner.js.map +1 -1
  29. package/dist/migrations/runner.mjs +7 -2
  30. package/dist/migrations/runner.mjs.map +1 -1
  31. package/dist/migrations/users.d.ts.map +1 -1
  32. package/dist/migrations/users.js +1 -0
  33. package/dist/migrations/users.js.map +1 -1
  34. package/dist/migrations/users.mjs +1 -0
  35. package/dist/migrations/users.mjs.map +1 -1
  36. package/dist/pool-diagnostics/details.d.ts +58 -0
  37. package/dist/pool-diagnostics/details.d.ts.map +1 -0
  38. package/dist/pool-diagnostics/details.js +42 -0
  39. package/dist/pool-diagnostics/details.js.map +1 -0
  40. package/dist/pool-diagnostics/details.mjs +38 -0
  41. package/dist/pool-diagnostics/details.mjs.map +1 -0
  42. package/dist/pool-diagnostics/event-loop.d.ts +31 -0
  43. package/dist/pool-diagnostics/event-loop.d.ts.map +1 -0
  44. package/dist/pool-diagnostics/event-loop.js +59 -0
  45. package/dist/pool-diagnostics/event-loop.js.map +1 -0
  46. package/dist/pool-diagnostics/event-loop.mjs +54 -0
  47. package/dist/pool-diagnostics/event-loop.mjs.map +1 -0
  48. package/dist/pool-diagnostics/format.d.ts +9 -0
  49. package/dist/pool-diagnostics/format.d.ts.map +1 -0
  50. package/dist/pool-diagnostics/format.js +46 -0
  51. package/dist/pool-diagnostics/format.js.map +1 -0
  52. package/dist/pool-diagnostics/format.mjs +42 -0
  53. package/dist/pool-diagnostics/format.mjs.map +1 -0
  54. package/dist/pool-diagnostics/index.d.ts +33 -0
  55. package/dist/pool-diagnostics/index.d.ts.map +1 -0
  56. package/dist/pool-diagnostics/index.js +117 -0
  57. package/dist/pool-diagnostics/index.js.map +1 -0
  58. package/dist/pool-diagnostics/index.mjs +109 -0
  59. package/dist/pool-diagnostics/index.mjs.map +1 -0
  60. package/dist/pool-diagnostics/stack.d.ts +12 -0
  61. package/dist/pool-diagnostics/stack.d.ts.map +1 -0
  62. package/dist/pool-diagnostics/stack.js +46 -0
  63. package/dist/pool-diagnostics/stack.js.map +1 -0
  64. package/dist/pool-diagnostics/stack.mjs +44 -0
  65. package/dist/pool-diagnostics/stack.mjs.map +1 -0
  66. package/dist/query/helpers/populate/apply.d.ts.map +1 -1
  67. package/dist/query/helpers/populate/apply.js +21 -3
  68. package/dist/query/helpers/populate/apply.js.map +1 -1
  69. package/dist/query/helpers/populate/apply.mjs +21 -3
  70. package/dist/query/helpers/populate/apply.mjs.map +1 -1
  71. package/dist/query/helpers/streams/readable.d.ts +1 -1
  72. package/dist/query/helpers/streams/readable.d.ts.map +1 -1
  73. package/dist/query/helpers/streams/readable.js +2 -2
  74. package/dist/query/helpers/streams/readable.js.map +1 -1
  75. package/dist/query/helpers/streams/readable.mjs +1 -1
  76. package/dist/query/helpers/streams/readable.mjs.map +1 -1
  77. package/dist/schema/diff.d.ts.map +1 -1
  78. package/dist/schema/diff.js +3 -3
  79. package/dist/schema/diff.js.map +1 -1
  80. package/dist/schema/diff.mjs +3 -3
  81. package/dist/schema/diff.mjs.map +1 -1
  82. package/dist/schema/index.d.ts.map +1 -1
  83. package/dist/schema/index.js +20 -18
  84. package/dist/schema/index.js.map +1 -1
  85. package/dist/schema/index.mjs +20 -18
  86. package/dist/schema/index.mjs.map +1 -1
  87. package/dist/utils/knex-log.d.ts +19 -0
  88. package/dist/utils/knex-log.d.ts.map +1 -0
  89. package/dist/utils/knex-log.js +51 -0
  90. package/dist/utils/knex-log.js.map +1 -0
  91. package/dist/utils/knex-log.mjs +47 -0
  92. package/dist/utils/knex-log.mjs.map +1 -0
  93. package/dist/utils/throttle.d.ts +26 -0
  94. package/dist/utils/throttle.d.ts.map +1 -0
  95. package/dist/utils/throttle.js +34 -0
  96. package/dist/utils/throttle.js.map +1 -0
  97. package/dist/utils/throttle.mjs +32 -0
  98. package/dist/utils/throttle.mjs.map +1 -0
  99. package/dist/utils/types.d.ts.map +1 -1
  100. package/dist/utils/types.js +9 -9
  101. package/dist/utils/types.js.map +1 -1
  102. package/dist/utils/types.mjs +9 -9
  103. package/dist/utils/types.mjs.map +1 -1
  104. 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":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AA2BzC,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;AAsrBnC,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"}
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 Promise.all(Object.keys(idsByType).map(async (type)=>{
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 Promise.all(Object.keys(populate).map(populateAttribute));
679
+ await forEachPopulate(Object.keys(populate), populateAttribute);
662
680
  };
663
681
 
664
682
  module.exports = applyPopulate;