@pipeline-builder/pipeline-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.
- package/LICENSE +202 -0
- package/README.md +69 -0
- package/lib/cdk.d.ts +38 -0
- package/lib/cdk.js +44 -0
- package/lib/config/app-config.d.ts +109 -0
- package/lib/config/app-config.js +197 -0
- package/lib/config/aws-config-cdk.d.ts +49 -0
- package/lib/config/aws-config-cdk.js +65 -0
- package/lib/config/billing-config.d.ts +24 -0
- package/lib/config/billing-config.js +497 -0
- package/lib/config/config-types.d.ts +346 -0
- package/lib/config/config-types.js +4 -0
- package/lib/config/entitlements.d.ts +23 -0
- package/lib/config/entitlements.js +46 -0
- package/lib/config/handler-constants.d.ts +10 -0
- package/lib/config/handler-constants.js +39 -0
- package/lib/config/infrastructure-config.d.ts +66 -0
- package/lib/config/infrastructure-config.js +197 -0
- package/lib/config/server-config.d.ts +53 -0
- package/lib/config/server-config.js +175 -0
- package/lib/config/service-client.d.ts +13 -0
- package/lib/config/service-client.js +20 -0
- package/lib/core/artifact-manager.d.ts +62 -0
- package/lib/core/artifact-manager.js +83 -0
- package/lib/core/id-generator.d.ts +50 -0
- package/lib/core/id-generator.js +50 -0
- package/lib/core/metadata-builder.d.ts +48 -0
- package/lib/core/metadata-builder.js +278 -0
- package/lib/core/metadata-helpers.d.ts +31 -0
- package/lib/core/metadata-helpers.js +55 -0
- package/lib/core/network-types.d.ts +200 -0
- package/lib/core/network-types.js +4 -0
- package/lib/core/network.d.ts +33 -0
- package/lib/core/network.js +112 -0
- package/lib/core/pipeline-helpers.d.ts +61 -0
- package/lib/core/pipeline-helpers.js +563 -0
- package/lib/core/pipeline-types.d.ts +118 -0
- package/lib/core/pipeline-types.js +131 -0
- package/lib/core/role-types.d.ts +254 -0
- package/lib/core/role-types.js +4 -0
- package/lib/core/role.d.ts +14 -0
- package/lib/core/role.js +117 -0
- package/lib/core/security-group-types.d.ts +84 -0
- package/lib/core/security-group-types.js +4 -0
- package/lib/core/security-group.d.ts +14 -0
- package/lib/core/security-group.js +33 -0
- package/lib/handlers/plugin-lookup-handler.d.ts +32 -0
- package/lib/handlers/plugin-lookup-handler.js +292 -0
- package/lib/handlers/pnpm-lock.yaml +12 -0
- package/lib/index.d.ts +45 -0
- package/lib/index.js +53 -0
- package/lib/pipeline/pipeline-builder.d.ts +123 -0
- package/lib/pipeline/pipeline-builder.js +476 -0
- package/lib/pipeline/pipeline-configuration.d.ts +81 -0
- package/lib/pipeline/pipeline-configuration.js +214 -0
- package/lib/pipeline/plugin-lookup.d.ts +131 -0
- package/lib/pipeline/plugin-lookup.js +316 -0
- package/lib/pipeline/plugin-spec.d.ts +138 -0
- package/lib/pipeline/plugin-spec.js +4 -0
- package/lib/pipeline/source-builder.d.ts +62 -0
- package/lib/pipeline/source-builder.js +164 -0
- package/lib/pipeline/source-types.d.ts +221 -0
- package/lib/pipeline/source-types.js +4 -0
- package/lib/pipeline/stage-builder.d.ts +78 -0
- package/lib/pipeline/stage-builder.js +129 -0
- package/lib/pipeline/step-types.d.ts +222 -0
- package/lib/pipeline/step-types.js +4 -0
- package/lib/template/evaluator.d.ts +42 -0
- package/lib/template/evaluator.js +164 -0
- package/lib/template/index.d.ts +34 -0
- package/lib/template/index.js +118 -0
- package/lib/template/metrics.d.ts +10 -0
- package/lib/template/metrics.js +43 -0
- package/lib/template/plugin-resolver.d.ts +8 -0
- package/lib/template/plugin-resolver.js +48 -0
- package/lib/template/tokenizer.d.ts +49 -0
- package/lib/template/tokenizer.js +233 -0
- package/lib/template/topo-sort.d.ts +17 -0
- package/lib/template/topo-sort.js +88 -0
- package/lib/template/validate.d.ts +45 -0
- package/lib/template/validate.js +96 -0
- package/lib/template/walker.d.ts +34 -0
- package/lib/template/walker.js +83 -0
- package/package.json +184 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import type { QuotaTier, QuotaTierLimits } from '@pipeline-builder/api-core';
|
|
2
|
+
import type { Algorithm } from 'jsonwebtoken';
|
|
3
|
+
/**
|
|
4
|
+
* Type-safe configuration interface
|
|
5
|
+
*/
|
|
6
|
+
export interface AppConfig {
|
|
7
|
+
readonly server: ServerConfig;
|
|
8
|
+
readonly auth: AuthConfig;
|
|
9
|
+
readonly database: DatabaseConfig;
|
|
10
|
+
readonly registry: RegistryConfig;
|
|
11
|
+
readonly redis: RedisConfig;
|
|
12
|
+
readonly pluginBuild: PluginBuildConfig;
|
|
13
|
+
readonly dockerConfig: BuildConfig;
|
|
14
|
+
readonly observability: ObservabilityConfig;
|
|
15
|
+
readonly compliance: ComplianceConfig;
|
|
16
|
+
readonly aws: AWSConfig;
|
|
17
|
+
readonly rateLimit: RateLimitConfig;
|
|
18
|
+
readonly billing: BillingConfig;
|
|
19
|
+
}
|
|
20
|
+
/** Express server configuration. */
|
|
21
|
+
export interface ServerConfig {
|
|
22
|
+
/** HTTP listen port (env: `PORT`). */
|
|
23
|
+
readonly port: number;
|
|
24
|
+
readonly cors: {
|
|
25
|
+
/** Whether to include credentials in CORS responses (env: `CORS_CREDENTIALS`). */
|
|
26
|
+
readonly credentials: boolean;
|
|
27
|
+
/** Allowed origin(s) — single string, array, or `'*'` (env: `CORS_ORIGIN`). */
|
|
28
|
+
readonly origin: string | string[];
|
|
29
|
+
};
|
|
30
|
+
/** Number of reverse proxy hops to trust (env: `TRUST_PROXY`). */
|
|
31
|
+
readonly trustProxy: number;
|
|
32
|
+
/** Frontend base URL, used as CORS fallback (env: `PLATFORM_BASE_URL`). */
|
|
33
|
+
readonly platformUrl: string;
|
|
34
|
+
readonly httpClient: {
|
|
35
|
+
/** Default HTTP request timeout in ms (env: `HTTP_CLIENT_TIMEOUT`). */
|
|
36
|
+
readonly timeout: number;
|
|
37
|
+
/** Maximum retry attempts for failed requests (env: `HTTP_CLIENT_MAX_RETRIES`). */
|
|
38
|
+
readonly maxRetries: number;
|
|
39
|
+
/** Base delay between retries in ms (env: `HTTP_CLIENT_RETRY_DELAY_MS`). */
|
|
40
|
+
readonly retryDelayMs: number;
|
|
41
|
+
};
|
|
42
|
+
readonly sse: {
|
|
43
|
+
/** Max SSE clients per request (env: `SSE_MAX_CLIENTS_PER_REQUEST`). */
|
|
44
|
+
readonly maxClientsPerRequest: number;
|
|
45
|
+
/** SSE client timeout in ms (env: `SSE_CLIENT_TIMEOUT_MS`). */
|
|
46
|
+
readonly clientTimeoutMs: number;
|
|
47
|
+
/** SSE cleanup interval in ms (env: `SSE_CLEANUP_INTERVAL_MS`). */
|
|
48
|
+
readonly cleanupIntervalMs: number;
|
|
49
|
+
};
|
|
50
|
+
readonly services: {
|
|
51
|
+
readonly pluginHost: string;
|
|
52
|
+
readonly pluginPort: number;
|
|
53
|
+
readonly pipelineHost: string;
|
|
54
|
+
readonly pipelinePort: number;
|
|
55
|
+
readonly messageHost: string;
|
|
56
|
+
readonly messagePort: number;
|
|
57
|
+
readonly platformHost: string;
|
|
58
|
+
readonly platformPort: number;
|
|
59
|
+
readonly complianceHost: string;
|
|
60
|
+
readonly compliancePort: number;
|
|
61
|
+
readonly billingHost: string;
|
|
62
|
+
readonly billingPort: number;
|
|
63
|
+
readonly billingTimeout: number;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/** JWT and refresh token authentication configuration. */
|
|
67
|
+
export interface AuthConfig {
|
|
68
|
+
readonly jwt: {
|
|
69
|
+
/** Signing secret for access tokens (env: `JWT_SECRET`). */
|
|
70
|
+
readonly secret: string;
|
|
71
|
+
/** Token lifetime in seconds (env: `JWT_EXPIRES_IN`). */
|
|
72
|
+
readonly expiresIn: number;
|
|
73
|
+
/** Signing algorithm, e.g. `'HS256'` (env: `JWT_ALGORITHM`). */
|
|
74
|
+
readonly algorithm: Algorithm;
|
|
75
|
+
/** bcrypt salt rounds for password hashing (env: `BCRYPT_SALT_ROUNDS`). */
|
|
76
|
+
readonly saltRounds: number;
|
|
77
|
+
};
|
|
78
|
+
readonly refreshToken: {
|
|
79
|
+
/** Signing secret for refresh tokens (env: `REFRESH_TOKEN_SECRET`). */
|
|
80
|
+
readonly secret: string;
|
|
81
|
+
/** Token lifetime in seconds (env: `REFRESH_TOKEN_EXPIRES_IN`). */
|
|
82
|
+
readonly expiresIn: number;
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/** PostgreSQL and Drizzle ORM database configuration. */
|
|
86
|
+
export interface DatabaseConfig {
|
|
87
|
+
readonly postgres: {
|
|
88
|
+
/** PostgreSQL host (env: `DB_HOST`). */
|
|
89
|
+
readonly host: string;
|
|
90
|
+
/** PostgreSQL port (env: `DB_PORT`). */
|
|
91
|
+
readonly port: number;
|
|
92
|
+
/** Database name (env: `DATABASE`). */
|
|
93
|
+
readonly database: string;
|
|
94
|
+
/** Database user (env: `DB_USER`). */
|
|
95
|
+
readonly user: string;
|
|
96
|
+
/** Database password (env: `DB_PASSWORD`). */
|
|
97
|
+
readonly password: string;
|
|
98
|
+
};
|
|
99
|
+
readonly drizzle: {
|
|
100
|
+
/** Maximum connection pool size (env: `DRIZZLE_MAX_POOL_SIZE`). */
|
|
101
|
+
readonly maxPoolSize: number;
|
|
102
|
+
/** Idle connection timeout in ms (env: `DRIZZLE_IDLE_TIMEOUT_MILLIS`). */
|
|
103
|
+
readonly idleTimeoutMillis: number;
|
|
104
|
+
/** New connection timeout in ms (env: `DRIZZLE_CONNECTION_TIMEOUT_MILLIS`). */
|
|
105
|
+
readonly connectionTimeoutMillis: number;
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
export interface RegistryConfig {
|
|
109
|
+
readonly host: string;
|
|
110
|
+
readonly port: number;
|
|
111
|
+
/**
|
|
112
|
+
* Host/port an OUT-OF-CLUSTER client (notably AWS CodeBuild) uses to pull
|
|
113
|
+
* plugin images, when it differs from the in-cluster `host`/`port`. The
|
|
114
|
+
* in-cluster `registry:5000` ClusterIP isn't resolvable from the VPC, so the
|
|
115
|
+
* synthesized CodeBuild image URI must point at the public gateway instead.
|
|
116
|
+
* Env: `IMAGE_REGISTRY_PULL_HOST` / `IMAGE_REGISTRY_PULL_PORT`. Optional;
|
|
117
|
+
* consumers fall back to `host`/`port` when unset (single-host /
|
|
118
|
+
* in-cluster-only deploys). `loadRegistryConfig` always populates them.
|
|
119
|
+
*/
|
|
120
|
+
readonly pullHost?: string;
|
|
121
|
+
readonly pullPort?: number;
|
|
122
|
+
/** Docker network for build/push (empty string = default). */
|
|
123
|
+
readonly network: string;
|
|
124
|
+
/**
|
|
125
|
+
* BuildKit talks to the registry over plain HTTP when true; HTTPS with the
|
|
126
|
+
* system CA bundle otherwise. Env: `IMAGE_REGISTRY_HTTP` (defaults true —
|
|
127
|
+
* the in-cluster registry has no TLS).
|
|
128
|
+
*/
|
|
129
|
+
readonly http: boolean;
|
|
130
|
+
}
|
|
131
|
+
export interface RedisConfig {
|
|
132
|
+
readonly host: string;
|
|
133
|
+
readonly port: number;
|
|
134
|
+
}
|
|
135
|
+
export interface PluginBuildConfig {
|
|
136
|
+
readonly concurrency: number;
|
|
137
|
+
readonly maxAttempts: number;
|
|
138
|
+
readonly backoffDelayMs: number;
|
|
139
|
+
readonly workerTimeoutMs: number;
|
|
140
|
+
readonly tempDirMaxAgeMs: number;
|
|
141
|
+
readonly dlqMaxAttempts: number;
|
|
142
|
+
readonly dlqBackoffBaseMs: number;
|
|
143
|
+
readonly dlqMaxSize: number;
|
|
144
|
+
}
|
|
145
|
+
export interface BuildConfig {
|
|
146
|
+
/** Root directory for build temp files. */
|
|
147
|
+
readonly tempRoot: string;
|
|
148
|
+
/** Build timeout in milliseconds. */
|
|
149
|
+
readonly timeoutMs: number;
|
|
150
|
+
/** Push timeout in milliseconds. */
|
|
151
|
+
readonly pushTimeoutMs: number;
|
|
152
|
+
/**
|
|
153
|
+
* Path to the buildkitd socket the plugin's `buildctl` connects to. In k8s
|
|
154
|
+
* and compose this is a shared emptyDir/tmpfs volume mounted from the
|
|
155
|
+
* buildkitd sidecar.
|
|
156
|
+
*/
|
|
157
|
+
readonly buildkitAddr: string;
|
|
158
|
+
}
|
|
159
|
+
export interface ObservabilityConfig {
|
|
160
|
+
readonly logLevel: string;
|
|
161
|
+
readonly logFormat: string;
|
|
162
|
+
readonly serviceName: string;
|
|
163
|
+
readonly tracing: {
|
|
164
|
+
readonly enabled: boolean;
|
|
165
|
+
readonly endpoint: string;
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
export interface ComplianceConfig {
|
|
169
|
+
readonly scanSchedulerIntervalMs: number;
|
|
170
|
+
/** When false, the scheduler skips scans/schedules for the system org. Default: false. */
|
|
171
|
+
readonly systemOrgScansEnabled: boolean;
|
|
172
|
+
/** Cross-pod leader-lock TTL for the scan scheduler (ms). Default: 300000. */
|
|
173
|
+
readonly scanLockTtlMs: number;
|
|
174
|
+
/** How often the digest scheduler checks for due digests (ms). Default: 3600000. */
|
|
175
|
+
readonly digestSchedulerIntervalMs: number;
|
|
176
|
+
/** Cross-pod leader-lock TTL for the digest scheduler (ms). Default: 300000. */
|
|
177
|
+
readonly digestLockTtlMs: number;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* AWS infrastructure configuration.
|
|
181
|
+
*
|
|
182
|
+
* Deliberately expressed in PLAIN DATA (numbers, strings, string unions) rather
|
|
183
|
+
* than CDK value objects (`Duration`, `Runtime`, `RetentionDays`, ...). Config is
|
|
184
|
+
* loaded by every service, but only the CDK entry point (`@pipeline-builder/
|
|
185
|
+
* pipeline-core/cdk`) builds stacks typing this block with CDK classes put
|
|
186
|
+
* `aws-cdk-lib` on the import graph of `Config`, and therefore of every API
|
|
187
|
+
* service that reads so much as a port number. Conversion to CDK objects happens
|
|
188
|
+
* at the construct boundary in `config/aws-config-cdk.ts`.
|
|
189
|
+
*/
|
|
190
|
+
export interface AWSConfig {
|
|
191
|
+
readonly lambda: {
|
|
192
|
+
/** Lambda runtime identifier, e.g. `'nodejs24.x'` (env: `LAMBDA_RUNTIME`). */
|
|
193
|
+
readonly runtime: string;
|
|
194
|
+
/** Function timeout in seconds (env: `LAMBDA_TIMEOUT`). */
|
|
195
|
+
readonly timeoutSeconds: number;
|
|
196
|
+
readonly memorySize: number;
|
|
197
|
+
readonly architecture: 'arm64' | 'x86_64';
|
|
198
|
+
readonly reservedConcurrentExecutions?: number;
|
|
199
|
+
};
|
|
200
|
+
readonly logging: {
|
|
201
|
+
readonly groupName: string;
|
|
202
|
+
/** CloudWatch retention in days; `0` means never expire (env: `LOG_RETENTION`). */
|
|
203
|
+
readonly retentionDays: number;
|
|
204
|
+
readonly removalPolicy: 'retain' | 'destroy';
|
|
205
|
+
};
|
|
206
|
+
readonly codeBuild: {
|
|
207
|
+
/** CodeBuild compute type name, e.g. `'SMALL'` (env: `CODEBUILD_COMPUTE_TYPE`). */
|
|
208
|
+
readonly computeType: string;
|
|
209
|
+
/**
|
|
210
|
+
* Image used for CodeBuild steps that don't have a plugin-baked image
|
|
211
|
+
* (cold-start synth bootstrap, ShellSteps with no registry, and
|
|
212
|
+
* `metadata_only` plugins). env: `CODEBUILD_DEFAULT_IMAGE`. Default
|
|
213
|
+
* `pipeline-bootstrap:1.0` — the local tag built by
|
|
214
|
+
* `deploy/codebuild/bootstrap/Dockerfile`, with `pipeline-manager`
|
|
215
|
+
* baked in.
|
|
216
|
+
*
|
|
217
|
+
* Contract: the image MUST have `pipeline-manager` on PATH. The
|
|
218
|
+
* bootstrap synth path no longer self-heals via `npm install -g`;
|
|
219
|
+
* pointing this at an image without the tool gets exit 127 on the
|
|
220
|
+
* first cold-start build.
|
|
221
|
+
*
|
|
222
|
+
* Resolution:
|
|
223
|
+
* - Bare tag (no `/`) → auto-prefixed to
|
|
224
|
+
* `<registry-host>:<port>/library/<tag>` using the registry config,
|
|
225
|
+
* with the per-org platform Secret as Basic auth (same flow as
|
|
226
|
+
* plugin images).
|
|
227
|
+
* - Fully-qualified registry URI (contains `/`) → used as-is, no
|
|
228
|
+
* auth wired (operator owns making it pullable).
|
|
229
|
+
* - When the registry isn't configured or per-org auth isn't
|
|
230
|
+
* available at the call site, falls back to
|
|
231
|
+
* `aws/codebuild/standard:8.0` with a warning — cold-start synth
|
|
232
|
+
* will then fail on `pipeline-manager: not found`, surfacing the
|
|
233
|
+
* misconfiguration loudly instead of silently swapping images.
|
|
234
|
+
*/
|
|
235
|
+
readonly defaultImage: string;
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
/** Express rate limiting configuration. */
|
|
239
|
+
export interface RateLimitConfig {
|
|
240
|
+
/** Maximum requests per window (env: `LIMITER_MAX`). */
|
|
241
|
+
readonly max: number;
|
|
242
|
+
/** Rate limit window in milliseconds (env: `LIMITER_WINDOWMS`). */
|
|
243
|
+
readonly windowMs: number;
|
|
244
|
+
/** Include legacy `X-RateLimit-*` headers. */
|
|
245
|
+
readonly legacyHeaders: boolean;
|
|
246
|
+
/** Include standard `RateLimit-*` headers (RFC 6585). */
|
|
247
|
+
readonly standardHeaders: boolean;
|
|
248
|
+
}
|
|
249
|
+
/** Price configuration for a single billing plan (in cents). */
|
|
250
|
+
export interface BillingPlanPrices {
|
|
251
|
+
readonly monthly: number;
|
|
252
|
+
readonly annual: number;
|
|
253
|
+
}
|
|
254
|
+
/** Full billing plan definition used for seeding and runtime configuration. */
|
|
255
|
+
export interface BillingPlanConfig {
|
|
256
|
+
readonly id: string;
|
|
257
|
+
readonly name: string;
|
|
258
|
+
readonly description: string;
|
|
259
|
+
readonly tier: QuotaTier;
|
|
260
|
+
readonly prices: BillingPlanPrices;
|
|
261
|
+
readonly features: readonly string[];
|
|
262
|
+
readonly isActive: boolean;
|
|
263
|
+
readonly isDefault: boolean;
|
|
264
|
+
readonly sortOrder: number;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* A purchasable add-on bundle: a quantity-stackable pack that ADDS to the
|
|
268
|
+
* account's base (tier) limits and adds a recurring line item. See
|
|
269
|
+
* docs/billing-bundles.md.
|
|
270
|
+
*/
|
|
271
|
+
export interface BundleConfig {
|
|
272
|
+
readonly id: string;
|
|
273
|
+
readonly name: string;
|
|
274
|
+
readonly description: string;
|
|
275
|
+
/** Per-unit deltas on `QuotaTierLimits` fields, e.g. `{ seats: 5 }`. Keys are
|
|
276
|
+
* constrained to real quota fields so a typo is a compile error (was
|
|
277
|
+
* `Record<string, number>`, where a misspelled key was silently unenforceable). */
|
|
278
|
+
readonly grants: Readonly<Partial<Record<keyof QuotaTierLimits, number>>>;
|
|
279
|
+
/** Feature flags granted by a feature bundle (e.g. `audit_log`). */
|
|
280
|
+
readonly features?: readonly string[];
|
|
281
|
+
/** Per-unit price (cents). Stripe multiplies by quantity. */
|
|
282
|
+
readonly prices: BillingPlanPrices;
|
|
283
|
+
/** True for stackable resource packs; false for a boolean feature bundle. */
|
|
284
|
+
readonly stackable: boolean;
|
|
285
|
+
/**
|
|
286
|
+
* Maximum purchasable quantity for a stackable pack. Enforced by the addon
|
|
287
|
+
* purchase/preview route (`quantity ≤ maxQuantity`, reject over-cap). Used to
|
|
288
|
+
* keep retention packs under the 730-day retention ceiling (e.g. the Standard
|
|
289
|
+
* Retention Pack caps at 7 so 30 + 7×90 = 660 ≤ 730). Absent ⇒ unbounded.
|
|
290
|
+
*/
|
|
291
|
+
readonly maxQuantity?: number;
|
|
292
|
+
/**
|
|
293
|
+
* Optional per-unit volume discount tiers for a stackable pack: at ≥ `minQuantity`
|
|
294
|
+
* purchased units, `discountPercent` comes off the line subtotal (unit × quantity).
|
|
295
|
+
* The highest matching tier wins. Realized as a recurring usage credit (like a
|
|
296
|
+
* combo), so the provider still charges unit×qty and the credit offsets the
|
|
297
|
+
* balance. Env-overridable via `BILLING_BUNDLE_<ID>_VOLUME_TIERS` (JSON). Used by
|
|
298
|
+
* the per-seat `seat` bundle. Absent ⇒ no volume discount.
|
|
299
|
+
*/
|
|
300
|
+
readonly volumeTiers?: readonly {
|
|
301
|
+
readonly minQuantity: number;
|
|
302
|
+
readonly discountPercent: number;
|
|
303
|
+
}[];
|
|
304
|
+
/** Base tiers this bundle is offered to. */
|
|
305
|
+
readonly availableForTiers: readonly QuotaTier[];
|
|
306
|
+
/**
|
|
307
|
+
* Prerequisite bundle ids that must be held (or purchased in the same action)
|
|
308
|
+
* before this bundle can be added. Enforced by the addon purchase/preview route
|
|
309
|
+
* (reject with 400 when unmet); a combo that includes the prerequisite satisfies
|
|
310
|
+
* it. Used by `compliance_advanced` (`requires: ['compliance_standard']`).
|
|
311
|
+
* Absent ⇒ no prerequisite.
|
|
312
|
+
*/
|
|
313
|
+
readonly requires?: readonly string[];
|
|
314
|
+
readonly isActive: boolean;
|
|
315
|
+
readonly sortOrder: number;
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* A combo discount: when an account holds EVERY bundle in `bundleIds`, the pair
|
|
319
|
+
* is billed at `prices` (the combined price) instead of the sum of the members'
|
|
320
|
+
* individual prices. Realized as a recurring usage credit for the difference —
|
|
321
|
+
* never a provider coupon — consistent with the usage-credit discount model. See
|
|
322
|
+
* docs/billing-bundles.md §Combo pricing.
|
|
323
|
+
*/
|
|
324
|
+
export interface ComboDiscountConfig {
|
|
325
|
+
readonly id: string;
|
|
326
|
+
readonly name: string;
|
|
327
|
+
/** The member bundle ids that must ALL be present for the combo to apply. */
|
|
328
|
+
readonly bundleIds: readonly string[];
|
|
329
|
+
/** Per-member minimum quantity (bundleId → count; absent ⇒ 1). Lets a stackable
|
|
330
|
+
* capacity pack anchor a combo — e.g. `{ seat: 5 }` = "≥ 5 seats". The credit
|
|
331
|
+
* basis uses this minimum, so extra units never inflate the discount. */
|
|
332
|
+
readonly minQuantities?: Readonly<Record<string, number>>;
|
|
333
|
+
/** The combined price for the minimum set (cents). The credit is
|
|
334
|
+
* `Σ member unit price × minQty − prices`, clamped ≥ 0. */
|
|
335
|
+
readonly prices: BillingPlanPrices;
|
|
336
|
+
/** Deterministic tie-break among equal-total optimal packings when combos overlap
|
|
337
|
+
* (lower wins). */
|
|
338
|
+
readonly sortOrder: number;
|
|
339
|
+
readonly isActive: boolean;
|
|
340
|
+
}
|
|
341
|
+
/** Billing plans + add-on bundle configuration. */
|
|
342
|
+
export interface BillingConfig {
|
|
343
|
+
readonly plans: readonly BillingPlanConfig[];
|
|
344
|
+
readonly bundles: readonly BundleConfig[];
|
|
345
|
+
readonly comboDiscounts: readonly ComboDiscountConfig[];
|
|
346
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// Copyright 2026 Pipeline Builder Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
export {};
|
|
4
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uZmlnLXR5cGVzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NvbmZpZy9jb25maWctdHlwZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsK0NBQStDO0FBQy9DLHNDQUFzQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG5pbXBvcnQgdHlwZSB7IFF1b3RhVGllciwgUXVvdGFUaWVyTGltaXRzIH0gZnJvbSAnQHBpcGVsaW5lLWJ1aWxkZXIvYXBpLWNvcmUnO1xuaW1wb3J0IHR5cGUgeyBBbGdvcml0aG0gfSBmcm9tICdqc29ud2VidG9rZW4nO1xuXG4vKipcbiAqIFR5cGUtc2FmZSBjb25maWd1cmF0aW9uIGludGVyZmFjZVxuICovXG5leHBvcnQgaW50ZXJmYWNlIEFwcENvbmZpZyB7XG4gIHJlYWRvbmx5IHNlcnZlcjogU2VydmVyQ29uZmlnO1xuICByZWFkb25seSBhdXRoOiBBdXRoQ29uZmlnO1xuICByZWFkb25seSBkYXRhYmFzZTogRGF0YWJhc2VDb25maWc7XG4gIHJlYWRvbmx5IHJlZ2lzdHJ5OiBSZWdpc3RyeUNvbmZpZztcbiAgcmVhZG9ubHkgcmVkaXM6IFJlZGlzQ29uZmlnO1xuICByZWFkb25seSBwbHVnaW5CdWlsZDogUGx1Z2luQnVpbGRDb25maWc7XG4gIHJlYWRvbmx5IGRvY2tlckNvbmZpZzogQnVpbGRDb25maWc7XG4gIHJlYWRvbmx5IG9ic2VydmFiaWxpdHk6IE9ic2VydmFiaWxpdHlDb25maWc7XG4gIHJlYWRvbmx5IGNvbXBsaWFuY2U6IENvbXBsaWFuY2VDb25maWc7XG4gIHJlYWRvbmx5IGF3czogQVdTQ29uZmlnO1xuICByZWFkb25seSByYXRlTGltaXQ6IFJhdGVMaW1pdENvbmZpZztcbiAgcmVhZG9ubHkgYmlsbGluZzogQmlsbGluZ0NvbmZpZztcbn1cblxuLyoqIEV4cHJlc3Mgc2VydmVyIGNvbmZpZ3VyYXRpb24uICovXG5leHBvcnQgaW50ZXJmYWNlIFNlcnZlckNvbmZpZyB7XG4gIC8qKiBIVFRQIGxpc3RlbiBwb3J0IChlbnY6IGBQT1JUYCkuICovXG4gIHJlYWRvbmx5IHBvcnQ6IG51bWJlcjtcbiAgcmVhZG9ubHkgY29yczoge1xuICAgIC8qKiBXaGV0aGVyIHRvIGluY2x1ZGUgY3JlZGVudGlhbHMgaW4gQ09SUyByZXNwb25zZXMgKGVudjogYENPUlNfQ1JFREVOVElBTFNgKS4gKi9cbiAgICByZWFkb25seSBjcmVkZW50aWFsczogYm9vbGVhbjtcbiAgICAvKiogQWxsb3dlZCBvcmlnaW4ocykg4oCUIHNpbmdsZSBzdHJpbmcsIGFycmF5LCBvciBgJyonYCAoZW52OiBgQ09SU19PUklHSU5gKS4gKi9cbiAgICByZWFkb25seSBvcmlnaW46IHN0cmluZyB8IHN0cmluZ1tdO1xuICB9O1xuICAvKiogTnVtYmVyIG9mIHJldmVyc2UgcHJveHkgaG9wcyB0byB0cnVzdCAoZW52OiBgVFJVU1RfUFJPWFlgKS4gKi9cbiAgcmVhZG9ubHkgdHJ1c3RQcm94eTogbnVtYmVyO1xuICAvKiogRnJvbnRlbmQgYmFzZSBVUkwsIHVzZWQgYXMgQ09SUyBmYWxsYmFjayAoZW52OiBgUExBVEZPUk1fQkFTRV9VUkxgKS4gKi9cbiAgcmVhZG9ubHkgcGxhdGZvcm1Vcmw6IHN0cmluZztcbiAgcmVhZG9ubHkgaHR0cENsaWVudDoge1xuICAgIC8qKiBEZWZhdWx0IEhUVFAgcmVxdWVzdCB0aW1lb3V0IGluIG1zIChlbnY6IGBIVFRQX0NMSUVOVF9USU1FT1VUYCkuICovXG4gICAgcmVhZG9ubHkgdGltZW91dDogbnVtYmVyO1xuICAgIC8qKiBNYXhpbXVtIHJldHJ5IGF0dGVtcHRzIGZvciBmYWlsZWQgcmVxdWVzdHMgKGVudjogYEhUVFBfQ0xJRU5UX01BWF9SRVRSSUVTYCkuICovXG4gICAgcmVhZG9ubHkgbWF4UmV0cmllczogbnVtYmVyO1xuICAgIC8qKiBCYXNlIGRlbGF5IGJldHdlZW4gcmV0cmllcyBpbiBtcyAoZW52OiBgSFRUUF9DTElFTlRfUkVUUllfREVMQVlfTVNgKS4gKi9cbiAgICByZWFkb25seSByZXRyeURlbGF5TXM6IG51bWJlcjtcbiAgfTtcbiAgcmVhZG9ubHkgc3NlOiB7XG4gICAgLyoqIE1heCBTU0UgY2xpZW50cyBwZXIgcmVxdWVzdCAoZW52OiBgU1NFX01BWF9DTElFTlRTX1BFUl9SRVFVRVNUYCkuICovXG4gICAgcmVhZG9ubHkgbWF4Q2xpZW50c1BlclJlcXVlc3Q6IG51bWJlcjtcbiAgICAvKiogU1NFIGNsaWVudCB0aW1lb3V0IGluIG1zIChlbnY6IGBTU0VfQ0xJRU5UX1RJTUVPVVRfTVNgKS4gKi9cbiAgICByZWFkb25seSBjbGllbnRUaW1lb3V0TXM6IG51bWJlcjtcbiAgICAvKiogU1NFIGNsZWFudXAgaW50ZXJ2YWwgaW4gbXMgKGVudjogYFNTRV9DTEVBTlVQX0lOVEVSVkFMX01TYCkuICovXG4gICAgcmVhZG9ubHkgY2xlYW51cEludGVydmFsTXM6IG51bWJlcjtcbiAgfTtcbiAgcmVhZG9ubHkgc2VydmljZXM6IHtcbiAgICByZWFkb25seSBwbHVnaW5Ib3N0OiBzdHJpbmc7XG4gICAgcmVhZG9ubHkgcGx1Z2luUG9ydDogbnVtYmVyO1xuICAgIHJlYWRvbmx5IHBpcGVsaW5lSG9zdDogc3RyaW5nO1xuICAgIHJlYWRvbmx5IHBpcGVsaW5lUG9ydDogbnVtYmVyO1xuICAgIHJlYWRvbmx5IG1lc3NhZ2VIb3N0OiBzdHJpbmc7XG4gICAgcmVhZG9ubHkgbWVzc2FnZVBvcnQ6IG51bWJlcjtcbiAgICByZWFkb25seSBwbGF0Zm9ybUhvc3Q6IHN0cmluZztcbiAgICByZWFkb25seSBwbGF0Zm9ybVBvcnQ6IG51bWJlcjtcbiAgICByZWFkb25seSBjb21wbGlhbmNlSG9zdDogc3RyaW5nO1xuICAgIHJlYWRvbmx5IGNvbXBsaWFuY2VQb3J0OiBudW1iZXI7XG4gICAgcmVhZG9ubHkgYmlsbGluZ0hvc3Q6IHN0cmluZztcbiAgICByZWFkb25seSBiaWxsaW5nUG9ydDogbnVtYmVyO1xuICAgIHJlYWRvbmx5IGJpbGxpbmdUaW1lb3V0OiBudW1iZXI7XG4gIH07XG59XG5cbi8qKiBKV1QgYW5kIHJlZnJlc2ggdG9rZW4gYXV0aGVudGljYXRpb24gY29uZmlndXJhdGlvbi4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQXV0aENvbmZpZyB7XG4gIHJlYWRvbmx5IGp3dDoge1xuICAgIC8qKiBTaWduaW5nIHNlY3JldCBmb3IgYWNjZXNzIHRva2VucyAoZW52OiBgSldUX1NFQ1JFVGApLiAqL1xuICAgIHJlYWRvbmx5IHNlY3JldDogc3RyaW5nO1xuICAgIC8qKiBUb2tlbiBsaWZldGltZSBpbiBzZWNvbmRzIChlbnY6IGBKV1RfRVhQSVJFU19JTmApLiAqL1xuICAgIHJlYWRvbmx5IGV4cGlyZXNJbjogbnVtYmVyO1xuICAgIC8qKiBTaWduaW5nIGFsZ29yaXRobSwgZS5nLiBgJ0hTMjU2J2AgKGVudjogYEpXVF9BTEdPUklUSE1gKS4gKi9cbiAgICByZWFkb25seSBhbGdvcml0aG06IEFsZ29yaXRobTtcbiAgICAvKiogYmNyeXB0IHNhbHQgcm91bmRzIGZvciBwYXNzd29yZCBoYXNoaW5nIChlbnY6IGBCQ1JZUFRfU0FMVF9ST1VORFNgKS4gKi9cbiAgICByZWFkb25seSBzYWx0Um91bmRzOiBudW1iZXI7XG4gIH07XG4gIHJlYWRvbmx5IHJlZnJlc2hUb2tlbjoge1xuICAgIC8qKiBTaWduaW5nIHNlY3JldCBmb3IgcmVmcmVzaCB0b2tlbnMgKGVudjogYFJFRlJFU0hfVE9LRU5fU0VDUkVUYCkuICovXG4gICAgcmVhZG9ubHkgc2VjcmV0OiBzdHJpbmc7XG4gICAgLyoqIFRva2VuIGxpZmV0aW1lIGluIHNlY29uZHMgKGVudjogYFJFRlJFU0hfVE9LRU5fRVhQSVJFU19JTmApLiAqL1xuICAgIHJlYWRvbmx5IGV4cGlyZXNJbjogbnVtYmVyO1xuICB9O1xufVxuXG4vKiogUG9zdGdyZVNRTCBhbmQgRHJpenpsZSBPUk0gZGF0YWJhc2UgY29uZmlndXJhdGlvbi4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgRGF0YWJhc2VDb25maWcge1xuICByZWFkb25seSBwb3N0Z3Jlczoge1xuICAgIC8qKiBQb3N0Z3JlU1FMIGhvc3QgKGVudjogYERCX0hPU1RgKS4gKi9cbiAgICByZWFkb25seSBob3N0OiBzdHJpbmc7XG4gICAgLyoqIFBvc3RncmVTUUwgcG9ydCAoZW52OiBgREJfUE9SVGApLiAqL1xuICAgIHJlYWRvbmx5IHBvcnQ6IG51bWJlcjtcbiAgICAvKiogRGF0YWJhc2UgbmFtZSAoZW52OiBgREFUQUJBU0VgKS4gKi9cbiAgICByZWFkb25seSBkYXRhYmFzZTogc3RyaW5nO1xuICAgIC8qKiBEYXRhYmFzZSB1c2VyIChlbnY6IGBEQl9VU0VSYCkuICovXG4gICAgcmVhZG9ubHkgdXNlcjogc3RyaW5nO1xuICAgIC8qKiBEYXRhYmFzZSBwYXNzd29yZCAoZW52OiBgREJfUEFTU1dPUkRgKS4gKi9cbiAgICByZWFkb25seSBwYXNzd29yZDogc3RyaW5nO1xuICB9O1xuICByZWFkb25seSBkcml6emxlOiB7XG4gICAgLyoqIE1heGltdW0gY29ubmVjdGlvbiBwb29sIHNpemUgKGVudjogYERSSVpaTEVfTUFYX1BPT0xfU0laRWApLiAqL1xuICAgIHJlYWRvbmx5IG1heFBvb2xTaXplOiBudW1iZXI7XG4gICAgLyoqIElkbGUgY29ubmVjdGlvbiB0aW1lb3V0IGluIG1zIChlbnY6IGBEUklaWkxFX0lETEVfVElNRU9VVF9NSUxMSVNgKS4gKi9cbiAgICByZWFkb25seSBpZGxlVGltZW91dE1pbGxpczogbnVtYmVyO1xuICAgIC8qKiBOZXcgY29ubmVjdGlvbiB0aW1lb3V0IGluIG1zIChlbnY6IGBEUklaWkxFX0NPTk5FQ1RJT05fVElNRU9VVF9NSUxMSVNgKS4gKi9cbiAgICByZWFkb25seSBjb25uZWN0aW9uVGltZW91dE1pbGxpczogbnVtYmVyO1xuICB9O1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFJlZ2lzdHJ5Q29uZmlnIHtcbiAgcmVhZG9ubHkgaG9zdDogc3RyaW5nO1xuICByZWFkb25seSBwb3J0OiBudW1iZXI7XG4gIC8qKlxuICAgKiBIb3N0L3BvcnQgYW4gT1VULU9GLUNMVVNURVIgY2xpZW50IChub3RhYmx5IEFXUyBDb2RlQnVpbGQpIHVzZXMgdG8gcHVsbFxuICAgKiBwbHVnaW4gaW1hZ2VzLCB3aGVuIGl0IGRpZmZlcnMgZnJvbSB0aGUgaW4tY2x1c3RlciBgaG9zdGAvYHBvcnRgLiBUaGVcbiAgICogaW4tY2x1c3RlciBgcmVnaXN0cnk6NTAwMGAgQ2x1c3RlcklQIGlzbid0IHJlc29sdmFibGUgZnJvbSB0aGUgVlBDLCBzbyB0aGVcbiAgICogc3ludGhlc2l6ZWQgQ29kZUJ1aWxkIGltYWdlIFVSSSBtdXN0IHBvaW50IGF0IHRoZSBwdWJsaWMgZ2F0ZXdheSBpbnN0ZWFkLlxuICAgKiBFbnY6IGBJTUFHRV9SRUdJU1RSWV9QVUxMX0hPU1RgIC8gYElNQUdFX1JFR0lTVFJZX1BVTExfUE9SVGAuIE9wdGlvbmFsO1xuICAgKiBjb25zdW1lcnMgZmFsbCBiYWNrIHRvIGBob3N0YC9gcG9ydGAgd2hlbiB1bnNldCAoc2luZ2xlLWhvc3QgL1xuICAgKiBpbi1jbHVzdGVyLW9ubHkgZGVwbG95cykuIGBsb2FkUmVnaXN0cnlDb25maWdgIGFsd2F5cyBwb3B1bGF0ZXMgdGhlbS5cbiAgICovXG4gIHJlYWRvbmx5IHB1bGxIb3N0Pzogc3RyaW5nO1xuICByZWFkb25seSBwdWxsUG9ydD86IG51bWJlcjtcbiAgLyoqIERvY2tlciBuZXR3b3JrIGZvciBidWlsZC9wdXNoIChlbXB0eSBzdHJpbmcgPSBkZWZhdWx0KS4gKi9cbiAgcmVhZG9ubHkgbmV0d29yazogc3RyaW5nO1xuICAvKipcbiAgICogQnVpbGRLaXQgdGFsa3MgdG8gdGhlIHJlZ2lzdHJ5IG92ZXIgcGxhaW4gSFRUUCB3aGVuIHRydWU7IEhUVFBTIHdpdGggdGhlXG4gICAqIHN5c3RlbSBDQSBidW5kbGUgb3RoZXJ3aXNlLiBFbnY6IGBJTUFHRV9SRUdJU1RSWV9IVFRQYCAoZGVmYXVsdHMgdHJ1ZSDigJRcbiAgICogdGhlIGluLWNsdXN0ZXIgcmVnaXN0cnkgaGFzIG5vIFRMUykuXG4gICAqL1xuICByZWFkb25seSBodHRwOiBib29sZWFuO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFJlZGlzQ29uZmlnIHtcbiAgcmVhZG9ubHkgaG9zdDogc3RyaW5nO1xuICByZWFkb25seSBwb3J0OiBudW1iZXI7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUGx1Z2luQnVpbGRDb25maWcge1xuICByZWFkb25seSBjb25jdXJyZW5jeTogbnVtYmVyO1xuICByZWFkb25seSBtYXhBdHRlbXB0czogbnVtYmVyO1xuICByZWFkb25seSBiYWNrb2ZmRGVsYXlNczogbnVtYmVyO1xuICByZWFkb25seSB3b3JrZXJUaW1lb3V0TXM6IG51bWJlcjtcbiAgcmVhZG9ubHkgdGVtcERpck1heEFnZU1zOiBudW1iZXI7XG4gIHJlYWRvbmx5IGRscU1heEF0dGVtcHRzOiBudW1iZXI7XG4gIHJlYWRvbmx5IGRscUJhY2tvZmZCYXNlTXM6IG51bWJlcjtcbiAgcmVhZG9ubHkgZGxxTWF4U2l6ZTogbnVtYmVyO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIEJ1aWxkQ29uZmlnIHtcbiAgLyoqIFJvb3QgZGlyZWN0b3J5IGZvciBidWlsZCB0ZW1wIGZpbGVzLiAqL1xuICByZWFkb25seSB0ZW1wUm9vdDogc3RyaW5nO1xuICAvKiogQnVpbGQgdGltZW91dCBpbiBtaWxsaXNlY29uZHMuICovXG4gIHJlYWRvbmx5IHRpbWVvdXRNczogbnVtYmVyO1xuICAvKiogUHVzaCB0aW1lb3V0IGluIG1pbGxpc2Vjb25kcy4gKi9cbiAgcmVhZG9ubHkgcHVzaFRpbWVvdXRNczogbnVtYmVyO1xuICAvKipcbiAgICogUGF0aCB0byB0aGUgYnVpbGRraXRkIHNvY2tldCB0aGUgcGx1Z2luJ3MgYGJ1aWxkY3RsYCBjb25uZWN0cyB0by4gSW4gazhzXG4gICAqIGFuZCBjb21wb3NlIHRoaXMgaXMgYSBzaGFyZWQgZW1wdHlEaXIvdG1wZnMgdm9sdW1lIG1vdW50ZWQgZnJvbSB0aGVcbiAgICogYnVpbGRraXRkIHNpZGVjYXIuXG4gICAqL1xuICByZWFkb25seSBidWlsZGtpdEFkZHI6IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBPYnNlcnZhYmlsaXR5Q29uZmlnIHtcbiAgcmVhZG9ubHkgbG9nTGV2ZWw6IHN0cmluZztcbiAgcmVhZG9ubHkgbG9nRm9ybWF0OiBzdHJpbmc7XG4gIHJlYWRvbmx5IHNlcnZpY2VOYW1lOiBzdHJpbmc7XG4gIHJlYWRvbmx5IHRyYWNpbmc6IHtcbiAgICByZWFkb25seSBlbmFibGVkOiBib29sZWFuO1xuICAgIHJlYWRvbmx5IGVuZHBvaW50OiBzdHJpbmc7XG4gIH07XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgQ29tcGxpYW5jZUNvbmZpZyB7XG4gIHJlYWRvbmx5IHNjYW5TY2hlZHVsZXJJbnRlcnZhbE1zOiBudW1iZXI7XG4gIC8qKiBXaGVuIGZhbHNlLCB0aGUgc2NoZWR1bGVyIHNraXBzIHNjYW5zL3NjaGVkdWxlcyBmb3IgdGhlIHN5c3RlbSBvcmcuIERlZmF1bHQ6IGZhbHNlLiAqL1xuICByZWFkb25seSBzeXN0ZW1PcmdTY2Fuc0VuYWJsZWQ6IGJvb2xlYW47XG4gIC8qKiBDcm9zcy1wb2QgbGVhZGVyLWxvY2sgVFRMIGZvciB0aGUgc2NhbiBzY2hlZHVsZXIgKG1zKS4gRGVmYXVsdDogMzAwMDAwLiAqL1xuICByZWFkb25seSBzY2FuTG9ja1R0bE1zOiBudW1iZXI7XG4gIC8qKiBIb3cgb2Z0ZW4gdGhlIGRpZ2VzdCBzY2hlZHVsZXIgY2hlY2tzIGZvciBkdWUgZGlnZXN0cyAobXMpLiBEZWZhdWx0OiAzNjAwMDAwLiAqL1xuICByZWFkb25seSBkaWdlc3RTY2hlZHVsZXJJbnRlcnZhbE1zOiBudW1iZXI7XG4gIC8qKiBDcm9zcy1wb2QgbGVhZGVyLWxvY2sgVFRMIGZvciB0aGUgZGlnZXN0IHNjaGVkdWxlciAobXMpLiBEZWZhdWx0OiAzMDAwMDAuICovXG4gIHJlYWRvbmx5IGRpZ2VzdExvY2tUdGxNczogbnVtYmVyO1xufVxuXG4vKipcbiAqIEFXUyBpbmZyYXN0cnVjdHVyZSBjb25maWd1cmF0aW9uLlxuICpcbiAqIERlbGliZXJhdGVseSBleHByZXNzZWQgaW4gUExBSU4gREFUQSAobnVtYmVycywgc3RyaW5ncywgc3RyaW5nIHVuaW9ucykgcmF0aGVyXG4gKiB0aGFuIENESyB2YWx1ZSBvYmplY3RzIChgRHVyYXRpb25gLCBgUnVudGltZWAsIGBSZXRlbnRpb25EYXlzYCwgLi4uKS4gQ29uZmlnIGlzXG4gKiBsb2FkZWQgYnkgZXZlcnkgc2VydmljZSwgYnV0IG9ubHkgdGhlIENESyBlbnRyeSBwb2ludCAoYEBwaXBlbGluZS1idWlsZGVyL1xuICogcGlwZWxpbmUtY29yZS9jZGtgKSBidWlsZHMgc3RhY2tzICB0eXBpbmcgdGhpcyBibG9jayB3aXRoIENESyBjbGFzc2VzIHB1dFxuICogYGF3cy1jZGstbGliYCBvbiB0aGUgaW1wb3J0IGdyYXBoIG9mIGBDb25maWdgLCBhbmQgdGhlcmVmb3JlIG9mIGV2ZXJ5IEFQSVxuICogc2VydmljZSB0aGF0IHJlYWRzIHNvIG11Y2ggYXMgYSBwb3J0IG51bWJlci4gQ29udmVyc2lvbiB0byBDREsgb2JqZWN0cyBoYXBwZW5zXG4gKiBhdCB0aGUgY29uc3RydWN0IGJvdW5kYXJ5IGluIGBjb25maWcvYXdzLWNvbmZpZy1jZGsudHNgLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIEFXU0NvbmZpZyB7XG4gIHJlYWRvbmx5IGxhbWJkYToge1xuICAgIC8qKiBMYW1iZGEgcnVudGltZSBpZGVudGlmaWVyLCBlLmcuIGAnbm9kZWpzMjQueCdgIChlbnY6IGBMQU1CREFfUlVOVElNRWApLiAqL1xuICAgIHJlYWRvbmx5IHJ1bnRpbWU6IHN0cmluZztcbiAgICAvKiogRnVuY3Rpb24gdGltZW91dCBpbiBzZWNvbmRzIChlbnY6IGBMQU1CREFfVElNRU9VVGApLiAqL1xuICAgIHJlYWRvbmx5IHRpbWVvdXRTZWNvbmRzOiBudW1iZXI7XG4gICAgcmVhZG9ubHkgbWVtb3J5U2l6ZTogbnVtYmVyO1xuICAgIHJlYWRvbmx5IGFyY2hpdGVjdHVyZTogJ2FybTY0JyB8ICd4ODZfNjQnO1xuICAgIHJlYWRvbmx5IHJlc2VydmVkQ29uY3VycmVudEV4ZWN1dGlvbnM/OiBudW1iZXI7XG4gIH07XG4gIHJlYWRvbmx5IGxvZ2dpbmc6IHtcbiAgICByZWFkb25seSBncm91cE5hbWU6IHN0cmluZztcbiAgICAvKiogQ2xvdWRXYXRjaCByZXRlbnRpb24gaW4gZGF5czsgYDBgIG1lYW5zIG5ldmVyIGV4cGlyZSAoZW52OiBgTE9HX1JFVEVOVElPTmApLiAqL1xuICAgIHJlYWRvbmx5IHJldGVudGlvbkRheXM6IG51bWJlcjtcbiAgICByZWFkb25seSByZW1vdmFsUG9saWN5OiAncmV0YWluJyB8ICdkZXN0cm95JztcbiAgfTtcbiAgcmVhZG9ubHkgY29kZUJ1aWxkOiB7XG4gICAgLyoqIENvZGVCdWlsZCBjb21wdXRlIHR5cGUgbmFtZSwgZS5nLiBgJ1NNQUxMJ2AgKGVudjogYENPREVCVUlMRF9DT01QVVRFX1RZUEVgKS4gKi9cbiAgICByZWFkb25seSBjb21wdXRlVHlwZTogc3RyaW5nO1xuICAgIC8qKlxuICAgICAqIEltYWdlIHVzZWQgZm9yIENvZGVCdWlsZCBzdGVwcyB0aGF0IGRvbid0IGhhdmUgYSBwbHVnaW4tYmFrZWQgaW1hZ2VcbiAgICAgKiAoY29sZC1zdGFydCBzeW50aCBib290c3RyYXAsIFNoZWxsU3RlcHMgd2l0aCBubyByZWdpc3RyeSwgYW5kXG4gICAgICogYG1ldGFkYXRhX29ubHlgIHBsdWdpbnMpLiBlbnY6IGBDT0RFQlVJTERfREVGQVVMVF9JTUFHRWAuIERlZmF1bHRcbiAgICAgKiBgcGlwZWxpbmUtYm9vdHN0cmFwOjEuMGAg4oCUIHRoZSBsb2NhbCB0YWcgYnVpbHQgYnlcbiAgICAgKiBgZGVwbG95L2NvZGVidWlsZC9ib290c3RyYXAvRG9ja2VyZmlsZWAsIHdpdGggYHBpcGVsaW5lLW1hbmFnZXJgXG4gICAgICogYmFrZWQgaW4uXG4gICAgICpcbiAgICAgKiBDb250cmFjdDogdGhlIGltYWdlIE1VU1QgaGF2ZSBgcGlwZWxpbmUtbWFuYWdlcmAgb24gUEFUSC4gVGhlXG4gICAgICogYm9vdHN0cmFwIHN5bnRoIHBhdGggbm8gbG9uZ2VyIHNlbGYtaGVhbHMgdmlhIGBucG0gaW5zdGFsbCAtZ2A7XG4gICAgICogcG9pbnRpbmcgdGhpcyBhdCBhbiBpbWFnZSB3aXRob3V0IHRoZSB0b29sIGdldHMgZXhpdCAxMjcgb24gdGhlXG4gICAgICogZmlyc3QgY29sZC1zdGFydCBidWlsZC5cbiAgICAgKlxuICAgICAqIFJlc29sdXRpb246XG4gICAgICogLSBCYXJlIHRhZyAobm8gYC9gKSDihpIgYXV0by1wcmVmaXhlZCB0b1xuICAgICAqICAgYDxyZWdpc3RyeS1ob3N0Pjo8cG9ydD4vbGlicmFyeS88dGFnPmAgdXNpbmcgdGhlIHJlZ2lzdHJ5IGNvbmZpZyxcbiAgICAgKiAgIHdpdGggdGhlIHBlci1vcmcgcGxhdGZvcm0gU2VjcmV0IGFzIEJhc2ljIGF1dGggKHNhbWUgZmxvdyBhc1xuICAgICAqICAgcGx1Z2luIGltYWdlcykuXG4gICAgICogLSBGdWxseS1xdWFsaWZpZWQgcmVnaXN0cnkgVVJJIChjb250YWlucyBgL2ApIOKGkiB1c2VkIGFzLWlzLCBub1xuICAgICAqICAgYXV0aCB3aXJlZCAob3BlcmF0b3Igb3ducyBtYWtpbmcgaXQgcHVsbGFibGUpLlxuICAgICAqIC0gV2hlbiB0aGUgcmVnaXN0cnkgaXNuJ3QgY29uZmlndXJlZCBvciBwZXItb3JnIGF1dGggaXNuJ3RcbiAgICAgKiAgIGF2YWlsYWJsZSBhdCB0aGUgY2FsbCBzaXRlLCBmYWxscyBiYWNrIHRvXG4gICAgICogICBgYXdzL2NvZGVidWlsZC9zdGFuZGFyZDo4LjBgIHdpdGggYSB3YXJuaW5nIOKAlCBjb2xkLXN0YXJ0IHN5bnRoXG4gICAgICogICB3aWxsIHRoZW4gZmFpbCBvbiBgcGlwZWxpbmUtbWFuYWdlcjogbm90IGZvdW5kYCwgc3VyZmFjaW5nIHRoZVxuICAgICAqICAgbWlzY29uZmlndXJhdGlvbiBsb3VkbHkgaW5zdGVhZCBvZiBzaWxlbnRseSBzd2FwcGluZyBpbWFnZXMuXG4gICAgICovXG4gICAgcmVhZG9ubHkgZGVmYXVsdEltYWdlOiBzdHJpbmc7XG4gIH07XG59XG5cbi8qKiBFeHByZXNzIHJhdGUgbGltaXRpbmcgY29uZmlndXJhdGlvbi4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUmF0ZUxpbWl0Q29uZmlnIHtcbiAgLyoqIE1heGltdW0gcmVxdWVzdHMgcGVyIHdpbmRvdyAoZW52OiBgTElNSVRFUl9NQVhgKS4gKi9cbiAgcmVhZG9ubHkgbWF4OiBudW1iZXI7XG4gIC8qKiBSYXRlIGxpbWl0IHdpbmRvdyBpbiBtaWxsaXNlY29uZHMgKGVudjogYExJTUlURVJfV0lORE9XTVNgKS4gKi9cbiAgcmVhZG9ubHkgd2luZG93TXM6IG51bWJlcjtcbiAgLyoqIEluY2x1ZGUgbGVnYWN5IGBYLVJhdGVMaW1pdC0qYCBoZWFkZXJzLiAqL1xuICByZWFkb25seSBsZWdhY3lIZWFkZXJzOiBib29sZWFuO1xuICAvKiogSW5jbHVkZSBzdGFuZGFyZCBgUmF0ZUxpbWl0LSpgIGhlYWRlcnMgKFJGQyA2NTg1KS4gKi9cbiAgcmVhZG9ubHkgc3RhbmRhcmRIZWFkZXJzOiBib29sZWFuO1xufVxuXG4vKiogUHJpY2UgY29uZmlndXJhdGlvbiBmb3IgYSBzaW5nbGUgYmlsbGluZyBwbGFuIChpbiBjZW50cykuICovXG5leHBvcnQgaW50ZXJmYWNlIEJpbGxpbmdQbGFuUHJpY2VzIHtcbiAgcmVhZG9ubHkgbW9udGhseTogbnVtYmVyO1xuICByZWFkb25seSBhbm51YWw6IG51bWJlcjtcbn1cblxuLyoqIEZ1bGwgYmlsbGluZyBwbGFuIGRlZmluaXRpb24gdXNlZCBmb3Igc2VlZGluZyBhbmQgcnVudGltZSBjb25maWd1cmF0aW9uLiAqL1xuZXhwb3J0IGludGVyZmFjZSBCaWxsaW5nUGxhbkNvbmZpZyB7XG4gIHJlYWRvbmx5IGlkOiBzdHJpbmc7XG4gIHJlYWRvbmx5IG5hbWU6IHN0cmluZztcbiAgcmVhZG9ubHkgZGVzY3JpcHRpb246IHN0cmluZztcbiAgcmVhZG9ubHkgdGllcjogUXVvdGFUaWVyO1xuICByZWFkb25seSBwcmljZXM6IEJpbGxpbmdQbGFuUHJpY2VzO1xuICByZWFkb25seSBmZWF0dXJlczogcmVhZG9ubHkgc3RyaW5nW107XG4gIHJlYWRvbmx5IGlzQWN0aXZlOiBib29sZWFuO1xuICByZWFkb25seSBpc0RlZmF1bHQ6IGJvb2xlYW47XG4gIHJlYWRvbmx5IHNvcnRPcmRlcjogbnVtYmVyO1xufVxuXG4vKipcbiAqIEEgcHVyY2hhc2FibGUgYWRkLW9uIGJ1bmRsZTogYSBxdWFudGl0eS1zdGFja2FibGUgcGFjayB0aGF0IEFERFMgdG8gdGhlXG4gKiBhY2NvdW50J3MgYmFzZSAodGllcikgbGltaXRzIGFuZCBhZGRzIGEgcmVjdXJyaW5nIGxpbmUgaXRlbS4gU2VlXG4gKiBkb2NzL2JpbGxpbmctYnVuZGxlcy5tZC5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBCdW5kbGVDb25maWcge1xuICByZWFkb25seSBpZDogc3RyaW5nO1xuICByZWFkb25seSBuYW1lOiBzdHJpbmc7XG4gIHJlYWRvbmx5IGRlc2NyaXB0aW9uOiBzdHJpbmc7XG4gIC8qKiBQZXItdW5pdCBkZWx0YXMgb24gYFF1b3RhVGllckxpbWl0c2AgZmllbGRzLCBlLmcuIGB7IHNlYXRzOiA1IH1gLiBLZXlzIGFyZVxuICAgKiAgY29uc3RyYWluZWQgdG8gcmVhbCBxdW90YSBmaWVsZHMgc28gYSB0eXBvIGlzIGEgY29tcGlsZSBlcnJvciAod2FzXG4gICAqICBgUmVjb3JkPHN0cmluZywgbnVtYmVyPmAsIHdoZXJlIGEgbWlzc3BlbGxlZCBrZXkgd2FzIHNpbGVudGx5IHVuZW5mb3JjZWFibGUpLiAqL1xuICByZWFkb25seSBncmFudHM6IFJlYWRvbmx5PFBhcnRpYWw8UmVjb3JkPGtleW9mIFF1b3RhVGllckxpbWl0cywgbnVtYmVyPj4+O1xuICAvKiogRmVhdHVyZSBmbGFncyBncmFudGVkIGJ5IGEgZmVhdHVyZSBidW5kbGUgKGUuZy4gYGF1ZGl0X2xvZ2ApLiAqL1xuICByZWFkb25seSBmZWF0dXJlcz86IHJlYWRvbmx5IHN0cmluZ1tdO1xuICAvKiogUGVyLXVuaXQgcHJpY2UgKGNlbnRzKS4gU3RyaXBlIG11bHRpcGxpZXMgYnkgcXVhbnRpdHkuICovXG4gIHJlYWRvbmx5IHByaWNlczogQmlsbGluZ1BsYW5QcmljZXM7XG4gIC8qKiBUcnVlIGZvciBzdGFja2FibGUgcmVzb3VyY2UgcGFja3M7IGZhbHNlIGZvciBhIGJvb2xlYW4gZmVhdHVyZSBidW5kbGUuICovXG4gIHJlYWRvbmx5IHN0YWNrYWJsZTogYm9vbGVhbjtcbiAgLyoqXG4gICAqIE1heGltdW0gcHVyY2hhc2FibGUgcXVhbnRpdHkgZm9yIGEgc3RhY2thYmxlIHBhY2suIEVuZm9yY2VkIGJ5IHRoZSBhZGRvblxuICAgKiBwdXJjaGFzZS9wcmV2aWV3IHJvdXRlIChgcXVhbnRpdHkg4omkIG1heFF1YW50aXR5YCwgcmVqZWN0IG92ZXItY2FwKS4gVXNlZCB0b1xuICAgKiBrZWVwIHJldGVudGlvbiBwYWNrcyB1bmRlciB0aGUgNzMwLWRheSByZXRlbnRpb24gY2VpbGluZyAoZS5nLiB0aGUgU3RhbmRhcmRcbiAgICogUmV0ZW50aW9uIFBhY2sgY2FwcyBhdCA3IHNvIDMwICsgN8OXOTAgPSA2NjAg4omkIDczMCkuIEFic2VudCDih5IgdW5ib3VuZGVkLlxuICAgKi9cbiAgcmVhZG9ubHkgbWF4UXVhbnRpdHk/OiBudW1iZXI7XG4gIC8qKlxuICAgKiBPcHRpb25hbCBwZXItdW5pdCB2b2x1bWUgZGlzY291bnQgdGllcnMgZm9yIGEgc3RhY2thYmxlIHBhY2s6IGF0IOKJpSBgbWluUXVhbnRpdHlgXG4gICAqIHB1cmNoYXNlZCB1bml0cywgYGRpc2NvdW50UGVyY2VudGAgY29tZXMgb2ZmIHRoZSBsaW5lIHN1YnRvdGFsICh1bml0IMOXIHF1YW50aXR5KS5cbiAgICogVGhlIGhpZ2hlc3QgbWF0Y2hpbmcgdGllciB3aW5zLiBSZWFsaXplZCBhcyBhIHJlY3VycmluZyB1c2FnZSBjcmVkaXQgKGxpa2UgYVxuICAgKiBjb21ibyksIHNvIHRoZSBwcm92aWRlciBzdGlsbCBjaGFyZ2VzIHVuaXTDl3F0eSBhbmQgdGhlIGNyZWRpdCBvZmZzZXRzIHRoZVxuICAgKiBiYWxhbmNlLiBFbnYtb3ZlcnJpZGFibGUgdmlhIGBCSUxMSU5HX0JVTkRMRV88SUQ+X1ZPTFVNRV9USUVSU2AgKEpTT04pLiBVc2VkIGJ5XG4gICAqIHRoZSBwZXItc2VhdCBgc2VhdGAgYnVuZGxlLiBBYnNlbnQg4oeSIG5vIHZvbHVtZSBkaXNjb3VudC5cbiAgICovXG4gIHJlYWRvbmx5IHZvbHVtZVRpZXJzPzogcmVhZG9ubHkgeyByZWFkb25seSBtaW5RdWFudGl0eTogbnVtYmVyOyByZWFkb25seSBkaXNjb3VudFBlcmNlbnQ6IG51bWJlciB9W107XG4gIC8qKiBCYXNlIHRpZXJzIHRoaXMgYnVuZGxlIGlzIG9mZmVyZWQgdG8uICovXG4gIHJlYWRvbmx5IGF2YWlsYWJsZUZvclRpZXJzOiByZWFkb25seSBRdW90YVRpZXJbXTtcbiAgLyoqXG4gICAqIFByZXJlcXVpc2l0ZSBidW5kbGUgaWRzIHRoYXQgbXVzdCBiZSBoZWxkIChvciBwdXJjaGFzZWQgaW4gdGhlIHNhbWUgYWN0aW9uKVxuICAgKiBiZWZvcmUgdGhpcyBidW5kbGUgY2FuIGJlIGFkZGVkLiBFbmZvcmNlZCBieSB0aGUgYWRkb24gcHVyY2hhc2UvcHJldmlldyByb3V0ZVxuICAgKiAocmVqZWN0IHdpdGggNDAwIHdoZW4gdW5tZXQpOyBhIGNvbWJvIHRoYXQgaW5jbHVkZXMgdGhlIHByZXJlcXVpc2l0ZSBzYXRpc2ZpZXNcbiAgICogaXQuIFVzZWQgYnkgYGNvbXBsaWFuY2VfYWR2YW5jZWRgIChgcmVxdWlyZXM6IFsnY29tcGxpYW5jZV9zdGFuZGFyZCddYCkuXG4gICAqIEFic2VudCDih5Igbm8gcHJlcmVxdWlzaXRlLlxuICAgKi9cbiAgcmVhZG9ubHkgcmVxdWlyZXM/OiByZWFkb25seSBzdHJpbmdbXTtcbiAgcmVhZG9ubHkgaXNBY3RpdmU6IGJvb2xlYW47XG4gIHJlYWRvbmx5IHNvcnRPcmRlcjogbnVtYmVyO1xufVxuXG4vKipcbiAqIEEgY29tYm8gZGlzY291bnQ6IHdoZW4gYW4gYWNjb3VudCBob2xkcyBFVkVSWSBidW5kbGUgaW4gYGJ1bmRsZUlkc2AsIHRoZSBwYWlyXG4gKiBpcyBiaWxsZWQgYXQgYHByaWNlc2AgKHRoZSBjb21iaW5lZCBwcmljZSkgaW5zdGVhZCBvZiB0aGUgc3VtIG9mIHRoZSBtZW1iZXJzJ1xuICogaW5kaXZpZHVhbCBwcmljZXMuIFJlYWxpemVkIGFzIGEgcmVjdXJyaW5nIHVzYWdlIGNyZWRpdCBmb3IgdGhlIGRpZmZlcmVuY2Ug4oCUXG4gKiBuZXZlciBhIHByb3ZpZGVyIGNvdXBvbiDigJQgY29uc2lzdGVudCB3aXRoIHRoZSB1c2FnZS1jcmVkaXQgZGlzY291bnQgbW9kZWwuIFNlZVxuICogZG9jcy9iaWxsaW5nLWJ1bmRsZXMubWQgwqdDb21ibyBwcmljaW5nLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIENvbWJvRGlzY291bnRDb25maWcge1xuICByZWFkb25seSBpZDogc3RyaW5nO1xuICByZWFkb25seSBuYW1lOiBzdHJpbmc7XG4gIC8qKiBUaGUgbWVtYmVyIGJ1bmRsZSBpZHMgdGhhdCBtdXN0IEFMTCBiZSBwcmVzZW50IGZvciB0aGUgY29tYm8gdG8gYXBwbHkuICovXG4gIHJlYWRvbmx5IGJ1bmRsZUlkczogcmVhZG9ubHkgc3RyaW5nW107XG4gIC8qKiBQZXItbWVtYmVyIG1pbmltdW0gcXVhbnRpdHkgKGJ1bmRsZUlkIOKGkiBjb3VudDsgYWJzZW50IOKHkiAxKS4gTGV0cyBhIHN0YWNrYWJsZVxuICAgKiAgY2FwYWNpdHkgcGFjayBhbmNob3IgYSBjb21ibyDigJQgZS5nLiBgeyBzZWF0OiA1IH1gID0gXCLiiaUgNSBzZWF0c1wiLiBUaGUgY3JlZGl0XG4gICAqICBiYXNpcyB1c2VzIHRoaXMgbWluaW11bSwgc28gZXh0cmEgdW5pdHMgbmV2ZXIgaW5mbGF0ZSB0aGUgZGlzY291bnQuICovXG4gIHJlYWRvbmx5IG1pblF1YW50aXRpZXM/OiBSZWFkb25seTxSZWNvcmQ8c3RyaW5nLCBudW1iZXI+PjtcbiAgLyoqIFRoZSBjb21iaW5lZCBwcmljZSBmb3IgdGhlIG1pbmltdW0gc2V0IChjZW50cykuIFRoZSBjcmVkaXQgaXNcbiAgICogIGDOoyBtZW1iZXIgdW5pdCBwcmljZSDDlyBtaW5RdHkg4oiSIHByaWNlc2AsIGNsYW1wZWQg4omlIDAuICovXG4gIHJlYWRvbmx5IHByaWNlczogQmlsbGluZ1BsYW5QcmljZXM7XG4gIC8qKiBEZXRlcm1pbmlzdGljIHRpZS1icmVhayBhbW9uZyBlcXVhbC10b3RhbCBvcHRpbWFsIHBhY2tpbmdzIHdoZW4gY29tYm9zIG92ZXJsYXBcbiAgICogIChsb3dlciB3aW5zKS4gKi9cbiAgcmVhZG9ubHkgc29ydE9yZGVyOiBudW1iZXI7XG4gIHJlYWRvbmx5IGlzQWN0aXZlOiBib29sZWFuO1xufVxuXG4vKiogQmlsbGluZyBwbGFucyArIGFkZC1vbiBidW5kbGUgY29uZmlndXJhdGlvbi4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQmlsbGluZ0NvbmZpZyB7XG4gIHJlYWRvbmx5IHBsYW5zOiByZWFkb25seSBCaWxsaW5nUGxhbkNvbmZpZ1tdO1xuICByZWFkb25seSBidW5kbGVzOiByZWFkb25seSBCdW5kbGVDb25maWdbXTtcbiAgcmVhZG9ubHkgY29tYm9EaXNjb3VudHM6IHJlYWRvbmx5IENvbWJvRGlzY291bnRDb25maWdbXTtcbn1cbiJdfQ==
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Effective-entitlement math for the account/entitlement model.
|
|
3
|
+
*
|
|
4
|
+
* The canonical "effective limits = tier base + Σ(bundle grants)" computation
|
|
5
|
+
* lives here — next to the plan/bundle config it operates on — so ANY
|
|
6
|
+
* account-scoped consumer (billing, and future quota/platform readers) shares
|
|
7
|
+
* one implementation instead of re-deriving the coalesce. Pure: the tier limits
|
|
8
|
+
* come from api-core (`getTierLimits`) and the bundle catalog is passed in.
|
|
9
|
+
*/
|
|
10
|
+
import { type QuotaTier, type QuotaTierLimits } from '@pipeline-builder/api-core';
|
|
11
|
+
import type { BundleConfig } from './config-types.js';
|
|
12
|
+
/**
|
|
13
|
+
* Compute an account's EFFECTIVE entitlements = tier base limits + Σ(bundle
|
|
14
|
+
* grants × quantity), plus the union of bundle-granted feature flags. A field
|
|
15
|
+
* already `-1` (unlimited) stays `-1`. Pure; the catalog is passed in.
|
|
16
|
+
*/
|
|
17
|
+
export declare function effectiveEntitlements(tier: QuotaTier, addons: ReadonlyArray<{
|
|
18
|
+
bundleId: string;
|
|
19
|
+
quantity: number;
|
|
20
|
+
}>, bundles: readonly BundleConfig[]): {
|
|
21
|
+
limits: Record<keyof QuotaTierLimits, number>;
|
|
22
|
+
features: string[];
|
|
23
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// Copyright 2026 Pipeline Builder Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Effective-entitlement math for the account/entitlement model.
|
|
5
|
+
*
|
|
6
|
+
* The canonical "effective limits = tier base + Σ(bundle grants)" computation
|
|
7
|
+
* lives here — next to the plan/bundle config it operates on — so ANY
|
|
8
|
+
* account-scoped consumer (billing, and future quota/platform readers) shares
|
|
9
|
+
* one implementation instead of re-deriving the coalesce. Pure: the tier limits
|
|
10
|
+
* come from api-core (`getTierLimits`) and the bundle catalog is passed in.
|
|
11
|
+
*/
|
|
12
|
+
import { getTierLimits } from '@pipeline-builder/api-core';
|
|
13
|
+
/**
|
|
14
|
+
* Compute an account's EFFECTIVE entitlements = tier base limits + Σ(bundle
|
|
15
|
+
* grants × quantity), plus the union of bundle-granted feature flags. A field
|
|
16
|
+
* already `-1` (unlimited) stays `-1`. Pure; the catalog is passed in.
|
|
17
|
+
*/
|
|
18
|
+
export function effectiveEntitlements(tier, addons, bundles) {
|
|
19
|
+
const limits = { ...getTierLimits(tier) };
|
|
20
|
+
const features = new Set();
|
|
21
|
+
const byId = new Map(bundles.map((b) => [b.id, b]));
|
|
22
|
+
for (const { bundleId, quantity } of addons) {
|
|
23
|
+
const bundle = byId.get(bundleId);
|
|
24
|
+
if (!bundle || quantity <= 0)
|
|
25
|
+
continue;
|
|
26
|
+
// Enforce the non-stackable invariant AND the maxQuantity ceiling in the
|
|
27
|
+
// CANONICAL math, not just at the purchase route: a `stackable:false` bundle
|
|
28
|
+
// counts once, and a stackable pack is clamped to its `maxQuantity` — so a
|
|
29
|
+
// non-route caller (drift reconciler, subscription-lifecycle) can't over-grant
|
|
30
|
+
// a quota from a stored/drifted quantity (e.g. a retention pack past its cap).
|
|
31
|
+
const effectiveQty = bundle.stackable ? Math.min(quantity, bundle.maxQuantity ?? quantity) : 1;
|
|
32
|
+
for (const [rawField, delta] of Object.entries(bundle.grants)) {
|
|
33
|
+
// `grants` is a Partial map, so a value can be undefined — skip those.
|
|
34
|
+
if (delta === undefined)
|
|
35
|
+
continue;
|
|
36
|
+
const field = rawField; // grants keys ARE quota fields
|
|
37
|
+
if (limits[field] === -1)
|
|
38
|
+
continue; // already unlimited
|
|
39
|
+
limits[field] = (limits[field] ?? 0) + delta * effectiveQty;
|
|
40
|
+
}
|
|
41
|
+
for (const f of bundle.features ?? [])
|
|
42
|
+
features.add(f);
|
|
43
|
+
}
|
|
44
|
+
return { limits, features: [...features] };
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW50aXRsZW1lbnRzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NvbmZpZy9lbnRpdGxlbWVudHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsK0NBQStDO0FBQy9DLHNDQUFzQztBQUV0Qzs7Ozs7Ozs7R0FRRztBQUVILE9BQU8sRUFBRSxhQUFhLEVBQXdDLE1BQU0sNEJBQTRCLENBQUM7QUFHakc7Ozs7R0FJRztBQUNILE1BQU0sVUFBVSxxQkFBcUIsQ0FDbkMsSUFBZSxFQUNmLE1BQTZELEVBQzdELE9BQWdDO0lBRWhDLE1BQU0sTUFBTSxHQUFHLEVBQUUsR0FBRyxhQUFhLENBQUMsSUFBSSxDQUFDLEVBQTJDLENBQUM7SUFDbkYsTUFBTSxRQUFRLEdBQUcsSUFBSSxHQUFHLEVBQVUsQ0FBQztJQUNuQyxNQUFNLElBQUksR0FBRyxJQUFJLEdBQUcsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3BELEtBQUssTUFBTSxFQUFFLFFBQVEsRUFBRSxRQUFRLEVBQUUsSUFBSSxNQUFNLEVBQUUsQ0FBQztRQUM1QyxNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ2xDLElBQUksQ0FBQyxNQUFNLElBQUksUUFBUSxJQUFJLENBQUM7WUFBRSxTQUFTO1FBQ3ZDLHlFQUF5RTtRQUN6RSw2RUFBNkU7UUFDN0UsMkVBQTJFO1FBQzNFLCtFQUErRTtRQUMvRSwrRUFBK0U7UUFDL0UsTUFBTSxZQUFZLEdBQUcsTUFBTSxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxRQUFRLEVBQUUsTUFBTSxDQUFDLFdBQVcsSUFBSSxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQy9GLEtBQUssTUFBTSxDQUFDLFFBQVEsRUFBRSxLQUFLLENBQUMsSUFBSSxNQUFNLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDO1lBQzlELHVFQUF1RTtZQUN2RSxJQUFJLEtBQUssS0FBSyxTQUFTO2dCQUFFLFNBQVM7WUFDbEMsTUFBTSxLQUFLLEdBQUcsUUFBaUMsQ0FBQyxDQUFDLCtCQUErQjtZQUNoRixJQUFJLE1BQU0sQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUM7Z0JBQUUsU0FBUyxDQUFDLG9CQUFvQjtZQUN4RCxNQUFNLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsS0FBSyxHQUFHLFlBQVksQ0FBQztRQUM5RCxDQUFDO1FBQ0QsS0FBSyxNQUFNLENBQUMsSUFBSSxNQUFNLENBQUMsUUFBUSxJQUFJLEVBQUU7WUFBRSxRQUFRLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3pELENBQUM7SUFDRCxPQUFPLEVBQUUsTUFBTSxFQUFFLFFBQVEsRUFBRSxDQUFDLEdBQUcsUUFBUSxDQUFDLEVBQUUsQ0FBQztBQUM3QyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbi8qKlxuICogRWZmZWN0aXZlLWVudGl0bGVtZW50IG1hdGggZm9yIHRoZSBhY2NvdW50L2VudGl0bGVtZW50IG1vZGVsLlxuICpcbiAqIFRoZSBjYW5vbmljYWwgXCJlZmZlY3RpdmUgbGltaXRzID0gdGllciBiYXNlICsgzqMoYnVuZGxlIGdyYW50cylcIiBjb21wdXRhdGlvblxuICogbGl2ZXMgaGVyZSDigJQgbmV4dCB0byB0aGUgcGxhbi9idW5kbGUgY29uZmlnIGl0IG9wZXJhdGVzIG9uIOKAlCBzbyBBTllcbiAqIGFjY291bnQtc2NvcGVkIGNvbnN1bWVyIChiaWxsaW5nLCBhbmQgZnV0dXJlIHF1b3RhL3BsYXRmb3JtIHJlYWRlcnMpIHNoYXJlc1xuICogb25lIGltcGxlbWVudGF0aW9uIGluc3RlYWQgb2YgcmUtZGVyaXZpbmcgdGhlIGNvYWxlc2NlLiBQdXJlOiB0aGUgdGllciBsaW1pdHNcbiAqIGNvbWUgZnJvbSBhcGktY29yZSAoYGdldFRpZXJMaW1pdHNgKSBhbmQgdGhlIGJ1bmRsZSBjYXRhbG9nIGlzIHBhc3NlZCBpbi5cbiAqL1xuXG5pbXBvcnQgeyBnZXRUaWVyTGltaXRzLCB0eXBlIFF1b3RhVGllciwgdHlwZSBRdW90YVRpZXJMaW1pdHMgfSBmcm9tICdAcGlwZWxpbmUtYnVpbGRlci9hcGktY29yZSc7XG5pbXBvcnQgdHlwZSB7IEJ1bmRsZUNvbmZpZyB9IGZyb20gJy4vY29uZmlnLXR5cGVzLmpzJztcblxuLyoqXG4gKiBDb21wdXRlIGFuIGFjY291bnQncyBFRkZFQ1RJVkUgZW50aXRsZW1lbnRzID0gdGllciBiYXNlIGxpbWl0cyArIM6jKGJ1bmRsZVxuICogZ3JhbnRzIMOXIHF1YW50aXR5KSwgcGx1cyB0aGUgdW5pb24gb2YgYnVuZGxlLWdyYW50ZWQgZmVhdHVyZSBmbGFncy4gQSBmaWVsZFxuICogYWxyZWFkeSBgLTFgICh1bmxpbWl0ZWQpIHN0YXlzIGAtMWAuIFB1cmU7IHRoZSBjYXRhbG9nIGlzIHBhc3NlZCBpbi5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGVmZmVjdGl2ZUVudGl0bGVtZW50cyhcbiAgdGllcjogUXVvdGFUaWVyLFxuICBhZGRvbnM6IFJlYWRvbmx5QXJyYXk8eyBidW5kbGVJZDogc3RyaW5nOyBxdWFudGl0eTogbnVtYmVyIH0+LFxuICBidW5kbGVzOiByZWFkb25seSBCdW5kbGVDb25maWdbXSxcbik6IHsgbGltaXRzOiBSZWNvcmQ8a2V5b2YgUXVvdGFUaWVyTGltaXRzLCBudW1iZXI+OyBmZWF0dXJlczogc3RyaW5nW10gfSB7XG4gIGNvbnN0IGxpbWl0cyA9IHsgLi4uZ2V0VGllckxpbWl0cyh0aWVyKSB9IGFzIFJlY29yZDxrZXlvZiBRdW90YVRpZXJMaW1pdHMsIG51bWJlcj47XG4gIGNvbnN0IGZlYXR1cmVzID0gbmV3IFNldDxzdHJpbmc+KCk7XG4gIGNvbnN0IGJ5SWQgPSBuZXcgTWFwKGJ1bmRsZXMubWFwKChiKSA9PiBbYi5pZCwgYl0pKTtcbiAgZm9yIChjb25zdCB7IGJ1bmRsZUlkLCBxdWFudGl0eSB9IG9mIGFkZG9ucykge1xuICAgIGNvbnN0IGJ1bmRsZSA9IGJ5SWQuZ2V0KGJ1bmRsZUlkKTtcbiAgICBpZiAoIWJ1bmRsZSB8fCBxdWFudGl0eSA8PSAwKSBjb250aW51ZTtcbiAgICAvLyBFbmZvcmNlIHRoZSBub24tc3RhY2thYmxlIGludmFyaWFudCBBTkQgdGhlIG1heFF1YW50aXR5IGNlaWxpbmcgaW4gdGhlXG4gICAgLy8gQ0FOT05JQ0FMIG1hdGgsIG5vdCBqdXN0IGF0IHRoZSBwdXJjaGFzZSByb3V0ZTogYSBgc3RhY2thYmxlOmZhbHNlYCBidW5kbGVcbiAgICAvLyBjb3VudHMgb25jZSwgYW5kIGEgc3RhY2thYmxlIHBhY2sgaXMgY2xhbXBlZCB0byBpdHMgYG1heFF1YW50aXR5YCDigJQgc28gYVxuICAgIC8vIG5vbi1yb3V0ZSBjYWxsZXIgKGRyaWZ0IHJlY29uY2lsZXIsIHN1YnNjcmlwdGlvbi1saWZlY3ljbGUpIGNhbid0IG92ZXItZ3JhbnRcbiAgICAvLyBhIHF1b3RhIGZyb20gYSBzdG9yZWQvZHJpZnRlZCBxdWFudGl0eSAoZS5nLiBhIHJldGVudGlvbiBwYWNrIHBhc3QgaXRzIGNhcCkuXG4gICAgY29uc3QgZWZmZWN0aXZlUXR5ID0gYnVuZGxlLnN0YWNrYWJsZSA/IE1hdGgubWluKHF1YW50aXR5LCBidW5kbGUubWF4UXVhbnRpdHkgPz8gcXVhbnRpdHkpIDogMTtcbiAgICBmb3IgKGNvbnN0IFtyYXdGaWVsZCwgZGVsdGFdIG9mIE9iamVjdC5lbnRyaWVzKGJ1bmRsZS5ncmFudHMpKSB7XG4gICAgICAvLyBgZ3JhbnRzYCBpcyBhIFBhcnRpYWwgbWFwLCBzbyBhIHZhbHVlIGNhbiBiZSB1bmRlZmluZWQg4oCUIHNraXAgdGhvc2UuXG4gICAgICBpZiAoZGVsdGEgPT09IHVuZGVmaW5lZCkgY29udGludWU7XG4gICAgICBjb25zdCBmaWVsZCA9IHJhd0ZpZWxkIGFzIGtleW9mIFF1b3RhVGllckxpbWl0czsgLy8gZ3JhbnRzIGtleXMgQVJFIHF1b3RhIGZpZWxkc1xuICAgICAgaWYgKGxpbWl0c1tmaWVsZF0gPT09IC0xKSBjb250aW51ZTsgLy8gYWxyZWFkeSB1bmxpbWl0ZWRcbiAgICAgIGxpbWl0c1tmaWVsZF0gPSAobGltaXRzW2ZpZWxkXSA/PyAwKSArIGRlbHRhICogZWZmZWN0aXZlUXR5O1xuICAgIH1cbiAgICBmb3IgKGNvbnN0IGYgb2YgYnVuZGxlLmZlYXR1cmVzID8/IFtdKSBmZWF0dXJlcy5hZGQoZik7XG4gIH1cbiAgcmV0dXJuIHsgbGltaXRzLCBmZWF0dXJlczogWy4uLmZlYXR1cmVzXSB9O1xufVxuIl19
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Default platform URL fallback when PLATFORM_BASE_URL is not set. */
|
|
2
|
+
export declare const DEFAULT_PLATFORM_URL = "https://localhost:8443";
|
|
3
|
+
/** Custom-resource handler timeout (must be < the Lambda's 30s to allow response handling). */
|
|
4
|
+
export declare const HANDLER_TIMEOUT_MS: number;
|
|
5
|
+
/** Platform base URL the handler calls; overridable per-request via ResourceProperties.baseURL. */
|
|
6
|
+
export declare const HANDLER_DEFAULT_BASE_URL: string;
|
|
7
|
+
/** Max retries on transient plugin-lookup failures. */
|
|
8
|
+
export declare const HANDLER_MAX_RETRIES: number;
|
|
9
|
+
/** Base backoff between handler retries (exponential). */
|
|
10
|
+
export declare const HANDLER_RETRY_DELAY_MS: number;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Copyright 2026 Pipeline Builder Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Leaf constants for the PluginLookup Lambda handler.
|
|
5
|
+
*
|
|
6
|
+
* Deliberately dependency-free (only `process.env` reads, no imports) so the
|
|
7
|
+
* handler's esbuild bundle stays small — it does not drag in the whole config
|
|
8
|
+
* machinery via `app-config.ts`. The handler imports THIS module, not
|
|
9
|
+
* `CoreConstants`.
|
|
10
|
+
*
|
|
11
|
+
* This originally guarded a much sharper edge: `app-config.ts` →
|
|
12
|
+
* `infrastructure-config.ts` → `aws-cdk-lib`, which OOM-killed esbuild
|
|
13
|
+
* (hundreds of MB, SIGKILL during cold-start synth). That chain is gone —
|
|
14
|
+
* `infrastructure-config.ts` is now CDK-free and the constructs live behind the
|
|
15
|
+
* `/cdk` entry point — but the dependency-free contract is still worth keeping.
|
|
16
|
+
*
|
|
17
|
+
* `CoreConstants` (app-config.ts) re-exports these so synth-side consumers
|
|
18
|
+
* (api-server, server-config) keep using `CoreConstants.HANDLER_*` unchanged —
|
|
19
|
+
* single source of truth lives here.
|
|
20
|
+
*/
|
|
21
|
+
/** Parse an integer env var, falling back on unset OR malformed (NaN). A raw
|
|
22
|
+
* `parseInt` returning NaN silently disables the handler's retry loop / axios
|
|
23
|
+
* timeout — breaking every plugin lookup — so guard it. Kept inline to preserve
|
|
24
|
+
* this module's dependency-free contract (no imports → small Lambda bundle). */
|
|
25
|
+
function envInt(value, fallback) {
|
|
26
|
+
const n = parseInt(value ?? '', 10);
|
|
27
|
+
return Number.isNaN(n) ? fallback : n;
|
|
28
|
+
}
|
|
29
|
+
/** Default platform URL fallback when PLATFORM_BASE_URL is not set. */
|
|
30
|
+
export const DEFAULT_PLATFORM_URL = 'https://localhost:8443';
|
|
31
|
+
/** Custom-resource handler timeout (must be < the Lambda's 30s to allow response handling). */
|
|
32
|
+
export const HANDLER_TIMEOUT_MS = envInt(process.env.HANDLER_TIMEOUT_MS, 25000); // 25s
|
|
33
|
+
/** Platform base URL the handler calls; overridable per-request via ResourceProperties.baseURL. */
|
|
34
|
+
export const HANDLER_DEFAULT_BASE_URL = process.env.PLATFORM_BASE_URL || DEFAULT_PLATFORM_URL;
|
|
35
|
+
/** Max retries on transient plugin-lookup failures. */
|
|
36
|
+
export const HANDLER_MAX_RETRIES = envInt(process.env.HANDLER_MAX_RETRIES, 2);
|
|
37
|
+
/** Base backoff between handler retries (exponential). */
|
|
38
|
+
export const HANDLER_RETRY_DELAY_MS = envInt(process.env.HANDLER_RETRY_DELAY_MS, 1000); // 1s
|
|
39
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGFuZGxlci1jb25zdGFudHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY29uZmlnL2hhbmRsZXItY29uc3RhbnRzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFFdEM7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBaUJHO0FBRUg7OztpRkFHaUY7QUFDakYsU0FBUyxNQUFNLENBQUMsS0FBeUIsRUFBRSxRQUFnQjtJQUN6RCxNQUFNLENBQUMsR0FBRyxRQUFRLENBQUMsS0FBSyxJQUFJLEVBQUUsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUNwQyxPQUFPLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQ3hDLENBQUM7QUFFRCx1RUFBdUU7QUFDdkUsTUFBTSxDQUFDLE1BQU0sb0JBQW9CLEdBQUcsd0JBQXdCLENBQUM7QUFFN0QsK0ZBQStGO0FBQy9GLE1BQU0sQ0FBQyxNQUFNLGtCQUFrQixHQUFHLE1BQU0sQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLGtCQUFrQixFQUFFLEtBQUssQ0FBQyxDQUFDLENBQUMsTUFBTTtBQUV2RixtR0FBbUc7QUFDbkcsTUFBTSxDQUFDLE1BQU0sd0JBQXdCLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxpQkFBaUIsSUFBSSxvQkFBb0IsQ0FBQztBQUU5Rix1REFBdUQ7QUFDdkQsTUFBTSxDQUFDLE1BQU0sbUJBQW1CLEdBQUcsTUFBTSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsbUJBQW1CLEVBQUUsQ0FBQyxDQUFDLENBQUM7QUFFOUUsMERBQTBEO0FBQzFELE1BQU0sQ0FBQyxNQUFNLHNCQUFzQixHQUFHLE1BQU0sQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLHNCQUFzQixFQUFFLElBQUksQ0FBQyxDQUFDLENBQUMsS0FBSyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG4vKipcbiAqIExlYWYgY29uc3RhbnRzIGZvciB0aGUgUGx1Z2luTG9va3VwIExhbWJkYSBoYW5kbGVyLlxuICpcbiAqIERlbGliZXJhdGVseSBkZXBlbmRlbmN5LWZyZWUgKG9ubHkgYHByb2Nlc3MuZW52YCByZWFkcywgbm8gaW1wb3J0cykgc28gdGhlXG4gKiBoYW5kbGVyJ3MgZXNidWlsZCBidW5kbGUgc3RheXMgc21hbGwg4oCUIGl0IGRvZXMgbm90IGRyYWcgaW4gdGhlIHdob2xlIGNvbmZpZ1xuICogbWFjaGluZXJ5IHZpYSBgYXBwLWNvbmZpZy50c2AuIFRoZSBoYW5kbGVyIGltcG9ydHMgVEhJUyBtb2R1bGUsIG5vdFxuICogYENvcmVDb25zdGFudHNgLlxuICpcbiAqIFRoaXMgb3JpZ2luYWxseSBndWFyZGVkIGEgbXVjaCBzaGFycGVyIGVkZ2U6IGBhcHAtY29uZmlnLnRzYCDihpJcbiAqIGBpbmZyYXN0cnVjdHVyZS1jb25maWcudHNgIOKGkiBgYXdzLWNkay1saWJgLCB3aGljaCBPT00ta2lsbGVkIGVzYnVpbGRcbiAqIChodW5kcmVkcyBvZiBNQiwgU0lHS0lMTCBkdXJpbmcgY29sZC1zdGFydCBzeW50aCkuIFRoYXQgY2hhaW4gaXMgZ29uZSDigJRcbiAqIGBpbmZyYXN0cnVjdHVyZS1jb25maWcudHNgIGlzIG5vdyBDREstZnJlZSBhbmQgdGhlIGNvbnN0cnVjdHMgbGl2ZSBiZWhpbmQgdGhlXG4gKiBgL2Nka2AgZW50cnkgcG9pbnQg4oCUIGJ1dCB0aGUgZGVwZW5kZW5jeS1mcmVlIGNvbnRyYWN0IGlzIHN0aWxsIHdvcnRoIGtlZXBpbmcuXG4gKlxuICogYENvcmVDb25zdGFudHNgIChhcHAtY29uZmlnLnRzKSByZS1leHBvcnRzIHRoZXNlIHNvIHN5bnRoLXNpZGUgY29uc3VtZXJzXG4gKiAoYXBpLXNlcnZlciwgc2VydmVyLWNvbmZpZykga2VlcCB1c2luZyBgQ29yZUNvbnN0YW50cy5IQU5ETEVSXypgIHVuY2hhbmdlZCDigJRcbiAqIHNpbmdsZSBzb3VyY2Ugb2YgdHJ1dGggbGl2ZXMgaGVyZS5cbiAqL1xuXG4vKiogUGFyc2UgYW4gaW50ZWdlciBlbnYgdmFyLCBmYWxsaW5nIGJhY2sgb24gdW5zZXQgT1IgbWFsZm9ybWVkIChOYU4pLiBBIHJhd1xuICogIGBwYXJzZUludGAgcmV0dXJuaW5nIE5hTiBzaWxlbnRseSBkaXNhYmxlcyB0aGUgaGFuZGxlcidzIHJldHJ5IGxvb3AgLyBheGlvc1xuICogIHRpbWVvdXQg4oCUIGJyZWFraW5nIGV2ZXJ5IHBsdWdpbiBsb29rdXAg4oCUIHNvIGd1YXJkIGl0LiBLZXB0IGlubGluZSB0byBwcmVzZXJ2ZVxuICogIHRoaXMgbW9kdWxlJ3MgZGVwZW5kZW5jeS1mcmVlIGNvbnRyYWN0IChubyBpbXBvcnRzIOKGkiBzbWFsbCBMYW1iZGEgYnVuZGxlKS4gKi9cbmZ1bmN0aW9uIGVudkludCh2YWx1ZTogc3RyaW5nIHwgdW5kZWZpbmVkLCBmYWxsYmFjazogbnVtYmVyKTogbnVtYmVyIHtcbiAgY29uc3QgbiA9IHBhcnNlSW50KHZhbHVlID8/ICcnLCAxMCk7XG4gIHJldHVybiBOdW1iZXIuaXNOYU4obikgPyBmYWxsYmFjayA6IG47XG59XG5cbi8qKiBEZWZhdWx0IHBsYXRmb3JtIFVSTCBmYWxsYmFjayB3aGVuIFBMQVRGT1JNX0JBU0VfVVJMIGlzIG5vdCBzZXQuICovXG5leHBvcnQgY29uc3QgREVGQVVMVF9QTEFURk9STV9VUkwgPSAnaHR0cHM6Ly9sb2NhbGhvc3Q6ODQ0Myc7XG5cbi8qKiBDdXN0b20tcmVzb3VyY2UgaGFuZGxlciB0aW1lb3V0IChtdXN0IGJlIDwgdGhlIExhbWJkYSdzIDMwcyB0byBhbGxvdyByZXNwb25zZSBoYW5kbGluZykuICovXG5leHBvcnQgY29uc3QgSEFORExFUl9USU1FT1VUX01TID0gZW52SW50KHByb2Nlc3MuZW52LkhBTkRMRVJfVElNRU9VVF9NUywgMjUwMDApOyAvLyAyNXNcblxuLyoqIFBsYXRmb3JtIGJhc2UgVVJMIHRoZSBoYW5kbGVyIGNhbGxzOyBvdmVycmlkYWJsZSBwZXItcmVxdWVzdCB2aWEgUmVzb3VyY2VQcm9wZXJ0aWVzLmJhc2VVUkwuICovXG5leHBvcnQgY29uc3QgSEFORExFUl9ERUZBVUxUX0JBU0VfVVJMID0gcHJvY2Vzcy5lbnYuUExBVEZPUk1fQkFTRV9VUkwgfHwgREVGQVVMVF9QTEFURk9STV9VUkw7XG5cbi8qKiBNYXggcmV0cmllcyBvbiB0cmFuc2llbnQgcGx1Z2luLWxvb2t1cCBmYWlsdXJlcy4gKi9cbmV4cG9ydCBjb25zdCBIQU5ETEVSX01BWF9SRVRSSUVTID0gZW52SW50KHByb2Nlc3MuZW52LkhBTkRMRVJfTUFYX1JFVFJJRVMsIDIpO1xuXG4vKiogQmFzZSBiYWNrb2ZmIGJldHdlZW4gaGFuZGxlciByZXRyaWVzIChleHBvbmVudGlhbCkuICovXG5leHBvcnQgY29uc3QgSEFORExFUl9SRVRSWV9ERUxBWV9NUyA9IGVudkludChwcm9jZXNzLmVudi5IQU5ETEVSX1JFVFJZX0RFTEFZX01TLCAxMDAwKTsgLy8gMXNcbiJdfQ==
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { AWSConfig, BuildConfig, ComplianceConfig, DatabaseConfig, ObservabilityConfig, PluginBuildConfig, RedisConfig, RegistryConfig } from './config-types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Load Docker registry configuration from environment variables.
|
|
4
|
+
*
|
|
5
|
+
* Environment variables:
|
|
6
|
+
* - `IMAGE_REGISTRY_HOST` — Registry hostname (default: `'registry'`)
|
|
7
|
+
* - `IMAGE_REGISTRY_PORT` — Registry port (default: `5000`)
|
|
8
|
+
* - `IMAGE_REGISTRY_PULL_HOST` — Host an out-of-cluster client (CodeBuild)
|
|
9
|
+
* uses to pull plugin images. When unset, derived from `PLATFORM_BASE_URL`'s
|
|
10
|
+
* host (the public endpoint CodeBuild can resolve), then `IMAGE_REGISTRY_HOST`.
|
|
11
|
+
* - `IMAGE_REGISTRY_PULL_PORT` — Port for the above. When unset, derived from
|
|
12
|
+
* `PLATFORM_BASE_URL`'s port, then `IMAGE_REGISTRY_PORT`.
|
|
13
|
+
* - `DOCKER_NETWORK` — Docker network for build/push (default: `''`)
|
|
14
|
+
* - `IMAGE_REGISTRY_HTTP` — Use plain HTTP instead of HTTPS (default: `true`,
|
|
15
|
+
* the in-cluster registry has no TLS).
|
|
16
|
+
*
|
|
17
|
+
* @returns Registry configuration
|
|
18
|
+
*/
|
|
19
|
+
export declare function loadRegistryConfig(): RegistryConfig;
|
|
20
|
+
/** Extract host + port from PLATFORM_BASE_URL (e.g. `https://pipeline-builder.com`
|
|
21
|
+
* → `{ host: 'pipeline-builder.com', port: 443 }`). Returns null when the value
|
|
22
|
+
* is unset or not a valid URL. Exported so the CLI (pipeline-manager) derives
|
|
23
|
+
* the registry pull target with the SAME parsing, instead of duplicating it. */
|
|
24
|
+
export declare function parsePlatformBaseUrl(raw?: string): {
|
|
25
|
+
host: string;
|
|
26
|
+
port: number;
|
|
27
|
+
} | null;
|
|
28
|
+
export declare function loadRedisConfig(): RedisConfig;
|
|
29
|
+
/**
|
|
30
|
+
* Load plugin build queue configuration.
|
|
31
|
+
*
|
|
32
|
+
* Environment variables:
|
|
33
|
+
* - `PLUGIN_BUILD_CONCURRENCY` — Max concurrent plugin builds (default: `1`)
|
|
34
|
+
*/
|
|
35
|
+
export declare function loadPluginBuildConfig(): PluginBuildConfig;
|
|
36
|
+
export declare function loadDatabaseConfig(): DatabaseConfig;
|
|
37
|
+
export declare function loadObservabilityConfig(): ObservabilityConfig;
|
|
38
|
+
export declare function loadComplianceConfig(): ComplianceConfig;
|
|
39
|
+
/**
|
|
40
|
+
* Load plugin build configuration. Builds run against a rootless `moby/buildkit`
|
|
41
|
+
* sidecar — see `BUILDKIT_HOST`.
|
|
42
|
+
*
|
|
43
|
+
* Environment variables:
|
|
44
|
+
* - `DOCKER_BUILD_TEMP_ROOT` — Temp directory for build contexts (default: `<cwd>/tmp`)
|
|
45
|
+
* - `DOCKER_BUILD_TIMEOUT_MS` — Build timeout in milliseconds (default: `900000` / 15 min)
|
|
46
|
+
* - `DOCKER_PUSH_TIMEOUT_MS` — Push timeout in milliseconds (default: `300000` / 5 min)
|
|
47
|
+
* - `BUILDKIT_HOST` — buildctl `--addr` value for the buildkitd sidecar
|
|
48
|
+
* (default: `unix:///run/buildkit/buildkitd.sock`)
|
|
49
|
+
*/
|
|
50
|
+
export declare function loadDockerConfig(): BuildConfig;
|
|
51
|
+
/**
|
|
52
|
+
* Load AWS infrastructure configuration from environment variables.
|
|
53
|
+
*
|
|
54
|
+
* Environment variables:
|
|
55
|
+
* - `LAMBDA_RUNTIME` — Lambda runtime (default: `'nodejs24.x'`; unknown values fall back to it)
|
|
56
|
+
* - `LAMBDA_TIMEOUT` — Lambda timeout in seconds (default: `900`)
|
|
57
|
+
* - `LAMBDA_MEMORY_SIZE` — Lambda memory in MB (default: `512`)
|
|
58
|
+
* - `LAMBDA_ARCHITECTURE` — `'x86_64'` or ARM (default: ARM_64)
|
|
59
|
+
* - `LOG_GROUP_NAME` — CloudWatch log group (default: `'/pipeline-builder/logs'`)
|
|
60
|
+
* - `LOG_RETENTION` — Log retention in days (default: `7`)
|
|
61
|
+
* - `LOG_REMOVAL_POLICY` — `'RETAIN'` or destroy (default: DESTROY)
|
|
62
|
+
* - `CODEBUILD_COMPUTE_TYPE` — CodeBuild compute type (default: `'SMALL'`)
|
|
63
|
+
*
|
|
64
|
+
* @returns AWS infrastructure configuration
|
|
65
|
+
*/
|
|
66
|
+
export declare function loadAWSConfig(): AWSConfig;
|