@pipeline-builder/api-core 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +51 -0
- package/lib/constants/ai-providers.d.ts +41 -0
- package/lib/constants/ai-providers.js +88 -0
- package/lib/constants/http-status.d.ts +24 -0
- package/lib/constants/http-status.js +29 -0
- package/lib/constants/index.d.ts +3 -0
- package/lib/constants/index.js +22 -0
- package/lib/constants/time.d.ts +10 -0
- package/lib/constants/time.js +16 -0
- package/lib/errors/app-errors.d.ts +30 -0
- package/lib/errors/app-errors.js +62 -0
- package/lib/errors/index.d.ts +1 -0
- package/lib/errors/index.js +20 -0
- package/lib/helpers/access-helpers.d.ts +40 -0
- package/lib/helpers/access-helpers.js +56 -0
- package/lib/helpers/crud-helpers.d.ts +16 -0
- package/lib/helpers/crud-helpers.js +34 -0
- package/lib/helpers/index.d.ts +4 -0
- package/lib/helpers/index.js +23 -0
- package/lib/helpers/mask-helpers.d.ts +33 -0
- package/lib/helpers/mask-helpers.js +54 -0
- package/lib/helpers/sse-helpers.d.ts +13 -0
- package/lib/helpers/sse-helpers.js +40 -0
- package/lib/index.d.ts +57 -0
- package/lib/index.js +86 -0
- package/lib/middleware/auth.d.ts +50 -0
- package/lib/middleware/auth.js +171 -0
- package/lib/middleware/index.d.ts +1 -0
- package/lib/middleware/index.js +20 -0
- package/lib/openapi/extend-zod.d.ts +1 -0
- package/lib/openapi/extend-zod.js +8 -0
- package/lib/openapi/index.d.ts +2 -0
- package/lib/openapi/index.js +10 -0
- package/lib/openapi/registry.d.ts +17 -0
- package/lib/openapi/registry.js +42 -0
- package/lib/openapi/routes/billing-routes.d.ts +1 -0
- package/lib/openapi/routes/billing-routes.js +69 -0
- package/lib/openapi/routes/index.d.ts +5 -0
- package/lib/openapi/routes/index.js +22 -0
- package/lib/openapi/routes/message-routes.d.ts +1 -0
- package/lib/openapi/routes/message-routes.js +108 -0
- package/lib/openapi/routes/pipeline-routes.d.ts +1 -0
- package/lib/openapi/routes/pipeline-routes.js +90 -0
- package/lib/openapi/routes/plugin-routes.d.ts +1 -0
- package/lib/openapi/routes/plugin-routes.js +99 -0
- package/lib/openapi/routes/quota-routes.d.ts +1 -0
- package/lib/openapi/routes/quota-routes.js +65 -0
- package/lib/openapi/schema-registry.d.ts +25 -0
- package/lib/openapi/schema-registry.js +95 -0
- package/lib/routes/health.d.ts +47 -0
- package/lib/routes/health.js +81 -0
- package/lib/routes/index.d.ts +1 -0
- package/lib/routes/index.js +20 -0
- package/lib/services/admin-audit.d.ts +13 -0
- package/lib/services/admin-audit.js +31 -0
- package/lib/services/cache-service.d.ts +108 -0
- package/lib/services/cache-service.js +212 -0
- package/lib/services/compliance-client.d.ts +46 -0
- package/lib/services/compliance-client.js +102 -0
- package/lib/services/compliance-event-subscriber.d.ts +11 -0
- package/lib/services/compliance-event-subscriber.js +60 -0
- package/lib/services/compliance-queue.d.ts +11 -0
- package/lib/services/compliance-queue.js +38 -0
- package/lib/services/entity-events.d.ts +44 -0
- package/lib/services/entity-events.js +63 -0
- package/lib/services/http-client.d.ts +108 -0
- package/lib/services/http-client.js +285 -0
- package/lib/services/index.d.ts +10 -0
- package/lib/services/index.js +40 -0
- package/lib/services/quota.d.ts +59 -0
- package/lib/services/quota.js +137 -0
- package/lib/services/retry-strategy.d.ts +74 -0
- package/lib/services/retry-strategy.js +127 -0
- package/lib/types/billing.d.ts +47 -0
- package/lib/types/billing.js +5 -0
- package/lib/types/common.d.ts +161 -0
- package/lib/types/common.js +53 -0
- package/lib/types/error-codes.d.ts +38 -0
- package/lib/types/error-codes.js +77 -0
- package/lib/types/feature-flags.d.ts +38 -0
- package/lib/types/feature-flags.js +107 -0
- package/lib/types/http.d.ts +37 -0
- package/lib/types/http.js +5 -0
- package/lib/types/index.d.ts +7 -0
- package/lib/types/index.js +26 -0
- package/lib/types/pipeline.d.ts +70 -0
- package/lib/types/pipeline.js +44 -0
- package/lib/types/quota-tiers.d.ts +23 -0
- package/lib/types/quota-tiers.js +26 -0
- package/lib/utils/alias-resolver.d.ts +16 -0
- package/lib/utils/alias-resolver.js +49 -0
- package/lib/utils/headers.d.ts +18 -0
- package/lib/utils/headers.js +24 -0
- package/lib/utils/identity.d.ts +61 -0
- package/lib/utils/identity.js +75 -0
- package/lib/utils/index.d.ts +7 -0
- package/lib/utils/index.js +26 -0
- package/lib/utils/logger.d.ts +28 -0
- package/lib/utils/logger.js +77 -0
- package/lib/utils/object.d.ts +13 -0
- package/lib/utils/object.js +21 -0
- package/lib/utils/params.d.ts +89 -0
- package/lib/utils/params.js +148 -0
- package/lib/utils/response.d.ts +142 -0
- package/lib/utils/response.js +237 -0
- package/lib/validation/ai-schemas.d.ts +61 -0
- package/lib/validation/ai-schemas.js +81 -0
- package/lib/validation/common-schemas.d.ts +72 -0
- package/lib/validation/common-schemas.js +58 -0
- package/lib/validation/index.d.ts +6 -0
- package/lib/validation/index.js +25 -0
- package/lib/validation/message-schemas.d.ts +79 -0
- package/lib/validation/message-schemas.js +42 -0
- package/lib/validation/middleware.d.ts +60 -0
- package/lib/validation/middleware.js +77 -0
- package/lib/validation/pipeline-schemas.d.ts +135 -0
- package/lib/validation/pipeline-schemas.js +85 -0
- package/lib/validation/plugin-schemas.d.ts +127 -0
- package/lib/validation/plugin-schemas.js +84 -0
- package/openapi.yaml +292 -0
- package/package.json +127 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright 2026 Pipeline Builder Contributors
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.getParam = getParam;
|
|
6
|
+
exports.getOrgId = getOrgId;
|
|
7
|
+
exports.getAuthHeader = getAuthHeader;
|
|
8
|
+
exports.parseQueryBoolean = parseQueryBoolean;
|
|
9
|
+
exports.parseQueryInt = parseQueryInt;
|
|
10
|
+
exports.parseQueryString = parseQueryString;
|
|
11
|
+
exports.parsePositiveInt = parsePositiveInt;
|
|
12
|
+
const headers_1 = require("./headers");
|
|
13
|
+
/**
|
|
14
|
+
* Extract a single string parameter from Express 5 route params.
|
|
15
|
+
*
|
|
16
|
+
* @param params - Request params object
|
|
17
|
+
* @param key - Parameter key
|
|
18
|
+
* @returns The parameter value as a string, or undefined if not present
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* // Route: GET /plugins/:id
|
|
23
|
+
* const id = getParam(req.params, 'id');
|
|
24
|
+
* if (!id) {
|
|
25
|
+
* return sendError(res, 400, 'Missing id parameter');
|
|
26
|
+
* }
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
function getParam(params, key) {
|
|
30
|
+
const value = params[key];
|
|
31
|
+
if (value === undefined) {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
return Array.isArray(value) ? value[0] : value;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Extract the organization ID from request.
|
|
38
|
+
* Checks params, headers, and user object.
|
|
39
|
+
*
|
|
40
|
+
* @param req - Express request object
|
|
41
|
+
* @returns Organization ID or undefined
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```typescript
|
|
45
|
+
* const orgId = getOrgId(req);
|
|
46
|
+
* if (!orgId) {
|
|
47
|
+
* return sendError(res, 400, 'Organization ID required');
|
|
48
|
+
* }
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
function getOrgId(req) {
|
|
52
|
+
// Check route params first
|
|
53
|
+
const paramOrgId = getParam(req.params, 'orgId');
|
|
54
|
+
if (paramOrgId) {
|
|
55
|
+
return paramOrgId;
|
|
56
|
+
}
|
|
57
|
+
// Check x-org-id header
|
|
58
|
+
const headerOrgId = (0, headers_1.getHeaderString)(req.headers['x-org-id'])?.trim();
|
|
59
|
+
if (headerOrgId) {
|
|
60
|
+
return headerOrgId;
|
|
61
|
+
}
|
|
62
|
+
// Check authenticated user
|
|
63
|
+
const userOrgId = req.user?.organizationId?.trim();
|
|
64
|
+
if (userOrgId) {
|
|
65
|
+
return userOrgId;
|
|
66
|
+
}
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Get the authorization header from request.
|
|
71
|
+
*
|
|
72
|
+
* @param req - Express request object
|
|
73
|
+
* @returns Authorization header value or empty string
|
|
74
|
+
*/
|
|
75
|
+
function getAuthHeader(req) {
|
|
76
|
+
return (0, headers_1.getHeaderString)(req.headers.authorization) ?? '';
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Parse a query parameter as a boolean.
|
|
80
|
+
*
|
|
81
|
+
* @param value - Query parameter value
|
|
82
|
+
* @returns Parsed boolean or undefined
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```typescript
|
|
86
|
+
* const isActive = parseQueryBoolean(req.query.isActive);
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
function parseQueryBoolean(value) {
|
|
90
|
+
if (value === undefined || value === null || value === '')
|
|
91
|
+
return undefined;
|
|
92
|
+
if (typeof value === 'boolean')
|
|
93
|
+
return value;
|
|
94
|
+
if (typeof value === 'string') {
|
|
95
|
+
const lower = value.toLowerCase();
|
|
96
|
+
if (lower === 'true' || lower === '1')
|
|
97
|
+
return true;
|
|
98
|
+
if (lower === 'false' || lower === '0')
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Parse a query parameter as an integer.
|
|
105
|
+
*
|
|
106
|
+
* @param value - Query parameter value
|
|
107
|
+
* @param defaultValue - Default value if parsing fails
|
|
108
|
+
* @returns Parsed integer
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```typescript
|
|
112
|
+
* const limit = parseQueryInt(req.query.limit, 10);
|
|
113
|
+
* const offset = parseQueryInt(req.query.offset, 0);
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
function parseQueryInt(value, defaultValue) {
|
|
117
|
+
if (value === undefined || value === null || value === '')
|
|
118
|
+
return defaultValue;
|
|
119
|
+
const parsed = parseInt(String(value), 10);
|
|
120
|
+
return isNaN(parsed) ? defaultValue : parsed;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Parse a query parameter as a string.
|
|
124
|
+
*
|
|
125
|
+
* @param value - Query parameter value
|
|
126
|
+
* @returns String value or undefined
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* ```typescript
|
|
130
|
+
* const search = parseQueryString(req.query.search);
|
|
131
|
+
* ```
|
|
132
|
+
*/
|
|
133
|
+
function parseQueryString(value) {
|
|
134
|
+
if (value === undefined || value === null || value === '')
|
|
135
|
+
return undefined;
|
|
136
|
+
return String(value);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Parse a string to a positive integer, returning a fallback if invalid.
|
|
140
|
+
* Useful for parsing environment variables with numeric defaults.
|
|
141
|
+
*/
|
|
142
|
+
function parsePositiveInt(value, fallback) {
|
|
143
|
+
if (!value)
|
|
144
|
+
return fallback;
|
|
145
|
+
const parsed = parseInt(value, 10);
|
|
146
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGFyYW1zLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3V0aWxzL3BhcmFtcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUEsK0NBQStDO0FBQy9DLHNDQUFzQzs7QUEwQnRDLDRCQVNDO0FBaUJELDRCQW9CQztBQVFELHNDQUVDO0FBYUQsOENBU0M7QUFlRCxzQ0FJQztBQWFELDRDQUdDO0FBTUQsNENBSUM7QUFsSkQsdUNBQTRDO0FBTzVDOzs7Ozs7Ozs7Ozs7Ozs7R0FlRztBQUNILFNBQWdCLFFBQVEsQ0FDdEIsTUFBa0MsRUFDbEMsR0FBVztJQUVYLE1BQU0sS0FBSyxHQUFHLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUMxQixJQUFJLEtBQUssS0FBSyxTQUFTLEVBQUUsQ0FBQztRQUN4QixPQUFPLFNBQVMsQ0FBQztJQUNuQixDQUFDO0lBQ0QsT0FBTyxLQUFLLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQztBQUNqRCxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7Ozs7O0dBY0c7QUFDSCxTQUFnQixRQUFRLENBQUMsR0FBWTtJQUNuQywyQkFBMkI7SUFDM0IsTUFBTSxVQUFVLEdBQUcsUUFBUSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDakQsSUFBSSxVQUFVLEVBQUUsQ0FBQztRQUNmLE9BQU8sVUFBVSxDQUFDO0lBQ3BCLENBQUM7SUFFRCx3QkFBd0I7SUFDeEIsTUFBTSxXQUFXLEdBQUcsSUFBQSx5QkFBZSxFQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLENBQUMsRUFBRSxJQUFJLEVBQUUsQ0FBQztJQUNyRSxJQUFJLFdBQVcsRUFBRSxDQUFDO1FBQ2hCLE9BQU8sV0FBVyxDQUFDO0lBQ3JCLENBQUM7SUFFRCwyQkFBMkI7SUFDM0IsTUFBTSxTQUFTLEdBQUcsR0FBRyxDQUFDLElBQUksRUFBRSxjQUFjLEVBQUUsSUFBSSxFQUFFLENBQUM7SUFDbkQsSUFBSSxTQUFTLEVBQUUsQ0FBQztRQUNkLE9BQU8sU0FBUyxDQUFDO0lBQ25CLENBQUM7SUFFRCxPQUFPLFNBQVMsQ0FBQztBQUNuQixDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxTQUFnQixhQUFhLENBQUMsR0FBWTtJQUN4QyxPQUFPLElBQUEseUJBQWUsRUFBQyxHQUFHLENBQUMsT0FBTyxDQUFDLGFBQWEsQ0FBQyxJQUFJLEVBQUUsQ0FBQztBQUMxRCxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7R0FVRztBQUNILFNBQWdCLGlCQUFpQixDQUFDLEtBQWM7SUFDOUMsSUFBSSxLQUFLLEtBQUssU0FBUyxJQUFJLEtBQUssS0FBSyxJQUFJLElBQUksS0FBSyxLQUFLLEVBQUU7UUFBRSxPQUFPLFNBQVMsQ0FBQztJQUM1RSxJQUFJLE9BQU8sS0FBSyxLQUFLLFNBQVM7UUFBRSxPQUFPLEtBQUssQ0FBQztJQUM3QyxJQUFJLE9BQU8sS0FBSyxLQUFLLFFBQVEsRUFBRSxDQUFDO1FBQzlCLE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQyxXQUFXLEVBQUUsQ0FBQztRQUNsQyxJQUFJLEtBQUssS0FBSyxNQUFNLElBQUksS0FBSyxLQUFLLEdBQUc7WUFBRSxPQUFPLElBQUksQ0FBQztRQUNuRCxJQUFJLEtBQUssS0FBSyxPQUFPLElBQUksS0FBSyxLQUFLLEdBQUc7WUFBRSxPQUFPLEtBQUssQ0FBQztJQUN2RCxDQUFDO0lBQ0QsT0FBTyxTQUFTLENBQUM7QUFDbkIsQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7R0FZRztBQUNILFNBQWdCLGFBQWEsQ0FBQyxLQUFjLEVBQUUsWUFBb0I7SUFDaEUsSUFBSSxLQUFLLEtBQUssU0FBUyxJQUFJLEtBQUssS0FBSyxJQUFJLElBQUksS0FBSyxLQUFLLEVBQUU7UUFBRSxPQUFPLFlBQVksQ0FBQztJQUMvRSxNQUFNLE1BQU0sR0FBRyxRQUFRLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBQzNDLE9BQU8sS0FBSyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQztBQUMvQyxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7R0FVRztBQUNILFNBQWdCLGdCQUFnQixDQUFDLEtBQWM7SUFDN0MsSUFBSSxLQUFLLEtBQUssU0FBUyxJQUFJLEtBQUssS0FBSyxJQUFJLElBQUksS0FBSyxLQUFLLEVBQUU7UUFBRSxPQUFPLFNBQVMsQ0FBQztJQUM1RSxPQUFPLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztBQUN2QixDQUFDO0FBRUQ7OztHQUdHO0FBQ0gsU0FBZ0IsZ0JBQWdCLENBQUMsS0FBeUIsRUFBRSxRQUFnQjtJQUMxRSxJQUFJLENBQUMsS0FBSztRQUFFLE9BQU8sUUFBUSxDQUFDO0lBQzVCLE1BQU0sTUFBTSxHQUFHLFFBQVEsQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDbkMsT0FBTyxNQUFNLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxJQUFJLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDO0FBQ25FLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuaW1wb3J0IHsgUmVxdWVzdCB9IGZyb20gJ2V4cHJlc3MnO1xuaW1wb3J0IHsgZ2V0SGVhZGVyU3RyaW5nIH0gZnJvbSAnLi9oZWFkZXJzJztcblxuLyoqXG4gKiBFeHByZXNzIDUgcGFyYW1ldGVyIHR5cGUuXG4gKi9cbnR5cGUgUGFyYW1WYWx1ZSA9IHN0cmluZyB8IHN0cmluZ1tdIHwgdW5kZWZpbmVkO1xuXG4vKipcbiAqIEV4dHJhY3QgYSBzaW5nbGUgc3RyaW5nIHBhcmFtZXRlciBmcm9tIEV4cHJlc3MgNSByb3V0ZSBwYXJhbXMuXG4gKlxuICogQHBhcmFtIHBhcmFtcyAtIFJlcXVlc3QgcGFyYW1zIG9iamVjdFxuICogQHBhcmFtIGtleSAtIFBhcmFtZXRlciBrZXlcbiAqIEByZXR1cm5zIFRoZSBwYXJhbWV0ZXIgdmFsdWUgYXMgYSBzdHJpbmcsIG9yIHVuZGVmaW5lZCBpZiBub3QgcHJlc2VudFxuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiAvLyBSb3V0ZTogR0VUIC9wbHVnaW5zLzppZFxuICogY29uc3QgaWQgPSBnZXRQYXJhbShyZXEucGFyYW1zLCAnaWQnKTtcbiAqIGlmICghaWQpIHtcbiAqICAgcmV0dXJuIHNlbmRFcnJvcihyZXMsIDQwMCwgJ01pc3NpbmcgaWQgcGFyYW1ldGVyJyk7XG4gKiB9XG4gKiBgYGBcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldFBhcmFtKFxuICBwYXJhbXM6IFJlY29yZDxzdHJpbmcsIFBhcmFtVmFsdWU+LFxuICBrZXk6IHN0cmluZyxcbik6IHN0cmluZyB8IHVuZGVmaW5lZCB7XG4gIGNvbnN0IHZhbHVlID0gcGFyYW1zW2tleV07XG4gIGlmICh2YWx1ZSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgfVxuICByZXR1cm4gQXJyYXkuaXNBcnJheSh2YWx1ZSkgPyB2YWx1ZVswXSA6IHZhbHVlO1xufVxuXG4vKipcbiAqIEV4dHJhY3QgdGhlIG9yZ2FuaXphdGlvbiBJRCBmcm9tIHJlcXVlc3QuXG4gKiBDaGVja3MgcGFyYW1zLCBoZWFkZXJzLCBhbmQgdXNlciBvYmplY3QuXG4gKlxuICogQHBhcmFtIHJlcSAtIEV4cHJlc3MgcmVxdWVzdCBvYmplY3RcbiAqIEByZXR1cm5zIE9yZ2FuaXphdGlvbiBJRCBvciB1bmRlZmluZWRcbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogY29uc3Qgb3JnSWQgPSBnZXRPcmdJZChyZXEpO1xuICogaWYgKCFvcmdJZCkge1xuICogICByZXR1cm4gc2VuZEVycm9yKHJlcywgNDAwLCAnT3JnYW5pemF0aW9uIElEIHJlcXVpcmVkJyk7XG4gKiB9XG4gKiBgYGBcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldE9yZ0lkKHJlcTogUmVxdWVzdCk6IHN0cmluZyB8IHVuZGVmaW5lZCB7XG4gIC8vIENoZWNrIHJvdXRlIHBhcmFtcyBmaXJzdFxuICBjb25zdCBwYXJhbU9yZ0lkID0gZ2V0UGFyYW0ocmVxLnBhcmFtcywgJ29yZ0lkJyk7XG4gIGlmIChwYXJhbU9yZ0lkKSB7XG4gICAgcmV0dXJuIHBhcmFtT3JnSWQ7XG4gIH1cblxuICAvLyBDaGVjayB4LW9yZy1pZCBoZWFkZXJcbiAgY29uc3QgaGVhZGVyT3JnSWQgPSBnZXRIZWFkZXJTdHJpbmcocmVxLmhlYWRlcnNbJ3gtb3JnLWlkJ10pPy50cmltKCk7XG4gIGlmIChoZWFkZXJPcmdJZCkge1xuICAgIHJldHVybiBoZWFkZXJPcmdJZDtcbiAgfVxuXG4gIC8vIENoZWNrIGF1dGhlbnRpY2F0ZWQgdXNlclxuICBjb25zdCB1c2VyT3JnSWQgPSByZXEudXNlcj8ub3JnYW5pemF0aW9uSWQ/LnRyaW0oKTtcbiAgaWYgKHVzZXJPcmdJZCkge1xuICAgIHJldHVybiB1c2VyT3JnSWQ7XG4gIH1cblxuICByZXR1cm4gdW5kZWZpbmVkO1xufVxuXG4vKipcbiAqIEdldCB0aGUgYXV0aG9yaXphdGlvbiBoZWFkZXIgZnJvbSByZXF1ZXN0LlxuICpcbiAqIEBwYXJhbSByZXEgLSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0XG4gKiBAcmV0dXJucyBBdXRob3JpemF0aW9uIGhlYWRlciB2YWx1ZSBvciBlbXB0eSBzdHJpbmdcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldEF1dGhIZWFkZXIocmVxOiBSZXF1ZXN0KTogc3RyaW5nIHtcbiAgcmV0dXJuIGdldEhlYWRlclN0cmluZyhyZXEuaGVhZGVycy5hdXRob3JpemF0aW9uKSA/PyAnJztcbn1cblxuLyoqXG4gKiBQYXJzZSBhIHF1ZXJ5IHBhcmFtZXRlciBhcyBhIGJvb2xlYW4uXG4gKlxuICogQHBhcmFtIHZhbHVlIC0gUXVlcnkgcGFyYW1ldGVyIHZhbHVlXG4gKiBAcmV0dXJucyBQYXJzZWQgYm9vbGVhbiBvciB1bmRlZmluZWRcbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogY29uc3QgaXNBY3RpdmUgPSBwYXJzZVF1ZXJ5Qm9vbGVhbihyZXEucXVlcnkuaXNBY3RpdmUpO1xuICogYGBgXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBwYXJzZVF1ZXJ5Qm9vbGVhbih2YWx1ZTogdW5rbm93bik6IGJvb2xlYW4gfCB1bmRlZmluZWQge1xuICBpZiAodmFsdWUgPT09IHVuZGVmaW5lZCB8fCB2YWx1ZSA9PT0gbnVsbCB8fCB2YWx1ZSA9PT0gJycpIHJldHVybiB1bmRlZmluZWQ7XG4gIGlmICh0eXBlb2YgdmFsdWUgPT09ICdib29sZWFuJykgcmV0dXJuIHZhbHVlO1xuICBpZiAodHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJykge1xuICAgIGNvbnN0IGxvd2VyID0gdmFsdWUudG9Mb3dlckNhc2UoKTtcbiAgICBpZiAobG93ZXIgPT09ICd0cnVlJyB8fCBsb3dlciA9PT0gJzEnKSByZXR1cm4gdHJ1ZTtcbiAgICBpZiAobG93ZXIgPT09ICdmYWxzZScgfHwgbG93ZXIgPT09ICcwJykgcmV0dXJuIGZhbHNlO1xuICB9XG4gIHJldHVybiB1bmRlZmluZWQ7XG59XG5cbi8qKlxuICogUGFyc2UgYSBxdWVyeSBwYXJhbWV0ZXIgYXMgYW4gaW50ZWdlci5cbiAqXG4gKiBAcGFyYW0gdmFsdWUgLSBRdWVyeSBwYXJhbWV0ZXIgdmFsdWVcbiAqIEBwYXJhbSBkZWZhdWx0VmFsdWUgLSBEZWZhdWx0IHZhbHVlIGlmIHBhcnNpbmcgZmFpbHNcbiAqIEByZXR1cm5zIFBhcnNlZCBpbnRlZ2VyXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIGNvbnN0IGxpbWl0ID0gcGFyc2VRdWVyeUludChyZXEucXVlcnkubGltaXQsIDEwKTtcbiAqIGNvbnN0IG9mZnNldCA9IHBhcnNlUXVlcnlJbnQocmVxLnF1ZXJ5Lm9mZnNldCwgMCk7XG4gKiBgYGBcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHBhcnNlUXVlcnlJbnQodmFsdWU6IHVua25vd24sIGRlZmF1bHRWYWx1ZTogbnVtYmVyKTogbnVtYmVyIHtcbiAgaWYgKHZhbHVlID09PSB1bmRlZmluZWQgfHwgdmFsdWUgPT09IG51bGwgfHwgdmFsdWUgPT09ICcnKSByZXR1cm4gZGVmYXVsdFZhbHVlO1xuICBjb25zdCBwYXJzZWQgPSBwYXJzZUludChTdHJpbmcodmFsdWUpLCAxMCk7XG4gIHJldHVybiBpc05hTihwYXJzZWQpID8gZGVmYXVsdFZhbHVlIDogcGFyc2VkO1xufVxuXG4vKipcbiAqIFBhcnNlIGEgcXVlcnkgcGFyYW1ldGVyIGFzIGEgc3RyaW5nLlxuICpcbiAqIEBwYXJhbSB2YWx1ZSAtIFF1ZXJ5IHBhcmFtZXRlciB2YWx1ZVxuICogQHJldHVybnMgU3RyaW5nIHZhbHVlIG9yIHVuZGVmaW5lZFxuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiBjb25zdCBzZWFyY2ggPSBwYXJzZVF1ZXJ5U3RyaW5nKHJlcS5xdWVyeS5zZWFyY2gpO1xuICogYGBgXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBwYXJzZVF1ZXJ5U3RyaW5nKHZhbHVlOiB1bmtub3duKTogc3RyaW5nIHwgdW5kZWZpbmVkIHtcbiAgaWYgKHZhbHVlID09PSB1bmRlZmluZWQgfHwgdmFsdWUgPT09IG51bGwgfHwgdmFsdWUgPT09ICcnKSByZXR1cm4gdW5kZWZpbmVkO1xuICByZXR1cm4gU3RyaW5nKHZhbHVlKTtcbn1cblxuLyoqXG4gKiBQYXJzZSBhIHN0cmluZyB0byBhIHBvc2l0aXZlIGludGVnZXIsIHJldHVybmluZyBhIGZhbGxiYWNrIGlmIGludmFsaWQuXG4gKiBVc2VmdWwgZm9yIHBhcnNpbmcgZW52aXJvbm1lbnQgdmFyaWFibGVzIHdpdGggbnVtZXJpYyBkZWZhdWx0cy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHBhcnNlUG9zaXRpdmVJbnQodmFsdWU6IHN0cmluZyB8IHVuZGVmaW5lZCwgZmFsbGJhY2s6IG51bWJlcik6IG51bWJlciB7XG4gIGlmICghdmFsdWUpIHJldHVybiBmYWxsYmFjaztcbiAgY29uc3QgcGFyc2VkID0gcGFyc2VJbnQodmFsdWUsIDEwKTtcbiAgcmV0dXJuIE51bWJlci5pc0Zpbml0ZShwYXJzZWQpICYmIHBhcnNlZCA+IDAgPyBwYXJzZWQgOiBmYWxsYmFjaztcbn1cbiJdfQ==
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { Response } from 'express';
|
|
2
|
+
import { QuotaInfo } from '../types/common';
|
|
3
|
+
import { ErrorCode } from '../types/error-codes';
|
|
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): void;
|
|
49
|
+
/**
|
|
50
|
+
* Paginated response interface.
|
|
51
|
+
*/
|
|
52
|
+
export interface PaginatedResponse<T> {
|
|
53
|
+
success: true;
|
|
54
|
+
statusCode: number;
|
|
55
|
+
message?: string;
|
|
56
|
+
data: T[];
|
|
57
|
+
count: number;
|
|
58
|
+
limit: number;
|
|
59
|
+
offset: number;
|
|
60
|
+
total?: number;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Send a paginated list response.
|
|
64
|
+
*
|
|
65
|
+
* @param res - Express response object
|
|
66
|
+
* @param data - Array of items to return
|
|
67
|
+
* @param options - Pagination options
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```typescript
|
|
71
|
+
* const plugins = await db.query.plugins.findMany({ limit, offset });
|
|
72
|
+
* sendPaginated(res, plugins, {
|
|
73
|
+
* limit: 10,
|
|
74
|
+
* offset: 0,
|
|
75
|
+
* total: 50,
|
|
76
|
+
* message: 'Plugins retrieved successfully',
|
|
77
|
+
* });
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
export declare function sendPaginated<T>(res: Response, data: T[], options: {
|
|
81
|
+
limit: number;
|
|
82
|
+
offset: number;
|
|
83
|
+
total?: number;
|
|
84
|
+
message?: string;
|
|
85
|
+
statusCode?: number;
|
|
86
|
+
}): void;
|
|
87
|
+
/**
|
|
88
|
+
* Send a paginated list response with the items nested under a named key.
|
|
89
|
+
* Use when the API contract is `{ <key>: [...], pagination: {...} }`
|
|
90
|
+
* (the dominant shape across api/* services).
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```typescript
|
|
94
|
+
* sendPaginatedNested(res, 'pipelines', items, {
|
|
95
|
+
* total: 50, limit: 25, offset: 0, hasMore: true,
|
|
96
|
+
* });
|
|
97
|
+
* // → { pipelines: [...], pagination: { total: 50, limit: 25, offset: 0, hasMore: true } }
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
export declare function sendPaginatedNested<T>(res: Response, dataKey: string, data: T[], options: {
|
|
101
|
+
total?: number;
|
|
102
|
+
limit: number;
|
|
103
|
+
offset: number;
|
|
104
|
+
hasMore: boolean;
|
|
105
|
+
nextCursor?: string;
|
|
106
|
+
statusCode?: number;
|
|
107
|
+
}): void;
|
|
108
|
+
/**
|
|
109
|
+
* Extract database error details for logging/response.
|
|
110
|
+
*
|
|
111
|
+
* Extracts PostgreSQL error codes, details, hints, and constraint names
|
|
112
|
+
* from database errors for better error messages.
|
|
113
|
+
*
|
|
114
|
+
* @param error - Error object from database operation
|
|
115
|
+
* @returns Object with extracted error details
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```typescript
|
|
119
|
+
* try {
|
|
120
|
+
* await db.insert(plugins).values(newPlugin);
|
|
121
|
+
* } catch (error) {
|
|
122
|
+
* const dbDetails = extractDbError(error);
|
|
123
|
+
* return sendError(res, 500, 'Database error', ErrorCode.DATABASE_ERROR, dbDetails);
|
|
124
|
+
* }
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
export declare function extractDbError(error: unknown): Record<string, unknown>;
|
|
128
|
+
/** Extract a message string from an unknown catch value. */
|
|
129
|
+
export declare function errorMessage(error: unknown): string;
|
|
130
|
+
/** Send a 400 bad-request response. */
|
|
131
|
+
export declare function sendBadRequest(res: Response, message: string, code?: ErrorCode | string): void;
|
|
132
|
+
/** Send a 500 internal error response. */
|
|
133
|
+
export declare function sendInternalError(res: Response, message: string, details?: Record<string, unknown>): void;
|
|
134
|
+
/** Common pagination + sorting parameters. */
|
|
135
|
+
export interface PaginationParams {
|
|
136
|
+
limit: number;
|
|
137
|
+
offset: number;
|
|
138
|
+
sortBy: string;
|
|
139
|
+
sortOrder: 'asc' | 'desc';
|
|
140
|
+
}
|
|
141
|
+
/** Parse pagination/sort params from query string, clamping to safe defaults. */
|
|
142
|
+
export declare function parsePaginationParams(query: Record<string, unknown>): PaginationParams;
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright 2026 Pipeline Builder Contributors
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.sendSuccess = sendSuccess;
|
|
6
|
+
exports.sendError = sendError;
|
|
7
|
+
exports.sendQuotaExceeded = sendQuotaExceeded;
|
|
8
|
+
exports.sendPaginated = sendPaginated;
|
|
9
|
+
exports.sendPaginatedNested = sendPaginatedNested;
|
|
10
|
+
exports.extractDbError = extractDbError;
|
|
11
|
+
exports.errorMessage = errorMessage;
|
|
12
|
+
exports.sendBadRequest = sendBadRequest;
|
|
13
|
+
exports.sendInternalError = sendInternalError;
|
|
14
|
+
exports.parsePaginationParams = parsePaginationParams;
|
|
15
|
+
const logger_1 = require("./logger");
|
|
16
|
+
const error_codes_1 = require("../types/error-codes");
|
|
17
|
+
const common_schemas_1 = require("../validation/common-schemas");
|
|
18
|
+
const logger = (0, logger_1.createLogger)('response');
|
|
19
|
+
/**
|
|
20
|
+
* Send a standardized success response.
|
|
21
|
+
*
|
|
22
|
+
* @param res - Express response object
|
|
23
|
+
* @param statusCode - HTTP status code (default: 200)
|
|
24
|
+
* @param data - Response data
|
|
25
|
+
* @param message - Optional success message
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* sendSuccess(res, 200, { plugin: pluginData });
|
|
30
|
+
* sendSuccess(res, 201, { id: newId }, 'Resource created');
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
function sendSuccess(res, statusCode = 200, data, message) {
|
|
34
|
+
if (res.headersSent) {
|
|
35
|
+
logger.warn('Attempted to send success after headers already sent', { statusCode });
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const response = {
|
|
39
|
+
success: true,
|
|
40
|
+
statusCode,
|
|
41
|
+
};
|
|
42
|
+
if (data !== undefined) {
|
|
43
|
+
response.data = data;
|
|
44
|
+
}
|
|
45
|
+
if (message) {
|
|
46
|
+
response.message = message;
|
|
47
|
+
}
|
|
48
|
+
res.status(statusCode).json(response);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Send a standardized error response.
|
|
52
|
+
*
|
|
53
|
+
* @param res - Express response object
|
|
54
|
+
* @param statusCode - HTTP status code
|
|
55
|
+
* @param message - Error message
|
|
56
|
+
* @param code - Error code from ErrorCode enum
|
|
57
|
+
* @param details - Optional additional details
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```typescript
|
|
61
|
+
* sendError(res, 404, 'Plugin not found', ErrorCode.NOT_FOUND);
|
|
62
|
+
* sendError(res, 400, 'Invalid input', ErrorCode.VALIDATION_ERROR, { field: 'name' });
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
function sendError(res, statusCode, message, code, details) {
|
|
66
|
+
if (res.headersSent) {
|
|
67
|
+
logger.warn('Attempted to send error after headers already sent', { statusCode, message });
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const response = {
|
|
71
|
+
success: false,
|
|
72
|
+
statusCode,
|
|
73
|
+
message,
|
|
74
|
+
};
|
|
75
|
+
if (code) {
|
|
76
|
+
response.code = code;
|
|
77
|
+
}
|
|
78
|
+
if (details !== undefined) {
|
|
79
|
+
response.details = details;
|
|
80
|
+
}
|
|
81
|
+
res.status(statusCode).json(response);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Send a quota exceeded error response.
|
|
85
|
+
*
|
|
86
|
+
* @param res - Express response object
|
|
87
|
+
* @param quotaType - Type of quota exceeded
|
|
88
|
+
* @param quota - Current quota information
|
|
89
|
+
* @param resetAt - ISO timestamp when quota resets
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```typescript
|
|
93
|
+
* sendQuotaExceeded(res, 'apiCalls', { type: 'apiCalls', limit: 10000, used: 10000, remaining: 0 }, resetAt);
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
function sendQuotaExceeded(res, quotaType, quota, resetAt) {
|
|
97
|
+
const resetDate = resetAt ? new Date(resetAt) : new Date();
|
|
98
|
+
const resetIn = Math.max(0, Math.ceil((resetDate.getTime() - Date.now()) / 1000));
|
|
99
|
+
res.setHeader('Retry-After', resetIn);
|
|
100
|
+
res.setHeader('X-Quota-Limit', quota.limit);
|
|
101
|
+
res.setHeader('X-Quota-Used', quota.used);
|
|
102
|
+
res.setHeader('X-Quota-Remaining', Math.max(0, quota.remaining));
|
|
103
|
+
if (resetAt) {
|
|
104
|
+
res.setHeader('X-Quota-Reset', resetAt);
|
|
105
|
+
}
|
|
106
|
+
sendError(res, 429, `${quotaType} quota exceeded (${quota.used}/${quota.limit}). Please try again later.`, error_codes_1.ErrorCode.QUOTA_EXCEEDED, { quota });
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Send a paginated list response.
|
|
110
|
+
*
|
|
111
|
+
* @param res - Express response object
|
|
112
|
+
* @param data - Array of items to return
|
|
113
|
+
* @param options - Pagination options
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* ```typescript
|
|
117
|
+
* const plugins = await db.query.plugins.findMany({ limit, offset });
|
|
118
|
+
* sendPaginated(res, plugins, {
|
|
119
|
+
* limit: 10,
|
|
120
|
+
* offset: 0,
|
|
121
|
+
* total: 50,
|
|
122
|
+
* message: 'Plugins retrieved successfully',
|
|
123
|
+
* });
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
function sendPaginated(res, data, options) {
|
|
127
|
+
if (res.headersSent) {
|
|
128
|
+
logger.warn('Attempted to send paginated response after headers already sent');
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const { limit, offset, total, message, statusCode = 200 } = options;
|
|
132
|
+
const response = {
|
|
133
|
+
success: true,
|
|
134
|
+
statusCode,
|
|
135
|
+
data,
|
|
136
|
+
count: data.length,
|
|
137
|
+
limit,
|
|
138
|
+
offset,
|
|
139
|
+
};
|
|
140
|
+
if (message) {
|
|
141
|
+
response.message = message;
|
|
142
|
+
}
|
|
143
|
+
if (total !== undefined) {
|
|
144
|
+
response.total = total;
|
|
145
|
+
}
|
|
146
|
+
res.status(statusCode).json(response);
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Send a paginated list response with the items nested under a named key.
|
|
150
|
+
* Use when the API contract is `{ <key>: [...], pagination: {...} }`
|
|
151
|
+
* (the dominant shape across api/* services).
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```typescript
|
|
155
|
+
* sendPaginatedNested(res, 'pipelines', items, {
|
|
156
|
+
* total: 50, limit: 25, offset: 0, hasMore: true,
|
|
157
|
+
* });
|
|
158
|
+
* // → { pipelines: [...], pagination: { total: 50, limit: 25, offset: 0, hasMore: true } }
|
|
159
|
+
* ```
|
|
160
|
+
*/
|
|
161
|
+
function sendPaginatedNested(res, dataKey, data, options) {
|
|
162
|
+
if (res.headersSent) {
|
|
163
|
+
logger.warn('Attempted to send paginated response after headers already sent');
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const { statusCode = 200, total, limit, offset, hasMore, nextCursor } = options;
|
|
167
|
+
const pagination = { limit, offset, hasMore };
|
|
168
|
+
if (total !== undefined)
|
|
169
|
+
pagination.total = total;
|
|
170
|
+
if (nextCursor)
|
|
171
|
+
pagination.nextCursor = nextCursor;
|
|
172
|
+
res.status(statusCode).json({ [dataKey]: data, pagination });
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Extract database error details for logging/response.
|
|
176
|
+
*
|
|
177
|
+
* Extracts PostgreSQL error codes, details, hints, and constraint names
|
|
178
|
+
* from database errors for better error messages.
|
|
179
|
+
*
|
|
180
|
+
* @param error - Error object from database operation
|
|
181
|
+
* @returns Object with extracted error details
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* ```typescript
|
|
185
|
+
* try {
|
|
186
|
+
* await db.insert(plugins).values(newPlugin);
|
|
187
|
+
* } catch (error) {
|
|
188
|
+
* const dbDetails = extractDbError(error);
|
|
189
|
+
* return sendError(res, 500, 'Database error', ErrorCode.DATABASE_ERROR, dbDetails);
|
|
190
|
+
* }
|
|
191
|
+
* ```
|
|
192
|
+
*/
|
|
193
|
+
function extractDbError(error) {
|
|
194
|
+
if (!error || typeof error !== 'object') {
|
|
195
|
+
return {};
|
|
196
|
+
}
|
|
197
|
+
// Drizzle ORM wraps the original pg error in error.cause —
|
|
198
|
+
// check the cause first, then fall back to the error itself.
|
|
199
|
+
const err = error;
|
|
200
|
+
const source = (err.cause && typeof err.cause === 'object' ? err.cause : err);
|
|
201
|
+
const details = {};
|
|
202
|
+
if (source.code)
|
|
203
|
+
details.dbCode = source.code;
|
|
204
|
+
if (source.detail)
|
|
205
|
+
details.dbDetail = source.detail;
|
|
206
|
+
if (source.hint)
|
|
207
|
+
details.dbHint = source.hint;
|
|
208
|
+
if (source.constraint)
|
|
209
|
+
details.constraint = source.constraint;
|
|
210
|
+
if (source.table)
|
|
211
|
+
details.table = source.table;
|
|
212
|
+
if (source.column)
|
|
213
|
+
details.column = source.column;
|
|
214
|
+
return details;
|
|
215
|
+
}
|
|
216
|
+
// Convenience helpers (shared across plugin, pipeline, quota services)
|
|
217
|
+
/** Extract a message string from an unknown catch value. */
|
|
218
|
+
function errorMessage(error) {
|
|
219
|
+
return error instanceof Error ? error.message : String(error);
|
|
220
|
+
}
|
|
221
|
+
/** Send a 400 bad-request response. */
|
|
222
|
+
function sendBadRequest(res, message, code = error_codes_1.ErrorCode.VALIDATION_ERROR) {
|
|
223
|
+
sendError(res, 400, message, code);
|
|
224
|
+
}
|
|
225
|
+
/** Send a 500 internal error response. */
|
|
226
|
+
function sendInternalError(res, message, details) {
|
|
227
|
+
sendError(res, 500, message, error_codes_1.ErrorCode.INTERNAL_ERROR, details);
|
|
228
|
+
}
|
|
229
|
+
/** Parse pagination/sort params from query string, clamping to safe defaults. */
|
|
230
|
+
function parsePaginationParams(query) {
|
|
231
|
+
const limit = Math.min(Math.max(parseInt(String(query.limit), 10) || 10, 1), common_schemas_1.MAX_PAGE_LIMIT);
|
|
232
|
+
const offset = Math.max(parseInt(String(query.offset), 10) || 0, 0);
|
|
233
|
+
const sortBy = String(query.sortBy || 'createdAt');
|
|
234
|
+
const sortOrder = String(query.sortOrder || 'desc').toLowerCase() === 'asc' ? 'asc' : 'desc';
|
|
235
|
+
return { limit, offset, sortBy, sortOrder };
|
|
236
|
+
}
|
|
237
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVzcG9uc2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdXRpbHMvcmVzcG9uc2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7O0FBd0J0QyxrQ0E4QkM7QUFpQkQsOEJBaUNDO0FBZUQsOENBeUJDO0FBa0NELHNDQW9DQztBQWVELGtEQXNCQztBQXFCRCx3Q0FtQkM7QUFLRCxvQ0FFQztBQUdELHdDQU1DO0FBR0QsOENBTUM7QUFhRCxzREFRQztBQTlVRCxxQ0FBd0M7QUFFeEMsc0RBQWlEO0FBQ2pELGlFQUE4RDtBQUU5RCxNQUFNLE1BQU0sR0FBRyxJQUFBLHFCQUFZLEVBQUMsVUFBVSxDQUFDLENBQUM7QUFFeEM7Ozs7Ozs7Ozs7Ozs7R0FhRztBQUNILFNBQWdCLFdBQVcsQ0FDekIsR0FBYSxFQUNiLGFBQXFCLEdBQUcsRUFDeEIsSUFBUSxFQUNSLE9BQWdCO0lBRWhCLElBQUksR0FBRyxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBQ3BCLE1BQU0sQ0FBQyxJQUFJLENBQUMsc0RBQXNELEVBQUUsRUFBRSxVQUFVLEVBQUUsQ0FBQyxDQUFDO1FBQ3BGLE9BQU87SUFDVCxDQUFDO0lBRUQsTUFBTSxRQUFRLEdBS1Y7UUFDRixPQUFPLEVBQUUsSUFBSTtRQUNiLFVBQVU7S0FDWCxDQUFDO0lBRUYsSUFBSSxJQUFJLEtBQUssU0FBUyxFQUFFLENBQUM7UUFDdkIsUUFBUSxDQUFDLElBQUksR0FBRyxJQUFJLENBQUM7SUFDdkIsQ0FBQztJQUVELElBQUksT0FBTyxFQUFFLENBQUM7UUFDWixRQUFRLENBQUMsT0FBTyxHQUFHLE9BQU8sQ0FBQztJQUM3QixDQUFDO0lBRUQsR0FBRyxDQUFDLE1BQU0sQ0FBQyxVQUFVLENBQUMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7QUFDeEMsQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7OztHQWNHO0FBQ0gsU0FBZ0IsU0FBUyxDQUN2QixHQUFh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QUUsR0FBRyxFQUFFLE9BQU8sRUFBRSxJQUFJLENBQUMsQ0FBQztBQUNyQyxDQUFDO0FBRUQsMENBQTBDO0FBQzFDLFNBQWdCLGlCQUFpQixDQUMvQixHQUFhLEVBQ2IsT0FBZSxFQUNmLE9BQWlDO0lBRWpDLFNBQVMsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLE9BQU8sRUFBRSx1QkFBUyxDQUFDLGNBQWMsRUFBRSxPQUFPLENBQUMsQ0FBQztBQUNsRSxDQUFDO0FBWUQsaUZBQWlGO0FBQ2pGLFNBQWdCLHFCQUFxQixDQUFDLEtBQThCO0lBQ2xFLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsRUFBRSxFQUFFLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FBQyxDQUFDLEVBQUUsK0JBQWMsQ0FBQyxDQUFDO0lBQzdGLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQ3BFLE1BQU0sTUFBTSxHQUFHLE1BQU0sQ0FBQyxLQUFLLENBQUMsTUFBTSxJQUFJLFdBQVcsQ0FBQyxDQUFDO0lBQ25ELE1BQU0sU0FBUyxHQUNiLE1BQU0sQ0FBQyxLQUFLLENBQUMsU0FBUyxJQUFJLE1BQU0sQ0FBQyxDQUFDLFdBQVcsRUFBRSxLQUFLLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUM7SUFFN0UsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLFNBQVMsRUFBRSxDQUFDO0FBQzlDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuaW1wb3J0IHsgUmVzcG9uc2UgfSBmcm9tICdleHByZXNzJztcbmltcG9ydCB7IGNyZWF0ZUxvZ2dlciB9IGZyb20gJy4vbG9nZ2VyJztcbmltcG9ydCB7IFF1b3RhSW5mbyB9IGZyb20gJy4uL3R5cGVzL2NvbW1vbic7XG5pbXBvcnQgeyBFcnJvckNvZGUgfSBmcm9tICcuLi90eXBlcy9lcnJvci1jb2Rlcyc7XG5pbXBvcnQgeyBNQVhfUEFHRV9MSU1JVCB9IGZyb20gJy4uL3ZhbGlkYXRpb24vY29tbW9uLXNjaGVtYXMnO1xuXG5jb25zdCBsb2dnZXIgPSBjcmVhdGVMb2dnZXIoJ3Jlc3BvbnNlJyk7XG5cbi8qKlxuICogU2VuZCBhIHN0YW5kYXJkaXplZCBzdWNjZXNzIHJlc3BvbnNlLlxuICpcbiAqIEBwYXJhbSByZXMgLSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdFxuICogQHBhcmFtIHN0YXR1c0NvZGUgLSBIVFRQIHN0YXR1cyBjb2RlIChkZWZhdWx0OiAyMDApXG4gKiBAcGFyYW0gZGF0YSAtIFJlc3BvbnNlIGRhdGFcbiAqIEBwYXJhbSBtZXNzYWdlIC0gT3B0aW9uYWwgc3VjY2VzcyBtZXNzYWdlXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIHNlbmRTdWNjZXNzKHJlcywgMjAwLCB7IHBsdWdpbjogcGx1Z2luRGF0YSB9KTtcbiAqIHNlbmRTdWNjZXNzKHJlcywgMjAxLCB7IGlkOiBuZXdJZCB9LCAnUmVzb3VyY2UgY3JlYXRlZCcpO1xuICogYGBgXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzZW5kU3VjY2VzczxUPihcbiAgcmVzOiBSZXNwb25zZSxcbiAgc3RhdHVzQ29kZTogbnVtYmVyID0gMjAwLFxuICBkYXRhPzogVCxcbiAgbWVzc2FnZT86IHN0cmluZyxcbik6IHZvaWQge1xuICBpZiAocmVzLmhlYWRlcnNTZW50KSB7XG4gICAgbG9nZ2VyLndhcm4oJ0F0dGVtcHRlZCB0byBzZW5kIHN1Y2Nlc3MgYWZ0ZXIgaGVhZGVycyBhbHJlYWR5IHNlbnQnLCB7IHN0YXR1c0NvZGUgfSk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgY29uc3QgcmVzcG9uc2U6IHtcbiAgICBzdWNjZXNzOiB0cnVlO1xuICAgIHN0YXR1c0NvZGU6IG51bWJlcjtcbiAgICBkYXRhPzogVDtcbiAgICBtZXNzYWdlPzogc3RyaW5nO1xuICB9ID0ge1xuICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgc3RhdHVzQ29kZSxcbiAgfTtcblxuICBpZiAoZGF0YSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgcmVzcG9uc2UuZGF0YSA9IGRhdGE7XG4gIH1cblxuICBpZiAobWVzc2FnZSkge1xuICAgIHJlc3BvbnNlLm1lc3NhZ2UgPSBtZXNzYWdlO1xuICB9XG5cbiAgcmVzLnN0YXR1cyhzdGF0dXNDb2RlKS5qc29uKHJlc3BvbnNlKTtcbn1cblxuLyoqXG4gKiBTZW5kIGEgc3RhbmRhcmRpemVkIGVycm9yIHJlc3BvbnNlLlxuICpcbiAqIEBwYXJhbSByZXMgLSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdFxuICogQHBhcmFtIHN0YXR1c0NvZGUgLSBIVFRQIHN0YXR1cyBjb2RlXG4gKiBAcGFyYW0gbWVzc2FnZSAtIEVycm9yIG1lc3NhZ2VcbiAqIEBwYXJhbSBjb2RlIC0gRXJyb3IgY29kZSBmcm9tIEVycm9yQ29kZSBlbnVtXG4gKiBAcGFyYW0gZGV0YWlscyAtIE9wdGlvbmFsIGFkZGl0aW9uYWwgZGV0YWlsc1xuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiBzZW5kRXJyb3IocmVzLCA0MDQsICdQbHVnaW4gbm90IGZvdW5kJywgRXJyb3JDb2RlLk5PVF9GT1VORCk7XG4gKiBzZW5kRXJyb3IocmVzLCA0MDAsICdJbnZhbGlkIGlucHV0JywgRXJyb3JDb2RlLlZBTElEQVRJT05fRVJST1IsIHsgZmllbGQ6ICduYW1lJyB9KTtcbiAqIGBgYFxuICovXG5leHBvcnQgZnVuY3Rpb24gc2VuZEVycm9yKFxuICByZXM6IFJlc3BvbnNlLFxuICBzdGF0dXNDb2RlOiBudW1iZXIsXG4gIG1lc3NhZ2U6IHN0cmluZyxcbiAgY29kZT86IEVycm9yQ29kZSB8IHN0cmluZyxcbiAgZGV0YWlscz86IHVua25vd24sXG4pOiB2b2lkIHtcbiAgaWYgKHJlcy5oZWFkZXJzU2VudCkge1xuICAgIGxvZ2dlci53YXJuKCdBdHRlbXB0ZWQgdG8gc2VuZCBlcnJvciBhZnRlciBoZWFkZXJzIGFscmVhZHkgc2VudCcsIHsgc3RhdHVzQ29kZSwgbWVzc2FnZSB9KTtcbiAgICByZXR1cm47XG4gIH1cblxuICBjb25zdCByZXNwb25zZToge1xuICAgIHN1Y2Nlc3M6IGZhbHNlO1xuICAgIHN0YXR1c0NvZGU6IG51bWJlcjtcbiAgICBtZXNzYWdlOiBzdHJpbmc7XG4gICAgY29kZT86IHN0cmluZztcbiAgICBkZXRhaWxzPzogdW5rbm93bjtcbiAgfSA9IHtcbiAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICBzdGF0dXNDb2RlLFxuICAgIG1lc3NhZ2UsXG4gIH07XG5cbiAgaWYgKGNvZGUpIHtcbiAgICByZXNwb25zZS5jb2RlID0gY29kZTtcbiAgfVxuXG4gIGlmIChkZXRhaWxzICE9PSB1bmRlZmluZWQpIHtcbiAgICByZXNwb25zZS5kZXRhaWxzID0gZGV0YWlscztcbiAgfVxuXG4gIHJlcy5zdGF0dXMoc3RhdHVzQ29kZSkuanNvbihyZXNwb25zZSk7XG59XG5cbi8qKlxuICogU2VuZCBhIHF1b3RhIGV4Y2VlZGVkIGVycm9yIHJlc3BvbnNlLlxuICpcbiAqIEBwYXJhbSByZXMgLSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdFxuICogQHBhcmFtIHF1b3RhVHlwZSAtIFR5cGUgb2YgcXVvdGEgZXhjZWVkZWRcbiAqIEBwYXJhbSBxdW90YSAtIEN1cnJlbnQgcXVvdGEgaW5mb3JtYXRpb25cbiAqIEBwYXJhbSByZXNldEF0IC0gSVNPIHRpbWVzdGFtcCB3aGVuIHF1b3RhIHJlc2V0c1xuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiBzZW5kUXVvdGFFeGNlZWRlZChyZXMsICdhcGlDYWxscycsIHsgdHlwZTogJ2FwaUNhbGxzJywgbGltaXQ6IDEwMDAwLCB1c2VkOiAxMDAwMCwgcmVtYWluaW5nOiAwIH0sIHJlc2V0QXQpO1xuICogYGBgXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzZW5kUXVvdGFFeGNlZWRlZChcbiAgcmVzOiBSZXNwb25zZSxcbiAgcXVvdGFUeXBlOiBzdHJpbmcsXG4gIHF1b3RhOiBRdW90YUluZm8sXG4gIHJlc2V0QXQ/OiBzdHJpbmcsXG4pOiB2b2lkIHtcbiAgY29uc3QgcmVzZXREYXRlID0gcmVzZXRBdCA/IG5ldyBEYXRlKHJlc2V0QXQpIDogbmV3IERhdGUoKTtcbiAgY29uc3QgcmVzZXRJbiA9IE1hdGgubWF4KDAsIE1hdGguY2VpbCgocmVzZXREYXRlLmdldFRpbWUoKSAtIERhdGUubm93KCkpIC8gMTAwMCkpO1xuXG4gIHJlcy5zZXRIZWFkZXIoJ1JldHJ5LUFmdGVyJywgcmVzZXRJbik7XG4gIHJlcy5zZXRIZWFkZXIoJ1gtUXVvdGEtTGltaXQnLCBxdW90YS5saW1pdCk7XG4gIHJlcy5zZXRIZWFkZXIoJ1gtUXVvdGEtVXNlZCcsIHF1b3RhLnVzZWQpO1xuICByZXMuc2V0SGVhZGVyKCdYLVF1b3RhLVJlbWFpbmluZycsIE1hdGgubWF4KDAsIHF1b3RhLnJlbWFpbmluZykpO1xuXG4gIGlmIChyZXNldEF0KSB7XG4gICAgcmVzLnNldEhlYWRlcignWC1RdW90YS1SZXNldCcsIHJlc2V0QXQpO1xuICB9XG5cbiAgc2VuZEVycm9yKFxuICAgIHJlcyxcbiAgICA0MjksXG4gICAgYCR7cXVvdGFUeXBlfSBxdW90YSBleGNlZWRlZCAoJHtxdW90YS51c2VkfS8ke3F1b3RhLmxpbWl0fSkuIFBsZWFzZSB0cnkgYWdhaW4gbGF0ZXIuYCxcbiAgICBFcnJvckNvZGUuUVVPVEFfRVhDRUVERUQsXG4gICAgeyBxdW90YSB9LFxuICApO1xufVxuXG4vKipcbiAqIFBhZ2luYXRlZCByZXNwb25zZSBpbnRlcmZhY2UuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUGFnaW5hdGVkUmVzcG9uc2U8VD4ge1xuICBzdWNjZXNzOiB0cnVlO1xuICBzdGF0dXNDb2RlOiBudW1iZXI7XG4gIG1lc3NhZ2U/OiBzdHJpbmc7XG4gIGRhdGE6IFRbXTtcbiAgY291bnQ6IG51bWJlcjtcbiAgbGltaXQ6IG51bWJlcjtcbiAgb2Zmc2V0OiBudW1iZXI7XG4gIHRvdGFsPzogbnVtYmVyO1xufVxuXG4vKipcbiAqIFNlbmQgYSBwYWdpbmF0ZWQgbGlzdCByZXNwb25zZS5cbiAqXG4gKiBAcGFyYW0gcmVzIC0gRXhwcmVzcyByZXNwb25zZSBvYmplY3RcbiAqIEBwYXJhbSBkYXRhIC0gQXJyYXkgb2YgaXRlbXMgdG8gcmV0dXJuXG4gKiBAcGFyYW0gb3B0aW9ucyAtIFBhZ2luYXRpb24gb3B0aW9uc1xuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiBjb25zdCBwbHVnaW5zID0gYXdhaXQgZGIucXVlcnkucGx1Z2lucy5maW5kTWFueSh7IGxpbWl0LCBvZmZzZXQgfSk7XG4gKiBzZW5kUGFnaW5hdGVkKHJlcywgcGx1Z2lucywge1xuICogICBsaW1pdDogMTAsXG4gKiAgIG9mZnNldDogMCxcbiAqICAgdG90YWw6IDUwLFxuICogICBtZXNzYWdlOiAnUGx1Z2lucyByZXRyaWV2ZWQgc3VjY2Vzc2Z1bGx5JyxcbiAqIH0pO1xuICogYGBgXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzZW5kUGFnaW5hdGVkPFQ+KFxuICByZXM6IFJlc3BvbnNlLFxuICBkYXRhOiBUW10sXG4gIG9wdGlvbnM6IHtcbiAgICBsaW1pdDogbnVtYmVyO1xuICAgIG9mZnNldDogbnVtYmVyO1xuICAgIHRvdGFsPzogbnVtYmVyO1xuICAgIG1lc3NhZ2U/OiBzdHJpbmc7XG4gICAgc3RhdHVzQ29kZT86IG51bWJlcjtcbiAgfSxcbik6IHZvaWQge1xuICBpZiAocmVzLmhlYWRlcnNTZW50KSB7XG4gICAgbG9nZ2VyLndhcm4oJ0F0dGVtcHRlZCB0byBzZW5kIHBhZ2luYXRlZCByZXNwb25zZSBhZnRlciBoZWFkZXJzIGFscmVhZHkgc2VudCcpO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIGNvbnN0IHsgbGltaXQsIG9mZnNldCwgdG90YWwsIG1lc3NhZ2UsIHN0YXR1c0NvZGUgPSAyMDAgfSA9IG9wdGlvbnM7XG5cbiAgY29uc3QgcmVzcG9uc2U6IFBhZ2luYXRlZFJlc3BvbnNlPFQ+ID0ge1xuICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgc3RhdHVzQ29kZSxcbiAgICBkYXRhLFxuICAgIGNvdW50OiBkYXRhLmxlbmd0aCxcbiAgICBsaW1pdCxcbiAgICBvZmZzZXQsXG4gIH07XG5cbiAgaWYgKG1lc3NhZ2UpIHtcbiAgICByZXNwb25zZS5tZXNzYWdlID0gbWVzc2FnZTtcbiAgfVxuXG4gIGlmICh0b3RhbCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgcmVzcG9uc2UudG90YWwgPSB0b3RhbDtcbiAgfVxuXG4gIHJlcy5zdGF0dXMoc3RhdHVzQ29kZSkuanNvbihyZXNwb25zZSk7XG59XG5cbi8qKlxuICogU2VuZCBhIHBhZ2luYXRlZCBsaXN0IHJlc3BvbnNlIHdpdGggdGhlIGl0ZW1zIG5lc3RlZCB1bmRlciBhIG5hbWVkIGtleS5cbiAqIFVzZSB3aGVuIHRoZSBBUEkgY29udHJhY3QgaXMgYHsgPGtleT46IFsuLi5dLCBwYWdpbmF0aW9uOiB7Li4ufSB9YFxuICogKHRoZSBkb21pbmFudCBzaGFwZSBhY3Jvc3MgYXBpLyogc2VydmljZXMpLlxuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiBzZW5kUGFnaW5hdGVkTmVzdGVkKHJlcywgJ3BpcGVsaW5lcycsIGl0ZW1zLCB7XG4gKiAgIHRvdGFsOiA1MCwgbGltaXQ6IDI1LCBvZmZzZXQ6IDAsIGhhc01vcmU6IHRydWUsXG4gKiB9KTtcbiAqIC8vIOKGkiB7IHBpcGVsaW5lczogWy4uLl0sIHBhZ2luYXRpb246IHsgdG90YWw6IDUwLCBsaW1pdDogMjUsIG9mZnNldDogMCwgaGFzTW9yZTogdHJ1ZSB9IH1cbiAqIGBgYFxuICovXG5leHBvcnQgZnVuY3Rpb24gc2VuZFBhZ2luYXRlZE5lc3RlZDxUPihcbiAgcmVzOiBSZXNwb25zZSxcbiAgZGF0YUtleTogc3RyaW5nLFxuICBkYXRhOiBUW10sXG4gIG9wdGlvbnM6IHtcbiAgICB0b3RhbD86IG51bWJlcjtcbiAgICBsaW1pdDogbnVtYmVyO1xuICAgIG9mZnNldDogbnVtYmVyO1xuICAgIGhhc01vcmU6IGJvb2xlYW47XG4gICAgbmV4dEN1cnNvcj86IHN0cmluZztcbiAgICBzdGF0dXNDb2RlPzogbnVtYmVyO1xuICB9LFxuKTogdm9pZCB7XG4gIGlmIChyZXMuaGVhZGVyc1NlbnQpIHtcbiAgICBsb2dnZXIud2FybignQXR0ZW1wdGVkIHRvIHNlbmQgcGFnaW5hdGVkIHJlc3BvbnNlIGFmdGVyIGhlYWRlcnMgYWxyZWFkeSBzZW50Jyk7XG4gICAgcmV0dXJuO1xuICB9XG4gIGNvbnN0IHsgc3RhdHVzQ29kZSA9IDIwMCwgdG90YWwsIGxpbWl0LCBvZmZzZXQsIGhhc01vcmUsIG5leHRDdXJzb3IgfSA9IG9wdGlvbnM7XG4gIGNvbnN0IHBhZ2luYXRpb246IFJlY29yZDxzdHJpbmcsIHVua25vd24+ID0geyBsaW1pdCwgb2Zmc2V0LCBoYXNNb3JlIH07XG4gIGlmICh0b3RhbCAhPT0gdW5kZWZpbmVkKSBwYWdpbmF0aW9uLnRvdGFsID0gdG90YWw7XG4gIGlmIChuZXh0Q3Vyc29yKSBwYWdpbmF0aW9uLm5leHRDdXJzb3IgPSBuZXh0Q3Vyc29yO1xuICByZXMuc3RhdHVzKHN0YXR1c0NvZGUpLmpzb24oeyBbZGF0YUtleV06IGRhdGEsIHBhZ2luYXRpb24gfSk7XG59XG5cbi8qKlxuICogRXh0cmFjdCBkYXRhYmFzZSBlcnJvciBkZXRhaWxzIGZvciBsb2dnaW5nL3Jlc3BvbnNlLlxuICpcbiAqIEV4dHJhY3RzIFBvc3RncmVTUUwgZXJyb3IgY29kZXMsIGRldGFpbHMsIGhpbnRzLCBhbmQgY29uc3RyYWludCBuYW1lc1xuICogZnJvbSBkYXRhYmFzZSBlcnJvcnMgZm9yIGJldHRlciBlcnJvciBtZXNzYWdlcy5cbiAqXG4gKiBAcGFyYW0gZXJyb3IgLSBFcnJvciBvYmplY3QgZnJvbSBkYXRhYmFzZSBvcGVyYXRpb25cbiAqIEByZXR1cm5zIE9iamVjdCB3aXRoIGV4dHJhY3RlZCBlcnJvciBkZXRhaWxzXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIHRyeSB7XG4gKiAgIGF3YWl0IGRiLmluc2VydChwbHVnaW5zKS52YWx1ZXMobmV3UGx1Z2luKTtcbiAqIH0gY2F0Y2ggKGVycm9yKSB7XG4gKiAgIGNvbnN0IGRiRGV0YWlscyA9IGV4dHJhY3REYkVycm9yKGVycm9yKTtcbiAqICAgcmV0dXJuIHNlbmRFcnJvcihyZXMsIDUwMCwgJ0RhdGFiYXNlIGVycm9yJywgRXJyb3JDb2RlLkRBVEFCQVNFX0VSUk9SLCBkYkRldGFpbHMpO1xuICogfVxuICogYGBgXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBleHRyYWN0RGJFcnJvcihlcnJvcjogdW5rbm93bik6IFJlY29yZDxzdHJpbmcsIHVua25vd24+IHtcbiAgaWYgKCFlcnJvciB8fCB0eXBlb2YgZXJyb3IgIT09ICdvYmplY3QnKSB7XG4gICAgcmV0dXJuIHt9O1xuICB9XG5cbiAgLy8gRHJpenpsZSBPUk0gd3JhcHMgdGhlIG9yaWdpbmFsIHBnIGVycm9yIGluIGVycm9yLmNhdXNlIOKAlFxuICAvLyBjaGVjayB0aGUgY2F1c2UgZmlyc3QsIHRoZW4gZmFsbCBiYWNrIHRvIHRoZSBlcnJvciBpdHNlbGYuXG4gIGNvbnN0IGVyciA9IGVycm9yIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuICBjb25zdCBzb3VyY2UgPSAoZXJyLmNhdXNlICYmIHR5cGVvZiBlcnIuY2F1c2UgPT09ICdvYmplY3QnID8gZXJyLmNhdXNlIDogZXJyKSBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgY29uc3QgZGV0YWlsczogUmVjb3JkPHN0cmluZywgdW5rbm93bj4gPSB7fTtcblxuICBpZiAoc291cmNlLmNvZGUpIGRldGFpbHMuZGJDb2RlID0gc291cmNlLmNvZGU7XG4gIGlmIChzb3VyY2UuZGV0YWlsKSBkZXRhaWxzLmRiRGV0YWlsID0gc291cmNlLmRldGFpbDtcbiAgaWYgKHNvdXJjZS5oaW50KSBkZXRhaWxzLmRiSGludCA9IHNvdXJjZS5oaW50O1xuICBpZiAoc291cmNlLmNvbnN0cmFpbnQpIGRldGFpbHMuY29uc3RyYWludCA9IHNvdXJjZS5jb25zdHJhaW50O1xuICBpZiAoc291cmNlLnRhYmxlKSBkZXRhaWxzLnRhYmxlID0gc291cmNlLnRhYmxlO1xuICBpZiAoc291cmNlLmNvbHVtbikgZGV0YWlscy5jb2x1bW4gPSBzb3VyY2UuY29sdW1uO1xuXG4gIHJldHVybiBkZXRhaWxzO1xufVxuXG4vLyBDb252ZW5pZW5jZSBoZWxwZXJzIChzaGFyZWQgYWNyb3NzIHBsdWdpbiwgcGlwZWxpbmUsIHF1b3RhIHNlcnZpY2VzKVxuXG4vKiogRXh0cmFjdCBhIG1lc3NhZ2Ugc3RyaW5nIGZyb20gYW4gdW5rbm93biBjYXRjaCB2YWx1ZS4gKi9cbmV4cG9ydCBmdW5jdGlvbiBlcnJvck1lc3NhZ2UoZXJyb3I6IHVua25vd24pOiBzdHJpbmcge1xuICByZXR1cm4gZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yLm1lc3NhZ2UgOiBTdHJpbmcoZXJyb3IpO1xufVxuXG4vKiogU2VuZCBhIDQwMCBiYWQtcmVxdWVzdCByZXNwb25zZS4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzZW5kQmFkUmVxdWVzdChcbiAgcmVzOiBSZXNwb25zZSxcbiAgbWVzc2FnZTogc3RyaW5nLFxuICBjb2RlOiBFcnJvckNvZGUgfCBzdHJpbmcgPSBFcnJvckNvZGUuVkFMSURBVElPTl9FUlJPUixcbik6IHZvaWQge1xuICBzZW5kRXJyb3IocmVzLCA0MDAsIG1lc3NhZ2UsIGNvZGUpO1xufVxuXG4vKiogU2VuZCBhIDUwMCBpbnRlcm5hbCBlcnJvciByZXNwb25zZS4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzZW5kSW50ZXJuYWxFcnJvcihcbiAgcmVzOiBSZXNwb25zZSxcbiAgbWVzc2FnZTogc3RyaW5nLFxuICBkZXRhaWxzPzogUmVjb3JkPHN0cmluZywgdW5rbm93bj4sXG4pOiB2b2lkIHtcbiAgc2VuZEVycm9yKHJlcywgNTAwLCBtZXNzYWdlLCBFcnJvckNvZGUuSU5URVJOQUxfRVJST1IsIGRldGFpbHMpO1xufVxuXG4vLyBQYWdpbmF0aW9uXG5cbi8qKiBDb21tb24gcGFnaW5hdGlvbiArIHNvcnRpbmcgcGFyYW1ldGVycy4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUGFnaW5hdGlvblBhcmFtcyB7XG4gIGxpbWl0OiBudW1iZXI7XG4gIG9mZnNldDogbnVtYmVyO1xuICBzb3J0Qnk6IHN0cmluZztcbiAgc29ydE9yZGVyOiAnYXNjJyB8ICdkZXNjJztcbn1cblxuLyoqIFBhcnNlIHBhZ2luYXRpb24vc29ydCBwYXJhbXMgZnJvbSBxdWVyeSBzdHJpbmcsIGNsYW1waW5nIHRvIHNhZmUgZGVmYXVsdHMuICovXG5leHBvcnQgZnVuY3Rpb24gcGFyc2VQYWdpbmF0aW9uUGFyYW1zKHF1ZXJ5OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPik6IFBhZ2luYXRpb25QYXJhbXMge1xuICBjb25zdCBsaW1pdCA9IE1hdGgubWluKE1hdGgubWF4KHBhcnNlSW50KFN0cmluZyhxdWVyeS5saW1pdCksIDEwKSB8fCAxMCwgMSksIE1BWF9QQUdFX0xJTUlUKTtcbiAgY29uc3Qgb2Zmc2V0ID0gTWF0aC5tYXgocGFyc2VJbnQoU3RyaW5nKHF1ZXJ5Lm9mZnNldCksIDEwKSB8fCAwLCAwKTtcbiAgY29uc3Qgc29ydEJ5ID0gU3RyaW5nKHF1ZXJ5LnNvcnRCeSB8fCAnY3JlYXRlZEF0Jyk7XG4gIGNvbnN0IHNvcnRPcmRlcjogJ2FzYycgfCAnZGVzYycgPVxuICAgIFN0cmluZyhxdWVyeS5zb3J0T3JkZXIgfHwgJ2Rlc2MnKS50b0xvd2VyQ2FzZSgpID09PSAnYXNjJyA/ICdhc2MnIDogJ2Rlc2MnO1xuXG4gIHJldHVybiB7IGxpbWl0LCBvZmZzZXQsIHNvcnRCeSwgc29ydE9yZGVyIH07XG59XG4iXX0=
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Schema for AI generation request body.
|
|
4
|
+
* Used by both POST /pipelines/generate and POST /plugins/generate.
|
|
5
|
+
*/
|
|
6
|
+
export declare const AIGenerateBodySchema: z.ZodObject<{
|
|
7
|
+
prompt: z.ZodString;
|
|
8
|
+
provider: z.ZodString;
|
|
9
|
+
model: z.ZodString;
|
|
10
|
+
apiKey: z.ZodOptional<z.ZodString>;
|
|
11
|
+
previousConfig: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
12
|
+
fallbackProviders: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
13
|
+
}, z.core.$strip>;
|
|
14
|
+
/** Validated type for AI generation request body. */
|
|
15
|
+
export type ValidatedAIGenerateBody = z.infer<typeof AIGenerateBodySchema>;
|
|
16
|
+
/**
|
|
17
|
+
* Schema for POST /plugins/deploy-generated request body.
|
|
18
|
+
* Validates the AI-generated plugin configuration before Docker build.
|
|
19
|
+
*/
|
|
20
|
+
export declare const PluginDeployGeneratedSchema: z.ZodObject<{
|
|
21
|
+
name: z.ZodString;
|
|
22
|
+
description: z.ZodOptional<z.ZodString>;
|
|
23
|
+
version: z.ZodString;
|
|
24
|
+
pluginType: z.ZodDefault<z.ZodEnum<{
|
|
25
|
+
CodeBuildStep: "CodeBuildStep";
|
|
26
|
+
ShellStep: "ShellStep";
|
|
27
|
+
ManualApprovalStep: "ManualApprovalStep";
|
|
28
|
+
}>>;
|
|
29
|
+
computeType: z.ZodDefault<z.ZodEnum<{
|
|
30
|
+
SMALL: "SMALL";
|
|
31
|
+
MEDIUM: "MEDIUM";
|
|
32
|
+
LARGE: "LARGE";
|
|
33
|
+
X2_LARGE: "X2_LARGE";
|
|
34
|
+
}>>;
|
|
35
|
+
keywords: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
36
|
+
primaryOutputDirectory: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
37
|
+
installCommands: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
38
|
+
commands: z.ZodArray<z.ZodString>;
|
|
39
|
+
env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
40
|
+
buildArgs: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
41
|
+
dockerfile: z.ZodString;
|
|
42
|
+
accessModifier: z.ZodDefault<z.ZodEnum<{
|
|
43
|
+
public: "public";
|
|
44
|
+
private: "private";
|
|
45
|
+
}>>;
|
|
46
|
+
}, z.core.$strip>;
|
|
47
|
+
/** Validated type for plugin deploy-generated request body. */
|
|
48
|
+
export type ValidatedPluginDeployGenerated = z.infer<typeof PluginDeployGeneratedSchema>;
|
|
49
|
+
/**
|
|
50
|
+
* Schema for POST /pipelines/generate/from-url/stream request body.
|
|
51
|
+
* Validates Git URL + AI provider configuration for repo-based pipeline generation.
|
|
52
|
+
*/
|
|
53
|
+
export declare const AIGenerateFromUrlBodySchema: z.ZodObject<{
|
|
54
|
+
gitUrl: z.ZodString;
|
|
55
|
+
provider: z.ZodString;
|
|
56
|
+
model: z.ZodString;
|
|
57
|
+
apiKey: z.ZodOptional<z.ZodString>;
|
|
58
|
+
repoToken: z.ZodOptional<z.ZodString>;
|
|
59
|
+
}, z.core.$strip>;
|
|
60
|
+
/** Validated type for AI generate-from-URL request body. */
|
|
61
|
+
export type ValidatedAIGenerateFromUrlBody = z.infer<typeof AIGenerateFromUrlBodySchema>;
|