@pipeline-builder/api-core 0.0.1

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 (184) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +98 -0
  3. package/lib/constants/ai-providers.d.ts +52 -0
  4. package/lib/constants/ai-providers.js +129 -0
  5. package/lib/constants/http-status.d.ts +20 -0
  6. package/lib/constants/http-status.js +26 -0
  7. package/lib/constants/index.d.ts +3 -0
  8. package/lib/constants/index.js +6 -0
  9. package/lib/constants/time.d.ts +10 -0
  10. package/lib/constants/time.js +13 -0
  11. package/lib/errors/app-errors.d.ts +30 -0
  12. package/lib/errors/app-errors.js +53 -0
  13. package/lib/errors/index.d.ts +1 -0
  14. package/lib/errors/index.js +4 -0
  15. package/lib/helpers/access-helpers.d.ts +63 -0
  16. package/lib/helpers/access-helpers.js +82 -0
  17. package/lib/helpers/crud-helpers.d.ts +9 -0
  18. package/lib/helpers/crud-helpers.js +23 -0
  19. package/lib/helpers/index.d.ts +6 -0
  20. package/lib/helpers/index.js +9 -0
  21. package/lib/helpers/org-hierarchy-http.d.ts +74 -0
  22. package/lib/helpers/org-hierarchy-http.js +144 -0
  23. package/lib/helpers/org-hierarchy.d.ts +57 -0
  24. package/lib/helpers/org-hierarchy.js +110 -0
  25. package/lib/helpers/restore-helpers.d.ts +49 -0
  26. package/lib/helpers/restore-helpers.js +78 -0
  27. package/lib/helpers/sse-helpers.d.ts +20 -0
  28. package/lib/helpers/sse-helpers.js +45 -0
  29. package/lib/index.d.ts +58 -0
  30. package/lib/index.js +71 -0
  31. package/lib/middleware/auth.d.ts +208 -0
  32. package/lib/middleware/auth.js +588 -0
  33. package/lib/middleware/index.d.ts +3 -0
  34. package/lib/middleware/index.js +6 -0
  35. package/lib/middleware/mongo-sanitize.d.ts +32 -0
  36. package/lib/middleware/mongo-sanitize.js +100 -0
  37. package/lib/middleware/step-up.d.ts +45 -0
  38. package/lib/middleware/step-up.js +138 -0
  39. package/lib/openapi/extend-zod.d.ts +1 -0
  40. package/lib/openapi/extend-zod.js +6 -0
  41. package/lib/openapi/index.d.ts +2 -0
  42. package/lib/openapi/index.js +5 -0
  43. package/lib/openapi/registry.d.ts +17 -0
  44. package/lib/openapi/registry.js +38 -0
  45. package/lib/openapi/routes/ask-routes.d.ts +7 -0
  46. package/lib/openapi/routes/ask-routes.js +62 -0
  47. package/lib/openapi/routes/billing-routes.d.ts +1 -0
  48. package/lib/openapi/routes/billing-routes.js +66 -0
  49. package/lib/openapi/routes/image-registry-routes.d.ts +7 -0
  50. package/lib/openapi/routes/image-registry-routes.js +98 -0
  51. package/lib/openapi/routes/index.d.ts +5 -0
  52. package/lib/openapi/routes/index.js +28 -0
  53. package/lib/openapi/routes/message-routes.d.ts +1 -0
  54. package/lib/openapi/routes/message-routes.js +105 -0
  55. package/lib/openapi/routes/pipeline-routes.d.ts +1 -0
  56. package/lib/openapi/routes/pipeline-routes.js +105 -0
  57. package/lib/openapi/routes/pipeline-template-routes.d.ts +1 -0
  58. package/lib/openapi/routes/pipeline-template-routes.js +61 -0
  59. package/lib/openapi/routes/plugin-routes.d.ts +1 -0
  60. package/lib/openapi/routes/plugin-routes.js +96 -0
  61. package/lib/openapi/routes/quota-routes.d.ts +1 -0
  62. package/lib/openapi/routes/quota-routes.js +62 -0
  63. package/lib/openapi/schema-registry.d.ts +25 -0
  64. package/lib/openapi/schema-registry.js +91 -0
  65. package/lib/routes/health.d.ts +41 -0
  66. package/lib/routes/health.js +82 -0
  67. package/lib/routes/index.d.ts +1 -0
  68. package/lib/routes/index.js +4 -0
  69. package/lib/services/audit-spool.d.ts +80 -0
  70. package/lib/services/audit-spool.js +172 -0
  71. package/lib/services/cache-service.d.ts +124 -0
  72. package/lib/services/cache-service.js +276 -0
  73. package/lib/services/circuit-breaker.d.ts +45 -0
  74. package/lib/services/circuit-breaker.js +138 -0
  75. package/lib/services/compliance-client.d.ts +46 -0
  76. package/lib/services/compliance-client.js +128 -0
  77. package/lib/services/compliance-event-subscriber.d.ts +16 -0
  78. package/lib/services/compliance-event-subscriber.js +72 -0
  79. package/lib/services/durable-event-bus.d.ts +96 -0
  80. package/lib/services/durable-event-bus.js +204 -0
  81. package/lib/services/entity-events.d.ts +48 -0
  82. package/lib/services/entity-events.js +79 -0
  83. package/lib/services/env-redis.d.ts +44 -0
  84. package/lib/services/env-redis.js +94 -0
  85. package/lib/services/http-client.d.ts +143 -0
  86. package/lib/services/http-client.js +335 -0
  87. package/lib/services/index.d.ts +18 -0
  88. package/lib/services/index.js +20 -0
  89. package/lib/services/leader-lock.d.ts +45 -0
  90. package/lib/services/leader-lock.js +85 -0
  91. package/lib/services/quota.d.ts +130 -0
  92. package/lib/services/quota.js +264 -0
  93. package/lib/services/remote-audit-client.d.ts +160 -0
  94. package/lib/services/remote-audit-client.js +390 -0
  95. package/lib/services/retry-strategy.d.ts +74 -0
  96. package/lib/services/retry-strategy.js +118 -0
  97. package/lib/services/scheduler.d.ts +41 -0
  98. package/lib/services/scheduler.js +86 -0
  99. package/lib/services/service-boot.d.ts +20 -0
  100. package/lib/services/service-boot.js +28 -0
  101. package/lib/services/sse-ticket-store.d.ts +51 -0
  102. package/lib/services/sse-ticket-store.js +130 -0
  103. package/lib/services/token-revocation.d.ts +52 -0
  104. package/lib/services/token-revocation.js +120 -0
  105. package/lib/testing/tier-mock.d.ts +44 -0
  106. package/lib/testing/tier-mock.js +65 -0
  107. package/lib/types/audit-events.d.ts +69 -0
  108. package/lib/types/audit-events.js +4 -0
  109. package/lib/types/catalog-metadata.d.ts +36 -0
  110. package/lib/types/catalog-metadata.js +21 -0
  111. package/lib/types/common.d.ts +255 -0
  112. package/lib/types/common.js +41 -0
  113. package/lib/types/error-codes.d.ts +54 -0
  114. package/lib/types/error-codes.js +107 -0
  115. package/lib/types/feature-flags.d.ts +36 -0
  116. package/lib/types/feature-flags.js +128 -0
  117. package/lib/types/http.d.ts +27 -0
  118. package/lib/types/http.js +4 -0
  119. package/lib/types/index.d.ts +11 -0
  120. package/lib/types/index.js +14 -0
  121. package/lib/types/permissions.d.ts +65 -0
  122. package/lib/types/permissions.js +131 -0
  123. package/lib/types/pipeline-template.d.ts +30 -0
  124. package/lib/types/pipeline-template.js +13 -0
  125. package/lib/types/pipeline.d.ts +59 -0
  126. package/lib/types/pipeline.js +31 -0
  127. package/lib/types/quota-tiers.d.ts +79 -0
  128. package/lib/types/quota-tiers.js +221 -0
  129. package/lib/types/visibility.d.ts +20 -0
  130. package/lib/types/visibility.js +22 -0
  131. package/lib/utils/alias-resolver.d.ts +37 -0
  132. package/lib/utils/alias-resolver.js +68 -0
  133. package/lib/utils/audit.d.ts +15 -0
  134. package/lib/utils/audit.js +39 -0
  135. package/lib/utils/aws-scrub.d.ts +11 -0
  136. package/lib/utils/aws-scrub.js +67 -0
  137. package/lib/utils/compliance-attributes.d.ts +6 -0
  138. package/lib/utils/compliance-attributes.js +69 -0
  139. package/lib/utils/concurrency.d.ts +9 -0
  140. package/lib/utils/concurrency.js +29 -0
  141. package/lib/utils/crash-handlers.d.ts +18 -0
  142. package/lib/utils/crash-handlers.js +36 -0
  143. package/lib/utils/env.d.ts +28 -0
  144. package/lib/utils/env.js +54 -0
  145. package/lib/utils/headers.d.ts +18 -0
  146. package/lib/utils/headers.js +21 -0
  147. package/lib/utils/identity.d.ts +40 -0
  148. package/lib/utils/identity.js +51 -0
  149. package/lib/utils/index.d.ts +17 -0
  150. package/lib/utils/index.js +20 -0
  151. package/lib/utils/logger.d.ts +38 -0
  152. package/lib/utils/logger.js +174 -0
  153. package/lib/utils/metric-emitter.d.ts +36 -0
  154. package/lib/utils/metric-emitter.js +32 -0
  155. package/lib/utils/object.d.ts +13 -0
  156. package/lib/utils/object.js +18 -0
  157. package/lib/utils/params.d.ts +150 -0
  158. package/lib/utils/params.js +206 -0
  159. package/lib/utils/response.d.ts +117 -0
  160. package/lib/utils/response.js +223 -0
  161. package/lib/utils/safe-require.d.ts +13 -0
  162. package/lib/utils/safe-require.js +19 -0
  163. package/lib/utils/secret-encryption.d.ts +176 -0
  164. package/lib/utils/secret-encryption.js +374 -0
  165. package/lib/utils/ssrf.d.ts +30 -0
  166. package/lib/utils/ssrf.js +106 -0
  167. package/lib/validation/ai-schemas.d.ts +56 -0
  168. package/lib/validation/ai-schemas.js +78 -0
  169. package/lib/validation/common-schemas.d.ts +133 -0
  170. package/lib/validation/common-schemas.js +94 -0
  171. package/lib/validation/index.d.ts +7 -0
  172. package/lib/validation/index.js +10 -0
  173. package/lib/validation/message-schemas.d.ts +160 -0
  174. package/lib/validation/message-schemas.js +156 -0
  175. package/lib/validation/middleware.d.ts +72 -0
  176. package/lib/validation/middleware.js +65 -0
  177. package/lib/validation/pipeline-schemas.d.ts +181 -0
  178. package/lib/validation/pipeline-schemas.js +101 -0
  179. package/lib/validation/pipeline-template-schemas.d.ts +155 -0
  180. package/lib/validation/pipeline-template-schemas.js +66 -0
  181. package/lib/validation/plugin-schemas.d.ts +171 -0
  182. package/lib/validation/plugin-schemas.js +81 -0
  183. package/openapi.yaml +292 -0
  184. package/package.json +167 -0
@@ -0,0 +1,206 @@
1
+ // Copyright 2026 Pipeline Builder Contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { getHeaderString } from './headers.js';
4
+ /**
5
+ * Extract a single string parameter from Express 5 route params.
6
+ *
7
+ * @param params - Request params object
8
+ * @param key - Parameter key
9
+ * @returns The parameter value as a string, or undefined if not present
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * // Route: GET /plugins/:id
14
+ * const id = getParam(req.params, 'id');
15
+ * if (!id) {
16
+ * return sendError(res, 400, 'Missing id parameter');
17
+ * }
18
+ * ```
19
+ */
20
+ export function getParam(params, key) {
21
+ const value = params[key];
22
+ if (value === undefined) {
23
+ return undefined;
24
+ }
25
+ return Array.isArray(value) ? value[0] : value;
26
+ }
27
+ /**
28
+ * Extract the organization ID from request.
29
+ * Checks params, headers, and user object.
30
+ *
31
+ * @param req - Express request object
32
+ * @returns Organization ID or undefined
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * const orgId = getOrgId(req);
37
+ * if (!orgId) {
38
+ * return sendError(res, 400, 'Organization ID required');
39
+ * }
40
+ * ```
41
+ */
42
+ export function getOrgId(req) {
43
+ // Prefer the VERIFIED JWT identity first. A spoofable `x-org-id` header must
44
+ // never override an authenticated user's org (cross-tenant footgun). For a
45
+ // superadmin acting cross-org, requireAuth already re-points
46
+ // `req.user.organizationId` to the chosen org via the isSuperAdmin-gated
47
+ // header override, so this still yields the intended scope.
48
+ const userOrgId = req.user?.organizationId?.trim();
49
+ if (userOrgId) {
50
+ return userOrgId;
51
+ }
52
+ // Pre-auth fallbacks only (req.user unset): route param, then header.
53
+ const paramOrgId = getParam(req.params, 'orgId');
54
+ if (paramOrgId) {
55
+ return paramOrgId;
56
+ }
57
+ const headerOrgId = getHeaderString(req.headers['x-org-id'])?.trim();
58
+ if (headerOrgId) {
59
+ return headerOrgId;
60
+ }
61
+ return undefined;
62
+ }
63
+ /**
64
+ * Parse a query parameter as an integer.
65
+ *
66
+ * @param value - Query parameter value
67
+ * @param defaultValue - Default value if parsing fails
68
+ * @returns Parsed integer
69
+ *
70
+ * @example
71
+ * ```typescript
72
+ * const limit = parseQueryInt(req.query.limit, 10);
73
+ * const offset = parseQueryInt(req.query.offset, 0);
74
+ * ```
75
+ */
76
+ export function parseQueryInt(value, defaultValue) {
77
+ if (value === undefined || value === null || value === '')
78
+ return defaultValue;
79
+ const parsed = parseInt(String(value), 10);
80
+ return isNaN(parsed) ? defaultValue : parsed;
81
+ }
82
+ /**
83
+ * Parse a query parameter as a string.
84
+ *
85
+ * @param value - Query parameter value
86
+ * @returns String value or undefined
87
+ *
88
+ * @example
89
+ * ```typescript
90
+ * const search = parseQueryString(req.query.search);
91
+ * ```
92
+ */
93
+ export function parseQueryString(value) {
94
+ if (value === undefined || value === null || value === '')
95
+ return undefined;
96
+ return String(value);
97
+ }
98
+ /**
99
+ * Parse a string to a positive integer, returning a fallback if invalid.
100
+ * Useful for parsing environment variables with numeric defaults.
101
+ */
102
+ export function parsePositiveInt(value, fallback) {
103
+ if (!value)
104
+ return fallback;
105
+ const parsed = parseInt(value, 10);
106
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
107
+ }
108
+ /**
109
+ * Parse a query int, then clamp to `[1, max]`. Returns `defaultValue` (after
110
+ * the same clamp) when the input is missing/invalid. Use for `limit`-style
111
+ * query parameters where an unbounded value would cost real money.
112
+ *
113
+ * @example
114
+ * const limit = parseQueryIntClamped(req.query.limit, 50, 1000);
115
+ */
116
+ export function parseQueryIntClamped(value, defaultValue, max) {
117
+ const raw = parseQueryInt(value, defaultValue);
118
+ return Math.max(1, Math.min(raw, max));
119
+ }
120
+ /**
121
+ * Bulk-array request guard. Validates that `value` is an array, non-empty,
122
+ * and within the configured cap. Returns the typed array or an error object
123
+ * the caller can forward to `sendError`/`sendBadRequest`.
124
+ *
125
+ * Replaces a hand-rolled `if (!Array.isArray(...)) { ... }` + length check
126
+ * block that was duplicated across bulk endpoints in pipeline, plugin,
127
+ * compliance, etc.
128
+ *
129
+ * @example
130
+ * const result = validateBulkArray(req.body.ids, 'ids');
131
+ * if ('error' in result) return sendBadRequest(res, result.error, ErrorCode.VALIDATION_ERROR);
132
+ * const ids = result.value;
133
+ */
134
+ export function validateBulkArray(value, fieldName, maxItems) {
135
+ if (!Array.isArray(value) || value.length === 0) {
136
+ return { error: `Request body must include a non-empty "${fieldName}" array` };
137
+ }
138
+ // CoreConstants.MAX_BULK_ITEMS isn't imported here to avoid a circular
139
+ // path through constants/index; callers pass it explicitly or omit for
140
+ // no upper bound.
141
+ if (maxItems !== undefined && value.length > maxItems) {
142
+ return { error: `Maximum ${maxItems} items per bulk operation` };
143
+ }
144
+ return { value: value };
145
+ }
146
+ /**
147
+ * Supported intervals for time-bucketed report queries. Used as the
148
+ * `DATE_TRUNC` first argument in Postgres; restricted to a fixed enum so
149
+ * the value can never be injected into raw SQL.
150
+ */
151
+ export const REPORT_INTERVALS = ['day', 'week', 'month'];
152
+ /**
153
+ * Parse + validate a `?interval=` report bucket (defaults to 'week'). Returns the
154
+ * validated interval or `{ error }`. Shared by the timeseries report routes so the
155
+ * default + allow-list + error message live in one place.
156
+ *
157
+ * @example
158
+ * const interval = parseReportInterval(req.query);
159
+ * if (typeof interval === 'object') return sendBadRequest(res, interval.error, ErrorCode.VALIDATION_ERROR);
160
+ */
161
+ export function parseReportInterval(query) {
162
+ const interval = String(query.interval ?? 'week');
163
+ if (!REPORT_INTERVALS.includes(interval)) {
164
+ return { error: `interval must be one of: ${REPORT_INTERVALS.join(', ')}` };
165
+ }
166
+ return interval;
167
+ }
168
+ /**
169
+ * Parse a `?from=&to=` date range with defaults of "30 days ago → now".
170
+ * Rejects non-string inputs (Express's parser can produce string[] for
171
+ * repeated keys), invalid ISO timestamps, and inverted ranges.
172
+ *
173
+ * Returns either `{ from, to }` ISO strings or `{ error }`.
174
+ *
175
+ * @example
176
+ * const range = parseDateRange(req.query);
177
+ * if ('error' in range) return sendBadRequest(res, range.error, ErrorCode.VALIDATION_ERROR);
178
+ * const rows = await svc.list(range.from, range.to);
179
+ */
180
+ export function parseDateRange(query, options = {}) {
181
+ const { maxRangeMs, defaultDaysBack = 30 } = options;
182
+ const rawFrom = query.from;
183
+ const rawTo = query.to;
184
+ if (rawFrom !== undefined && typeof rawFrom !== 'string') {
185
+ return { error: '"from" must be a single ISO timestamp string' };
186
+ }
187
+ if (rawTo !== undefined && typeof rawTo !== 'string') {
188
+ return { error: '"to" must be a single ISO timestamp string' };
189
+ }
190
+ const now = Date.now();
191
+ const fromStr = rawFrom ?? new Date(now - defaultDaysBack * 24 * 60 * 60 * 1000).toISOString();
192
+ const toStr = rawTo ?? new Date(now).toISOString();
193
+ const fromMs = Date.parse(fromStr);
194
+ const toMs = Date.parse(toStr);
195
+ if (!Number.isFinite(fromMs))
196
+ return { error: '"from" is not a valid ISO timestamp' };
197
+ if (!Number.isFinite(toMs))
198
+ return { error: '"to" is not a valid ISO timestamp' };
199
+ if (fromMs > toMs)
200
+ return { error: '"from" must be earlier than "to"' };
201
+ if (maxRangeMs !== undefined && toMs - fromMs > maxRangeMs) {
202
+ return { error: `Date range exceeds maximum of ${Math.floor(maxRangeMs / (24 * 60 * 60 * 1000))} days` };
203
+ }
204
+ return { from: fromStr, to: toStr };
205
+ }
206
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGFyYW1zLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3V0aWxzL3BhcmFtcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBR3RDLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFvQi9DOzs7Ozs7Ozs7Ozs7Ozs7R0FlRztBQUNILE1BQU0sVUFBVSxRQUFRLENBQ3RCLE1BQWtDLEVBQ2xDLEdBQVc7SUFFWCxNQUFNLEtBQUssR0FBRyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDMUIsSUFBSSxLQUFLLEtBQUssU0FBUyxFQUFFLENBQUM7UUFDeEIsT0FBTyxTQUFTLENBQUM7SUFDbkIsQ0FBQztJQUNELE9BQU8sS0FBSyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUM7QUFDakQsQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7OztHQWNHO0FBQ0gsTUFBTSxVQUFVLFFBQVEsQ0FBQyxHQUFZO0lBQ25DLDZFQUE2RTtJQUM3RSwyRUFBMkU7SUFDM0UsNkRBQTZEO0lBQzdELHlFQUF5RTtJQUN6RSw0REFBNEQ7SUFDNUQsTUFBTSxTQUFTLEdBQUcsR0FBRyxDQUFDLElBQUksRUFBRSxjQUFjLEVBQUUsSUFBSSxFQUFFLENBQUM7SUFDbkQsSUFBSSxTQUFTLEVBQUUsQ0FBQztRQUNkLE9BQU8sU0FBUyxDQUFDO0lBQ25CLENBQUM7SUFFRCxzRUFBc0U7SUFDdEUsTUFBTSxVQUFVLEdBQUcsUUFBUSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDakQsSUFBSSxVQUFVLEVBQUUsQ0FBQztRQUNmLE9BQU8sVUFBVSxDQUFDO0lBQ3BCLENBQUM7SUFFRCxNQUFNLFdBQVcsR0FBRyxlQUFlLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxVQUFVLENBQUMsQ0FBQyxFQUFFLElBQUksRUFBRSxDQUFDO0lBQ3JFLElBQUksV0FBVyxFQUFFLENBQUM7UUFDaEIsT0FBTyxXQUFXLENBQUM7SUFDckIsQ0FBQztJQUVELE9BQU8sU0FBUyxDQUFDO0FBQ25CLENBQUM7QUFFRDs7Ozs7Ozs7Ozs7O0dBWUc7QUFDSCxNQUFNLFVBQVUsYUFBYSxDQUFDLEtBQWMsRUFBRSxZQUFvQjtJQUNoRSxJQUFJLEtBQUssS0FBSyxTQUFTLElBQUksS0FBSyxLQUFLLElBQUksSUFBSSxLQUFLLEtBQUssRUFBRTtRQUFFLE9BQU8sWUFBWSxDQUFDO0lBQy9FLE1BQU0sTUFBTSxHQUFHLFFBQVEsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDM0MsT0FBTyxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDO0FBQy9DLENBQUM7QUFFRDs7Ozs7Ozs7OztHQVVHO0FBQ0gsTUFBTSxVQUFVLGdCQUFnQixDQUFDLEtBQWM7SUFDN0MsSUFBSSxLQUFLLEtBQUssU0FBUyxJQUFJLEtBQUssS0FBSyxJQUFJLElBQUksS0FBSyxLQUFLLEVBQUU7UUFBRSxPQUFPLFNBQVMsQ0FBQztJQUM1RSxPQUFPLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztBQUN2QixDQUFDO0FBRUQ7OztHQUdHO0FBQ0gsTUFBTSxVQUFVLGdCQUFnQixDQUFDLEtBQXlCLEVBQUUsUUFBZ0I7SUFDMUUsSUFBSSxDQUFDLEtBQUs7UUFBRSxPQUFPLFFBQVEsQ0FBQztJQUM1QixNQUFNLE1BQU0sR0FBRyxRQUFRLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBQ25DLE9BQU8sTUFBTSxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsSUFBSSxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQztBQUNuRSxDQUFDO0FBRUQ7Ozs7Ozs7R0FPRztBQUNILE1BQU0sVUFBVSxvQkFBb0IsQ0FDbEMsS0FBYyxFQUNkLFlBQW9CLEVBQ3BCLEdBQVc7SUFFWCxNQUFNLEdBQUcsR0FBRyxhQUFhLENBQUMsS0FBSyxFQUFFLFlBQVksQ0FBQyxDQUFDO0lBQy9DLE9BQU8sSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsR0FBRyxDQUFDLENBQUMsQ0FBQztBQUN6QyxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7Ozs7R0FhRztBQUNILE1BQU0sVUFBVSxpQkFBaUIsQ0FDL0IsS0FBYyxFQUNkLFNBQWlCLEVBQ2pCLFFBQWlCO0lBRWpCLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxJQUFJLEtBQUssQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7UUFDaEQsT0FBTyxFQUFFLEtBQUssRUFBRSwwQ0FBMEMsU0FBUyxTQUFTLEVBQUUsQ0FBQztJQUNqRixDQUFDO0lBQ0QsdUVBQXVFO0lBQ3ZFLHVFQUF1RTtJQUN2RSxrQkFBa0I7SUFDbEIsSUFBSSxRQUFRLEtBQUssU0FBUyxJQUFJLEtBQUssQ0FBQyxNQUFNLEdBQUcsUUFBUSxFQUFFLENBQUM7UUFDdEQsT0FBTyxFQUFFLEtBQUssRUFBRSxXQUFXLFFBQVEsMkJBQTJCLEVBQUUsQ0FBQztJQUNuRSxDQUFDO0lBQ0QsT0FBTyxFQUFFLEtBQUssRUFBRSxLQUFZLEVBQUUsQ0FBQztBQUNqQyxDQUFDO0FBRUQ7Ozs7R0FJRztBQUNILE1BQU0sQ0FBQyxNQUFNLGdCQUFnQixHQUFHLENBQUMsS0FBSyxFQUFFLE1BQU0sRUFBRSxPQUFPLENBQVUsQ0FBQztBQUdsRTs7Ozs7Ozs7R0FRRztBQUNILE1BQU0sVUFBVSxtQkFBbUIsQ0FBQyxLQUE4QjtJQUNoRSxNQUFNLFFBQVEsR0FBRyxNQUFNLENBQUMsS0FBSyxDQUFDLFFBQVEsSUFBSSxNQUFNLENBQUMsQ0FBQztJQUNsRCxJQUFJLENBQUMsZ0JBQWdCLENBQUMsUUFBUSxDQUFDLFFBQTBCLENBQUMsRUFBRSxDQUFDO1FBQzNELE9BQU8sRUFBRSxLQUFLLEVBQUUsNEJBQTRCLGdCQUFnQixDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxFQUFFLENBQUM7SUFDOUUsQ0FBQztJQUNELE9BQU8sUUFBMEIsQ0FBQztBQUNwQyxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7O0dBV0c7QUFDSCxNQUFNLFVBQVUsY0FBYyxDQUM1QixLQUE4QixFQUM5QixPQUFPLEdBQXNELEVBQUU7SUFFL0QsTUFBTSxFQUFFLFVBQVUsRUFBRSxlQUFlLEdBQUcsRUFBRSxFQUFFLEdBQUcsT0FBTyxDQUFDO0lBRXJELE1BQU0sT0FBTyxHQUFHLEtBQUssQ0FBQyxJQUFJLENBQUM7SUFDM0IsTUFBTSxLQUFLLEdBQUcsS0FBSyxDQUFDLEVBQUUsQ0FBQztJQUV2QixJQUFJLE9BQU8sS0FBSyxTQUFTLElBQUksT0FBTyxPQUFPLEtBQUssUUFBUSxFQUFFLENBQUM7UUFDekQsT0FBTyxFQUFFLEtBQUssRUFBRSw4Q0FBOEMsRUFBRSxDQUFDO0lBQ25FLENBQUM7SUFDRCxJQUFJLEtBQUssS0FBSyxTQUFTLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUSxFQUFFLENBQUM7UUFDckQsT0FBTyxFQUFFLEtBQUssRUFBRSw0Q0FBNEMsRUFBRSxDQUFDO0lBQ2pFLENBQUM7SUFFRCxNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7SUFDdkIsTUFBTSxPQUFPLEdBQUcsT0FBTyxJQUFJLElBQUksSUFBSSxDQUFDLEdBQUcsR0FBRyxlQUFlLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDLENBQUMsV0FBVyxFQUFFLENBQUM7SUFDL0YsTUFBTSxLQUFLLEdBQUcsS0FBSyxJQUFJLElBQUksSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLFdBQVcsRUFBRSxDQUFDO0lBRW5ELE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDbkMsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUMvQixJQUFJLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUM7UUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLHFDQUFxQyxFQUFFLENBQUM7SUFDdEYsSUFBSSxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDO1FBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxtQ0FBbUMsRUFBRSxDQUFDO0lBQ2xGLElBQUksTUFBTSxHQUFHLElBQUk7UUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLGtDQUFrQyxFQUFFLENBQUM7SUFDeEUsSUFBSSxVQUFVLEtBQUssU0FBUyxJQUFJLElBQUksR0FBRyxNQUFNLEdBQUcsVUFBVSxFQUFFLENBQUM7UUFDM0QsT0FBTyxFQUFFLEtBQUssRUFBRSxpQ0FBaUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxVQUFVLEdBQUcsQ0FBQyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxJQUFJLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztJQUMzRyxDQUFDO0lBRUQsT0FBTyxFQUFFLElBQUksRUFBRSxPQUFPLEVBQUUsRUFBRSxFQUFFLEtBQUssRUFBRSxDQUFDO0FBQ3RDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuaW1wb3J0IHR5cGUgeyBSZXF1ZXN0IH0gZnJvbSAnZXhwcmVzcyc7XG5pbXBvcnQgeyBnZXRIZWFkZXJTdHJpbmcgfSBmcm9tICcuL2hlYWRlcnMuanMnO1xuXG4vKipcbiAqIFNoYXBlIGNvbnZlbnRpb24gZm9yIHRoZSBpbmxpbmUgcGFyc2UtZ3VhcmQgaGVscGVycyBiZWxvdy5cbiAqXG4gKiBUaGVzZSByZXR1cm4gYSBsaWdodHdlaWdodCBgeyB2YWx1ZSB9IHwgeyBlcnJvciB9YCAobmFycm93ZWQgd2l0aFxuICogYCdlcnJvcicgaW4gcmVzdWx0YCkg4oCUIGRlbGliZXJhdGVseSBESVNUSU5DVCBmcm9tIHRoZSBzY2hlbWEtdmFsaWRhdGlvblxuICogYFZhbGlkYXRpb25SZXN1bHQ8VD5gIChgeyBvaywgdmFsdWUgfSB8IHsgb2ssIGVycm9yIH1gKSBpblxuICogYHZhbGlkYXRpb24vbWlkZGxld2FyZS50c2AuIFpvZCBzY2hlbWEgcGFyc2luZyB1c2VzIHRoZSBgb2tgLWRpc2NyaW1pbmF0ZWRcbiAqIHNoYXBlOyB0aGVzZSBhZC1ob2MgcmVxdWVzdC1wYXJhbWV0ZXIgZ3VhcmRzIHVzZSBgJ2Vycm9yJyBpbmAuIFRoZSBzcGxpdCBpc1xuICogaW50ZW50aW9uYWwgYW5kIGRvY3VtZW50ZWQgKHNlZSB0aGUgbm90ZSBvbiBgVmFsaWRhdGlvblJlc3VsdGApOiBjb252ZXJnaW5nXG4gKiB0aGVtIHdvdWxkIGNodXJuIGV2ZXJ5IGAnZXJyb3InIGluIHJlc3VsdGAgY2FsbCBzaXRlIGFjcm9zcyB0aGUgZG93bnN0cmVhbVxuICogc2VydmljZXMgd2l0aCBubyBiZWhhdmlvdXJhbCBjaGFuZ2UsIHNvIGl0J3MgbGVmdCBhcyBhIGRvY3VtZW50ZWQgY29udmVudGlvbi5cbiAqL1xuXG4vKipcbiAqIEV4cHJlc3MgNSBwYXJhbWV0ZXIgdHlwZS5cbiAqL1xudHlwZSBQYXJhbVZhbHVlID0gc3RyaW5nIHwgc3RyaW5nW10gfCB1bmRlZmluZWQ7XG5cbi8qKlxuICogRXh0cmFjdCBhIHNpbmdsZSBzdHJpbmcgcGFyYW1ldGVyIGZyb20gRXhwcmVzcyA1IHJvdXRlIHBhcmFtcy5cbiAqXG4gKiBAcGFyYW0gcGFyYW1zIC0gUmVxdWVzdCBwYXJhbXMgb2JqZWN0XG4gKiBAcGFyYW0ga2V5IC0gUGFyYW1ldGVyIGtleVxuICogQHJldHVybnMgVGhlIHBhcmFtZXRlciB2YWx1ZSBhcyBhIHN0cmluZywgb3IgdW5kZWZpbmVkIGlmIG5vdCBwcmVzZW50XG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIC8vIFJvdXRlOiBHRVQgL3BsdWdpbnMvOmlkXG4gKiBjb25zdCBpZCA9IGdldFBhcmFtKHJlcS5wYXJhbXMsICdpZCcpO1xuICogaWYgKCFpZCkge1xuICogICByZXR1cm4gc2VuZEVycm9yKHJlcywgNDAwLCAnTWlzc2luZyBpZCBwYXJhbWV0ZXInKTtcbiAqIH1cbiAqIGBgYFxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0UGFyYW0oXG4gIHBhcmFtczogUmVjb3JkPHN0cmluZywgUGFyYW1WYWx1ZT4sXG4gIGtleTogc3RyaW5nLFxuKTogc3RyaW5nIHwgdW5kZWZpbmVkIHtcbiAgY29uc3QgdmFsdWUgPSBwYXJhbXNba2V5XTtcbiAgaWYgKHZhbHVlID09PSB1bmRlZmluZWQpIHtcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG4gIHJldHVybiBBcnJheS5pc0FycmF5KHZhbHVlKSA/IHZhbHVlWzBdIDogdmFsdWU7XG59XG5cbi8qKlxuICogRXh0cmFjdCB0aGUgb3JnYW5pemF0aW9uIElEIGZyb20gcmVxdWVzdC5cbiAqIENoZWNrcyBwYXJhbXMsIGhlYWRlcnMsIGFuZCB1c2VyIG9iamVjdC5cbiAqXG4gKiBAcGFyYW0gcmVxIC0gRXhwcmVzcyByZXF1ZXN0IG9iamVjdFxuICogQHJldHVybnMgT3JnYW5pemF0aW9uIElEIG9yIHVuZGVmaW5lZFxuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiBjb25zdCBvcmdJZCA9IGdldE9yZ0lkKHJlcSk7XG4gKiBpZiAoIW9yZ0lkKSB7XG4gKiAgIHJldHVybiBzZW5kRXJyb3IocmVzLCA0MDAsICdPcmdhbml6YXRpb24gSUQgcmVxdWlyZWQnKTtcbiAqIH1cbiAqIGBgYFxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0T3JnSWQocmVxOiBSZXF1ZXN0KTogc3RyaW5nIHwgdW5kZWZpbmVkIHtcbiAgLy8gUHJlZmVyIHRoZSBWRVJJRklFRCBKV1QgaWRlbnRpdHkgZmlyc3QuIEEgc3Bvb2ZhYmxlIGB4LW9yZy1pZGAgaGVhZGVyIG11c3RcbiAgLy8gbmV2ZXIgb3ZlcnJpZGUgYW4gYXV0aGVudGljYXRlZCB1c2VyJ3Mgb3JnIChjcm9zcy10ZW5hbnQgZm9vdGd1bikuIEZvciBhXG4gIC8vIHN1cGVyYWRtaW4gYWN0aW5nIGNyb3NzLW9yZywgcmVxdWlyZUF1dGggYWxyZWFkeSByZS1wb2ludHNcbiAgLy8gYHJlcS51c2VyLm9yZ2FuaXphdGlvbklkYCB0byB0aGUgY2hvc2VuIG9yZyB2aWEgdGhlIGlzU3VwZXJBZG1pbi1nYXRlZFxuICAvLyBoZWFkZXIgb3ZlcnJpZGUsIHNvIHRoaXMgc3RpbGwgeWllbGRzIHRoZSBpbnRlbmRlZCBzY29wZS5cbiAgY29uc3QgdXNlck9yZ0lkID0gcmVxLnVzZXI/Lm9yZ2FuaXphdGlvbklkPy50cmltKCk7XG4gIGlmICh1c2VyT3JnSWQpIHtcbiAgICByZXR1cm4gdXNlck9yZ0lkO1xuICB9XG5cbiAgLy8gUHJlLWF1dGggZmFsbGJhY2tzIG9ubHkgKHJlcS51c2VyIHVuc2V0KTogcm91dGUgcGFyYW0sIHRoZW4gaGVhZGVyLlxuICBjb25zdCBwYXJhbU9yZ0lkID0gZ2V0UGFyYW0ocmVxLnBhcmFtcywgJ29yZ0lkJyk7XG4gIGlmIChwYXJhbU9yZ0lkKSB7XG4gICAgcmV0dXJuIHBhcmFtT3JnSWQ7XG4gIH1cblxuICBjb25zdCBoZWFkZXJPcmdJZCA9IGdldEhlYWRlclN0cmluZyhyZXEuaGVhZGVyc1sneC1vcmctaWQnXSk/LnRyaW0oKTtcbiAgaWYgKGhlYWRlck9yZ0lkKSB7XG4gICAgcmV0dXJuIGhlYWRlck9yZ0lkO1xuICB9XG5cbiAgcmV0dXJuIHVuZGVmaW5lZDtcbn1cblxuLyoqXG4gKiBQYXJzZSBhIHF1ZXJ5IHBhcmFtZXRlciBhcyBhbiBpbnRlZ2VyLlxuICpcbiAqIEBwYXJhbSB2YWx1ZSAtIFF1ZXJ5IHBhcmFtZXRlciB2YWx1ZVxuICogQHBhcmFtIGRlZmF1bHRWYWx1ZSAtIERlZmF1bHQgdmFsdWUgaWYgcGFyc2luZyBmYWlsc1xuICogQHJldHVybnMgUGFyc2VkIGludGVnZXJcbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogY29uc3QgbGltaXQgPSBwYXJzZVF1ZXJ5SW50KHJlcS5xdWVyeS5saW1pdCwgMTApO1xuICogY29uc3Qgb2Zmc2V0ID0gcGFyc2VRdWVyeUludChyZXEucXVlcnkub2Zmc2V0LCAwKTtcbiAqIGBgYFxuICovXG5leHBvcnQgZnVuY3Rpb24gcGFyc2VRdWVyeUludCh2YWx1ZTogdW5rbm93biwgZGVmYXVsdFZhbHVlOiBudW1iZXIpOiBudW1iZXIge1xuICBpZiAodmFsdWUgPT09IHVuZGVmaW5lZCB8fCB2YWx1ZSA9PT0gbnVsbCB8fCB2YWx1ZSA9PT0gJycpIHJldHVybiBkZWZhdWx0VmFsdWU7XG4gIGNvbnN0IHBhcnNlZCA9IHBhcnNlSW50KFN0cmluZyh2YWx1ZSksIDEwKTtcbiAgcmV0dXJuIGlzTmFOKHBhcnNlZCkgPyBkZWZhdWx0VmFsdWUgOiBwYXJzZWQ7XG59XG5cbi8qKlxuICogUGFyc2UgYSBxdWVyeSBwYXJhbWV0ZXIgYXMgYSBzdHJpbmcuXG4gKlxuICogQHBhcmFtIHZhbHVlIC0gUXVlcnkgcGFyYW1ldGVyIHZhbHVlXG4gKiBAcmV0dXJucyBTdHJpbmcgdmFsdWUgb3IgdW5kZWZpbmVkXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIGNvbnN0IHNlYXJjaCA9IHBhcnNlUXVlcnlTdHJpbmcocmVxLnF1ZXJ5LnNlYXJjaCk7XG4gKiBgYGBcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHBhcnNlUXVlcnlTdHJpbmcodmFsdWU6IHVua25vd24pOiBzdHJpbmcgfCB1bmRlZmluZWQge1xuICBpZiAodmFsdWUgPT09IHVuZGVmaW5lZCB8fCB2YWx1ZSA9PT0gbnVsbCB8fCB2YWx1ZSA9PT0gJycpIHJldHVybiB1bmRlZmluZWQ7XG4gIHJldHVybiBTdHJpbmcodmFsdWUpO1xufVxuXG4vKipcbiAqIFBhcnNlIGEgc3RyaW5nIHRvIGEgcG9zaXRpdmUgaW50ZWdlciwgcmV0dXJuaW5nIGEgZmFsbGJhY2sgaWYgaW52YWxpZC5cbiAqIFVzZWZ1bCBmb3IgcGFyc2luZyBlbnZpcm9ubWVudCB2YXJpYWJsZXMgd2l0aCBudW1lcmljIGRlZmF1bHRzLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcGFyc2VQb3NpdGl2ZUludCh2YWx1ZTogc3RyaW5nIHwgdW5kZWZpbmVkLCBmYWxsYmFjazogbnVtYmVyKTogbnVtYmVyIHtcbiAgaWYgKCF2YWx1ZSkgcmV0dXJuIGZhbGxiYWNrO1xuICBjb25zdCBwYXJzZWQgPSBwYXJzZUludCh2YWx1ZSwgMTApO1xuICByZXR1cm4gTnVtYmVyLmlzRmluaXRlKHBhcnNlZCkgJiYgcGFyc2VkID4gMCA/IHBhcnNlZCA6IGZhbGxiYWNrO1xufVxuXG4vKipcbiAqIFBhcnNlIGEgcXVlcnkgaW50LCB0aGVuIGNsYW1wIHRvIGBbMSwgbWF4XWAuIFJldHVybnMgYGRlZmF1bHRWYWx1ZWAgKGFmdGVyXG4gKiB0aGUgc2FtZSBjbGFtcCkgd2hlbiB0aGUgaW5wdXQgaXMgbWlzc2luZy9pbnZhbGlkLiBVc2UgZm9yIGBsaW1pdGAtc3R5bGVcbiAqIHF1ZXJ5IHBhcmFtZXRlcnMgd2hlcmUgYW4gdW5ib3VuZGVkIHZhbHVlIHdvdWxkIGNvc3QgcmVhbCBtb25leS5cbiAqXG4gKiBAZXhhbXBsZVxuICogY29uc3QgbGltaXQgPSBwYXJzZVF1ZXJ5SW50Q2xhbXBlZChyZXEucXVlcnkubGltaXQsIDUwLCAxMDAwKTtcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHBhcnNlUXVlcnlJbnRDbGFtcGVkKFxuICB2YWx1ZTogdW5rbm93bixcbiAgZGVmYXVsdFZhbHVlOiBudW1iZXIsXG4gIG1heDogbnVtYmVyLFxuKTogbnVtYmVyIHtcbiAgY29uc3QgcmF3ID0gcGFyc2VRdWVyeUludCh2YWx1ZSwgZGVmYXVsdFZhbHVlKTtcbiAgcmV0dXJuIE1hdGgubWF4KDEsIE1hdGgubWluKHJhdywgbWF4KSk7XG59XG5cbi8qKlxuICogQnVsay1hcnJheSByZXF1ZXN0IGd1YXJkLiBWYWxpZGF0ZXMgdGhhdCBgdmFsdWVgIGlzIGFuIGFycmF5LCBub24tZW1wdHksXG4gKiBhbmQgd2l0aGluIHRoZSBjb25maWd1cmVkIGNhcC4gUmV0dXJucyB0aGUgdHlwZWQgYXJyYXkgb3IgYW4gZXJyb3Igb2JqZWN0XG4gKiB0aGUgY2FsbGVyIGNhbiBmb3J3YXJkIHRvIGBzZW5kRXJyb3JgL2BzZW5kQmFkUmVxdWVzdGAuXG4gKlxuICogUmVwbGFjZXMgYSBoYW5kLXJvbGxlZCBgaWYgKCFBcnJheS5pc0FycmF5KC4uLikpIHsgLi4uIH1gICsgbGVuZ3RoIGNoZWNrXG4gKiBibG9jayB0aGF0IHdhcyBkdXBsaWNhdGVkIGFjcm9zcyBidWxrIGVuZHBvaW50cyBpbiBwaXBlbGluZSwgcGx1Z2luLFxuICogY29tcGxpYW5jZSwgZXRjLlxuICpcbiAqIEBleGFtcGxlXG4gKiBjb25zdCByZXN1bHQgPSB2YWxpZGF0ZUJ1bGtBcnJheShyZXEuYm9keS5pZHMsICdpZHMnKTtcbiAqIGlmICgnZXJyb3InIGluIHJlc3VsdCkgcmV0dXJuIHNlbmRCYWRSZXF1ZXN0KHJlcywgcmVzdWx0LmVycm9yLCBFcnJvckNvZGUuVkFMSURBVElPTl9FUlJPUik7XG4gKiBjb25zdCBpZHMgPSByZXN1bHQudmFsdWU7XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB2YWxpZGF0ZUJ1bGtBcnJheTxUID0gdW5rbm93bj4oXG4gIHZhbHVlOiB1bmtub3duLFxuICBmaWVsZE5hbWU6IHN0cmluZyxcbiAgbWF4SXRlbXM/OiBudW1iZXIsXG4pOiB7IHZhbHVlOiBUW10gfSB8IHsgZXJyb3I6IHN0cmluZyB9IHtcbiAgaWYgKCFBcnJheS5pc0FycmF5KHZhbHVlKSB8fCB2YWx1ZS5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4geyBlcnJvcjogYFJlcXVlc3QgYm9keSBtdXN0IGluY2x1ZGUgYSBub24tZW1wdHkgXCIke2ZpZWxkTmFtZX1cIiBhcnJheWAgfTtcbiAgfVxuICAvLyBDb3JlQ29uc3RhbnRzLk1BWF9CVUxLX0lURU1TIGlzbid0IGltcG9ydGVkIGhlcmUgdG8gYXZvaWQgYSBjaXJjdWxhclxuICAvLyBwYXRoIHRocm91Z2ggY29uc3RhbnRzL2luZGV4OyBjYWxsZXJzIHBhc3MgaXQgZXhwbGljaXRseSBvciBvbWl0IGZvclxuICAvLyBubyB1cHBlciBib3VuZC5cbiAgaWYgKG1heEl0ZW1zICE9PSB1bmRlZmluZWQgJiYgdmFsdWUubGVuZ3RoID4gbWF4SXRlbXMpIHtcbiAgICByZXR1cm4geyBlcnJvcjogYE1heGltdW0gJHttYXhJdGVtc30gaXRlbXMgcGVyIGJ1bGsgb3BlcmF0aW9uYCB9O1xuICB9XG4gIHJldHVybiB7IHZhbHVlOiB2YWx1ZSBhcyBUW10gfTtcbn1cblxuLyoqXG4gKiBTdXBwb3J0ZWQgaW50ZXJ2YWxzIGZvciB0aW1lLWJ1Y2tldGVkIHJlcG9ydCBxdWVyaWVzLiBVc2VkIGFzIHRoZVxuICogYERBVEVfVFJVTkNgIGZpcnN0IGFyZ3VtZW50IGluIFBvc3RncmVzOyByZXN0cmljdGVkIHRvIGEgZml4ZWQgZW51bSBzb1xuICogdGhlIHZhbHVlIGNhbiBuZXZlciBiZSBpbmplY3RlZCBpbnRvIHJhdyBTUUwuXG4gKi9cbmV4cG9ydCBjb25zdCBSRVBPUlRfSU5URVJWQUxTID0gWydkYXknLCAnd2VlaycsICdtb250aCddIGFzIGNvbnN0O1xuZXhwb3J0IHR5cGUgUmVwb3J0SW50ZXJ2YWwgPSAodHlwZW9mIFJFUE9SVF9JTlRFUlZBTFMpW251bWJlcl07XG5cbi8qKlxuICogUGFyc2UgKyB2YWxpZGF0ZSBhIGA/aW50ZXJ2YWw9YCByZXBvcnQgYnVja2V0IChkZWZhdWx0cyB0byAnd2VlaycpLiBSZXR1cm5zIHRoZVxuICogdmFsaWRhdGVkIGludGVydmFsIG9yIGB7IGVycm9yIH1gLiBTaGFyZWQgYnkgdGhlIHRpbWVzZXJpZXMgcmVwb3J0IHJvdXRlcyBzbyB0aGVcbiAqIGRlZmF1bHQgKyBhbGxvdy1saXN0ICsgZXJyb3IgbWVzc2FnZSBsaXZlIGluIG9uZSBwbGFjZS5cbiAqXG4gKiBAZXhhbXBsZVxuICogY29uc3QgaW50ZXJ2YWwgPSBwYXJzZVJlcG9ydEludGVydmFsKHJlcS5xdWVyeSk7XG4gKiBpZiAodHlwZW9mIGludGVydmFsID09PSAnb2JqZWN0JykgcmV0dXJuIHNlbmRCYWRSZXF1ZXN0KHJlcywgaW50ZXJ2YWwuZXJyb3IsIEVycm9yQ29kZS5WQUxJREFUSU9OX0VSUk9SKTtcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHBhcnNlUmVwb3J0SW50ZXJ2YWwocXVlcnk6IFJlY29yZDxzdHJpbmcsIHVua25vd24+KTogUmVwb3J0SW50ZXJ2YWwgfCB7IGVycm9yOiBzdHJpbmcgfSB7XG4gIGNvbnN0IGludGVydmFsID0gU3RyaW5nKHF1ZXJ5LmludGVydmFsID8/ICd3ZWVrJyk7XG4gIGlmICghUkVQT1JUX0lOVEVSVkFMUy5pbmNsdWRlcyhpbnRlcnZhbCBhcyBSZXBvcnRJbnRlcnZhbCkpIHtcbiAgICByZXR1cm4geyBlcnJvcjogYGludGVydmFsIG11c3QgYmUgb25lIG9mOiAke1JFUE9SVF9JTlRFUlZBTFMuam9pbignLCAnKX1gIH07XG4gIH1cbiAgcmV0dXJuIGludGVydmFsIGFzIFJlcG9ydEludGVydmFsO1xufVxuXG4vKipcbiAqIFBhcnNlIGEgYD9mcm9tPSZ0bz1gIGRhdGUgcmFuZ2Ugd2l0aCBkZWZhdWx0cyBvZiBcIjMwIGRheXMgYWdvIOKGkiBub3dcIi5cbiAqIFJlamVjdHMgbm9uLXN0cmluZyBpbnB1dHMgKEV4cHJlc3MncyBwYXJzZXIgY2FuIHByb2R1Y2Ugc3RyaW5nW10gZm9yXG4gKiByZXBlYXRlZCBrZXlzKSwgaW52YWxpZCBJU08gdGltZXN0YW1wcywgYW5kIGludmVydGVkIHJhbmdlcy5cbiAqXG4gKiBSZXR1cm5zIGVpdGhlciBgeyBmcm9tLCB0byB9YCBJU08gc3RyaW5ncyBvciBgeyBlcnJvciB9YC5cbiAqXG4gKiBAZXhhbXBsZVxuICogY29uc3QgcmFuZ2UgPSBwYXJzZURhdGVSYW5nZShyZXEucXVlcnkpO1xuICogaWYgKCdlcnJvcicgaW4gcmFuZ2UpIHJldHVybiBzZW5kQmFkUmVxdWVzdChyZXMsIHJhbmdlLmVycm9yLCBFcnJvckNvZGUuVkFMSURBVElPTl9FUlJPUik7XG4gKiBjb25zdCByb3dzID0gYXdhaXQgc3ZjLmxpc3QocmFuZ2UuZnJvbSwgcmFuZ2UudG8pO1xuICovXG5leHBvcnQgZnVuY3Rpb24gcGFyc2VEYXRlUmFuZ2UoXG4gIHF1ZXJ5OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPixcbiAgb3B0aW9uczogeyBtYXhSYW5nZU1zPzogbnVtYmVyOyBkZWZhdWx0RGF5c0JhY2s/OiBudW1iZXIgfSA9IHt9LFxuKTogeyBmcm9tOiBzdHJpbmc7IHRvOiBzdHJpbmcgfSB8IHsgZXJyb3I6IHN0cmluZyB9IHtcbiAgY29uc3QgeyBtYXhSYW5nZU1zLCBkZWZhdWx0RGF5c0JhY2sgPSAzMCB9ID0gb3B0aW9ucztcblxuICBjb25zdCByYXdGcm9tID0gcXVlcnkuZnJvbTtcbiAgY29uc3QgcmF3VG8gPSBxdWVyeS50bztcblxuICBpZiAocmF3RnJvbSAhPT0gdW5kZWZpbmVkICYmIHR5cGVvZiByYXdGcm9tICE9PSAnc3RyaW5nJykge1xuICAgIHJldHVybiB7IGVycm9yOiAnXCJmcm9tXCIgbXVzdCBiZSBhIHNpbmdsZSBJU08gdGltZXN0YW1wIHN0cmluZycgfTtcbiAgfVxuICBpZiAocmF3VG8gIT09IHVuZGVmaW5lZCAmJiB0eXBlb2YgcmF3VG8gIT09ICdzdHJpbmcnKSB7XG4gICAgcmV0dXJuIHsgZXJyb3I6ICdcInRvXCIgbXVzdCBiZSBhIHNpbmdsZSBJU08gdGltZXN0YW1wIHN0cmluZycgfTtcbiAgfVxuXG4gIGNvbnN0IG5vdyA9IERhdGUubm93KCk7XG4gIGNvbnN0IGZyb21TdHIgPSByYXdGcm9tID8/IG5ldyBEYXRlKG5vdyAtIGRlZmF1bHREYXlzQmFjayAqIDI0ICogNjAgKiA2MCAqIDEwMDApLnRvSVNPU3RyaW5nKCk7XG4gIGNvbnN0IHRvU3RyID0gcmF3VG8gPz8gbmV3IERhdGUobm93KS50b0lTT1N0cmluZygpO1xuXG4gIGNvbnN0IGZyb21NcyA9IERhdGUucGFyc2UoZnJvbVN0cik7XG4gIGNvbnN0IHRvTXMgPSBEYXRlLnBhcnNlKHRvU3RyKTtcbiAgaWYgKCFOdW1iZXIuaXNGaW5pdGUoZnJvbU1zKSkgcmV0dXJuIHsgZXJyb3I6ICdcImZyb21cIiBpcyBub3QgYSB2YWxpZCBJU08gdGltZXN0YW1wJyB9O1xuICBpZiAoIU51bWJlci5pc0Zpbml0ZSh0b01zKSkgcmV0dXJuIHsgZXJyb3I6ICdcInRvXCIgaXMgbm90IGEgdmFsaWQgSVNPIHRpbWVzdGFtcCcgfTtcbiAgaWYgKGZyb21NcyA+IHRvTXMpIHJldHVybiB7IGVycm9yOiAnXCJmcm9tXCIgbXVzdCBiZSBlYXJsaWVyIHRoYW4gXCJ0b1wiJyB9O1xuICBpZiAobWF4UmFuZ2VNcyAhPT0gdW5kZWZpbmVkICYmIHRvTXMgLSBmcm9tTXMgPiBtYXhSYW5nZU1zKSB7XG4gICAgcmV0dXJuIHsgZXJyb3I6IGBEYXRlIHJhbmdlIGV4Y2VlZHMgbWF4aW11bSBvZiAke01hdGguZmxvb3IobWF4UmFuZ2VNcyAvICgyNCAqIDYwICogNjAgKiAxMDAwKSl9IGRheXNgIH07XG4gIH1cblxuICByZXR1cm4geyBmcm9tOiBmcm9tU3RyLCB0bzogdG9TdHIgfTtcbn1cbiJdfQ==
@@ -0,0 +1,117 @@
1
+ import type { Response } from 'express';
2
+ import type { QuotaInfo } from '../types/common.js';
3
+ import { ErrorCode } from '../types/error-codes.js';
4
+ /**
5
+ * Send a standardized success response.
6
+ *
7
+ * @param res - Express response object
8
+ * @param statusCode - HTTP status code (default: 200)
9
+ * @param data - Response data
10
+ * @param message - Optional success message
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * sendSuccess(res, 200, { plugin: pluginData });
15
+ * sendSuccess(res, 201, { id: newId }, 'Resource created');
16
+ * ```
17
+ */
18
+ export declare function sendSuccess<T>(res: Response, statusCode?: number, data?: T, message?: string): void;
19
+ /**
20
+ * Send a standardized error response.
21
+ *
22
+ * @param res - Express response object
23
+ * @param statusCode - HTTP status code
24
+ * @param message - Error message
25
+ * @param code - Error code from ErrorCode enum
26
+ * @param details - Optional additional details
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * sendError(res, 404, 'Plugin not found', ErrorCode.NOT_FOUND);
31
+ * sendError(res, 400, 'Invalid input', ErrorCode.VALIDATION_ERROR, { field: 'name' });
32
+ * ```
33
+ */
34
+ export declare function sendError(res: Response, statusCode: number, message: string, code?: ErrorCode | string, details?: unknown): void;
35
+ /**
36
+ * Send a quota exceeded error response.
37
+ *
38
+ * @param res - Express response object
39
+ * @param quotaType - Type of quota exceeded
40
+ * @param quota - Current quota information
41
+ * @param resetAt - ISO timestamp when quota resets
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * sendQuotaExceeded(res, 'apiCalls', { type: 'apiCalls', limit: 10000, used: 10000, remaining: 0 }, resetAt);
46
+ * ```
47
+ */
48
+ export declare function sendQuotaExceeded(res: Response, quotaType: string, quota: QuotaInfo, resetAt?: string, message?: string): void;
49
+ /**
50
+ * Send a paginated list response with the items nested under a named key.
51
+ * Use when the API contract is `{ <key>: [...], pagination: {...} }`
52
+ * (the dominant shape across api/* services).
53
+ *
54
+ * @example
55
+ * ```typescript
56
+ * sendPaginatedNested(res, 'pipelines', items, {
57
+ * total: 50, limit: 25, offset: 0, hasMore: true,
58
+ * });
59
+ * // → { pipelines: [...], pagination: { total: 50, limit: 25, offset: 0, hasMore: true } }
60
+ * ```
61
+ */
62
+ export declare function sendPaginatedNested<T>(res: Response, dataKey: string, data: T[], options: {
63
+ total?: number;
64
+ limit: number;
65
+ offset: number;
66
+ hasMore: boolean;
67
+ nextCursor?: string;
68
+ statusCode?: number;
69
+ }): void;
70
+ /**
71
+ * Extract database error details for logging/response.
72
+ *
73
+ * Extracts PostgreSQL error codes, details, hints, and constraint names
74
+ * from database errors for better error messages.
75
+ *
76
+ * @param error - Error object from database operation
77
+ * @returns Object with extracted error details
78
+ *
79
+ * @example
80
+ * ```typescript
81
+ * try {
82
+ * await db.insert(plugins).values(newPlugin);
83
+ * } catch (error) {
84
+ * const dbDetails = extractDbError(error);
85
+ * return sendError(res, 500, 'Database error', ErrorCode.DATABASE_ERROR, dbDetails);
86
+ * }
87
+ * ```
88
+ */
89
+ export declare function extractDbError(error: unknown): Record<string, unknown>;
90
+ /**
91
+ * Extract a human-readable message from an unknown catch value.
92
+ *
93
+ * Falls back beyond `.message` because several common Node errors carry an
94
+ * EMPTY `.message`:
95
+ * - dual-stack (IPv4+IPv6) connect failures surface as an `AggregateError`
96
+ * whose own message is blank — the cause lives in `.code` (e.g.
97
+ * `ECONNREFUSED`/`ETIMEDOUT`) and/or the `.errors[]` sub-errors;
98
+ * - raw socket `ErrnoException`s can likewise carry only a `.code`.
99
+ * Without this, an unreachable internal service (e.g. compliance still
100
+ * starting) produced blank per-item errors in bulk responses and `error:""`
101
+ * in logs — a silent black box. Order: message → code → joined sub-errors →
102
+ * error name.
103
+ */
104
+ export declare function errorMessage(error: unknown): string;
105
+ /** Send a 400 bad-request response. */
106
+ export declare function sendBadRequest(res: Response, message: string, code?: ErrorCode | string): void;
107
+ /** Send a 500 internal error response. */
108
+ export declare function sendInternalError(res: Response, message: string, details?: Record<string, unknown>): void;
109
+ /** Common pagination + sorting parameters. */
110
+ export interface PaginationParams {
111
+ limit: number;
112
+ offset: number;
113
+ sortBy: string;
114
+ sortOrder: 'asc' | 'desc';
115
+ }
116
+ /** Parse pagination/sort params from query string, clamping to safe defaults. */
117
+ export declare function parsePaginationParams(query: Record<string, unknown>): PaginationParams;
@@ -0,0 +1,223 @@
1
+ // Copyright 2026 Pipeline Builder Contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { createLogger } from './logger.js';
4
+ import { ErrorCode } from '../types/error-codes.js';
5
+ import { MAX_PAGE_LIMIT, MAX_PAGE_OFFSET } from '../validation/common-schemas.js';
6
+ const logger = createLogger('response');
7
+ /**
8
+ * Send a standardized success response.
9
+ *
10
+ * @param res - Express response object
11
+ * @param statusCode - HTTP status code (default: 200)
12
+ * @param data - Response data
13
+ * @param message - Optional success message
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * sendSuccess(res, 200, { plugin: pluginData });
18
+ * sendSuccess(res, 201, { id: newId }, 'Resource created');
19
+ * ```
20
+ */
21
+ export function sendSuccess(res, statusCode = 200, data, message) {
22
+ if (res.headersSent) {
23
+ logger.warn('Attempted to send success after headers already sent', { statusCode });
24
+ return;
25
+ }
26
+ const response = {
27
+ success: true,
28
+ statusCode,
29
+ };
30
+ if (data !== undefined) {
31
+ response.data = data;
32
+ }
33
+ if (message) {
34
+ response.message = message;
35
+ }
36
+ res.status(statusCode).json(response);
37
+ }
38
+ /**
39
+ * Send a standardized error response.
40
+ *
41
+ * @param res - Express response object
42
+ * @param statusCode - HTTP status code
43
+ * @param message - Error message
44
+ * @param code - Error code from ErrorCode enum
45
+ * @param details - Optional additional details
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * sendError(res, 404, 'Plugin not found', ErrorCode.NOT_FOUND);
50
+ * sendError(res, 400, 'Invalid input', ErrorCode.VALIDATION_ERROR, { field: 'name' });
51
+ * ```
52
+ */
53
+ export function sendError(res, statusCode, message, code, details) {
54
+ if (res.headersSent) {
55
+ logger.warn('Attempted to send error after headers already sent', { statusCode, message });
56
+ return;
57
+ }
58
+ const response = {
59
+ success: false,
60
+ statusCode,
61
+ message,
62
+ };
63
+ if (code) {
64
+ response.code = code;
65
+ }
66
+ if (details !== undefined) {
67
+ response.details = details;
68
+ }
69
+ res.status(statusCode).json(response);
70
+ }
71
+ /**
72
+ * Send a quota exceeded error response.
73
+ *
74
+ * @param res - Express response object
75
+ * @param quotaType - Type of quota exceeded
76
+ * @param quota - Current quota information
77
+ * @param resetAt - ISO timestamp when quota resets
78
+ *
79
+ * @example
80
+ * ```typescript
81
+ * sendQuotaExceeded(res, 'apiCalls', { type: 'apiCalls', limit: 10000, used: 10000, remaining: 0 }, resetAt);
82
+ * ```
83
+ */
84
+ export function sendQuotaExceeded(res, quotaType, quota, resetAt, message) {
85
+ const resetDate = resetAt ? new Date(resetAt) : new Date();
86
+ const resetIn = Math.max(0, Math.ceil((resetDate.getTime() - Date.now()) / 1000));
87
+ // Guard like the sibling helpers: `setHeader` throws synchronously once the
88
+ // response has been sent, so skip the quota headers (and let `sendError` no-op)
89
+ // instead of crashing the handler on an already-committed response.
90
+ if (!res.headersSent) {
91
+ res.setHeader('Retry-After', resetIn);
92
+ res.setHeader('X-Quota-Limit', quota.limit);
93
+ res.setHeader('X-Quota-Used', quota.used);
94
+ res.setHeader('X-Quota-Remaining', Math.max(0, quota.remaining));
95
+ if (resetAt) {
96
+ res.setHeader('X-Quota-Reset', resetAt);
97
+ }
98
+ }
99
+ sendError(res, 429, message ?? `${quotaType} quota exceeded (${quota.used}/${quota.limit}). Please try again later.`, ErrorCode.QUOTA_EXCEEDED, { quota });
100
+ }
101
+ /**
102
+ * Send a paginated list response with the items nested under a named key.
103
+ * Use when the API contract is `{ <key>: [...], pagination: {...} }`
104
+ * (the dominant shape across api/* services).
105
+ *
106
+ * @example
107
+ * ```typescript
108
+ * sendPaginatedNested(res, 'pipelines', items, {
109
+ * total: 50, limit: 25, offset: 0, hasMore: true,
110
+ * });
111
+ * // → { pipelines: [...], pagination: { total: 50, limit: 25, offset: 0, hasMore: true } }
112
+ * ```
113
+ */
114
+ export function sendPaginatedNested(res, dataKey, data, options) {
115
+ if (res.headersSent) {
116
+ logger.warn('Attempted to send paginated response after headers already sent');
117
+ return;
118
+ }
119
+ const { statusCode = 200, total, limit, offset, hasMore, nextCursor } = options;
120
+ const pagination = { limit, offset, hasMore };
121
+ if (total !== undefined)
122
+ pagination.total = total;
123
+ if (nextCursor)
124
+ pagination.nextCursor = nextCursor;
125
+ res.status(statusCode).json({
126
+ success: true,
127
+ statusCode,
128
+ data: { [dataKey]: data, pagination },
129
+ });
130
+ }
131
+ /**
132
+ * Extract database error details for logging/response.
133
+ *
134
+ * Extracts PostgreSQL error codes, details, hints, and constraint names
135
+ * from database errors for better error messages.
136
+ *
137
+ * @param error - Error object from database operation
138
+ * @returns Object with extracted error details
139
+ *
140
+ * @example
141
+ * ```typescript
142
+ * try {
143
+ * await db.insert(plugins).values(newPlugin);
144
+ * } catch (error) {
145
+ * const dbDetails = extractDbError(error);
146
+ * return sendError(res, 500, 'Database error', ErrorCode.DATABASE_ERROR, dbDetails);
147
+ * }
148
+ * ```
149
+ */
150
+ export function extractDbError(error) {
151
+ if (!error || typeof error !== 'object') {
152
+ return {};
153
+ }
154
+ // Drizzle ORM wraps the original pg error in error.cause —
155
+ // check the cause first, then fall back to the error itself.
156
+ const err = error;
157
+ const source = (err.cause && typeof err.cause === 'object' ? err.cause : err);
158
+ const details = {};
159
+ if (source.code)
160
+ details.dbCode = source.code;
161
+ if (source.detail)
162
+ details.dbDetail = source.detail;
163
+ if (source.hint)
164
+ details.dbHint = source.hint;
165
+ if (source.constraint)
166
+ details.constraint = source.constraint;
167
+ if (source.table)
168
+ details.table = source.table;
169
+ if (source.column)
170
+ details.column = source.column;
171
+ return details;
172
+ }
173
+ // Convenience helpers (shared across plugin, pipeline, quota services)
174
+ /**
175
+ * Extract a human-readable message from an unknown catch value.
176
+ *
177
+ * Falls back beyond `.message` because several common Node errors carry an
178
+ * EMPTY `.message`:
179
+ * - dual-stack (IPv4+IPv6) connect failures surface as an `AggregateError`
180
+ * whose own message is blank — the cause lives in `.code` (e.g.
181
+ * `ECONNREFUSED`/`ETIMEDOUT`) and/or the `.errors[]` sub-errors;
182
+ * - raw socket `ErrnoException`s can likewise carry only a `.code`.
183
+ * Without this, an unreachable internal service (e.g. compliance still
184
+ * starting) produced blank per-item errors in bulk responses and `error:""`
185
+ * in logs — a silent black box. Order: message → code → joined sub-errors →
186
+ * error name.
187
+ */
188
+ export function errorMessage(error) {
189
+ if (!(error instanceof Error))
190
+ return String(error);
191
+ if (error.message)
192
+ return error.message;
193
+ const code = error.code;
194
+ if (code)
195
+ return code;
196
+ const inner = error.errors;
197
+ if (Array.isArray(inner) && inner.length > 0) {
198
+ const joined = inner.map(errorMessage).filter(Boolean).join('; ');
199
+ if (joined)
200
+ return joined;
201
+ }
202
+ return error.name || 'Unknown error';
203
+ }
204
+ /** Send a 400 bad-request response. */
205
+ export function sendBadRequest(res, message, code = ErrorCode.VALIDATION_ERROR) {
206
+ sendError(res, 400, message, code);
207
+ }
208
+ /** Send a 500 internal error response. */
209
+ export function sendInternalError(res, message, details) {
210
+ sendError(res, 500, message, ErrorCode.INTERNAL_ERROR, details);
211
+ }
212
+ /** Parse pagination/sort params from query string, clamping to safe defaults. */
213
+ export function parsePaginationParams(query) {
214
+ const limit = Math.min(Math.max(parseInt(String(query.limit), 10) || 10, 1), MAX_PAGE_LIMIT);
215
+ // Clamp offset too — an unbounded offset forces Postgres to scan+discard huge row
216
+ // counts on hot list endpoints (a cheap DoS amplifier). Deep paging should use the
217
+ // cursor API. Ceiling = MAX_PAGE_OFFSET.
218
+ const offset = Math.min(Math.max(parseInt(String(query.offset), 10) || 0, 0), MAX_PAGE_OFFSET);
219
+ const sortBy = String(query.sortBy || 'createdAt');
220
+ const sortOrder = String(query.sortOrder || 'desc').toLowerCase() === 'asc' ? 'asc' : 'desc';
221
+ return { limit, offset, sortBy, sortOrder };
222
+ }
223
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVzcG9uc2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdXRpbHMvcmVzcG9uc2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsK0NBQStDO0FBQy9DLHNDQUFzQztBQUd0QyxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBRTNDLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUNwRCxPQUFPLEVBQUUsY0FBYyxFQUFFLGVBQWUsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBRWxGLE1BQU0sTUFBTSxHQUFHLFlBQVksQ0FBQyxVQUFVLENBQUMsQ0FBQztBQUV4Qzs7Ozs7Ozs7Ozs7OztHQWFHO0FBQ0gsTUFBTSxVQUFVLFdBQVcsQ0FDekIsR0FBYSxFQUNiLFVBQVUsR0FBVyxHQUFHLEVBQ3hCLElBQVEsRUFDUixPQUFnQjtJQUVoQixJQUFJLEdBQUcsQ0FBQyxXQUFXLEVBQUUsQ0FBQztRQUNwQixNQUFNLENBQUMsSUFBSSxDQUFDLHNEQUFzRCxFQUFFLEVBQUUsVUFBVSxFQUFFLENBQUMsQ0FBQztRQUNwRixPQUFPO0lBQ1QsQ0FBQztJQUVELE1BQU0sUUFBUSxHQUtWO1FBQ0YsT0FBTyxFQUFFLElBQUk7UUFDYixVQUFVO0tBQ1gsQ0FBQztJQUVGLElBQUksSUFBSSxLQUFLLFNBQVMsRUFBRSxDQUFDO1FBQ3ZCLFFBQVEsQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDO0lBQ3ZCLENBQUM7SUFFRCxJQUFJLE9BQU8sRUFBRSxDQUFDO1FBQ1osUUFBUSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUM7SUFDN0IsQ0FBQztJQUVELEdBQUcsQ0FBQyxNQUFNLENBQUMsVUFBVSxDQUFDLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO0FBQ3hDLENBQUM7QUFFRDs7Ozs7Ozs7Ozs7Ozs7R0FjRztBQUNILE1BQU0sVUFBVSxTQUFTLENBQ3ZCLEdBQWEsRUFDYixVQUFrQixFQUNsQixP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cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuaW1wb3J0IHR5cGUgeyBSZXNwb25zZSB9IGZyb20gJ2V4cHJlc3MnO1xuaW1wb3J0IHsgY3JlYXRlTG9nZ2VyIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xuaW1wb3J0IHR5cGUgeyBRdW90YUluZm8gfSBmcm9tICcuLi90eXBlcy9jb21tb24uanMnO1xuaW1wb3J0IHsgRXJyb3JDb2RlIH0gZnJvbSAnLi4vdHlwZXMvZXJyb3ItY29kZXMuanMnO1xuaW1wb3J0IHsgTUFYX1BBR0VfTElNSVQsIE1BWF9QQUdFX09GRlNFVCB9IGZyb20gJy4uL3ZhbGlkYXRpb24vY29tbW9uLXNjaGVtYXMuanMnO1xuXG5jb25zdCBsb2dnZXIgPSBjcmVhdGVMb2dnZXIoJ3Jlc3BvbnNlJyk7XG5cbi8qKlxuICogU2VuZCBhIHN0YW5kYXJkaXplZCBzdWNjZXNzIHJlc3BvbnNlLlxuICpcbiAqIEBwYXJhbSByZXMgLSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdFxuICogQHBhcmFtIHN0YXR1c0NvZGUgLSBIVFRQIHN0YXR1cyBjb2RlIChkZWZhdWx0OiAyMDApXG4gKiBAcGFyYW0gZGF0YSAtIFJlc3BvbnNlIGRhdGFcbiAqIEBwYXJhbSBtZXNzYWdlIC0gT3B0aW9uYWwgc3VjY2VzcyBtZXNzYWdlXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIHNlbmRTdWNjZXNzKHJlcywgMjAwLCB7IHBsdWdpbjogcGx1Z2luRGF0YSB9KTtcbiAqIHNlbmRTdWNjZXNzKHJlcywgMjAxLCB7IGlkOiBuZXdJZCB9LCAnUmVzb3VyY2UgY3JlYXRlZCcpO1xuICogYGBgXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzZW5kU3VjY2VzczxUPihcbiAgcmVzOiBSZXNwb25zZSxcbiAgc3RhdHVzQ29kZTogbnVtYmVyID0gMjAwLFxuICBkYXRhPzogVCxcbiAgbWVzc2FnZT86IHN0cmluZyxcbik6IHZvaWQge1xuICBpZiAocmVzLmhlYWRlcnNTZW50KSB7XG4gICAgbG9nZ2VyLndhcm4oJ0F0dGVtcHRlZCB0byBzZW5kIHN1Y2Nlc3MgYWZ0ZXIgaGVhZGVycyBhbHJlYWR5IHNlbnQnLCB7IHN0YXR1c0NvZGUgfSk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgY29uc3QgcmVzcG9uc2U6IHtcbiAgICBzdWNjZXNzOiB0cnVlO1xuICAgIHN0YXR1c0NvZGU6IG51bWJlcjtcbiAgICBkYXRhPzogVDtcbiAgICBtZXNzYWdlPzogc3RyaW5nO1xuICB9ID0ge1xuICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgc3RhdHVzQ29kZSxcbiAgfTtcblxuICBpZiAoZGF0YSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgcmVzcG9uc2UuZGF0YSA9IGRhdGE7XG4gIH1cblxuICBpZiAobWVzc2FnZSkge1xuICAgIHJlc3BvbnNlLm1lc3NhZ2UgPSBtZXNzYWdlO1xuICB9XG5cbiAgcmVzLnN0YXR1cyhzdGF0dXNDb2RlKS5qc29uKHJlc3BvbnNlKTtcbn1cblxuLyoqXG4gKiBTZW5kIGEgc3RhbmRhcmRpemVkIGVycm9yIHJlc3BvbnNlLlxuICpcbiAqIEBwYXJhbSByZXMgLSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdFxuICogQHBhcmFtIHN0YXR1c0NvZGUgLSBIVFRQIHN0YXR1cyBjb2RlXG4gKiBAcGFyYW0gbWVzc2FnZSAtIEVycm9yIG1lc3NhZ2VcbiAqIEBwYXJhbSBjb2RlIC0gRXJyb3IgY29kZSBmcm9tIEVycm9yQ29kZSBlbnVtXG4gKiBAcGFyYW0gZGV0YWlscyAtIE9wdGlvbmFsIGFkZGl0aW9uYWwgZGV0YWlsc1xuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiBzZW5kRXJyb3IocmVzLCA0MDQsICdQbHVnaW4gbm90IGZvdW5kJywgRXJyb3JDb2RlLk5PVF9GT1VORCk7XG4gKiBzZW5kRXJyb3IocmVzLCA0MDAsICdJbnZhbGlkIGlucHV0JywgRXJyb3JDb2RlLlZBTElEQVRJT05fRVJST1IsIHsgZmllbGQ6ICduYW1lJyB9KTtcbiAqIGBgYFxuICovXG5leHBvcnQgZnVuY3Rpb24gc2VuZEVycm9yKFxuICByZXM6IFJlc3BvbnNlLFxuICBzdGF0dXNDb2RlOiBudW1iZXIsXG4gIG1lc3NhZ2U6IHN0cmluZyxcbiAgY29kZT86IEVycm9yQ29kZSB8IHN0cmluZyxcbiAgZGV0YWlscz86IHVua25vd24sXG4pOiB2b2lkIHtcbiAgaWYgKHJlcy5oZWFkZXJzU2VudCkge1xuICAgIGxvZ2dlci53YXJuKCdBdHRlbXB0ZWQgdG8gc2VuZCBlcnJvciBhZnRlciBoZWFkZXJzIGFscmVhZHkgc2VudCcsIHsgc3RhdHVzQ29kZSwgbWVzc2FnZSB9KTtcbiAgICByZXR1cm47XG4gIH1cblxuICBjb25zdCByZXNwb25zZToge1xuICAgIHN1Y2Nlc3M6IGZhbHNlO1xuICAgIHN0YXR1c0NvZGU6IG51bWJlcjtcbiAgICBtZXNzYWdlOiBzdHJpbmc7XG4gICAgY29kZT86IHN0cmluZztcbiAgICBkZXRhaWxzPzogdW5rbm93bjtcbiAgfSA9IHtcbiAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICBzdGF0dXNDb2RlLFxuICAgIG1lc3NhZ2UsXG4gIH07XG5cbiAgaWYgKGNvZGUpIHtcbiAgICByZXNwb25zZS5jb2RlID0gY29kZTtcbiAgfVxuXG4gIGlmIChkZXRhaWxzICE9PSB1bmRlZmluZWQpIHtcbiAgICByZXNwb25zZS5kZXRhaWxzID0gZGV0YWlscztcbiAgfVxuXG4gIHJlcy5zdGF0dXMoc3RhdHVzQ29kZSkuanNvbihyZXNwb25zZSk7XG59XG5cbi8qKlxuICogU2VuZCBhIHF1b3RhIGV4Y2VlZGVkIGVycm9yIHJlc3BvbnNlLlxuICpcbiAqIEBwYXJhbSByZXMgLSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdFxuICogQHBhcmFtIHF1b3RhVHlwZSAtIFR5cGUgb2YgcXVvdGEgZXhjZWVkZWRcbiAqIEBwYXJhbSBxdW90YSAtIEN1cnJlbnQgcXVvdGEgaW5mb3JtYXRpb25cbiAqIEBwYXJhbSByZXNldEF0IC0gSVNPIHRpbWVzdGFtcCB3aGVuIHF1b3RhIHJlc2V0c1xuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiBzZW5kUXVvdGFFeGNlZWRlZChyZXMsICdhcGlDYWxscycsIHsgdHlwZTogJ2FwaUNhbGxzJywgbGltaXQ6IDEwMDAwLCB1c2VkOiAxMDAwMCwgcmVtYWluaW5nOiAwIH0sIHJlc2V0QXQpO1xuICogYGBgXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzZW5kUXVvdGFFeGNlZWRlZChcbiAgcmVzOiBSZXNwb25zZSxcbiAgcXVvdGFUeXBlOiBzdHJpbmcsXG4gIHF1b3RhOiBRdW90YUluZm8sXG4gIHJlc2V0QXQ/OiBzdHJpbmcsXG4gIG1lc3NhZ2U/OiBzdHJpbmcsXG4pOiB2b2lkIHtcbiAgY29uc3QgcmVzZXREYXRlID0gcmVzZXRBdCA/IG5ldyBEYXRlKHJlc2V0QXQpIDogbmV3IERhdGUoKTtcbiAgY29uc3QgcmVzZXRJbiA9IE1hdGgubWF4KDAsIE1hdGguY2VpbCgocmVzZXREYXRlLmdldFRpbWUoKSAtIERhdGUubm93KCkpIC8gMTAwMCkpO1xuXG4gIC8vIEd1YXJkIGxpa2UgdGhlIHNpYmxpbmcgaGVscGVyczogYHNldEhlYWRlcmAgdGhyb3dzIHN5bmNocm9ub3VzbHkgb25jZSB0aGVcbiAgLy8gcmVzcG9uc2UgaGFzIGJlZW4gc2VudCwgc28gc2tpcCB0aGUgcXVvdGEgaGVhZGVycyAoYW5kIGxldCBgc2VuZEVycm9yYCBuby1vcClcbiAgLy8gaW5zdGVhZCBvZiBjcmFzaGluZyB0aGUgaGFuZGxlciBvbiBhbiBhbHJlYWR5LWNvbW1pdHRlZCByZXNwb25zZS5cbiAgaWYgKCFyZXMuaGVhZGVyc1NlbnQpIHtcbiAgICByZXMuc2V0SGVhZGVyKCdSZXRyeS1BZnRlcicsIHJlc2V0SW4pO1xuICAgIHJlcy5zZXRIZWFkZXIoJ1gtUXVvdGEtTGltaXQnLCBxdW90YS5saW1pdCk7XG4gICAgcmVzLnNldEhlYWRlcignWC1RdW90YS1Vc2VkJywgcXVvdGEudXNlZCk7XG4gICAgcmVzLnNldEhlYWRlcignWC1RdW90YS1SZW1haW5pbmcnLCBNYXRoLm1heCgwLCBxdW90YS5yZW1haW5pbmcpKTtcblxuICAgIGlmIChyZXNldEF0KSB7XG4gICAgICByZXMuc2V0SGVhZGVyKCdYLVF1b3RhLVJlc2V0JywgcmVzZXRBdCk7XG4gICAgfVxuICB9XG5cbiAgc2VuZEVycm9yKFxuICAgIHJlcyxcbiAgICA0MjksXG4gICAgbWVzc2FnZSA/PyBgJHtxdW90YVR5cGV9IHF1b3RhIGV4Y2VlZGVkICgke3F1b3RhLnVzZWR9LyR7cXVvdGEubGltaXR9KS4gUGxlYXNlIHRyeSBhZ2FpbiBsYXRlci5gLFxuICAgIEVycm9yQ29kZS5RVU9UQV9FWENFRURFRCxcbiAgICB7IHF1b3RhIH0sXG4gICk7XG59XG5cbi8qKlxuICogU2VuZCBhIHBhZ2luYXRlZCBsaXN0IHJlc3BvbnNlIHdpdGggdGhlIGl0ZW1zIG5lc3RlZCB1bmRlciBhIG5hbWVkIGtleS5cbiAqIFVzZSB3aGVuIHRoZSBBUEkgY29udHJhY3QgaXMgYHsgPGtleT46IFsuLi5dLCBwYWdpbmF0aW9uOiB7Li4ufSB9YFxuICogKHRoZSBkb21pbmFudCBzaGFwZSBhY3Jvc3MgYXBpLyogc2VydmljZXMpLlxuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiBzZW5kUGFnaW5hdGVkTmVzdGVkKHJlcywgJ3BpcGVsaW5lcycsIGl0ZW1zLCB7XG4gKiAgIHRvdGFsOiA1MCwgbGltaXQ6IDI1LCBvZmZzZXQ6IDAsIGhhc01vcmU6IHRydWUsXG4gKiB9KTtcbiAqIC8vIOKGkiB7IHBpcGVsaW5lczogWy4uLl0sIHBhZ2luYXRpb246IHsgdG90YWw6IDUwLCBsaW1pdDogMjUsIG9mZnNldDogMCwgaGFzTW9yZTogdHJ1ZSB9IH1cbiAqIGBgYFxuICovXG5leHBvcnQgZnVuY3Rpb24gc2VuZFBhZ2luYXRlZE5lc3RlZDxUPihcbiAgcmVzOiBSZXNwb25zZSxcbiAgZGF0YUtleTogc3RyaW5nLFxuICBkYXRhOiBUW10sXG4gIG9wdGlvbnM6IHtcbiAgICB0b3RhbD86IG51bWJlcjtcbiAgICBsaW1pdDogbnVtYmVyO1xuICAgIG9mZnNldDogbnVtYmVyO1xuICAgIGhhc01vcmU6IGJvb2xlYW47XG4gICAgbmV4dEN1cnNvcj86IHN0cmluZztcbiAgICBzdGF0dXNDb2RlPzogbnVtYmVyO1xuICB9LFxuKTogdm9pZCB7XG4gIGlmIChyZXMuaGVhZGVyc1NlbnQpIHtcbiAgICBsb2dnZXIud2FybignQXR0ZW1wdGVkIHRvIHNlbmQgcGFnaW5hdGVkIHJlc3BvbnNlIGFmdGVyIGhlYWRlcnMgYWxyZWFkeSBzZW50Jyk7XG4gICAgcmV0dXJuO1xuICB9XG4gIGNvbnN0IHsgc3RhdHVzQ29kZSA9IDIwMCwgdG90YWwsIGxpbWl0LCBvZmZzZXQsIGhhc01vcmUsIG5leHRDdXJzb3IgfSA9IG9wdGlvbnM7XG4gIGNvbnN0IHBhZ2luYXRpb246IFJlY29yZDxzdHJpbmcsIHVua25vd24+ID0geyBsaW1pdCwgb2Zmc2V0LCBoYXNNb3JlIH07XG4gIGlmICh0b3RhbCAhPT0gdW5kZWZpbmVkKSBwYWdpbmF0aW9uLnRvdGFsID0gdG90YWw7XG4gIGlmIChuZXh0Q3Vyc29yKSBwYWdpbmF0aW9uLm5leHRDdXJzb3IgPSBuZXh0Q3Vyc29yO1xuICByZXMuc3RhdHVzKHN0YXR1c0NvZGUpLmpzb24oe1xuICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgc3RhdHVzQ29kZSxcbiAgICBkYXRhOiB7IFtkYXRhS2V5XTogZGF0YSwgcGFnaW5hdGlvbiB9LFxuICB9KTtcbn1cblxuLyoqXG4gKiBFeHRyYWN0IGRhdGFiYXNlIGVycm9yIGRldGFpbHMgZm9yIGxvZ2dpbmcvcmVzcG9uc2UuXG4gKlxuICogRXh0cmFjdHMgUG9zdGdyZVNRTCBlcnJvciBjb2RlcywgZGV0YWlscywgaGludHMsIGFuZCBjb25zdHJhaW50IG5hbWVzXG4gKiBmcm9tIGRhdGFiYXNlIGVycm9ycyBmb3IgYmV0dGVyIGVycm9yIG1lc3NhZ2VzLlxuICpcbiAqIEBwYXJhbSBlcnJvciAtIEVycm9yIG9iamVjdCBmcm9tIGRhdGFiYXNlIG9wZXJhdGlvblxuICogQHJldHVybnMgT2JqZWN0IHdpdGggZXh0cmFjdGVkIGVycm9yIGRldGFpbHNcbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogdHJ5IHtcbiAqICAgYXdhaXQgZGIuaW5zZXJ0KHBsdWdpbnMpLnZhbHVlcyhuZXdQbHVnaW4pO1xuICogfSBjYXRjaCAoZXJyb3IpIHtcbiAqICAgY29uc3QgZGJEZXRhaWxzID0gZXh0cmFjdERiRXJyb3IoZXJyb3IpO1xuICogICByZXR1cm4gc2VuZEVycm9yKHJlcywgNTAwLCAnRGF0YWJhc2UgZXJyb3InLCBFcnJvckNvZGUuREFUQUJBU0VfRVJST1IsIGRiRGV0YWlscyk7XG4gKiB9XG4gKiBgYGBcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGV4dHJhY3REYkVycm9yKGVycm9yOiB1bmtub3duKTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4ge1xuICBpZiAoIWVycm9yIHx8IHR5cGVvZiBlcnJvciAhPT0gJ29iamVjdCcpIHtcbiAgICByZXR1cm4ge307XG4gIH1cblxuICAvLyBEcml6emxlIE9STSB3cmFwcyB0aGUgb3JpZ2luYWwgcGcgZXJyb3IgaW4gZXJyb3IuY2F1c2Ug4oCUXG4gIC8vIGNoZWNrIHRoZSBjYXVzZSBmaXJzdCwgdGhlbiBmYWxsIGJhY2sgdG8gdGhlIGVycm9yIGl0c2VsZi5cbiAgY29uc3QgZXJyID0gZXJyb3IgYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj47XG4gIGNvbnN0IHNvdXJjZSA9IChlcnIuY2F1c2UgJiYgdHlwZW9mIGVyci5jYXVzZSA9PT0gJ29iamVjdCcgPyBlcnIuY2F1c2UgOiBlcnIpIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuICBjb25zdCBkZXRhaWxzOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiA9IHt9O1xuXG4gIGlmIChzb3VyY2UuY29kZSkgZGV0YWlscy5kYkNvZGUgPSBzb3VyY2UuY29kZTtcbiAgaWYgKHNvdXJjZS5kZXRhaWwpIGRldGFpbHMuZGJEZXRhaWwgPSBzb3VyY2UuZGV0YWlsO1xuICBpZiAoc291cmNlLmhpbnQpIGRldGFpbHMuZGJIaW50ID0gc291cmNlLmhpbnQ7XG4gIGlmIChzb3VyY2UuY29uc3RyYWludCkgZGV0YWlscy5jb25zdHJhaW50ID0gc291cmNlLmNvbnN0cmFpbnQ7XG4gIGlmIChzb3VyY2UudGFibGUpIGRldGFpbHMudGFibGUgPSBzb3VyY2UudGFibGU7XG4gIGlmIChzb3VyY2UuY29sdW1uKSBkZXRhaWxzLmNvbHVtbiA9IHNvdXJjZS5jb2x1bW47XG5cbiAgcmV0dXJuIGRldGFpbHM7XG59XG5cbi8vIENvbnZlbmllbmNlIGhlbHBlcnMgKHNoYXJlZCBhY3Jvc3MgcGx1Z2luLCBwaXBlbGluZSwgcXVvdGEgc2VydmljZXMpXG5cbi8qKlxuICogRXh0cmFjdCBhIGh1bWFuLXJlYWRhYmxlIG1lc3NhZ2UgZnJvbSBhbiB1bmtub3duIGNhdGNoIHZhbHVlLlxuICpcbiAqIEZhbGxzIGJhY2sgYmV5b25kIGAubWVzc2FnZWAgYmVjYXVzZSBzZXZlcmFsIGNvbW1vbiBOb2RlIGVycm9ycyBjYXJyeSBhblxuICogRU1QVFkgYC5tZXNzYWdlYDpcbiAqICAtIGR1YWwtc3RhY2sgKElQdjQrSVB2NikgY29ubmVjdCBmYWlsdXJlcyBzdXJmYWNlIGFzIGFuIGBBZ2dyZWdhdGVFcnJvcmBcbiAqICAgIHdob3NlIG93biBtZXNzYWdlIGlzIGJsYW5rIOKAlCB0aGUgY2F1c2UgbGl2ZXMgaW4gYC5jb2RlYCAoZS5nLlxuICogICAgYEVDT05OUkVGVVNFRGAvYEVUSU1FRE9VVGApIGFuZC9vciB0aGUgYC5lcnJvcnNbXWAgc3ViLWVycm9ycztcbiAqICAtIHJhdyBzb2NrZXQgYEVycm5vRXhjZXB0aW9uYHMgY2FuIGxpa2V3aXNlIGNhcnJ5IG9ubHkgYSBgLmNvZGVgLlxuICogV2l0aG91dCB0aGlzLCBhbiB1bnJlYWNoYWJsZSBpbnRlcm5hbCBzZXJ2aWNlIChlLmcuIGNvbXBsaWFuY2Ugc3RpbGxcbiAqIHN0YXJ0aW5nKSBwcm9kdWNlZCBibGFuayBwZXItaXRlbSBlcnJvcnMgaW4gYnVsayByZXNwb25zZXMgYW5kIGBlcnJvcjpcIlwiYFxuICogaW4gbG9ncyDigJQgYSBzaWxlbnQgYmxhY2sgYm94LiBPcmRlcjogbWVzc2FnZSDihpIgY29kZSDihpIgam9pbmVkIHN1Yi1lcnJvcnMg4oaSXG4gKiBlcnJvciBuYW1lLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZXJyb3JNZXNzYWdlKGVycm9yOiB1bmtub3duKTogc3RyaW5nIHtcbiAgaWYgKCEoZXJyb3IgaW5zdGFuY2VvZiBFcnJvcikpIHJldHVybiBTdHJpbmcoZXJyb3IpO1xuICBpZiAoZXJyb3IubWVzc2FnZSkgcmV0dXJuIGVycm9yLm1lc3NhZ2U7XG5cbiAgY29uc3QgY29kZSA9IChlcnJvciBhcyB7IGNvZGU/OiBzdHJpbmcgfSkuY29kZTtcbiAgaWYgKGNvZGUpIHJldHVybiBjb2RlO1xuXG4gIGNvbnN0IGlubmVyID0gKGVycm9yIGFzIHsgZXJyb3JzPzogdW5rbm93bltdIH0pLmVycm9ycztcbiAgaWYgKEFycmF5LmlzQXJyYXkoaW5uZXIpICYmIGlubmVyLmxlbmd0aCA+IDApIHtcbiAgICBjb25zdCBqb2luZWQgPSBpbm5lci5tYXAoZXJyb3JNZXNzYWdlKS5maWx0ZXIoQm9vbGVhbikuam9pbignOyAnKTtcbiAgICBpZiAoam9pbmVkKSByZXR1cm4gam9pbmVkO1xuICB9XG5cbiAgcmV0dXJuIGVycm9yLm5hbWUgfHwgJ1Vua25vd24gZXJyb3InO1xufVxuXG4vKiogU2VuZCBhIDQwMCBiYWQtcmVxdWVzdCByZXNwb25zZS4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzZW5kQmFkUmVxdWVzdChcbiAgcmVzOiBSZXNwb25zZSxcbiAgbWVzc2FnZTogc3RyaW5nLFxuICBjb2RlOiBFcnJvckNvZGUgfCBzdHJpbmcgPSBFcnJvckNvZGUuVkFMSURBVElPTl9FUlJPUixcbik6IHZvaWQge1xuICBzZW5kRXJyb3IocmVzLCA0MDAsIG1lc3NhZ2UsIGNvZGUpO1xufVxuXG4vKiogU2VuZCBhIDUwMCBpbnRlcm5hbCBlcnJvciByZXNwb25zZS4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzZW5kSW50ZXJuYWxFcnJvcihcbiAgcmVzOiBSZXNwb25zZSxcbiAgbWVzc2FnZTogc3RyaW5nLFxuICBkZXRhaWxzPzogUmVjb3JkPHN0cmluZywgdW5rbm93bj4sXG4pOiB2b2lkIHtcbiAgc2VuZEVycm9yKHJlcywgNTAwLCBtZXNzYWdlLCBFcnJvckNvZGUuSU5URVJOQUxfRVJST1IsIGRldGFpbHMpO1xufVxuXG4vLyBQYWdpbmF0aW9uXG5cbi8qKiBDb21tb24gcGFnaW5hdGlvbiArIHNvcnRpbmcgcGFyYW1ldGVycy4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUGFnaW5hdGlvblBhcmFtcyB7XG4gIGxpbWl0OiBudW1iZXI7XG4gIG9mZnNldDogbnVtYmVyO1xuICBzb3J0Qnk6IHN0cmluZztcbiAgc29ydE9yZGVyOiAnYXNjJyB8ICdkZXNjJztcbn1cblxuLyoqIFBhcnNlIHBhZ2luYXRpb24vc29ydCBwYXJhbXMgZnJvbSBxdWVyeSBzdHJpbmcsIGNsYW1waW5nIHRvIHNhZmUgZGVmYXVsdHMuICovXG5leHBvcnQgZnVuY3Rpb24gcGFyc2VQYWdpbmF0aW9uUGFyYW1zKHF1ZXJ5OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPik6IFBhZ2luYXRpb25QYXJhbXMge1xuICBjb25zdCBsaW1pdCA9IE1hdGgubWluKE1hdGgubWF4KHBhcnNlSW50KFN0cmluZyhxdWVyeS5saW1pdCksIDEwKSB8fCAxMCwgMSksIE1BWF9QQUdFX0xJTUlUKTtcbiAgLy8gQ2xhbXAgb2Zmc2V0IHRvbyDigJQgYW4gdW5ib3VuZGVkIG9mZnNldCBmb3JjZXMgUG9zdGdyZXMgdG8gc2NhbitkaXNjYXJkIGh1Z2Ugcm93XG4gIC8vIGNvdW50cyBvbiBob3QgbGlzdCBlbmRwb2ludHMgKGEgY2hlYXAgRG9TIGFtcGxpZmllcikuIERlZXAgcGFnaW5nIHNob3VsZCB1c2UgdGhlXG4gIC8vIGN1cnNvciBBUEkuIENlaWxpbmcgPSBNQVhfUEFHRV9PRkZTRVQuXG4gIGNvbnN0IG9mZnNldCA9IE1hdGgubWluKE1hdGgubWF4KHBhcnNlSW50KFN0cmluZyhxdWVyeS5vZmZzZXQpLCAxMCkgfHwgMCwgMCksIE1BWF9QQUdFX09GRlNFVCk7XG4gIGNvbnN0IHNvcnRCeSA9IFN0cmluZyhxdWVyeS5zb3J0QnkgfHwgJ2NyZWF0ZWRBdCcpO1xuICBjb25zdCBzb3J0T3JkZXI6ICdhc2MnIHwgJ2Rlc2MnID1cbiAgICBTdHJpbmcocXVlcnkuc29ydE9yZGVyIHx8ICdkZXNjJykudG9Mb3dlckNhc2UoKSA9PT0gJ2FzYycgPyAnYXNjJyA6ICdkZXNjJztcblxuICByZXR1cm4geyBsaW1pdCwgb2Zmc2V0LCBzb3J0QnksIHNvcnRPcmRlciB9O1xufVxuIl19
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Build a CommonJS `require` for synchronously loading OPTIONAL installed
3
+ * packages from ESM code (e.g. prom-client, @opentelemetry/api, ioredis behind
4
+ * a try/catch).
5
+ *
6
+ * Pass `import.meta.url`. When this ESM source is bundled to CJS — as the CDK
7
+ * Lambda handlers are — esbuild replaces `import.meta` with `{}`, so the value
8
+ * is `undefined` and a bare `createRequire(import.meta.url)` throws at module
9
+ * load ("The argument 'filename' must be a file URL… Received undefined"). We
10
+ * fall back to the process entry path so there's always a valid resolution base
11
+ * (the base only needs to exist within the project for node_modules lookup).
12
+ */
13
+ export declare function safeCreateRequire(metaUrl: string | undefined): NodeRequire;
@@ -0,0 +1,19 @@
1
+ // Copyright 2026 Pipeline Builder Contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { createRequire } from 'node:module';
4
+ /**
5
+ * Build a CommonJS `require` for synchronously loading OPTIONAL installed
6
+ * packages from ESM code (e.g. prom-client, @opentelemetry/api, ioredis behind
7
+ * a try/catch).
8
+ *
9
+ * Pass `import.meta.url`. When this ESM source is bundled to CJS — as the CDK
10
+ * Lambda handlers are — esbuild replaces `import.meta` with `{}`, so the value
11
+ * is `undefined` and a bare `createRequire(import.meta.url)` throws at module
12
+ * load ("The argument 'filename' must be a file URL… Received undefined"). We
13
+ * fall back to the process entry path so there's always a valid resolution base
14
+ * (the base only needs to exist within the project for node_modules lookup).
15
+ */
16
+ export function safeCreateRequire(metaUrl) {
17
+ return createRequire(metaUrl || process.argv[1] || `${process.cwd()}/index.js`);
18
+ }
19
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2FmZS1yZXF1aXJlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3V0aWxzL3NhZmUtcmVxdWlyZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFFNUM7Ozs7Ozs7Ozs7O0dBV0c7QUFDSCxNQUFNLFVBQVUsaUJBQWlCLENBQUMsT0FBMkI7SUFDM0QsT0FBTyxhQUFhLENBQUMsT0FBTyxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUksR0FBRyxPQUFPLENBQUMsR0FBRyxFQUFFLFdBQVcsQ0FBQyxDQUFDO0FBQ2xGLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuaW1wb3J0IHsgY3JlYXRlUmVxdWlyZSB9IGZyb20gJ25vZGU6bW9kdWxlJztcblxuLyoqXG4gKiBCdWlsZCBhIENvbW1vbkpTIGByZXF1aXJlYCBmb3Igc3luY2hyb25vdXNseSBsb2FkaW5nIE9QVElPTkFMIGluc3RhbGxlZFxuICogcGFja2FnZXMgZnJvbSBFU00gY29kZSAoZS5nLiBwcm9tLWNsaWVudCwgQG9wZW50ZWxlbWV0cnkvYXBpLCBpb3JlZGlzIGJlaGluZFxuICogYSB0cnkvY2F0Y2gpLlxuICpcbiAqIFBhc3MgYGltcG9ydC5tZXRhLnVybGAuIFdoZW4gdGhpcyBFU00gc291cmNlIGlzIGJ1bmRsZWQgdG8gQ0pTIOKAlCBhcyB0aGUgQ0RLXG4gKiBMYW1iZGEgaGFuZGxlcnMgYXJlIOKAlCBlc2J1aWxkIHJlcGxhY2VzIGBpbXBvcnQubWV0YWAgd2l0aCBge31gLCBzbyB0aGUgdmFsdWVcbiAqIGlzIGB1bmRlZmluZWRgIGFuZCBhIGJhcmUgYGNyZWF0ZVJlcXVpcmUoaW1wb3J0Lm1ldGEudXJsKWAgdGhyb3dzIGF0IG1vZHVsZVxuICogbG9hZCAoXCJUaGUgYXJndW1lbnQgJ2ZpbGVuYW1lJyBtdXN0IGJlIGEgZmlsZSBVUkzigKYgUmVjZWl2ZWQgdW5kZWZpbmVkXCIpLiBXZVxuICogZmFsbCBiYWNrIHRvIHRoZSBwcm9jZXNzIGVudHJ5IHBhdGggc28gdGhlcmUncyBhbHdheXMgYSB2YWxpZCByZXNvbHV0aW9uIGJhc2VcbiAqICh0aGUgYmFzZSBvbmx5IG5lZWRzIHRvIGV4aXN0IHdpdGhpbiB0aGUgcHJvamVjdCBmb3Igbm9kZV9tb2R1bGVzIGxvb2t1cCkuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzYWZlQ3JlYXRlUmVxdWlyZShtZXRhVXJsOiBzdHJpbmcgfCB1bmRlZmluZWQpOiBOb2RlUmVxdWlyZSB7XG4gIHJldHVybiBjcmVhdGVSZXF1aXJlKG1ldGFVcmwgfHwgcHJvY2Vzcy5hcmd2WzFdIHx8IGAke3Byb2Nlc3MuY3dkKCl9L2luZGV4LmpzYCk7XG59XG4iXX0=