@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,255 @@
1
+ import type { Permission } from './permissions.js';
2
+ import type { QuotaTier } from './quota-tiers.js';
3
+ /**
4
+ * Capability scope catalog for machine/automation tokens. A scoped token carries
5
+ * exactly one of these instead of role-based permissions; endpoints that accept a
6
+ * scoped identity check it via `hasScope`. Kept a closed union (mirroring
7
+ * {@link Permission}) so a typo on either the mint or the check side is a compile
8
+ * error. Add a value here when introducing a new scoped-token surface.
9
+ */
10
+ export type TokenScope = 'reporting:ingest';
11
+ /**
12
+ * Quota type identifiers.
13
+ *
14
+ * - `plugins` / `pipelines` — count of created entities
15
+ * - `apiCalls` — generic API call count (read-heavy paths)
16
+ * - `aiCalls` — AI provider invocations (counted separately because each call
17
+ * has external dollar cost; sized smaller than apiCalls per tier)
18
+ * - `storageBytes` — registry storage budget per org. Unlike the others
19
+ * (which count discrete events), this is a measured total recomputed
20
+ * on demand. Incremented by the image-registry's push-gate before
21
+ * issuing a token whose scope includes `push`; the GC scheduler
22
+ * eventually frees the bytes, then the next push-gate check reads
23
+ * the lower value. NOT a counter-style quota in the sense of
24
+ * `incrementUsage` — the registry pushes the measured total via
25
+ * `updateLimits`/`resetUsage` flows. Tier limits are bytes.
26
+ * - `dashboards` / `alertRules` / `alertDestinations` / `idpConfigs` —
27
+ * resource-count quotas added to close per-org DoS surfaces in the
28
+ * user-editable feature tables. Without these caps a single org could
29
+ * spam thousands of dashboards / rules and bloat the shared Postgres /
30
+ * Mongo working sets. Counted at create time; decremented on delete.
31
+ */
32
+ export type QuotaType = 'plugins' | 'pipelines' | 'apiCalls' | 'aiCalls' | 'storageBytes' | 'dashboards' | 'alertRules' | 'alertDestinations' | 'idpConfigs';
33
+ /**
34
+ * Valid quota type values.
35
+ */
36
+ export declare const VALID_QUOTA_TYPES: readonly ['plugins', 'pipelines', 'apiCalls', 'aiCalls', 'storageBytes', 'dashboards', 'alertRules', 'alertDestinations', 'idpConfigs'];
37
+ /**
38
+ * Type guard to check if a value is a valid QuotaType.
39
+ *
40
+ * @param value - Value to check
41
+ * @returns True if value is a valid QuotaType
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * if (isValidQuotaType(req.body.quotaType)) {
46
+ * // quotaType is guaranteed to be QuotaType
47
+ * }
48
+ * ```
49
+ */
50
+ export declare function isValidQuotaType(value: unknown): value is QuotaType;
51
+ /**
52
+ * Result from quota check operation.
53
+ */
54
+ export interface QuotaCheckResult {
55
+ /** Whether the request is allowed */
56
+ allowed: boolean;
57
+ /** Maximum quota limit (-1 for unlimited) */
58
+ limit: number;
59
+ /** Current usage count */
60
+ used: number;
61
+ /** Remaining quota (-1 for unlimited) */
62
+ remaining: number;
63
+ /** ISO timestamp when quota resets */
64
+ resetAt: string;
65
+ /** Whether quota is unlimited */
66
+ unlimited: boolean;
67
+ /**
68
+ * True ONLY when this result is the fail-open sentinel returned because the
69
+ * quota service was unreachable / returned non-ok (not a real quota reading).
70
+ * Lets fail-closed callers (e.g. the registry storage push-gate) distinguish
71
+ * an outage from a genuine `limit: -1` (unlimited) org. Absent on real results.
72
+ */
73
+ failOpen?: boolean;
74
+ }
75
+ /**
76
+ * Quota information for error responses.
77
+ */
78
+ export interface QuotaInfo {
79
+ type: QuotaType;
80
+ limit: number;
81
+ used: number;
82
+ remaining: number;
83
+ }
84
+ /**
85
+ * Standard API success response.
86
+ */
87
+ export interface ApiSuccessResponse<T = unknown> {
88
+ success: true;
89
+ statusCode: number;
90
+ data?: T;
91
+ message?: string;
92
+ }
93
+ /**
94
+ * Standard API error response.
95
+ */
96
+ export interface ApiErrorResponse {
97
+ success: false;
98
+ statusCode: number;
99
+ message: string;
100
+ code?: string;
101
+ details?: unknown;
102
+ quota?: QuotaInfo;
103
+ }
104
+ /**
105
+ * Combined API response type.
106
+ */
107
+ export type ApiResponse<T = unknown> = ApiSuccessResponse<T> | ApiErrorResponse;
108
+ /**
109
+ * JWT payload from access tokens.
110
+ *
111
+ * Users can belong to multiple organizations. The token is scoped to one
112
+ * active organization at a time. The `role` field is the user's per-org
113
+ * role in that organization (from the UserOrganization junction collection),
114
+ * and `isAdmin` is derived as `role === 'admin' || role === 'owner'`.
115
+ *
116
+ * Use `POST /auth/switch-org` to change the active organization, which
117
+ * re-issues tokens with the new org's role and context.
118
+ */
119
+ /**
120
+ * Coarse per-org membership label, DERIVED from the Roles a user is assigned
121
+ * (the highest `grantsRole` among them; `owner` is set only by ownership
122
+ * transfer). It is a display/authority label — driving `isAdmin`, ownership,
123
+ * and seat accounting — NOT a permission source. Effective permissions come
124
+ * solely from the user's assigned Roles (see `resolveUserPermissions`). Not a
125
+ * global role.
126
+ */
127
+ export type OrgRole = 'owner' | 'admin' | 'member';
128
+ export interface JwtPayload {
129
+ /** User ID (subject) */
130
+ sub: string;
131
+ /** Username */
132
+ username: string;
133
+ /** User email */
134
+ email: string;
135
+ /** Per-org role in the active organization ('owner' | 'admin' | 'member'). Not a global role. */
136
+ role: OrgRole;
137
+ /** Derived: true when role is 'admin' or 'owner' in the active organization */
138
+ isAdmin?: boolean;
139
+ /**
140
+ * Resolved fine-grained permissions for the active org/team — the union of
141
+ * the permissions carried by every Role the user is assigned (see
142
+ * `resolveUserPermissions`). Absent on machine/scoped tokens. Superadmins are
143
+ * granted all permissions implicitly and may omit this. Endpoints enforce via
144
+ * `requirePermission(...)`.
145
+ */
146
+ permissions?: Permission[];
147
+ /**
148
+ * Global super-admin flag (cross-org). When `true`, the user is treated
149
+ * as a system administrator regardless of which org they're currently
150
+ * acting under. This is the SOLE signal for sysadmin authority — the old path
151
+ * (membership in the well-known "system" org with role admin/owner) has been
152
+ * removed because it conflated "Pipeline Builder operator" with "real customer
153
+ * tenant" in the data model. Grant sysadmin only via `isSuperAdmin`.
154
+ */
155
+ isSuperAdmin?: boolean;
156
+ /** Organization's quota tier. */
157
+ tier?: QuotaTier;
158
+ /** Resolved feature flags for this user/org */
159
+ features?: string[];
160
+ /**
161
+ * Narrow capability scope for machine/automation tokens (e.g.
162
+ * `'reporting:ingest'`). Absent on normal interactive user tokens. Endpoints
163
+ * that accept a scoped identity check this (see `hasScope`) instead of role;
164
+ * a scoped token is minted with minimal role for least-privilege.
165
+ */
166
+ scope?: TokenScope;
167
+ /** Active organization ID (from UserOrganization membership) */
168
+ organizationId?: string;
169
+ /** Active organization name */
170
+ organizationName?: string;
171
+ /**
172
+ * Org → team hierarchy (org-team-hierarchy proposal, phase 1).
173
+ * `parentOrganizationId` is the active org's direct parent; `rootOrganizationId`
174
+ * is the top of its ancestry chain. Both are **omitted for flat (root) orgs** —
175
+ * consumers should treat the effective root as `rootOrganizationId ?? organizationId`
176
+ * (use the shared {@link effectiveRootOrgId} accessor rather than coalescing inline).
177
+ * Currently every org is flat, so these are absent on all tokens today.
178
+ */
179
+ parentOrganizationId?: string;
180
+ /** Root organization ID of the active org's ancestry chain (see `parentOrganizationId`). */
181
+ rootOrganizationId?: string;
182
+ /**
183
+ * Set on tokens issued by the sysadmin impersonation flow
184
+ * (`POST /admin/impersonate/:userId`). Carries the original sysadmin's
185
+ * user id so audit events still attribute actions correctly.
186
+ */
187
+ impersonatorId?: string;
188
+ /**
189
+ * When true, the token is read-only — any non-GET request is rejected
190
+ * upstream by the platform's read-only impersonation gate. Lets
191
+ * sysadmins "view as user X" without risking a destructive action.
192
+ */
193
+ impersonationReadOnly?: boolean;
194
+ /**
195
+ * The user's `tokenVersion` at the moment this token was issued. Every
196
+ * privilege change (deactivate / role or tier downgrade / membership or
197
+ * ownership change / password change / logout-all) increments the user's
198
+ * server-side `tokenVersion`, so a token whose embedded version is behind the
199
+ * current one has been REVOKED. Platform validates this against Mongo; the
200
+ * stateless services validate it against a Redis-published current-version
201
+ * store (see `setTokenRevocationStore`). Absent on machine/service tokens.
202
+ */
203
+ tokenVersion?: number;
204
+ /** Token type */
205
+ type: 'access' | 'refresh';
206
+ /** Issued at timestamp */
207
+ iat?: number;
208
+ /** Expiration timestamp */
209
+ exp?: number;
210
+ }
211
+ /**
212
+ * The account boundary (root org) for a token's active org.
213
+ *
214
+ * The account root is `rootOrganizationId ?? organizationId`: a flat (root) org
215
+ * omits `rootOrganizationId`, so it IS its own root; a team (child) org carries
216
+ * the top of its ancestry chain. Every account-scoped read (quota/seat/billing
217
+ * pooling) must resolve through this coalesce rather than reading
218
+ * `organizationId` directly — the latter silently works today (all orgs flat)
219
+ * but would bind to the team instead of the account once teams exist.
220
+ *
221
+ * @returns the effective root org id, or `undefined` when neither field is set.
222
+ */
223
+ export declare function effectiveRootOrgId(payload: Pick<JwtPayload, 'organizationId' | 'rootOrganizationId'>): string | undefined;
224
+ /**
225
+ * Extended Express Request with user property.
226
+ */
227
+ declare global {
228
+ namespace Express {
229
+ interface Request {
230
+ user?: JwtPayload;
231
+ }
232
+ }
233
+ }
234
+ /**
235
+ * Service configuration for internal HTTP client.
236
+ */
237
+ export interface ServiceConfig {
238
+ /** Service hostname */
239
+ host: string;
240
+ /** Service port */
241
+ port: number;
242
+ /** Request timeout in milliseconds */
243
+ timeout?: number;
244
+ }
245
+ /**
246
+ * Health check response.
247
+ */
248
+ export interface HealthCheckResponse {
249
+ status: 'healthy' | 'unhealthy';
250
+ service: string;
251
+ timestamp: string;
252
+ uptime: number;
253
+ version?: string;
254
+ dependencies?: Record<string, 'connected' | 'disconnected' | 'unknown'>;
255
+ }
@@ -0,0 +1,41 @@
1
+ // Copyright 2026 Pipeline Builder Contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * Valid quota type values.
5
+ */
6
+ export const VALID_QUOTA_TYPES = [
7
+ 'plugins', 'pipelines', 'apiCalls', 'aiCalls', 'storageBytes',
8
+ 'dashboards', 'alertRules', 'alertDestinations', 'idpConfigs',
9
+ ];
10
+ /**
11
+ * Type guard to check if a value is a valid QuotaType.
12
+ *
13
+ * @param value - Value to check
14
+ * @returns True if value is a valid QuotaType
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * if (isValidQuotaType(req.body.quotaType)) {
19
+ * // quotaType is guaranteed to be QuotaType
20
+ * }
21
+ * ```
22
+ */
23
+ export function isValidQuotaType(value) {
24
+ return typeof value === 'string' && VALID_QUOTA_TYPES.includes(value);
25
+ }
26
+ /**
27
+ * The account boundary (root org) for a token's active org.
28
+ *
29
+ * The account root is `rootOrganizationId ?? organizationId`: a flat (root) org
30
+ * omits `rootOrganizationId`, so it IS its own root; a team (child) org carries
31
+ * the top of its ancestry chain. Every account-scoped read (quota/seat/billing
32
+ * pooling) must resolve through this coalesce rather than reading
33
+ * `organizationId` directly — the latter silently works today (all orgs flat)
34
+ * but would bind to the team instead of the account once teams exist.
35
+ *
36
+ * @returns the effective root org id, or `undefined` when neither field is set.
37
+ */
38
+ export function effectiveRootOrgId(payload) {
39
+ return payload.rootOrganizationId ?? payload.organizationId;
40
+ }
41
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tbW9uLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3R5cGVzL2NvbW1vbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBMEN0Qzs7R0FFRztBQUNILE1BQU0sQ0FBQyxNQUFNLGlCQUFpQixHQUFHO0lBQy9CLFNBQVMsRUFBRSxXQUFXLEVBQUUsVUFBVSxFQUFFLFNBQVMsRUFBRSxjQUFjO0lBQzdELFlBQVksRUFBRSxZQUFZLEVBQUUsbUJBQW1CLEVBQUUsWUFBWTtDQUNyRCxDQUFDO0FBRVg7Ozs7Ozs7Ozs7OztHQVlHO0FBQ0gsTUFBTSxVQUFVLGdCQUFnQixDQUFDLEtBQWM7SUFDN0MsT0FBTyxPQUFPLEtBQUssS0FBSyxRQUFRLElBQUksaUJBQWlCLENBQUMsUUFBUSxDQUFDLEtBQWtCLENBQUMsQ0FBQztBQUNyRixDQUFDO0FBeUtEOzs7Ozs7Ozs7OztHQVdHO0FBQ0gsTUFBTSxVQUFVLGtCQUFrQixDQUNoQyxPQUFrRTtJQUVsRSxPQUFPLE9BQU8sQ0FBQyxrQkFBa0IsSUFBSSxPQUFPLENBQUMsY0FBYyxDQUFDO0FBQzlELENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuaW1wb3J0IHR5cGUgeyBQZXJtaXNzaW9uIH0gZnJvbSAnLi9wZXJtaXNzaW9ucy5qcyc7XG5pbXBvcnQgdHlwZSB7IFF1b3RhVGllciB9IGZyb20gJy4vcXVvdGEtdGllcnMuanMnO1xuXG4vKipcbiAqIENhcGFiaWxpdHkgc2NvcGUgY2F0YWxvZyBmb3IgbWFjaGluZS9hdXRvbWF0aW9uIHRva2Vucy4gQSBzY29wZWQgdG9rZW4gY2Fycmllc1xuICogZXhhY3RseSBvbmUgb2YgdGhlc2UgaW5zdGVhZCBvZiByb2xlLWJhc2VkIHBlcm1pc3Npb25zOyBlbmRwb2ludHMgdGhhdCBhY2NlcHQgYVxuICogc2NvcGVkIGlkZW50aXR5IGNoZWNrIGl0IHZpYSBgaGFzU2NvcGVgLiBLZXB0IGEgY2xvc2VkIHVuaW9uIChtaXJyb3JpbmdcbiAqIHtAbGluayBQZXJtaXNzaW9ufSkgc28gYSB0eXBvIG9uIGVpdGhlciB0aGUgbWludCBvciB0aGUgY2hlY2sgc2lkZSBpcyBhIGNvbXBpbGVcbiAqIGVycm9yLiBBZGQgYSB2YWx1ZSBoZXJlIHdoZW4gaW50cm9kdWNpbmcgYSBuZXcgc2NvcGVkLXRva2VuIHN1cmZhY2UuXG4gKi9cbmV4cG9ydCB0eXBlIFRva2VuU2NvcGUgPSAncmVwb3J0aW5nOmluZ2VzdCc7XG5cbi8qKlxuICogUXVvdGEgdHlwZSBpZGVudGlmaWVycy5cbiAqXG4gKiAtIGBwbHVnaW5zYCAvIGBwaXBlbGluZXNgIOKAlCBjb3VudCBvZiBjcmVhdGVkIGVudGl0aWVzXG4gKiAtIGBhcGlDYWxsc2Ag4oCUIGdlbmVyaWMgQVBJIGNhbGwgY291bnQgKHJlYWQtaGVhdnkgcGF0aHMpXG4gKiAtIGBhaUNhbGxzYCDigJQgQUkgcHJvdmlkZXIgaW52b2NhdGlvbnMgKGNvdW50ZWQgc2VwYXJhdGVseSBiZWNhdXNlIGVhY2ggY2FsbFxuICogICBoYXMgZXh0ZXJuYWwgZG9sbGFyIGNvc3Q7IHNpemVkIHNtYWxsZXIgdGhhbiBhcGlDYWxscyBwZXIgdGllcilcbiAqIC0gYHN0b3JhZ2VCeXRlc2Ag4oCUIHJlZ2lzdHJ5IHN0b3JhZ2UgYnVkZ2V0IHBlciBvcmcuIFVubGlrZSB0aGUgb3RoZXJzXG4gKiAgICh3aGljaCBjb3VudCBkaXNjcmV0ZSBldmVudHMpLCB0aGlzIGlzIGEgbWVhc3VyZWQgdG90YWwgcmVjb21wdXRlZFxuICogICBvbiBkZW1hbmQuIEluY3JlbWVudGVkIGJ5IHRoZSBpbWFnZS1yZWdpc3RyeSdzIHB1c2gtZ2F0ZSBiZWZvcmVcbiAqICAgaXNzdWluZyBhIHRva2VuIHdob3NlIHNjb3BlIGluY2x1ZGVzIGBwdXNoYDsgdGhlIEdDIHNjaGVkdWxlclxuICogICBldmVudHVhbGx5IGZyZWVzIHRoZSBieXRlcywgdGhlbiB0aGUgbmV4dCBwdXNoLWdhdGUgY2hlY2sgcmVhZHNcbiAqICAgdGhlIGxvd2VyIHZhbHVlLiBOT1QgYSBjb3VudGVyLXN0eWxlIHF1b3RhIGluIHRoZSBzZW5zZSBvZlxuICogICBgaW5jcmVtZW50VXNhZ2VgIOKAlCB0aGUgcmVnaXN0cnkgcHVzaGVzIHRoZSBtZWFzdXJlZCB0b3RhbCB2aWFcbiAqICAgYHVwZGF0ZUxpbWl0c2AvYHJlc2V0VXNhZ2VgIGZsb3dzLiBUaWVyIGxpbWl0cyBhcmUgYnl0ZXMuXG4gKiAtIGBkYXNoYm9hcmRzYCAvIGBhbGVydFJ1bGVzYCAvIGBhbGVydERlc3RpbmF0aW9uc2AgLyBgaWRwQ29uZmlnc2Ag4oCUXG4gKiAgIHJlc291cmNlLWNvdW50IHF1b3RhcyBhZGRlZCB0byBjbG9zZSBwZXItb3JnIERvUyBzdXJmYWNlcyBpbiB0aGVcbiAqICAgdXNlci1lZGl0YWJsZSBmZWF0dXJlIHRhYmxlcy4gV2l0aG91dCB0aGVzZSBjYXBzIGEgc2luZ2xlIG9yZyBjb3VsZFxuICogICBzcGFtIHRob3VzYW5kcyBvZiBkYXNoYm9hcmRzIC8gcnVsZXMgYW5kIGJsb2F0IHRoZSBzaGFyZWQgUG9zdGdyZXMgL1xuICogICBNb25nbyB3b3JraW5nIHNldHMuIENvdW50ZWQgYXQgY3JlYXRlIHRpbWU7IGRlY3JlbWVudGVkIG9uIGRlbGV0ZS5cbiAqL1xuLy8gTk9URTogYHNlYXRzYCBpcyBpbnRlbnRpb25hbGx5IE5PVCBoZXJlLiBJdCdzIGEgdGllciBsaW1pdCAoUXVvdGFUaWVyTGltaXRzKVxuLy8gZW5mb3JjZWQgYnkgY29tcGFyaW5nIHRoZSBvcmcncyBMSVZFIG1lbWJlciBjb3VudCBhdCBpbnZpdGUgdGltZSDigJQgbm90IGFuXG4vLyBpbmNyZW1lbnRpbmcgcGVyLXBlcmlvZCBjb3VudGVyIGxpa2UgdGhlIGNvbnN1bWFibGUgcXVvdGFzIGJlbG93LlxuZXhwb3J0IHR5cGUgUXVvdGFUeXBlID1cbiAgfCAncGx1Z2lucycgfCAncGlwZWxpbmVzJyB8ICdhcGlDYWxscycgfCAnYWlDYWxscycgfCAnc3RvcmFnZUJ5dGVzJ1xuICB8ICdkYXNoYm9hcmRzJyB8ICdhbGVydFJ1bGVzJyB8ICdhbGVydERlc3RpbmF0aW9ucycgfCAnaWRwQ29uZmlncyc7XG5cbi8qKlxuICogVmFsaWQgcXVvdGEgdHlwZSB2YWx1ZXMuXG4gKi9cbmV4cG9ydCBjb25zdCBWQUxJRF9RVU9UQV9UWVBFUyA9IFtcbiAgJ3BsdWdpbnMnLCAncGlwZWxpbmVzJywgJ2FwaUNhbGxzJywgJ2FpQ2FsbHMnLCAnc3RvcmFnZUJ5dGVzJyxcbiAgJ2Rhc2hib2FyZHMnLCAnYWxlcnRSdWxlcycsICdhbGVydERlc3RpbmF0aW9ucycsICdpZHBDb25maWdzJyxcbl0gYXMgY29uc3Q7XG5cbi8qKlxuICogVHlwZSBndWFyZCB0byBjaGVjayBpZiBhIHZhbHVlIGlzIGEgdmFsaWQgUXVvdGFUeXBlLlxuICpcbiAqIEBwYXJhbSB2YWx1ZSAtIFZhbHVlIHRvIGNoZWNrXG4gKiBAcmV0dXJucyBUcnVlIGlmIHZhbHVlIGlzIGEgdmFsaWQgUXVvdGFUeXBlXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIGlmIChpc1ZhbGlkUXVvdGFUeXBlKHJlcS5ib2R5LnF1b3RhVHlwZSkpIHtcbiAqICAgLy8gcXVvdGFUeXBlIGlzIGd1YXJhbnRlZWQgdG8gYmUgUXVvdGFUeXBlXG4gKiB9XG4gKiBgYGBcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGlzVmFsaWRRdW90YVR5cGUodmFsdWU6IHVua25vd24pOiB2YWx1ZSBpcyBRdW90YVR5cGUge1xuICByZXR1cm4gdHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyAmJiBWQUxJRF9RVU9UQV9UWVBFUy5pbmNsdWRlcyh2YWx1ZSBhcyBRdW90YVR5cGUpO1xufVxuXG4vKipcbiAqIFJlc3VsdCBmcm9tIHF1b3RhIGNoZWNrIG9wZXJhdGlvbi5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBRdW90YUNoZWNrUmVzdWx0IHtcbiAgLyoqIFdoZXRoZXIgdGhlIHJlcXVlc3QgaXMgYWxsb3dlZCAqL1xuICBhbGxvd2VkOiBib29sZWFuO1xuICAvKiogTWF4aW11bSBxdW90YSBsaW1pdCAoLTEgZm9yIHVubGltaXRlZCkgKi9cbiAgbGltaXQ6IG51bWJlcjtcbiAgLyoqIEN1cnJlbnQgdXNhZ2UgY291bnQgKi9cbiAgdXNlZDogbnVtYmVyO1xuICAvKiogUmVtYWluaW5nIHF1b3RhICgtMSBmb3IgdW5saW1pdGVkKSAqL1xuICByZW1haW5pbmc6IG51bWJlcjtcbiAgLyoqIElTTyB0aW1lc3RhbXAgd2hlbiBxdW90YSByZXNldHMgKi9cbiAgcmVzZXRBdDogc3RyaW5nO1xuICAvKiogV2hldGhlciBxdW90YSBpcyB1bmxpbWl0ZWQgKi9cbiAgdW5saW1pdGVkOiBib29sZWFuO1xuICAvKipcbiAgICogVHJ1ZSBPTkxZIHdoZW4gdGhpcyByZXN1bHQgaXMgdGhlIGZhaWwtb3BlbiBzZW50aW5lbCByZXR1cm5lZCBiZWNhdXNlIHRoZVxuICAgKiBxdW90YSBzZXJ2aWNlIHdhcyB1bnJlYWNoYWJsZSAvIHJldHVybmVkIG5vbi1vayAobm90IGEgcmVhbCBxdW90YSByZWFkaW5nKS5cbiAgICogTGV0cyBmYWlsLWNsb3NlZCBjYWxsZXJzIChlLmcuIHRoZSByZWdpc3RyeSBzdG9yYWdlIHB1c2gtZ2F0ZSkgZGlzdGluZ3Vpc2hcbiAgICogYW4gb3V0YWdlIGZyb20gYSBnZW51aW5lIGBsaW1pdDogLTFgICh1bmxpbWl0ZWQpIG9yZy4gQWJzZW50IG9uIHJlYWwgcmVzdWx0cy5cbiAgICovXG4gIGZhaWxPcGVuPzogYm9vbGVhbjtcbn1cblxuLyoqXG4gKiBRdW90YSBpbmZvcm1hdGlvbiBmb3IgZXJyb3IgcmVzcG9uc2VzLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIFF1b3RhSW5mbyB7XG4gIHR5cGU6IFF1b3RhVHlwZTtcbiAgbGltaXQ6IG51bWJlcjtcbiAgdXNlZDogbnVtYmVyO1xuICByZW1haW5pbmc6IG51bWJlcjtcbn1cblxuLyoqXG4gKiBTdGFuZGFyZCBBUEkgc3VjY2VzcyByZXNwb25zZS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBBcGlTdWNjZXNzUmVzcG9uc2U8VCA9IHVua25vd24+IHtcbiAgc3VjY2VzczogdHJ1ZTtcbiAgc3RhdHVzQ29kZTogbnVtYmVyO1xuICBkYXRhPzogVDtcbiAgbWVzc2FnZT86IHN0cmluZztcbn1cblxuLyoqXG4gKiBTdGFuZGFyZCBBUEkgZXJyb3IgcmVzcG9uc2UuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQXBpRXJyb3JSZXNwb25zZSB7XG4gIHN1Y2Nlc3M6IGZhbHNlO1xuICBzdGF0dXNDb2RlOiBudW1iZXI7XG4gIG1lc3NhZ2U6IHN0cmluZztcbiAgY29kZT86IHN0cmluZztcbiAgZGV0YWlscz86IHVua25vd247XG4gIHF1b3RhPzogUXVvdGFJbmZvO1xufVxuXG4vKipcbiAqIENvbWJpbmVkIEFQSSByZXNwb25zZSB0eXBlLlxuICovXG5leHBvcnQgdHlwZSBBcGlSZXNwb25zZTxUID0gdW5rbm93bj4gPSBBcGlTdWNjZXNzUmVzcG9uc2U8VD4gfCBBcGlFcnJvclJlc3BvbnNlO1xuXG4vKipcbiAqIEpXVCBwYXlsb2FkIGZyb20gYWNjZXNzIHRva2Vucy5cbiAqXG4gKiBVc2VycyBjYW4gYmVsb25nIHRvIG11bHRpcGxlIG9yZ2FuaXphdGlvbnMuIFRoZSB0b2tlbiBpcyBzY29wZWQgdG8gb25lXG4gKiBhY3RpdmUgb3JnYW5pemF0aW9uIGF0IGEgdGltZS4gVGhlIGByb2xlYCBmaWVsZCBpcyB0aGUgdXNlcidzIHBlci1vcmdcbiAqIHJvbGUgaW4gdGhhdCBvcmdhbml6YXRpb24gKGZyb20gdGhlIFVzZXJPcmdhbml6YXRpb24ganVuY3Rpb24gY29sbGVjdGlvbiksXG4gKiBhbmQgYGlzQWRtaW5gIGlzIGRlcml2ZWQgYXMgYHJvbGUgPT09ICdhZG1pbicgfHwgcm9sZSA9PT0gJ293bmVyJ2AuXG4gKlxuICogVXNlIGBQT1NUIC9hdXRoL3N3aXRjaC1vcmdgIHRvIGNoYW5nZSB0aGUgYWN0aXZlIG9yZ2FuaXphdGlvbiwgd2hpY2hcbiAqIHJlLWlzc3VlcyB0b2tlbnMgd2l0aCB0aGUgbmV3IG9yZydzIHJvbGUgYW5kIGNvbnRleHQuXG4gKi9cbi8qKlxuICogQ29hcnNlIHBlci1vcmcgbWVtYmVyc2hpcCBsYWJlbCwgREVSSVZFRCBmcm9tIHRoZSBSb2xlcyBhIHVzZXIgaXMgYXNzaWduZWRcbiAqICh0aGUgaGlnaGVzdCBgZ3JhbnRzUm9sZWAgYW1vbmcgdGhlbTsgYG93bmVyYCBpcyBzZXQgb25seSBieSBvd25lcnNoaXBcbiAqIHRyYW5zZmVyKS4gSXQgaXMgYSBkaXNwbGF5L2F1dGhvcml0eSBsYWJlbCDigJQgZHJpdmluZyBgaXNBZG1pbmAsIG93bmVyc2hpcCxcbiAqIGFuZCBzZWF0IGFjY291bnRpbmcg4oCUIE5PVCBhIHBlcm1pc3Npb24gc291cmNlLiBFZmZlY3RpdmUgcGVybWlzc2lvbnMgY29tZVxuICogc29sZWx5IGZyb20gdGhlIHVzZXIncyBhc3NpZ25lZCBSb2xlcyAoc2VlIGByZXNvbHZlVXNlclBlcm1pc3Npb25zYCkuIE5vdCBhXG4gKiBnbG9iYWwgcm9sZS5cbiAqL1xuZXhwb3J0IHR5cGUgT3JnUm9sZSA9ICdvd25lcicgfCAnYWRtaW4nIHwgJ21lbWJlcic7XG5cbmV4cG9ydCBpbnRlcmZhY2UgSnd0UGF5bG9hZCB7XG4gIC8qKiBVc2VyIElEIChzdWJqZWN0KSAqL1xuICBzdWI6IHN0cmluZztcbiAgLyoqIFVzZXJuYW1lICovXG4gIHVzZXJuYW1lOiBzdHJpbmc7XG4gIC8qKiBVc2VyIGVtYWlsICovXG4gIGVtYWlsOiBzdHJpbmc7XG4gIC8qKiBQZXItb3JnIHJvbGUgaW4gdGhlIGFjdGl2ZSBvcmdhbml6YXRpb24gKCdvd25lcicgfCAnYWRtaW4nIHwgJ21lbWJlcicpLiBOb3QgYSBnbG9iYWwgcm9sZS4gKi9cbiAgcm9sZTogT3JnUm9sZTtcbiAgLyoqIERlcml2ZWQ6IHRydWUgd2hlbiByb2xlIGlzICdhZG1pbicgb3IgJ293bmVyJyBpbiB0aGUgYWN0aXZlIG9yZ2FuaXphdGlvbiAqL1xuICBpc0FkbWluPzogYm9vbGVhbjtcbiAgLyoqXG4gICAqIFJlc29sdmVkIGZpbmUtZ3JhaW5lZCBwZXJtaXNzaW9ucyBmb3IgdGhlIGFjdGl2ZSBvcmcvdGVhbSDigJQgdGhlIHVuaW9uIG9mXG4gICAqIHRoZSBwZXJtaXNzaW9ucyBjYXJyaWVkIGJ5IGV2ZXJ5IFJvbGUgdGhlIHVzZXIgaXMgYXNzaWduZWQgKHNlZVxuICAgKiBgcmVzb2x2ZVVzZXJQZXJtaXNzaW9uc2ApLiBBYnNlbnQgb24gbWFjaGluZS9zY29wZWQgdG9rZW5zLiBTdXBlcmFkbWlucyBhcmVcbiAgICogZ3JhbnRlZCBhbGwgcGVybWlzc2lvbnMgaW1wbGljaXRseSBhbmQgbWF5IG9taXQgdGhpcy4gRW5kcG9pbnRzIGVuZm9yY2UgdmlhXG4gICAqIGByZXF1aXJlUGVybWlzc2lvbiguLi4pYC5cbiAgICovXG4gIHBlcm1pc3Npb25zPzogUGVybWlzc2lvbltdO1xuICAvKipcbiAgICogR2xvYmFsIHN1cGVyLWFkbWluIGZsYWcgKGNyb3NzLW9yZykuIFdoZW4gYHRydWVgLCB0aGUgdXNlciBpcyB0cmVhdGVkXG4gICAqIGFzIGEgc3lzdGVtIGFkbWluaXN0cmF0b3IgcmVnYXJkbGVzcyBvZiB3aGljaCBvcmcgdGhleSdyZSBjdXJyZW50bHlcbiAgICogYWN0aW5nIHVuZGVyLiBUaGlzIGlzIHRoZSBTT0xFIHNpZ25hbCBmb3Igc3lzYWRtaW4gYXV0aG9yaXR5IOKAlCB0aGUgb2xkIHBhdGhcbiAgICogKG1lbWJlcnNoaXAgaW4gdGhlIHdlbGwta25vd24gXCJzeXN0ZW1cIiBvcmcgd2l0aCByb2xlIGFkbWluL293bmVyKSBoYXMgYmVlblxuICAgKiByZW1vdmVkIGJlY2F1c2UgaXQgY29uZmxhdGVkIFwiUGlwZWxpbmUgQnVpbGRlciBvcGVyYXRvclwiIHdpdGggXCJyZWFsIGN1c3RvbWVyXG4gICAqIHRlbmFudFwiIGluIHRoZSBkYXRhIG1vZGVsLiBHcmFudCBzeXNhZG1pbiBvbmx5IHZpYSBgaXNTdXBlckFkbWluYC5cbiAgICovXG4gIGlzU3VwZXJBZG1pbj86IGJvb2xlYW47XG4gIC8qKiBPcmdhbml6YXRpb24ncyBxdW90YSB0aWVyLiAqL1xuICB0aWVyPzogUXVvdGFUaWVyO1xuICAvKiogUmVzb2x2ZWQgZmVhdHVyZSBmbGFncyBmb3IgdGhpcyB1c2VyL29yZyAqL1xuICBmZWF0dXJlcz86IHN0cmluZ1tdO1xuICAvKipcbiAgICogTmFycm93IGNhcGFiaWxpdHkgc2NvcGUgZm9yIG1hY2hpbmUvYXV0b21hdGlvbiB0b2tlbnMgKGUuZy5cbiAgICogYCdyZXBvcnRpbmc6aW5nZXN0J2ApLiBBYnNlbnQgb24gbm9ybWFsIGludGVyYWN0aXZlIHVzZXIgdG9rZW5zLiBFbmRwb2ludHNcbiAgICogdGhhdCBhY2NlcHQgYSBzY29wZWQgaWRlbnRpdHkgY2hlY2sgdGhpcyAoc2VlIGBoYXNTY29wZWApIGluc3RlYWQgb2Ygcm9sZTtcbiAgICogYSBzY29wZWQgdG9rZW4gaXMgbWludGVkIHdpdGggbWluaW1hbCByb2xlIGZvciBsZWFzdC1wcml2aWxlZ2UuXG4gICAqL1xuICBzY29wZT86IFRva2VuU2NvcGU7XG4gIC8qKiBBY3RpdmUgb3JnYW5pemF0aW9uIElEIChmcm9tIFVzZXJPcmdhbml6YXRpb24gbWVtYmVyc2hpcCkgKi9cbiAgb3JnYW5pemF0aW9uSWQ/OiBzdHJpbmc7XG4gIC8qKiBBY3RpdmUgb3JnYW5pemF0aW9uIG5hbWUgKi9cbiAgb3JnYW5pemF0aW9uTmFtZT86IHN0cmluZztcbiAgLyoqXG4gICAqIE9yZyDihpIgdGVhbSBoaWVyYXJjaHkgKG9yZy10ZWFtLWhpZXJhcmNoeSBwcm9wb3NhbCwgcGhhc2UgMSkuXG4gICAqIGBwYXJlbnRPcmdhbml6YXRpb25JZGAgaXMgdGhlIGFjdGl2ZSBvcmcncyBkaXJlY3QgcGFyZW50OyBgcm9vdE9yZ2FuaXphdGlvbklkYFxuICAgKiBpcyB0aGUgdG9wIG9mIGl0cyBhbmNlc3RyeSBjaGFpbi4gQm90aCBhcmUgKipvbWl0dGVkIGZvciBmbGF0IChyb290KSBvcmdzKiog4oCUXG4gICAqIGNvbnN1bWVycyBzaG91bGQgdHJlYXQgdGhlIGVmZmVjdGl2ZSByb290IGFzIGByb290T3JnYW5pemF0aW9uSWQgPz8gb3JnYW5pemF0aW9uSWRgXG4gICAqICh1c2UgdGhlIHNoYXJlZCB7QGxpbmsgZWZmZWN0aXZlUm9vdE9yZ0lkfSBhY2Nlc3NvciByYXRoZXIgdGhhbiBjb2FsZXNjaW5nIGlubGluZSkuXG4gICAqIEN1cnJlbnRseSBldmVyeSBvcmcgaXMgZmxhdCwgc28gdGhlc2UgYXJlIGFic2VudCBvbiBhbGwgdG9rZW5zIHRvZGF5LlxuICAgKi9cbiAgcGFyZW50T3JnYW5pemF0aW9uSWQ/OiBzdHJpbmc7XG4gIC8qKiBSb290IG9yZ2FuaXphdGlvbiBJRCBvZiB0aGUgYWN0aXZlIG9yZydzIGFuY2VzdHJ5IGNoYWluIChzZWUgYHBhcmVudE9yZ2FuaXphdGlvbklkYCkuICovXG4gIHJvb3RPcmdhbml6YXRpb25JZD86IHN0cmluZztcbiAgLyoqXG4gICAqIFNldCBvbiB0b2tlbnMgaXNzdWVkIGJ5IHRoZSBzeXNhZG1pbiBpbXBlcnNvbmF0aW9uIGZsb3dcbiAgICogKGBQT1NUIC9hZG1pbi9pbXBlcnNvbmF0ZS86dXNlcklkYCkuIENhcnJpZXMgdGhlIG9yaWdpbmFsIHN5c2FkbWluJ3NcbiAgICogdXNlciBpZCBzbyBhdWRpdCBldmVudHMgc3RpbGwgYXR0cmlidXRlIGFjdGlvbnMgY29ycmVjdGx5LlxuICAgKi9cbiAgaW1wZXJzb25hdG9ySWQ/OiBzdHJpbmc7XG4gIC8qKlxuICAgKiBXaGVuIHRydWUsIHRoZSB0b2tlbiBpcyByZWFkLW9ubHkg4oCUIGFueSBub24tR0VUIHJlcXVlc3QgaXMgcmVqZWN0ZWRcbiAgICogdXBzdHJlYW0gYnkgdGhlIHBsYXRmb3JtJ3MgcmVhZC1vbmx5IGltcGVyc29uYXRpb24gZ2F0ZS4gTGV0c1xuICAgKiBzeXNhZG1pbnMgXCJ2aWV3IGFzIHVzZXIgWFwiIHdpdGhvdXQgcmlza2luZyBhIGRlc3RydWN0aXZlIGFjdGlvbi5cbiAgICovXG4gIGltcGVyc29uYXRpb25SZWFkT25seT86IGJvb2xlYW47XG4gIC8qKlxuICAgKiBUaGUgdXNlcidzIGB0b2tlblZlcnNpb25gIGF0IHRoZSBtb21lbnQgdGhpcyB0b2tlbiB3YXMgaXNzdWVkLiBFdmVyeVxuICAgKiBwcml2aWxlZ2UgY2hhbmdlIChkZWFjdGl2YXRlIC8gcm9sZSBvciB0aWVyIGRvd25ncmFkZSAvIG1lbWJlcnNoaXAgb3JcbiAgICogb3duZXJzaGlwIGNoYW5nZSAvIHBhc3N3b3JkIGNoYW5nZSAvIGxvZ291dC1hbGwpIGluY3JlbWVudHMgdGhlIHVzZXInc1xuICAgKiBzZXJ2ZXItc2lkZSBgdG9rZW5WZXJzaW9uYCwgc28gYSB0b2tlbiB3aG9zZSBlbWJlZGRlZCB2ZXJzaW9uIGlzIGJlaGluZCB0aGVcbiAgICogY3VycmVudCBvbmUgaGFzIGJlZW4gUkVWT0tFRC4gUGxhdGZvcm0gdmFsaWRhdGVzIHRoaXMgYWdhaW5zdCBNb25nbzsgdGhlXG4gICAqIHN0YXRlbGVzcyBzZXJ2aWNlcyB2YWxpZGF0ZSBpdCBhZ2FpbnN0IGEgUmVkaXMtcHVibGlzaGVkIGN1cnJlbnQtdmVyc2lvblxuICAgKiBzdG9yZSAoc2VlIGBzZXRUb2tlblJldm9jYXRpb25TdG9yZWApLiBBYnNlbnQgb24gbWFjaGluZS9zZXJ2aWNlIHRva2Vucy5cbiAgICovXG4gIHRva2VuVmVyc2lvbj86IG51bWJlcjtcbiAgLyoqIFRva2VuIHR5cGUgKi9cbiAgdHlwZTogJ2FjY2VzcycgfCAncmVmcmVzaCc7XG4gIC8qKiBJc3N1ZWQgYXQgdGltZXN0YW1wICovXG4gIGlhdD86IG51bWJlcjtcbiAgLyoqIEV4cGlyYXRpb24gdGltZXN0YW1wICovXG4gIGV4cD86IG51bWJlcjtcbn1cblxuLyoqXG4gKiBUaGUgYWNjb3VudCBib3VuZGFyeSAocm9vdCBvcmcpIGZvciBhIHRva2VuJ3MgYWN0aXZlIG9yZy5cbiAqXG4gKiBUaGUgYWNjb3VudCByb290IGlzIGByb290T3JnYW5pemF0aW9uSWQgPz8gb3JnYW5pemF0aW9uSWRgOiBhIGZsYXQgKHJvb3QpIG9yZ1xuICogb21pdHMgYHJvb3RPcmdhbml6YXRpb25JZGAsIHNvIGl0IElTIGl0cyBvd24gcm9vdDsgYSB0ZWFtIChjaGlsZCkgb3JnIGNhcnJpZXNcbiAqIHRoZSB0b3Agb2YgaXRzIGFuY2VzdHJ5IGNoYWluLiBFdmVyeSBhY2NvdW50LXNjb3BlZCByZWFkIChxdW90YS9zZWF0L2JpbGxpbmdcbiAqIHBvb2xpbmcpIG11c3QgcmVzb2x2ZSB0aHJvdWdoIHRoaXMgY29hbGVzY2UgcmF0aGVyIHRoYW4gcmVhZGluZ1xuICogYG9yZ2FuaXphdGlvbklkYCBkaXJlY3RseSDigJQgdGhlIGxhdHRlciBzaWxlbnRseSB3b3JrcyB0b2RheSAoYWxsIG9yZ3MgZmxhdClcbiAqIGJ1dCB3b3VsZCBiaW5kIHRvIHRoZSB0ZWFtIGluc3RlYWQgb2YgdGhlIGFjY291bnQgb25jZSB0ZWFtcyBleGlzdC5cbiAqXG4gKiBAcmV0dXJucyB0aGUgZWZmZWN0aXZlIHJvb3Qgb3JnIGlkLCBvciBgdW5kZWZpbmVkYCB3aGVuIG5laXRoZXIgZmllbGQgaXMgc2V0LlxuICovXG5leHBvcnQgZnVuY3Rpb24gZWZmZWN0aXZlUm9vdE9yZ0lkKFxuICBwYXlsb2FkOiBQaWNrPEp3dFBheWxvYWQsICdvcmdhbml6YXRpb25JZCcgfCAncm9vdE9yZ2FuaXphdGlvbklkJz4sXG4pOiBzdHJpbmcgfCB1bmRlZmluZWQge1xuICByZXR1cm4gcGF5bG9hZC5yb290T3JnYW5pemF0aW9uSWQgPz8gcGF5bG9hZC5vcmdhbml6YXRpb25JZDtcbn1cblxuLyoqXG4gKiBFeHRlbmRlZCBFeHByZXNzIFJlcXVlc3Qgd2l0aCB1c2VyIHByb3BlcnR5LlxuICovXG5kZWNsYXJlIGdsb2JhbCB7XG4gIG5hbWVzcGFjZSBFeHByZXNzIHtcbiAgICBpbnRlcmZhY2UgUmVxdWVzdCB7XG4gICAgICB1c2VyPzogSnd0UGF5bG9hZDtcbiAgICB9XG4gIH1cbn1cblxuLyoqXG4gKiBTZXJ2aWNlIGNvbmZpZ3VyYXRpb24gZm9yIGludGVybmFsIEhUVFAgY2xpZW50LlxuICovXG5leHBvcnQgaW50ZXJmYWNlIFNlcnZpY2VDb25maWcge1xuICAvKiogU2VydmljZSBob3N0bmFtZSAqL1xuICBob3N0OiBzdHJpbmc7XG4gIC8qKiBTZXJ2aWNlIHBvcnQgKi9cbiAgcG9ydDogbnVtYmVyO1xuICAvKiogUmVxdWVzdCB0aW1lb3V0IGluIG1pbGxpc2Vjb25kcyAqL1xuICB0aW1lb3V0PzogbnVtYmVyO1xufVxuXG4vKipcbiAqIEhlYWx0aCBjaGVjayByZXNwb25zZS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBIZWFsdGhDaGVja1Jlc3BvbnNlIHtcbiAgc3RhdHVzOiAnaGVhbHRoeScgfCAndW5oZWFsdGh5JztcbiAgc2VydmljZTogc3RyaW5nO1xuICB0aW1lc3RhbXA6IHN0cmluZztcbiAgdXB0aW1lOiBudW1iZXI7XG4gIHZlcnNpb24/OiBzdHJpbmc7XG4gIGRlcGVuZGVuY2llcz86IFJlY29yZDxzdHJpbmcsICdjb25uZWN0ZWQnIHwgJ2Rpc2Nvbm5lY3RlZCcgfCAndW5rbm93bic+O1xufVxuIl19
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Standardized error codes used across all API microservices.
3
+ * Use these codes in error responses for consistent client handling.
4
+ *
5
+ * Each HTTP status category has one primary code. Use the `details`
6
+ * field in error responses for sub-type information (e.g. which quota
7
+ * was exceeded, which field failed validation).
8
+ */
9
+ export declare enum ErrorCode {
10
+ UNAUTHORIZED = "UNAUTHORIZED",
11
+ TOKEN_EXPIRED = "TOKEN_EXPIRED",
12
+ TOKEN_INVALID = "TOKEN_INVALID",
13
+ TOKEN_MISSING = "TOKEN_MISSING",
14
+ TOKEN_REVOKED = "TOKEN_REVOKED",
15
+ INSUFFICIENT_PERMISSIONS = "INSUFFICIENT_PERMISSIONS",
16
+ ORG_MISMATCH = "ORG_MISMATCH",
17
+ COMPLIANCE_VIOLATION = "COMPLIANCE_VIOLATION",
18
+ NOT_FOUND = "NOT_FOUND",
19
+ ORG_NOT_FOUND = "ORG_NOT_FOUND",
20
+ VALIDATION_ERROR = "VALIDATION_ERROR",
21
+ MISSING_REQUIRED_FIELD = "MISSING_REQUIRED_FIELD",
22
+ TEMPLATE_UNKNOWN_PATH = "TEMPLATE_UNKNOWN_PATH",
23
+ TEMPLATE_CYCLE = "TEMPLATE_CYCLE",
24
+ TEMPLATE_PARSE_ERROR = "TEMPLATE_PARSE_ERROR",
25
+ TEMPLATE_TYPE_MISMATCH = "TEMPLATE_TYPE_MISMATCH",
26
+ TEMPLATE_SECRETS_RESERVED = "TEMPLATE_SECRETS_RESERVED",
27
+ TEMPLATE_CONTRACT_VIOLATION = "TEMPLATE_CONTRACT_VIOLATION",
28
+ TEMPLATE_SIZE_EXCEEDED = "TEMPLATE_SIZE_EXCEEDED",
29
+ TEMPLATE_VALIDATION_FAILED = "TEMPLATE_VALIDATION_FAILED",
30
+ QUOTA_EXCEEDED = "QUOTA_EXCEEDED",
31
+ RATE_LIMIT_EXCEEDED = "RATE_LIMIT_EXCEEDED",
32
+ PAYLOAD_TOO_LARGE = "PAYLOAD_TOO_LARGE",
33
+ CONFLICT = "CONFLICT",
34
+ DUPLICATE_ENTRY = "DUPLICATE_ENTRY",
35
+ SCAN_CONFLICT = "SCAN_CONFLICT",
36
+ PAYMENT_METHOD_REQUIRED = "PAYMENT_METHOD_REQUIRED",// 402
37
+ DISCOUNT_CEILING_EXCEEDED = "DISCOUNT_CEILING_EXCEEDED",// 400
38
+ DISCOUNT_NOT_FOUND = "DISCOUNT_NOT_FOUND",// 404
39
+ DISCOUNT_INACTIVE = "DISCOUNT_INACTIVE",// 409
40
+ PLAN_OVER_CAP = "PLAN_OVER_CAP",// 409
41
+ ADDON_OVER_CAP = "ADDON_OVER_CAP",// 409
42
+ INTERNAL_ERROR = "INTERNAL_ERROR",
43
+ DATABASE_ERROR = "DATABASE_ERROR",
44
+ SERVICE_UNAVAILABLE = "SERVICE_UNAVAILABLE",
45
+ COMPLIANCE_SERVICE_UNAVAILABLE = "COMPLIANCE_SERVICE_UNAVAILABLE"
46
+ }
47
+ /**
48
+ * Maps error codes to their default HTTP status codes.
49
+ */
50
+ export declare const ErrorCodeStatus: Record<ErrorCode, number>;
51
+ /**
52
+ * Get the HTTP status code for an error code.
53
+ */
54
+ export declare function getStatusForErrorCode(code: ErrorCode): number;
@@ -0,0 +1,107 @@
1
+ // Copyright 2026 Pipeline Builder Contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * Standardized error codes used across all API microservices.
5
+ * Use these codes in error responses for consistent client handling.
6
+ *
7
+ * Each HTTP status category has one primary code. Use the `details`
8
+ * field in error responses for sub-type information (e.g. which quota
9
+ * was exceeded, which field failed validation).
10
+ */
11
+ export var ErrorCode;
12
+ (function (ErrorCode) {
13
+ // Authentication errors (401)
14
+ ErrorCode["UNAUTHORIZED"] = "UNAUTHORIZED";
15
+ ErrorCode["TOKEN_EXPIRED"] = "TOKEN_EXPIRED";
16
+ ErrorCode["TOKEN_INVALID"] = "TOKEN_INVALID";
17
+ ErrorCode["TOKEN_MISSING"] = "TOKEN_MISSING";
18
+ ErrorCode["TOKEN_REVOKED"] = "TOKEN_REVOKED";
19
+ // Authorization errors (403)
20
+ ErrorCode["INSUFFICIENT_PERMISSIONS"] = "INSUFFICIENT_PERMISSIONS";
21
+ ErrorCode["ORG_MISMATCH"] = "ORG_MISMATCH";
22
+ ErrorCode["COMPLIANCE_VIOLATION"] = "COMPLIANCE_VIOLATION";
23
+ // Not found errors (404)
24
+ ErrorCode["NOT_FOUND"] = "NOT_FOUND";
25
+ ErrorCode["ORG_NOT_FOUND"] = "ORG_NOT_FOUND";
26
+ // Validation errors (400)
27
+ ErrorCode["VALIDATION_ERROR"] = "VALIDATION_ERROR";
28
+ ErrorCode["MISSING_REQUIRED_FIELD"] = "MISSING_REQUIRED_FIELD";
29
+ ErrorCode["TEMPLATE_UNKNOWN_PATH"] = "TEMPLATE_UNKNOWN_PATH";
30
+ ErrorCode["TEMPLATE_CYCLE"] = "TEMPLATE_CYCLE";
31
+ ErrorCode["TEMPLATE_PARSE_ERROR"] = "TEMPLATE_PARSE_ERROR";
32
+ ErrorCode["TEMPLATE_TYPE_MISMATCH"] = "TEMPLATE_TYPE_MISMATCH";
33
+ ErrorCode["TEMPLATE_SECRETS_RESERVED"] = "TEMPLATE_SECRETS_RESERVED";
34
+ ErrorCode["TEMPLATE_CONTRACT_VIOLATION"] = "TEMPLATE_CONTRACT_VIOLATION";
35
+ ErrorCode["TEMPLATE_SIZE_EXCEEDED"] = "TEMPLATE_SIZE_EXCEEDED";
36
+ ErrorCode["TEMPLATE_VALIDATION_FAILED"] = "TEMPLATE_VALIDATION_FAILED";
37
+ // Quota/Rate limit errors (429)
38
+ ErrorCode["QUOTA_EXCEEDED"] = "QUOTA_EXCEEDED";
39
+ ErrorCode["RATE_LIMIT_EXCEEDED"] = "RATE_LIMIT_EXCEEDED";
40
+ // Payload errors (413)
41
+ ErrorCode["PAYLOAD_TOO_LARGE"] = "PAYLOAD_TOO_LARGE";
42
+ // Conflict errors (409)
43
+ ErrorCode["CONFLICT"] = "CONFLICT";
44
+ ErrorCode["DUPLICATE_ENTRY"] = "DUPLICATE_ENTRY";
45
+ ErrorCode["SCAN_CONFLICT"] = "SCAN_CONFLICT";
46
+ // Billing errors
47
+ ErrorCode["PAYMENT_METHOD_REQUIRED"] = "PAYMENT_METHOD_REQUIRED";
48
+ ErrorCode["DISCOUNT_CEILING_EXCEEDED"] = "DISCOUNT_CEILING_EXCEEDED";
49
+ ErrorCode["DISCOUNT_NOT_FOUND"] = "DISCOUNT_NOT_FOUND";
50
+ ErrorCode["DISCOUNT_INACTIVE"] = "DISCOUNT_INACTIVE";
51
+ ErrorCode["PLAN_OVER_CAP"] = "PLAN_OVER_CAP";
52
+ ErrorCode["ADDON_OVER_CAP"] = "ADDON_OVER_CAP";
53
+ // Server errors (500)
54
+ ErrorCode["INTERNAL_ERROR"] = "INTERNAL_ERROR";
55
+ ErrorCode["DATABASE_ERROR"] = "DATABASE_ERROR";
56
+ // Service unavailable (503)
57
+ ErrorCode["SERVICE_UNAVAILABLE"] = "SERVICE_UNAVAILABLE";
58
+ ErrorCode["COMPLIANCE_SERVICE_UNAVAILABLE"] = "COMPLIANCE_SERVICE_UNAVAILABLE";
59
+ })(ErrorCode || (ErrorCode = {}));
60
+ /**
61
+ * Maps error codes to their default HTTP status codes.
62
+ */
63
+ export const ErrorCodeStatus = {
64
+ [ErrorCode.UNAUTHORIZED]: 401,
65
+ [ErrorCode.TOKEN_EXPIRED]: 401,
66
+ [ErrorCode.TOKEN_INVALID]: 401,
67
+ [ErrorCode.TOKEN_MISSING]: 401,
68
+ [ErrorCode.TOKEN_REVOKED]: 401,
69
+ [ErrorCode.INSUFFICIENT_PERMISSIONS]: 403,
70
+ [ErrorCode.ORG_MISMATCH]: 403,
71
+ [ErrorCode.COMPLIANCE_VIOLATION]: 403,
72
+ [ErrorCode.NOT_FOUND]: 404,
73
+ [ErrorCode.ORG_NOT_FOUND]: 404,
74
+ [ErrorCode.VALIDATION_ERROR]: 400,
75
+ [ErrorCode.MISSING_REQUIRED_FIELD]: 400,
76
+ [ErrorCode.TEMPLATE_UNKNOWN_PATH]: 400,
77
+ [ErrorCode.TEMPLATE_CYCLE]: 400,
78
+ [ErrorCode.TEMPLATE_PARSE_ERROR]: 400,
79
+ [ErrorCode.TEMPLATE_TYPE_MISMATCH]: 400,
80
+ [ErrorCode.TEMPLATE_SECRETS_RESERVED]: 400,
81
+ [ErrorCode.TEMPLATE_CONTRACT_VIOLATION]: 400,
82
+ [ErrorCode.TEMPLATE_SIZE_EXCEEDED]: 400,
83
+ [ErrorCode.TEMPLATE_VALIDATION_FAILED]: 400,
84
+ [ErrorCode.QUOTA_EXCEEDED]: 429,
85
+ [ErrorCode.RATE_LIMIT_EXCEEDED]: 429,
86
+ [ErrorCode.PAYLOAD_TOO_LARGE]: 413,
87
+ [ErrorCode.CONFLICT]: 409,
88
+ [ErrorCode.DUPLICATE_ENTRY]: 409,
89
+ [ErrorCode.SCAN_CONFLICT]: 409,
90
+ [ErrorCode.PAYMENT_METHOD_REQUIRED]: 402,
91
+ [ErrorCode.DISCOUNT_CEILING_EXCEEDED]: 400,
92
+ [ErrorCode.DISCOUNT_NOT_FOUND]: 404,
93
+ [ErrorCode.DISCOUNT_INACTIVE]: 409,
94
+ [ErrorCode.PLAN_OVER_CAP]: 409,
95
+ [ErrorCode.ADDON_OVER_CAP]: 409,
96
+ [ErrorCode.INTERNAL_ERROR]: 500,
97
+ [ErrorCode.DATABASE_ERROR]: 500,
98
+ [ErrorCode.SERVICE_UNAVAILABLE]: 503,
99
+ [ErrorCode.COMPLIANCE_SERVICE_UNAVAILABLE]: 503,
100
+ };
101
+ /**
102
+ * Get the HTTP status code for an error code.
103
+ */
104
+ export function getStatusForErrorCode(code) {
105
+ return ErrorCodeStatus[code] ?? 500;
106
+ }
107
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXJyb3ItY29kZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdHlwZXMvZXJyb3ItY29kZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsK0NBQStDO0FBQy9DLHNDQUFzQztBQUV0Qzs7Ozs7OztHQU9HO0FBQ0gsTUFBTSxDQUFOLElBQVksU0F3RFg7QUF4REQsV0FBWSxTQUFTO0lBQ25CLDhCQUE4QjtJQUM5QiwwQ0FBNkIsQ0FBQTtJQUM3Qiw0Q0FBK0IsQ0FBQTtJQUMvQiw0Q0FBK0IsQ0FBQTtJQUMvQiw0Q0FBK0IsQ0FBQTtJQUMvQiw0Q0FBK0IsQ0FBQTtJQUUvQiw2QkFBNkI7SUFDN0Isa0VBQXFELENBQUE7SUFDckQsMENBQTZCLENBQUE7SUFDN0IsMERBQTZDLENBQUE7SUFFN0MseUJBQXlCO0lBQ3pCLG9DQUF1QixDQUFBO0lBQ3ZCLDRDQUErQixDQUFBO0lBRS9CLDBCQUEwQjtJQUMxQixrREFBcUMsQ0FBQTtJQUNyQyw4REFBaUQsQ0FBQTtJQUNqRCw0REFBK0MsQ0FBQTtJQUMvQyw4Q0FBaUMsQ0FBQTtJQUNqQywwREFBNkMsQ0FBQTtJQUM3Qyw4REFBaUQsQ0FBQTtJQUNqRCxvRUFBdUQsQ0FBQTtJQUN2RCx3RUFBMkQsQ0FBQTtJQUMzRCw4REFBaUQsQ0FBQTtJQUNqRCxzRUFBeUQsQ0FBQTtJQUV6RCxnQ0FBZ0M7SUFDaEMsOENBQWlDLENBQUE7SUFDakMsd0RBQTJDLENBQUE7SUFFM0MsdUJBQXVCO0lBQ3ZCLG9EQUF1QyxDQUFBO0lBRXZDLHdCQUF3QjtJQUN4QixrQ0FBcUIsQ0FBQTtJQUNyQixnREFBbUMsQ0FBQTtJQUNuQyw0Q0FBK0IsQ0FBQTtJQUUvQixpQkFBaUI7SUFDakIsZ0VBQW1ELENBQUE7SUFDbkQsb0VBQXVELENBQUE7SUFDdkQsc0RBQXlDLENBQUE7SUFDekMsb0RBQXVDLENBQUE7SUFDdkMsNENBQStCLENBQUE7SUFDL0IsOENBQWlDLENBQUE7SUFFakMsc0JBQXNCO0lB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bnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbi8qKlxuICogU3RhbmRhcmRpemVkIGVycm9yIGNvZGVzIHVzZWQgYWNyb3NzIGFsbCBBUEkgbWljcm9zZXJ2aWNlcy5cbiAqIFVzZSB0aGVzZSBjb2RlcyBpbiBlcnJvciByZXNwb25zZXMgZm9yIGNvbnNpc3RlbnQgY2xpZW50IGhhbmRsaW5nLlxuICpcbiAqIEVhY2ggSFRUUCBzdGF0dXMgY2F0ZWdvcnkgaGFzIG9uZSBwcmltYXJ5IGNvZGUuIFVzZSB0aGUgYGRldGFpbHNgXG4gKiBmaWVsZCBpbiBlcnJvciByZXNwb25zZXMgZm9yIHN1Yi10eXBlIGluZm9ybWF0aW9uIChlLmcuIHdoaWNoIHF1b3RhXG4gKiB3YXMgZXhjZWVkZWQsIHdoaWNoIGZpZWxkIGZhaWxlZCB2YWxpZGF0aW9uKS5cbiAqL1xuZXhwb3J0IGVudW0gRXJyb3JDb2RlIHtcbiAgLy8gQXV0aGVudGljYXRpb24gZXJyb3JzICg0MDEpXG4gIFVOQVVUSE9SSVpFRCA9ICdVTkFVVEhPUklaRUQnLFxuICBUT0tFTl9FWFBJUkVEID0gJ1RPS0VOX0VYUElSRUQnLFxuICBUT0tFTl9JTlZBTElEID0gJ1RPS0VOX0lOVkFMSUQnLFxuICBUT0tFTl9NSVNTSU5HID0gJ1RPS0VOX01JU1NJTkcnLFxuICBUT0tFTl9SRVZPS0VEID0gJ1RPS0VOX1JFVk9LRUQnLFxuXG4gIC8vIEF1dGhvcml6YXRpb24gZXJyb3JzICg0MDMpXG4gIElOU1VGRklDSUVOVF9QRVJNSVNTSU9OUyA9ICdJTlNVRkZJQ0lFTlRfUEVSTUlTU0lPTlMnLFxuICBPUkdfTUlTTUFUQ0ggPSAnT1JHX01JU01BVENIJyxcbiAgQ09NUExJQU5DRV9WSU9MQVRJT04gPSAnQ09NUExJQU5DRV9WSU9MQVRJT04nLFxuXG4gIC8vIE5vdCBmb3VuZCBlcnJvcnMgKDQwNClcbiAgTk9UX0ZPVU5EID0gJ05PVF9GT1VORCcsXG4gIE9SR19OT1RfRk9VTkQgPSAnT1JHX05PVF9GT1VORCcsXG5cbiAgLy8gVmFsaWRhdGlvbiBlcnJvcnMgKDQwMClcbiAgVkFMSURBVElPTl9FUlJPUiA9ICdWQUxJREFUSU9OX0VSUk9SJyxcbiAgTUlTU0lOR19SRVFVSVJFRF9GSUVMRCA9ICdNSVNTSU5HX1JFUVVJUkVEX0ZJRUxEJyxcbiAgVEVNUExBVEVfVU5LTk9XTl9QQVRIID0gJ1RFTVBMQVRFX1VOS05PV05fUEFUSCcsXG4gIFRFTVBMQVRFX0NZQ0xFID0gJ1RFTVBMQVRFX0NZQ0xFJyxcbiAgVEVNUExBVEVfUEFSU0VfRVJST1IgPSAnVEVNUExBVEVfUEFSU0VfRVJST1InLFxuICBURU1QTEFURV9UWVBFX01JU01BVENIID0gJ1RFTVBMQVRFX1RZUEVfTUlTTUFUQ0gnLFxuICBURU1QTEFURV9TRUNSRVRTX1JFU0VSVkVEID0gJ1RFTVBMQVRFX1NFQ1JFVFNfUkVTRVJWRUQnLFxuICBURU1QTEFURV9DT05UUkFDVF9WSU9MQVRJT04gPSAnVEVNUExBVEVfQ09OVFJBQ1RfVklPTEFUSU9OJyxcbiAgVEVNUExBVEVfU0laRV9FWENFRURFRCA9ICdURU1QTEFURV9TSVpFX0VYQ0VFREVEJyxcbiAgVEVNUExBVEVfVkFMSURBVElPTl9GQUlMRUQgPSAnVEVNUExBVEVfVkFMSURBVElPTl9GQUlMRUQnLFxuXG4gIC8vIFF1b3RhL1JhdGUgbGltaXQgZXJyb3JzICg0MjkpXG4gIFFVT1RBX0VYQ0VFREVEID0gJ1FVT1RBX0VYQ0VFREVEJyxcbiAgUkFURV9MSU1JVF9FWENFRURFRCA9ICdSQVRFX0xJTUlUX0VYQ0VFREVEJyxcblxuICAvLyBQYXlsb2FkIGVycm9ycyAoNDEzKVxuICBQQVlMT0FEX1RPT19MQVJHRSA9ICdQQVlMT0FEX1RPT19MQVJHRScsXG5cbiAgLy8gQ29uZmxpY3QgZXJyb3JzICg0MDkpXG4gIENPTkZMSUNUID0gJ0NPTkZMSUNUJyxcbiAgRFVQTElDQVRFX0VOVFJZID0gJ0RVUExJQ0FURV9FTlRSWScsXG4gIFNDQU5fQ09ORkxJQ1QgPSAnU0NBTl9DT05GTElDVCcsXG5cbiAgLy8gQmlsbGluZyBlcnJvcnNcbiAgUEFZTUVOVF9NRVRIT0RfUkVRVUlSRUQgPSAnUEFZTUVOVF9NRVRIT0RfUkVRVUlSRUQnLCAvLyA0MDJcbiAgRElTQ09VTlRfQ0VJTElOR19FWENFRURFRCA9ICdESVNDT1VOVF9DRUlMSU5HX0VYQ0VFREVEJywgLy8gNDAwXG4gIERJU0NPVU5UX05PVF9GT1VORCA9ICdESVNDT1VOVF9OT1RfRk9VTkQnLCAvLyA0MDRcbiAgRElTQ09VTlRfSU5BQ1RJVkUgPSAnRElTQ09VTlRfSU5BQ1RJVkUnLCAvLyA0MDlcbiAgUExBTl9PVkVSX0NBUCA9ICdQTEFOX09WRVJfQ0FQJywgLy8gNDA5XG4gIEFERE9OX09WRVJfQ0FQID0gJ0FERE9OX09WRVJfQ0FQJywgLy8gNDA5XG5cbiAgLy8gU2VydmVyIGVycm9ycyAoNTAwKVxuICBJTlRFUk5BTF9FUlJPUiA9ICdJTlRFUk5BTF9FUlJPUicsXG4gIERBVEFCQVNFX0VSUk9SID0gJ0RBVEFCQVNFX0VSUk9SJyxcblxuICAvLyBTZXJ2aWNlIHVuYXZhaWxhYmxlICg1MDMpXG4gIFNFUlZJQ0VfVU5BVkFJTEFCTEUgPSAnU0VSVklDRV9VTkFWQUlMQUJMRScsXG4gIENPTVBMSUFOQ0VfU0VSVklDRV9VTkFWQUlMQUJMRSA9ICdDT01QTElBTkNFX1NFUlZJQ0VfVU5BVkFJTEFCTEUnLFxufVxuXG4vKipcbiAqIE1hcHMgZXJyb3IgY29kZXMgdG8gdGhlaXIgZGVmYXVsdCBIVFRQIHN0YXR1cyBjb2Rlcy5cbiAqL1xuZXhwb3J0IGNvbnN0IEVycm9yQ29kZVN0YXR1czogUmVjb3JkPEVycm9yQ29kZSwgbnVtYmVyPiA9IHtcbiAgW0Vycm9yQ29kZS5VTkFVVEhPUklaRURdOiA0MDEsXG4gIFtFcnJvckNvZGUuVE9LRU5fRVhQSVJFRF06IDQwMSxcbiAgW0Vycm9yQ29kZS5UT0tFTl9JTlZBTElEXTogNDAxLFxuICBbRXJyb3JDb2RlLlRPS0VOX01JU1NJTkddOiA0MDEsXG4gIFtFcnJvckNvZGUuVE9LRU5fUkVWT0tFRF06IDQwMSxcbiAgW0Vycm9yQ29kZS5JTlNVRkZJQ0lFTlRfUEVSTUlTU0lPTlNdOiA0MDMsXG4gIFtFcnJvckNvZGUuT1JHX01JU01BVENIXTogNDAzLFxuICBbRXJyb3JDb2RlLkNPTVBMSUFOQ0VfVklPTEFUSU9OXTogNDAzLFxuICBbRXJyb3JDb2RlLk5PVF9GT1VORF06IDQwNCxcbiAgW0Vycm9yQ29kZS5PUkdfTk9UX0ZPVU5EXTogNDA0LFxuICBbRXJyb3JDb2RlLlZBTElEQVRJT05fRVJST1JdOiA0MDAsXG4gIFtFcnJvckNvZGUuTUlTU0lOR19SRVFVSVJFRF9GSUVMRF06IDQwMCxcbiAgW0Vycm9yQ29kZS5URU1QTEFURV9VTktOT1dOX1BBVEhdOiA0MDAsXG4gIFtFcnJvckNvZGUuVEVNUExBVEVfQ1lDTEVdOiA0MDAsXG4gIFtFcnJvckNvZGUuVEVNUExBVEVfUEFSU0VfRVJST1JdOiA0MDAsXG4gIFtFcnJvckNvZGUuVEVNUExBVEVfVFlQRV9NSVNNQVRDSF06IDQwMCxcbiAgW0Vycm9yQ29kZS5URU1QTEFURV9TRUNSRVRTX1JFU0VSVkVEXTogNDAwLFxuICBbRXJyb3JDb2RlLlRFTVBMQVRFX0NPTlRSQUNUX1ZJT0xBVElPTl06IDQwMCxcbiAgW0Vycm9yQ29kZS5URU1QTEFURV9TSVpFX0VYQ0VFREVEXTogNDAwLFxuICBbRXJyb3JDb2RlLlRFTVBMQVRFX1ZBTElEQVRJT05fRkFJTEVEXTogNDAwLFxuICBbRXJyb3JDb2RlLlFVT1RBX0VYQ0VFREVEXTogNDI5LFxuICBbRXJyb3JDb2RlLlJBVEVfTElNSVRfRVhDRUVERURdOiA0MjksXG4gIFtFcnJvckNvZGUuUEFZTE9BRF9UT09fTEFSR0VdOiA0MTMsXG4gIFtFcnJvckNvZGUuQ09ORkxJQ1RdOiA0MDksXG4gIFtFcnJvckNvZGUuRFVQTElDQVRFX0VOVFJZXTogNDA5LFxuICBbRXJyb3JDb2RlLlNDQU5fQ09ORkxJQ1RdOiA0MDksXG4gIFtFcnJvckNvZGUuUEFZTUVOVF9NRVRIT0RfUkVRVUlSRURdOiA0MDIsXG4gIFtFcnJvckNvZGUuRElTQ09VTlRfQ0VJTElOR19FWENFRURFRF06IDQwMCxcbiAgW0Vycm9yQ29kZS5ESVNDT1VOVF9OT1RfRk9VTkRdOiA0MDQsXG4gIFtFcnJvckNvZGUuRElTQ09VTlRfSU5BQ1RJVkVdOiA0MDksXG4gIFtFcnJvckNvZGUuUExBTl9PVkVSX0NBUF06IDQwOSxcbiAgW0Vycm9yQ29kZS5BRERPTl9PVkVSX0NBUF06IDQwOSxcbiAgW0Vycm9yQ29kZS5JTlRFUk5BTF9FUlJPUl06IDUwMCxcbiAgW0Vycm9yQ29kZS5EQVRBQkFTRV9FUlJPUl06IDUwMCxcbiAgW0Vycm9yQ29kZS5TRVJWSUNFX1VOQVZBSUxBQkxFXTogNTAzLFxuICBbRXJyb3JDb2RlLkNPTVBMSUFOQ0VfU0VSVklDRV9VTkFWQUlMQUJMRV06IDUwMyxcbn07XG5cbi8qKlxuICogR2V0IHRoZSBIVFRQIHN0YXR1cyBjb2RlIGZvciBhbiBlcnJvciBjb2RlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0U3RhdHVzRm9yRXJyb3JDb2RlKGNvZGU6IEVycm9yQ29kZSk6IG51bWJlciB7XG4gIHJldHVybiBFcnJvckNvZGVTdGF0dXNbY29kZV0gPz8gNTAwO1xufVxuIl19
@@ -0,0 +1,36 @@
1
+ import type { QuotaTier } from './quota-tiers.js';
2
+ /** Canonical feature flag identifiers. */
3
+ export type FeatureFlag = 'priority_support' | 'custom_integrations' | 'ai_generation' | 'bulk_operations' | 'audit_log' | 'sso' | 'advanced_reporting' | 'team_usage_analytics' | 'compliance_standard' | 'compliance_advanced';
4
+ /** All valid feature flags (order determines display order). */
5
+ export declare const ALL_FEATURE_FLAGS: readonly FeatureFlag[];
6
+ /** Check whether a string is a valid FeatureFlag. */
7
+ export declare function isValidFeatureFlag(value: string): value is FeatureFlag;
8
+ /** Features enabled by default for each tier. */
9
+ export declare const TIER_FEATURES: Record<QuotaTier, readonly FeatureFlag[]>;
10
+ /** Human-readable metadata for each feature flag. */
11
+ export declare const FEATURE_METADATA: Record<FeatureFlag, {
12
+ label: string;
13
+ description: string;
14
+ }>;
15
+ /**
16
+ * Resolve a user's effective feature set.
17
+ *
18
+ * 1. Sysadmins (isSuperAdmin) always get ALL features.
19
+ * 2. Start with the tier's default features.
20
+ * 3. Apply per-user overrides: `true` adds a feature, `false` removes it.
21
+ * 4. Invalid override keys are silently ignored.
22
+ *
23
+ * @param tier - The organization's quota tier
24
+ * @param opts - Resolution options:
25
+ * - `overrides`: per-user overrides (key = feature flag, value = enabled)
26
+ * - `isSuperAdmin`: whether the user has the global super-admin flag
27
+ * - `accountFeatures`: account-level entitlements (e.g. purchased add-on
28
+ * bundles). A NAMED field so it can never be silently dropped by a caller
29
+ * that omits a trailing positional arg.
30
+ * @returns Sorted array of enabled feature flags
31
+ */
32
+ export declare function resolveUserFeatures(tier: QuotaTier, opts?: {
33
+ overrides?: Record<string, boolean> | null;
34
+ isSuperAdmin?: boolean;
35
+ accountFeatures?: readonly string[] | null;
36
+ }): FeatureFlag[];