@pipeline-builder/api-core 5.2.2 → 5.2.4
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/lib/constants/ai-providers.d.ts +3 -22
- package/lib/constants/ai-providers.js +6 -56
- package/lib/errors/app-errors.d.ts +14 -0
- package/lib/errors/app-errors.js +21 -1
- package/lib/helpers/index.d.ts +1 -0
- package/lib/helpers/index.js +2 -1
- package/lib/helpers/sse-helpers.d.ts +33 -3
- package/lib/helpers/sse-helpers.js +91 -19
- package/lib/helpers/support-calendar.d.ts +69 -0
- package/lib/helpers/support-calendar.js +179 -0
- package/lib/middleware/auth.js +6 -3
- package/lib/middleware/deferred-quota-check.d.ts +5 -0
- package/lib/middleware/deferred-quota-check.js +33 -0
- package/lib/middleware/ecosystem-guard.d.ts +3 -3
- package/lib/middleware/ecosystem-guard.js +5 -5
- package/lib/middleware/index.d.ts +5 -2
- package/lib/middleware/index.js +6 -3
- package/lib/middleware/on-behalf.d.ts +37 -0
- package/lib/middleware/on-behalf.js +143 -0
- package/lib/middleware/permission-gates.d.ts +11 -0
- package/lib/middleware/permission-gates.js +7 -1
- package/lib/middleware/quota-exempt-routes.d.ts +36 -0
- package/lib/middleware/quota-exempt-routes.js +47 -0
- package/lib/middleware/route-table.d.ts +104 -0
- package/lib/middleware/route-table.js +72 -2
- package/lib/middleware/service-tokens.d.ts +8 -0
- package/lib/middleware/service-tokens.js +5 -3
- package/lib/middleware/step-up.js +11 -1
- package/lib/services/ai-call-weight.d.ts +24 -0
- package/lib/services/ai-call-weight.js +77 -0
- package/lib/services/alert-delivery.d.ts +99 -0
- package/lib/services/alert-delivery.js +132 -0
- package/lib/services/audit-context.d.ts +105 -0
- package/lib/services/audit-context.js +134 -0
- package/lib/services/index.d.ts +10 -2
- package/lib/services/index.js +10 -2
- package/lib/services/org-ai-client.d.ts +61 -0
- package/lib/services/org-ai-client.js +112 -0
- package/lib/services/pagerduty-inbound.d.ts +101 -0
- package/lib/services/pagerduty-inbound.js +195 -0
- package/lib/services/pagerduty.d.ts +109 -0
- package/lib/services/pagerduty.js +186 -0
- package/lib/services/quota-increment-batcher.d.ts +81 -0
- package/lib/services/quota-increment-batcher.js +166 -0
- package/lib/services/quota.d.ts +158 -14
- package/lib/services/quota.js +252 -22
- package/lib/services/remote-audit-client.d.ts +44 -1
- package/lib/services/remote-audit-client.js +106 -20
- package/lib/services/version-vuln-policy.d.ts +174 -0
- package/lib/services/version-vuln-policy.js +242 -0
- package/lib/types/ai-catalog.d.ts +63 -0
- package/lib/types/ai-catalog.js +82 -0
- package/lib/types/ask-proposals.js +6 -6
- package/lib/types/build-catalog.d.ts +154 -0
- package/lib/types/build-catalog.js +158 -0
- package/lib/types/common.d.ts +37 -3
- package/lib/types/common.js +20 -2
- package/lib/types/ecosystem-notifications.d.ts +5 -5
- package/lib/types/ecosystem-notifications.js +9 -3
- package/lib/types/error-codes.d.ts +13 -6
- package/lib/types/error-codes.js +18 -8
- package/lib/types/feature-flags.d.ts +71 -1
- package/lib/types/feature-flags.js +131 -11
- package/lib/types/index.d.ts +6 -4
- package/lib/types/index.js +12 -5
- package/lib/types/metadata-keys.d.ts +6 -0
- package/lib/types/metadata-keys.js +4 -1
- package/lib/types/permissions.d.ts +351 -3
- package/lib/types/permissions.js +610 -21
- package/lib/types/pipeline.d.ts +39 -0
- package/lib/types/pipeline.js +57 -1
- package/lib/types/plugin-health.d.ts +0 -2
- package/lib/types/plugin-health.js +1 -11
- package/lib/types/plugin-scan.d.ts +192 -42
- package/lib/types/plugin-scan.js +159 -43
- package/lib/types/quota-catalog.d.ts +247 -0
- package/lib/types/quota-catalog.js +122 -0
- package/lib/types/quota-tiers.d.ts +150 -0
- package/lib/types/quota-tiers.js +167 -2
- package/lib/types/remote-audit-actions.d.ts +1 -1
- package/lib/types/remote-audit-actions.js +217 -4
- package/lib/types/wire-vocabulary.js +8 -1
- package/lib/utils/index.d.ts +1 -1
- package/lib/utils/index.js +2 -2
- package/lib/utils/response.d.ts +35 -2
- package/lib/utils/response.js +42 -3
- package/lib/validation/ai-schemas.d.ts +27 -0
- package/lib/validation/ai-schemas.js +31 -5
- package/lib/validation/plugin-schemas.d.ts +15 -0
- package/lib/validation/plugin-schemas.js +28 -1
- package/package.json +31 -2
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The quota-type display catalog, as DATA ONLY — what a quota is CALLED and how
|
|
3
|
+
* its number is RENDERED, for every `QuotaType`.
|
|
4
|
+
*
|
|
5
|
+
* Dependency-free and env-free so the browser imports it directly
|
|
6
|
+
* (`@pipeline-builder/api-core/quota-catalog`). Its only import is a type, which
|
|
7
|
+
* is erased at compile time, so nothing of the server graph reaches the bundle.
|
|
8
|
+
*
|
|
9
|
+
* WHY IT EXISTS. The same facts were spelled in six places: this package's
|
|
10
|
+
* `QUOTA_TYPE_LABELS`, and five independent maps in the frontend — the pressure
|
|
11
|
+
* banner's labels, the quotas page's `QUOTA_META`, the org home's `QUOTA_LABELS`,
|
|
12
|
+
* the billing usage card's `QUOTA_LABELS` (with its own byte flag), and the
|
|
13
|
+
* add-on grid's `PACK_UNITS`. Each knew a subset, each could drift, and adding a
|
|
14
|
+
* quota type meant finding all six. `PACK_UNITS` had exactly one member, so the
|
|
15
|
+
* add-on grid rendered a GB step for storage and a bare pack count for
|
|
16
|
+
* everything else — not a decision anyone revisited, just the one entry somebody
|
|
17
|
+
* needed at the time.
|
|
18
|
+
*
|
|
19
|
+
* Add a quota type here and every surface renders it: `Record<QuotaType, …>`
|
|
20
|
+
* makes the compiler demand the entry, so none of the six can be forgotten
|
|
21
|
+
* again.
|
|
22
|
+
*
|
|
23
|
+
* EVERY display fact lives here, including the plural label. A first draft kept
|
|
24
|
+
* plurals in a frontend helper with a `Record<string, string>` and a fallback to
|
|
25
|
+
* the singular — which would have rendered a new quota type's plural wrongly and
|
|
26
|
+
* silently, reproducing the exact partial-map trap this file removes. A fact
|
|
27
|
+
* split across two packages is a fact that drifts.
|
|
28
|
+
*/
|
|
29
|
+
import type { QuotaType } from './common.js';
|
|
30
|
+
/** How a quota's usage is learned: incremented as it happens, or read on demand. */
|
|
31
|
+
export type QuotaObservation =
|
|
32
|
+
/** The platform increments a counter as the work happens. */
|
|
33
|
+
'metered'
|
|
34
|
+
/** Nothing increments: the figure is read from the system of record. */
|
|
35
|
+
| 'measured';
|
|
36
|
+
/** Whether a quota refills on the period or persists until something changes. */
|
|
37
|
+
export type QuotaAccrual =
|
|
38
|
+
/** A per-period rate: reserved on use, zeroed at the period boundary. */
|
|
39
|
+
'flow'
|
|
40
|
+
/** A level that stays until the tenant frees it. No reset, no forecast. */
|
|
41
|
+
| 'stock';
|
|
42
|
+
/** How a raw quota number should be rendered. */
|
|
43
|
+
export type QuotaRender =
|
|
44
|
+
/** A plain integer — "4 / 10". */
|
|
45
|
+
'count'
|
|
46
|
+
/** A byte count — render with `formatBytes` ("18.4 GB"), never raw. */
|
|
47
|
+
| 'bytes';
|
|
48
|
+
/** What a surface needs to know to show one quota type. */
|
|
49
|
+
export interface QuotaTypeMeta {
|
|
50
|
+
/**
|
|
51
|
+
* SINGULAR human label — the one spelling every 429 body, notification and
|
|
52
|
+
* dashboard uses ("API call quota reached …").
|
|
53
|
+
*/
|
|
54
|
+
label: string;
|
|
55
|
+
/**
|
|
56
|
+
* PLURAL label, for surfaces that list dimensions rather than address one
|
|
57
|
+
* ("API calls" on a usage card, against "API call quota reached" in a 429).
|
|
58
|
+
*
|
|
59
|
+
* REQUIRED, and here rather than in the frontend, because the first draft put
|
|
60
|
+
* it in a `Record<string, string>` helper that fell back to the singular when
|
|
61
|
+
* a type was missing — so a new quota type would have rendered "Build minute"
|
|
62
|
+
* in plural contexts, wrongly and silently. That is the same partial-map trap
|
|
63
|
+
* this catalog exists to remove, and `Record<QuotaType, …>` closes it only if
|
|
64
|
+
* every display fact lives here.
|
|
65
|
+
*/
|
|
66
|
+
labelPlural: string;
|
|
67
|
+
/** How to render a raw value for this type. */
|
|
68
|
+
render: QuotaRender;
|
|
69
|
+
/**
|
|
70
|
+
* Whether this quota REFILLS on the period, or persists until the tenant
|
|
71
|
+
* changes something.
|
|
72
|
+
*
|
|
73
|
+
* This is the single most load-bearing semantic difference between quota
|
|
74
|
+
* types and nothing could read it before: a surface had to know, by hand,
|
|
75
|
+
* that "resets in 2 days" is true of `apiCalls` and false of `storageBytes`.
|
|
76
|
+
* Getting it wrong is not cosmetic — the quotas page told an organization its
|
|
77
|
+
* registry storage would reset, which is the opposite of the one thing it
|
|
78
|
+
* needs to understand about storage.
|
|
79
|
+
*
|
|
80
|
+
* - `flow` — a per-period rate, reserved on create/build and zeroed at the
|
|
81
|
+
* boundary. Hitting it is temporary and the account self-heals. The period is
|
|
82
|
+
* PER TYPE rather than global: `QUOTA_RESET_DAYS` (3 days) for every flow but
|
|
83
|
+
* `cpuCredits`, which is monthly. Read it through `quotaPeriodDays` and never
|
|
84
|
+
* assume the global — a forecast or a "resets in N days" line computed
|
|
85
|
+
* against the wrong window is wrong by a factor of ten for credits.
|
|
86
|
+
* - `stock` — a level, not a rate. It stays where it is until bytes are freed
|
|
87
|
+
* or rows are deleted, so a reset date, a "resets in N days" line and a
|
|
88
|
+
* burn-rate forecast are all meaningless for it. `storageBytes` is measured;
|
|
89
|
+
* the resource-count caps are counted at create time and decremented on
|
|
90
|
+
* delete; `listings` counts active publications.
|
|
91
|
+
*
|
|
92
|
+
* The {@link QuotaTypeMeta.description} wording follows from this ("this
|
|
93
|
+
* period" against "currently"), and a test holds the two in agreement.
|
|
94
|
+
*/
|
|
95
|
+
accrual: QuotaAccrual;
|
|
96
|
+
/**
|
|
97
|
+
* How usage is OBSERVED — which is a different question from whether the
|
|
98
|
+
* quota refills, and the two do not line up.
|
|
99
|
+
*
|
|
100
|
+
* - `metered` — the platform INCREMENTS a counter as the work happens. Every
|
|
101
|
+
* quota but one, including the create-time counts that are stocks
|
|
102
|
+
* (`dashboards`, `listings`, …).
|
|
103
|
+
* - `measured` — nothing increments anything; the figure is READ from the
|
|
104
|
+
* system of record on demand. `storageBytes` only: the registry walks its
|
|
105
|
+
* own catalog, which is why it is "the one quota nothing increments".
|
|
106
|
+
*
|
|
107
|
+
* It decides which code path may legitimately notify about a quota. A storage
|
|
108
|
+
* notice arriving from the increment path could only be a bug, and a
|
|
109
|
+
* `listings` notice arriving from a sweep likewise — conflating this with
|
|
110
|
+
* {@link accrual} would have silenced every create-time count quota, because
|
|
111
|
+
* those are stocks that are nonetheless metered.
|
|
112
|
+
*/
|
|
113
|
+
observed: QuotaObservation;
|
|
114
|
+
/**
|
|
115
|
+
* What this counter actually counts, in one short phrase — the answer to "am I
|
|
116
|
+
* spending this one?".
|
|
117
|
+
*
|
|
118
|
+
* REQUIRED, and a display FACT rather than page copy, which is the call a
|
|
119
|
+
* first pass got wrong by judging the field's name instead of its content. It
|
|
120
|
+
* had been left in a frontend `Record<DisplayedQuotaType, string>` on the
|
|
121
|
+
* reasoning that descriptions are a page's own voice. They are not: each of
|
|
122
|
+
* these answers a question whose answer is the same on every surface, and two
|
|
123
|
+
* surfaces disagreeing is a bug, not a difference in tone. Two of the five
|
|
124
|
+
* local copies had already drifted — `automationCalls` omitted personal
|
|
125
|
+
* access keys (so a PAT holder read the page and concluded they were spending
|
|
126
|
+
* `apiCalls`), and `plugins`/`pipelines` were phrased as stocks.
|
|
127
|
+
*
|
|
128
|
+
* The test is whether `docs/quotas.md` and this string can legitimately
|
|
129
|
+
* differ. For these, no. For a tier's positioning line ("Starter tier") or the
|
|
130
|
+
* quotas page's pooling explanation, yes — those stay in the page, exactly as
|
|
131
|
+
* `TIER_META` already splits a tier's label from its sales copy.
|
|
132
|
+
*
|
|
133
|
+
* Phrasing follows the SEMANTICS in {@link QuotaType}'s own documentation, not
|
|
134
|
+
* a house style: "this period" for the per-period flows, "currently" for the
|
|
135
|
+
* create-time counts that decrement on delete, and neither for the measured
|
|
136
|
+
* storage total.
|
|
137
|
+
*/
|
|
138
|
+
description: string;
|
|
139
|
+
/**
|
|
140
|
+
* The unit an ADD-ON PACK of this quota is typed in, and the divisor from the
|
|
141
|
+
* raw grant to that unit — so a +10 GB storage pack offers a GB step rather
|
|
142
|
+
* than a pack count.
|
|
143
|
+
*
|
|
144
|
+
* Absent means "type pack counts", which is right where the quota has no
|
|
145
|
+
* natural unit: "1 pack of API calls" reads better than "25000 calls". The
|
|
146
|
+
* step itself is always derived from the bundle's own `grants`, never from a
|
|
147
|
+
* constant here, because a grant is env-overridable.
|
|
148
|
+
*/
|
|
149
|
+
packUnit?: {
|
|
150
|
+
name: string;
|
|
151
|
+
per: number;
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Display metadata for every quota type. Labels are the canonical ones — the
|
|
156
|
+
* strings here are user-facing and are relied on by 429 bodies and notices, so
|
|
157
|
+
* they change only deliberately.
|
|
158
|
+
*/
|
|
159
|
+
export declare const QUOTA_CATALOG: Readonly<Record<QuotaType, QuotaTypeMeta>>;
|
|
160
|
+
/**
|
|
161
|
+
* Consumables that are NOT quota types but render beside them — same label,
|
|
162
|
+
* same bar, same card.
|
|
163
|
+
*
|
|
164
|
+
* `seats` is the only one: it is a STOCK (how many people may be on the
|
|
165
|
+
* account) rather than a per-period flow, so it is deliberately outside
|
|
166
|
+
* `QuotaType` and is sourced separately from the quota rollup. But the billing
|
|
167
|
+
* usage card draws it in the same list, and before this entry existed that one
|
|
168
|
+
* row carried its own inline "Seats" label while the five beside it came from
|
|
169
|
+
* the catalog — the precise split-fact drift this file exists to remove.
|
|
170
|
+
*
|
|
171
|
+
* Keep this for things rendered AS a consumable. It is not a back door for
|
|
172
|
+
* quota types: a real quota belongs in {@link QUOTA_CATALOG}, where
|
|
173
|
+
* `Record<QuotaType, …>` forces the entry.
|
|
174
|
+
*/
|
|
175
|
+
export declare const NON_QUOTA_CONSUMABLE_CATALOG: Readonly<Record<string, QuotaTypeMeta>>;
|
|
176
|
+
/**
|
|
177
|
+
* This type's singular label, or the raw key for a value outside the catalog.
|
|
178
|
+
*
|
|
179
|
+
* The accessors take `string` because the quota type often arrives from the wire
|
|
180
|
+
* (a 429 body, a quota response) where an unknown value must render as something
|
|
181
|
+
* rather than crash a page. The `QuotaType` overload is for internal callers: it
|
|
182
|
+
* makes a typo a compile error instead of a key rendered as a label.
|
|
183
|
+
*/
|
|
184
|
+
export declare function quotaLabel(type: QuotaType): string;
|
|
185
|
+
export declare function quotaLabel(type: string): string;
|
|
186
|
+
/** This type's PLURAL label, or the raw key for a value outside the catalog. */
|
|
187
|
+
export declare function quotaLabelPlural(type: QuotaType): string;
|
|
188
|
+
export declare function quotaLabelPlural(type: string): string;
|
|
189
|
+
/**
|
|
190
|
+
* How to render this type's values. `count` for anything the catalog does not
|
|
191
|
+
* know, so an unfamiliar wire value renders as a plain number.
|
|
192
|
+
*
|
|
193
|
+
* Read THIS rather than asking "is it bytes?". A boolean helper degrades
|
|
194
|
+
* silently the moment {@link QuotaRender} grows a third member — a new mode
|
|
195
|
+
* would simply render as a count, with nothing failing — whereas a caller that
|
|
196
|
+
* switches on this value exhaustively stops compiling until it handles the new
|
|
197
|
+
* mode. That is the whole point of the catalog, applied to its own accessors.
|
|
198
|
+
*/
|
|
199
|
+
export declare function quotaRender(type: QuotaType): QuotaRender;
|
|
200
|
+
export declare function quotaRender(type: string): QuotaRender;
|
|
201
|
+
/**
|
|
202
|
+
* A byte count as a human amount — `1.5 GB`, `512 MB`, `0 B`.
|
|
203
|
+
*
|
|
204
|
+
* The ONE implementation: the frontend's `formatBytes` delegates here rather
|
|
205
|
+
* than keeping its own copy, so a storage figure reads identically in a quota
|
|
206
|
+
* notice, a 403 explanation and the dashboard. Binary units (1024), one decimal
|
|
207
|
+
* below 10 and rounded above, which is what the dashboard already rendered —
|
|
208
|
+
* this moved the algorithm, it did not change it.
|
|
209
|
+
*/
|
|
210
|
+
export declare function formatByteAmount(bytes: number): string;
|
|
211
|
+
/**
|
|
212
|
+
* A raw quota value as the amount a HUMAN should read, per this type's
|
|
213
|
+
* {@link QuotaRender} — bytes as `25 GB`, counts grouped as `60,000`.
|
|
214
|
+
*
|
|
215
|
+
* For server-side copy (quota notices, audit reasons, 403 explanations), which
|
|
216
|
+
* is why it is here rather than in the frontend: a storage notice that
|
|
217
|
+
* interpolated the raw number would read "used all 26843545600 of its registry
|
|
218
|
+
* storage". The frontend has its own thin wrapper because it renders an
|
|
219
|
+
* unlimited (-1) limit as `∞`, which is a display choice no server copy makes
|
|
220
|
+
* (unlimited orgs are skipped before any copy is generated).
|
|
221
|
+
*/
|
|
222
|
+
export declare function formatQuotaAmount(type: string, value: number): string;
|
|
223
|
+
/**
|
|
224
|
+
* How this quota's usage is observed: `metered` (incremented) or `measured`
|
|
225
|
+
* (read on demand). Unknown wire values read as `metered`, matching every
|
|
226
|
+
* quota but storage.
|
|
227
|
+
*/
|
|
228
|
+
export declare function quotaObservation(type: QuotaType): QuotaObservation;
|
|
229
|
+
export declare function quotaObservation(type: string): QuotaObservation;
|
|
230
|
+
/**
|
|
231
|
+
* Whether this quota refills on the period (`flow`) or persists (`stock`).
|
|
232
|
+
* Unknown wire values are treated as `flow`, matching every metered quota.
|
|
233
|
+
*/
|
|
234
|
+
export declare function quotaAccrual(type: QuotaType): QuotaAccrual;
|
|
235
|
+
export declare function quotaAccrual(type: string): QuotaAccrual;
|
|
236
|
+
/** What this type counts, or the empty string for a value outside the catalog. */
|
|
237
|
+
export declare function quotaDescription(type: QuotaType): string;
|
|
238
|
+
export declare function quotaDescription(type: string): string;
|
|
239
|
+
/** The pack unit for this type, or `null` to type pack counts instead. */
|
|
240
|
+
export declare function quotaPackUnit(type: QuotaType): {
|
|
241
|
+
name: string;
|
|
242
|
+
per: number;
|
|
243
|
+
} | null;
|
|
244
|
+
export declare function quotaPackUnit(type: string): {
|
|
245
|
+
name: string;
|
|
246
|
+
per: number;
|
|
247
|
+
} | null;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// Copyright 2026 Pipeline Builder Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
const GB = 1024 ** 3;
|
|
4
|
+
/**
|
|
5
|
+
* Display metadata for every quota type. Labels are the canonical ones — the
|
|
6
|
+
* strings here are user-facing and are relied on by 429 bodies and notices, so
|
|
7
|
+
* they change only deliberately.
|
|
8
|
+
*/
|
|
9
|
+
export const QUOTA_CATALOG = {
|
|
10
|
+
// Per-period FLOWS — a creation rate, not a stock. Deleting the entity does
|
|
11
|
+
// not give the slot back (`decrement-on-delete` was considered and rejected),
|
|
12
|
+
// so the wording says "this period" to stop the stock reading.
|
|
13
|
+
plugins: { label: 'Plugin build', labelPlural: 'Plugins', render: 'count', accrual: 'flow', observed: 'metered', description: 'Plugin versions deployed this period' },
|
|
14
|
+
pipelines: { label: 'Pipeline creation', labelPlural: 'Pipelines', render: 'count', accrual: 'flow', observed: 'metered', description: 'Pipelines created this period' },
|
|
15
|
+
// The call budgets. WHICH ONE a request spends is decided by
|
|
16
|
+
// `meteredQuotaTypeFor`, never by the route: a service-account key, a PERSONAL
|
|
17
|
+
// ACCESS key (`token_use: 'api_key'`) or anything minted while holding one
|
|
18
|
+
// (`parentKeyId`) spends `automationCalls` INSTEAD of `apiCalls`.
|
|
19
|
+
apiCalls: { label: 'API call', labelPlural: 'API calls', render: 'count', accrual: 'flow', observed: 'metered', description: 'Requests made by people in browser sessions this period' },
|
|
20
|
+
automationCalls: { label: 'Automation call', labelPlural: 'Automation calls', render: 'count', accrual: 'flow', observed: 'metered', description: 'Requests made with service-account or personal access keys this period' },
|
|
21
|
+
aiCalls: { label: 'AI call', labelPlural: 'AI calls', render: 'count', accrual: 'flow', observed: 'metered', description: 'AI generation invocations this period' },
|
|
22
|
+
// The one flow quota on a period of its OWN — a MONTH, where every other flow
|
|
23
|
+
// rolls on QUOTA_RESET_DAYS (3 days). Rendered as a COUNT and never as a
|
|
24
|
+
// duration: the unit is an abstract credit precisely so it cannot be misread as
|
|
25
|
+
// wall-clock ("11,250 build minutes" reads as 187 hours when it buys 125), and
|
|
26
|
+
// formatting it back into "187h 30m" would undo that. The wall-clock conversion
|
|
27
|
+
// belongs in the pre-build cost hint, where it explains rather than misleads.
|
|
28
|
+
cpuCredits: { label: 'CPU credit', labelPlural: 'CPU credits', render: 'count', accrual: 'flow', observed: 'metered', description: 'vCPU-minutes of build compute used this month', packUnit: { name: 'credits', per: 1 } },
|
|
29
|
+
// A MEASURED total, recomputed on demand rather than counted — the one quota
|
|
30
|
+
// that is neither a period flow nor a create-time count.
|
|
31
|
+
storageBytes: { label: 'Registry storage', labelPlural: 'Registry storage', render: 'bytes', accrual: 'stock', observed: 'measured', description: 'Registry storage currently in use', packUnit: { name: 'GB', per: GB } },
|
|
32
|
+
// Resource-count caps: counted at create time and DECREMENTED on delete, so
|
|
33
|
+
// these really are stocks and "this period" would be wrong.
|
|
34
|
+
dashboards: { label: 'Dashboard', labelPlural: 'Dashboards', render: 'count', accrual: 'stock', observed: 'metered', description: 'Dashboards currently defined' },
|
|
35
|
+
alertRules: { label: 'Alert rule', labelPlural: 'Alert rules', render: 'count', accrual: 'stock', observed: 'metered', description: 'Alert rules currently defined' },
|
|
36
|
+
alertDestinations: { label: 'Alert destination', labelPlural: 'Alert destinations', render: 'count', accrual: 'stock', observed: 'metered', description: 'Alert destinations currently defined' },
|
|
37
|
+
idpConfigs: { label: 'IdP configuration', labelPlural: 'IdP configurations', render: 'count', accrual: 'stock', observed: 'metered', description: 'Identity-provider configurations currently defined' },
|
|
38
|
+
listings: { label: 'Plugin listing', labelPlural: 'Plugin listings', render: 'count', accrual: 'stock', observed: 'metered', description: 'Active plugin listings you publish' },
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Consumables that are NOT quota types but render beside them — same label,
|
|
42
|
+
* same bar, same card.
|
|
43
|
+
*
|
|
44
|
+
* `seats` is the only one: it is a STOCK (how many people may be on the
|
|
45
|
+
* account) rather than a per-period flow, so it is deliberately outside
|
|
46
|
+
* `QuotaType` and is sourced separately from the quota rollup. But the billing
|
|
47
|
+
* usage card draws it in the same list, and before this entry existed that one
|
|
48
|
+
* row carried its own inline "Seats" label while the five beside it came from
|
|
49
|
+
* the catalog — the precise split-fact drift this file exists to remove.
|
|
50
|
+
*
|
|
51
|
+
* Keep this for things rendered AS a consumable. It is not a back door for
|
|
52
|
+
* quota types: a real quota belongs in {@link QUOTA_CATALOG}, where
|
|
53
|
+
* `Record<QuotaType, …>` forces the entry.
|
|
54
|
+
*/
|
|
55
|
+
export const NON_QUOTA_CONSUMABLE_CATALOG = {
|
|
56
|
+
seats: { label: 'Seat', labelPlural: 'Seats', render: 'count', accrual: 'stock', observed: 'measured', description: 'Members this account may have' },
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Every described consumable, widened for lookups by a value that came off the
|
|
60
|
+
* wire.
|
|
61
|
+
*/
|
|
62
|
+
const byKey = {
|
|
63
|
+
...NON_QUOTA_CONSUMABLE_CATALOG,
|
|
64
|
+
...QUOTA_CATALOG,
|
|
65
|
+
};
|
|
66
|
+
export function quotaLabel(type) {
|
|
67
|
+
return byKey[type]?.label ?? type;
|
|
68
|
+
}
|
|
69
|
+
export function quotaLabelPlural(type) {
|
|
70
|
+
return byKey[type]?.labelPlural ?? type;
|
|
71
|
+
}
|
|
72
|
+
export function quotaRender(type) {
|
|
73
|
+
return byKey[type]?.render ?? 'count';
|
|
74
|
+
}
|
|
75
|
+
const BYTE_UNITS = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
76
|
+
/**
|
|
77
|
+
* A byte count as a human amount — `1.5 GB`, `512 MB`, `0 B`.
|
|
78
|
+
*
|
|
79
|
+
* The ONE implementation: the frontend's `formatBytes` delegates here rather
|
|
80
|
+
* than keeping its own copy, so a storage figure reads identically in a quota
|
|
81
|
+
* notice, a 403 explanation and the dashboard. Binary units (1024), one decimal
|
|
82
|
+
* below 10 and rounded above, which is what the dashboard already rendered —
|
|
83
|
+
* this moved the algorithm, it did not change it.
|
|
84
|
+
*/
|
|
85
|
+
export function formatByteAmount(bytes) {
|
|
86
|
+
if (!Number.isFinite(bytes) || bytes < 0)
|
|
87
|
+
return '0 B';
|
|
88
|
+
let value = bytes;
|
|
89
|
+
let i = 0;
|
|
90
|
+
while (value >= 1024 && i < BYTE_UNITS.length - 1) {
|
|
91
|
+
value /= 1024;
|
|
92
|
+
i++;
|
|
93
|
+
}
|
|
94
|
+
return value < 10 ? `${value.toFixed(1)} ${BYTE_UNITS[i]}` : `${Math.round(value)} ${BYTE_UNITS[i]}`;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* A raw quota value as the amount a HUMAN should read, per this type's
|
|
98
|
+
* {@link QuotaRender} — bytes as `25 GB`, counts grouped as `60,000`.
|
|
99
|
+
*
|
|
100
|
+
* For server-side copy (quota notices, audit reasons, 403 explanations), which
|
|
101
|
+
* is why it is here rather than in the frontend: a storage notice that
|
|
102
|
+
* interpolated the raw number would read "used all 26843545600 of its registry
|
|
103
|
+
* storage". The frontend has its own thin wrapper because it renders an
|
|
104
|
+
* unlimited (-1) limit as `∞`, which is a display choice no server copy makes
|
|
105
|
+
* (unlimited orgs are skipped before any copy is generated).
|
|
106
|
+
*/
|
|
107
|
+
export function formatQuotaAmount(type, value) {
|
|
108
|
+
return quotaRender(type) === 'bytes' ? formatByteAmount(value) : value.toLocaleString('en-US');
|
|
109
|
+
}
|
|
110
|
+
export function quotaObservation(type) {
|
|
111
|
+
return byKey[type]?.observed ?? 'metered';
|
|
112
|
+
}
|
|
113
|
+
export function quotaAccrual(type) {
|
|
114
|
+
return byKey[type]?.accrual ?? 'flow';
|
|
115
|
+
}
|
|
116
|
+
export function quotaDescription(type) {
|
|
117
|
+
return byKey[type]?.description ?? '';
|
|
118
|
+
}
|
|
119
|
+
export function quotaPackUnit(type) {
|
|
120
|
+
return byKey[type]?.packUnit ?? null;
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicXVvdGEtY2F0YWxvZy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy90eXBlcy9xdW90YS1jYXRhbG9nLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUE4SnRDLE1BQU0sRUFBRSxHQUFHLElBQUksSUFBSSxDQUFDLENBQUM7QUFFckI7Ozs7R0FJRztBQUNILE1BQU0sQ0FBQyxNQUFNLGFBQWEsR0FBK0M7SUFDdkUsNEVBQTRFO0lBQzVFLDhFQUE4RTtJQUM5RSwrREFBK0Q7SUFDL0QsT0FBTyxFQUFFLEVBQUUsS0FBSyxFQUFFLGNBQWMsRUFBRSxXQUFXLEVBQUUsU0FBUyxFQUFFLE1BQU0sRUFBRSxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxRQUFRLEVBQUUsU0FBUyxFQUFFLFdBQVcsRUFBRSxzQ0FBc0MsRUFBRTtJQUN0SyxTQUFTLEVBQUUsRUFBRSxLQUFLLEVBQUUsbUJBQW1CLEVBQUUsV0FBVyxFQUFFLFdBQVcsRUFBRSxNQUFNLEVBQUUsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLEVBQUUsUUFBUSxFQUFFLFNBQVMsRUFBRSxXQUFXLEVBQUUsK0JBQStCLEVBQUU7SUFDeEssNkRBQTZEO0lBQzdELCtFQUErRTtJQUMvRSwyRUFBMkU7SUFDM0Usa0VBQWtFO0lBQ2xFLFFBQVEsRUFBRSxFQUFFLEtBQUssRUFBRSxVQUFVLEVBQUUsV0FBVyxFQUFFLFdBQVcsRUFBRSxNQUFNLEVBQUUsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLEVBQUUsUUFBUSxFQUFFLFNBQVMsRUFBRSxXQUFXLEVBQUUseURBQXlELEVBQUU7SUFDeEwsZUFBZSxFQUFFLEVBQUUsS0FBSyxFQUFFLGlCQUFpQixFQUFFLFdBQVcsRUFBRSxrQkFBa0IsRUFBRSxNQUFNLEVBQUUsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLEVBQUUsUUFBUSxFQUFFLFNBQVMsRUFBRSxXQUFXLEVBQUUsd0VBQXdF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QyxJQUFJLENBQUMsS0FBSyxPQUFPLENBQUMsQ0FBQyxDQUFDLGdCQUFnQixDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsY0FBYyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0FBQ2pHLENBQUM7QUFTRCxNQUFNLFVBQVUsZ0JBQWdCLENBQUMsSUFBWTtJQUMzQyxPQUFPLEtBQUssQ0FBQyxJQUFJLENBQUMsRUFBRSxRQUFRLElBQUksU0FBUyxDQUFDO0FBQzVDLENBQUM7QUFRRCxNQUFNLFVBQVUsWUFBWSxDQUFDLElBQVk7SUFDdkMsT0FBTyxLQUFLLENBQUMsSUFBSSxDQUFDLEVBQUUsT0FBTyxJQUFJLE1BQU0sQ0FBQztBQUN4QyxDQUFDO0FBS0QsTUFBTSxVQUFVLGdCQUFnQixDQUFDLElBQVk7SUFDM0MsT0FBTyxLQUFLLENBQUMsSUFBSSxDQUFDLEVBQUUsV0FBVyxJQUFJLEVBQUUsQ0FBQztBQUN4QyxDQUFDO0FBS0QsTUFBTSxVQUFVLGFBQWEsQ0FBQyxJQUFZO0lBQ3hDLE9BQU8sS0FBSyxDQUFDLElBQUksQ0FBQyxFQUFFLFFBQVEsSUFBSSxJQUFJLENBQUM7QUFDdkMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG4vKipcbiAqIFRoZSBxdW90YS10eXBlIGRpc3BsYXkgY2F0YWxvZywgYXMgREFUQSBPTkxZIOKAlCB3aGF0IGEgcXVvdGEgaXMgQ0FMTEVEIGFuZCBob3dcbiAqIGl0cyBudW1iZXIgaXMgUkVOREVSRUQsIGZvciBldmVyeSBgUXVvdGFUeXBlYC5cbiAqXG4gKiBEZXBlbmRlbmN5LWZyZWUgYW5kIGVudi1mcmVlIHNvIHRoZSBicm93c2VyIGltcG9ydHMgaXQgZGlyZWN0bHlcbiAqIChgQHBpcGVsaW5lLWJ1aWxkZXIvYXBpLWNvcmUvcXVvdGEtY2F0YWxvZ2ApLiBJdHMgb25seSBpbXBvcnQgaXMgYSB0eXBlLCB3aGljaFxuICogaXMgZXJhc2VkIGF0IGNvbXBpbGUgdGltZSwgc28gbm90aGluZyBvZiB0aGUgc2VydmVyIGdyYXBoIHJlYWNoZXMgdGhlIGJ1bmRsZS5cbiAqXG4gKiBXSFkgSVQgRVhJU1RTLiBUaGUgc2FtZSBmYWN0cyB3ZXJlIHNwZWxsZWQgaW4gc2l4IHBsYWNlczogdGhpcyBwYWNrYWdlJ3NcbiAqIGBRVU9UQV9UWVBFX0xBQkVMU2AsIGFuZCBmaXZlIGluZGVwZW5kZW50IG1hcHMgaW4gdGhlIGZyb250ZW5kIOKAlCB0aGUgcHJlc3N1cmVcbiAqIGJhbm5lcidzIGxhYmVscywgdGhlIHF1b3RhcyBwYWdlJ3MgYFFVT1RBX01FVEFgLCB0aGUgb3JnIGhvbWUncyBgUVVPVEFfTEFCRUxTYCxcbiAqIHRoZSBiaWxsaW5nIHVzYWdlIGNhcmQncyBgUVVPVEFfTEFCRUxTYCAod2l0aCBpdHMgb3duIGJ5dGUgZmxhZyksIGFuZCB0aGVcbiAqIGFkZC1vbiBncmlkJ3MgYFBBQ0tfVU5JVFNgLiBFYWNoIGtuZXcgYSBzdWJzZXQsIGVhY2ggY291bGQgZHJpZnQsIGFuZCBhZGRpbmcgYVxuICogcXVvdGEgdHlwZSBtZWFudCBmaW5kaW5nIGFsbCBzaXguIGBQQUNLX1VOSVRTYCBoYWQgZXhhY3RseSBvbmUgbWVtYmVyLCBzbyB0aGVcbiAqIGFkZC1vbiBncmlkIHJlbmRlcmVkIGEgR0Igc3RlcCBmb3Igc3RvcmFnZSBhbmQgYSBiYXJlIHBhY2sgY291bnQgZm9yXG4gKiBldmVyeXRoaW5nIGVsc2Ug4oCUIG5vdCBhIGRlY2lzaW9uIGFueW9uZSByZXZpc2l0ZWQsIGp1c3QgdGhlIG9uZSBlbnRyeSBzb21lYm9keVxuICogbmVlZGVkIGF0IHRoZSB0aW1lLlxuICpcbiAqIEFkZCBhIHF1b3RhIHR5cGUgaGVyZSBhbmQgZXZlcnkgc3VyZmFjZSByZW5kZXJzIGl0OiBgUmVjb3JkPFF1b3RhVHlwZSwg4oCmPmBcbiAqIG1ha2VzIHRoZSBjb21waWxlciBkZW1hbmQgdGhlIGVudHJ5LCBzbyBub25lIG9mIHRoZSBzaXggY2FuIGJlIGZvcmdvdHRlblxuICogYWdhaW4uXG4gKlxuICogRVZFUlkgZGlzcGxheSBmYWN0IGxpdmVzIGhlcmUsIGluY2x1ZGluZyB0aGUgcGx1cmFsIGxhYmVsLiBBIGZpcnN0IGRyYWZ0IGtlcHRcbiAqIHBsdXJhbHMgaW4gYSBmcm9udGVuZCBoZWxwZXIgd2l0aCBhIGBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+YCBhbmQgYSBmYWxsYmFjayB0b1xuICogdGhlIHNpbmd1bGFyIOKAlCB3aGljaCB3b3VsZCBoYXZlIHJlbmRlcmVkIGEgbmV3IHF1b3RhIHR5cGUncyBwbHVyYWwgd3JvbmdseSBhbmRcbiAqIHNpbGVudGx5LCByZXByb2R1Y2luZyB0aGUgZXhhY3QgcGFydGlhbC1tYXAgdHJhcCB0aGlzIGZpbGUgcmVtb3Zlcy4gQSBmYWN0XG4gKiBzcGxpdCBhY3Jvc3MgdHdvIHBhY2thZ2VzIGlzIGEgZmFjdCB0aGF0IGRyaWZ0cy5cbiAqL1xuXG5pbXBvcnQgdHlwZSB7IFF1b3RhVHlwZSB9IGZyb20gJy4vY29tbW9uLmpzJztcblxuLyoqIEhvdyBhIHF1b3RhJ3MgdXNhZ2UgaXMgbGVhcm5lZDogaW5jcmVtZW50ZWQgYXMgaXQgaGFwcGVucywgb3IgcmVhZCBvbiBkZW1hbmQuICovXG5leHBvcnQgdHlwZSBRdW90YU9ic2VydmF0aW9uID1cbiAgLyoqIFRoZSBwbGF0Zm9ybSBpbmNyZW1lbnRzIGEgY291bnRlciBhcyB0aGUgd29yayBoYXBwZW5zLiAqL1xuICB8ICdtZXRlcmVkJ1xuICAvKiogTm90aGluZyBpbmNyZW1lbnRzOiB0aGUgZmlndXJlIGlzIHJlYWQgZnJvbSB0aGUgc3lzdGVtIG9mIHJlY29yZC4gKi9cbiAgfCAnbWVhc3VyZWQnO1xuXG4vKiogV2hldGhlciBhIHF1b3RhIHJlZmlsbHMgb24gdGhlIHBlcmlvZCBvciBwZXJzaXN0cyB1bnRpbCBzb21ldGhpbmcgY2hhbmdlcy4gKi9cbmV4cG9ydCB0eXBlIFF1b3RhQWNjcnVhbCA9XG4gIC8qKiBBIHBlci1wZXJpb2QgcmF0ZTogcmVzZXJ2ZWQgb24gdXNlLCB6ZXJvZWQgYXQgdGhlIHBlcmlvZCBib3VuZGFyeS4gKi9cbiAgfCAnZmxvdydcbiAgLyoqIEEgbGV2ZWwgdGhhdCBzdGF5cyB1bnRpbCB0aGUgdGVuYW50IGZyZWVzIGl0LiBObyByZXNldCwgbm8gZm9yZWNhc3QuICovXG4gIHwgJ3N0b2NrJztcblxuLyoqIEhvdyBhIHJhdyBxdW90YSBudW1iZXIgc2hvdWxkIGJlIHJlbmRlcmVkLiAqL1xuZXhwb3J0IHR5cGUgUXVvdGFSZW5kZXIgPVxuICAvKiogQSBwbGFpbiBpbnRlZ2VyIOKAlCBcIjQgLyAxMFwiLiAqL1xuICB8ICdjb3VudCdcbiAgLyoqIEEgYnl0ZSBjb3VudCDigJQgcmVuZGVyIHdpdGggYGZvcm1hdEJ5dGVzYCAoXCIxOC40IEdCXCIpLCBuZXZlciByYXcuICovXG4gIHwgJ2J5dGVzJztcblxuLyoqIFdoYXQgYSBzdXJmYWNlIG5lZWRzIHRvIGtub3cgdG8gc2hvdyBvbmUgcXVvdGEgdHlwZS4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUXVvdGFUeXBlTWV0YSB7XG4gIC8qKlxuICAgKiBTSU5HVUxBUiBodW1hbiBsYWJlbCDigJQgdGhlIG9uZSBzcGVsbGluZyBldmVyeSA0MjkgYm9keSwgbm90aWZpY2F0aW9uIGFuZFxuICAgKiBkYXNoYm9hcmQgdXNlcyAoXCJBUEkgY2FsbCBxdW90YSByZWFjaGVkIOKAplwiKS5cbiAgICovXG4gIGxhYmVsOiBzdHJpbmc7XG4gIC8qKlxuICAgKiBQTFVSQUwgbGFiZWwsIGZvciBzdXJmYWNlcyB0aGF0IGxpc3QgZGltZW5zaW9ucyByYXRoZXIgdGhhbiBhZGRyZXNzIG9uZVxuICAgKiAoXCJBUEkgY2FsbHNcIiBvbiBhIHVzYWdlIGNhcmQsIGFnYWluc3QgXCJBUEkgY2FsbCBxdW90YSByZWFjaGVkXCIgaW4gYSA0MjkpLlxuICAgKlxuICAgKiBSRVFVSVJFRCwgYW5kIGhlcmUgcmF0aGVyIHRoYW4gaW4gdGhlIGZyb250ZW5kLCBiZWNhdXNlIHRoZSBmaXJzdCBkcmFmdCBwdXRcbiAgICogaXQgaW4gYSBgUmVjb3JkPHN0cmluZywgc3RyaW5nPmAgaGVscGVyIHRoYXQgZmVsbCBiYWNrIHRvIHRoZSBzaW5ndWxhciB3aGVuXG4gICAqIGEgdHlwZSB3YXMgbWlzc2luZyDigJQgc28gYSBuZXcgcXVvdGEgdHlwZSB3b3VsZCBoYXZlIHJlbmRlcmVkIFwiQnVpbGQgbWludXRlXCJcbiAgICogaW4gcGx1cmFsIGNvbnRleHRzLCB3cm9uZ2x5IGFuZCBzaWxlbnRseS4gVGhhdCBpcyB0aGUgc2FtZSBwYXJ0aWFsLW1hcCB0cmFwXG4gICAqIHRoaXMgY2F0YWxvZyBleGlzdHMgdG8gcmVtb3ZlLCBhbmQgYFJlY29yZDxRdW90YVR5cGUsIOKApj5gIGNsb3NlcyBpdCBvbmx5IGlmXG4gICAqIGV2ZXJ5IGRpc3BsYXkgZmFjdCBsaXZlcyBoZXJlLlxuICAgKi9cbiAgbGFiZWxQbHVyYWw6IHN0cmluZztcbiAgLyoqIEhvdyB0byByZW5kZXIgYSByYXcgdmFsdWUgZm9yIHRoaXMgdHlwZS4gKi9cbiAgcmVuZGVyOiBRdW90YVJlbmRlcjtcbiAgLyoqXG4gICAqIFdoZXRoZXIgdGhpcyBxdW90YSBSRUZJTExTIG9uIHRoZSBwZXJpb2QsIG9yIHBlcnNpc3RzIHVudGlsIHRoZSB0ZW5hbnRcbiAgICogY2hhbmdlcyBzb21ldGhpbmcuXG4gICAqXG4gICAqIFRoaXMgaXMgdGhlIHNpbmdsZSBtb3N0IGxvYWQtYmVhcmluZyBzZW1hbnRpYyBkaWZmZXJlbmNlIGJldHdlZW4gcXVvdGFcbiAgICogdHlwZXMgYW5kIG5vdGhpbmcgY291bGQgcmVhZCBpdCBiZWZvcmU6IGEgc3VyZmFjZSBoYWQgdG8ga25vdywgYnkgaGFuZCxcbiAgICogdGhhdCBcInJlc2V0cyBpbiAyIGRheXNcIiBpcyB0cnVlIG9mIGBhcGlDYWxsc2AgYW5kIGZhbHNlIG9mIGBzdG9yYWdlQnl0ZXNgLlxuICAgKiBHZXR0aW5nIGl0IHdyb25nIGlzIG5vdCBjb3NtZXRpYyDigJQgdGhlIHF1b3RhcyBwYWdlIHRvbGQgYW4gb3JnYW5pemF0aW9uIGl0c1xuICAgKiByZWdpc3RyeSBzdG9yYWdlIHdvdWxkIHJlc2V0LCB3aGljaCBpcyB0aGUgb3Bwb3NpdGUgb2YgdGhlIG9uZSB0aGluZyBpdFxuICAgKiBuZWVkcyB0byB1bmRlcnN0YW5kIGFib3V0IHN0b3JhZ2UuXG4gICAqXG4gICAqIC0gYGZsb3dgIOKAlCBhIHBlci1wZXJpb2QgcmF0ZSwgcmVzZXJ2ZWQgb24gY3JlYXRlL2J1aWxkIGFuZCB6ZXJvZWQgYXQgdGhlXG4gICAqICAgYm91bmRhcnkuIEhpdHRpbmcgaXQgaXMgdGVtcG9yYXJ5IGFuZCB0aGUgYWNjb3VudCBzZWxmLWhlYWxzLiBUaGUgcGVyaW9kIGlzXG4gICAqICAgUEVSIFRZUEUgcmF0aGVyIHRoYW4gZ2xvYmFsOiBgUVVPVEFfUkVTRVRfREFZU2AgKDMgZGF5cykgZm9yIGV2ZXJ5IGZsb3cgYnV0XG4gICAqICAgYGNwdUNyZWRpdHNgLCB3aGljaCBpcyBtb250aGx5LiBSZWFkIGl0IHRocm91Z2ggYHF1b3RhUGVyaW9kRGF5c2AgYW5kIG5ldmVyXG4gICAqICAgYXNzdW1lIHRoZSBnbG9iYWwg4oCUIGEgZm9yZWNhc3Qgb3IgYSBcInJlc2V0cyBpbiBOIGRheXNcIiBsaW5lIGNvbXB1dGVkXG4gICAqICAgYWdhaW5zdCB0aGUgd3Jvbmcgd2luZG93IGlzIHdyb25nIGJ5IGEgZmFjdG9yIG9mIHRlbiBmb3IgY3JlZGl0cy5cbiAgICogLSBgc3RvY2tgIOKAlCBhIGxldmVsLCBub3QgYSByYXRlLiBJdCBzdGF5cyB3aGVyZSBpdCBpcyB1bnRpbCBieXRlcyBhcmUgZnJlZWRcbiAgICogICBvciByb3dzIGFyZSBkZWxldGVkLCBzbyBhIHJlc2V0IGRhdGUsIGEgXCJyZXNldHMgaW4gTiBkYXlzXCIgbGluZSBhbmQgYVxuICAgKiAgIGJ1cm4tcmF0ZSBmb3JlY2FzdCBhcmUgYWxsIG1lYW5pbmdsZXNzIGZvciBpdC4gYHN0b3JhZ2VCeXRlc2AgaXMgbWVhc3VyZWQ7XG4gICAqICAgdGhlIHJlc291cmNlLWNvdW50IGNhcHMgYXJlIGNvdW50ZWQgYXQgY3JlYXRlIHRpbWUgYW5kIGRlY3JlbWVudGVkIG9uXG4gICAqICAgZGVsZXRlOyBgbGlzdGluZ3NgIGNvdW50cyBhY3RpdmUgcHVibGljYXRpb25zLlxuICAgKlxuICAgKiBUaGUge0BsaW5rIFF1b3RhVHlwZU1ldGEuZGVzY3JpcHRpb259IHdvcmRpbmcgZm9sbG93cyBmcm9tIHRoaXMgKFwidGhpc1xuICAgKiBwZXJpb2RcIiBhZ2FpbnN0IFwiY3VycmVudGx5XCIpLCBhbmQgYSB0ZXN0IGhvbGRzIHRoZSB0d28gaW4gYWdyZWVtZW50LlxuICAgKi9cbiAgYWNjcnVhbDogUXVvdGFBY2NydWFsO1xuICAvKipcbiAgICogSG93IHVzYWdlIGlzIE9CU0VSVkVEIOKAlCB3aGljaCBpcyBhIGRpZmZlcmVudCBxdWVzdGlvbiBmcm9tIHdoZXRoZXIgdGhlXG4gICAqIHF1b3RhIHJlZmlsbHMsIGFuZCB0aGUgdHdvIGRvIG5vdCBsaW5lIHVwLlxuICAgKlxuICAgKiAtIGBtZXRlcmVkYCDigJQgdGhlIHBsYXRmb3JtIElOQ1JFTUVOVFMgYSBjb3VudGVyIGFzIHRoZSB3b3JrIGhhcHBlbnMuIEV2ZXJ5XG4gICAqICAgcXVvdGEgYnV0IG9uZSwgaW5jbHVkaW5nIHRoZSBjcmVhdGUtdGltZSBjb3VudHMgdGhhdCBhcmUgc3RvY2tzXG4gICAqICAgKGBkYXNoYm9hcmRzYCwgYGxpc3RpbmdzYCwg4oCmKS5cbiAgICogLSBgbWVhc3VyZWRgIOKAlCBub3RoaW5nIGluY3JlbWVudHMgYW55dGhpbmc7IHRoZSBmaWd1cmUgaXMgUkVBRCBmcm9tIHRoZVxuICAgKiAgIHN5c3RlbSBvZiByZWNvcmQgb24gZGVtYW5kLiBgc3RvcmFnZUJ5dGVzYCBvbmx5OiB0aGUgcmVnaXN0cnkgd2Fsa3MgaXRzXG4gICAqICAgb3duIGNhdGFsb2csIHdoaWNoIGlzIHdoeSBpdCBpcyBcInRoZSBvbmUgcXVvdGEgbm90aGluZyBpbmNyZW1lbnRzXCIuXG4gICAqXG4gICAqIEl0IGRlY2lkZXMgd2hpY2ggY29kZSBwYXRoIG1heSBsZWdpdGltYXRlbHkgbm90aWZ5IGFib3V0IGEgcXVvdGEuIEEgc3RvcmFnZVxuICAgKiBub3RpY2UgYXJyaXZpbmcgZnJvbSB0aGUgaW5jcmVtZW50IHBhdGggY291bGQgb25seSBiZSBhIGJ1ZywgYW5kIGFcbiAgICogYGxpc3RpbmdzYCBub3RpY2UgYXJyaXZpbmcgZnJvbSBhIHN3ZWVwIGxpa2V3aXNlIOKAlCBjb25mbGF0aW5nIHRoaXMgd2l0aFxuICAgKiB7QGxpbmsgYWNjcnVhbH0gd291bGQgaGF2ZSBzaWxlbmNlZCBldmVyeSBjcmVhdGUtdGltZSBjb3VudCBxdW90YSwgYmVjYXVzZVxuICAgKiB0aG9zZSBhcmUgc3RvY2tzIHRoYXQgYXJlIG5vbmV0aGVsZXNzIG1ldGVyZWQuXG4gICAqL1xuICBvYnNlcnZlZDogUXVvdGFPYnNlcnZhdGlvbjtcbiAgLyoqXG4gICAqIFdoYXQgdGhpcyBjb3VudGVyIGFjdHVhbGx5IGNvdW50cywgaW4gb25lIHNob3J0IHBocmFzZSDigJQgdGhlIGFuc3dlciB0byBcImFtIElcbiAgICogc3BlbmRpbmcgdGhpcyBvbmU/XCIuXG4gICAqXG4gICAqIFJFUVVJUkVELCBhbmQgYSBkaXNwbGF5IEZBQ1QgcmF0aGVyIHRoYW4gcGFnZSBjb3B5LCB3aGljaCBpcyB0aGUgY2FsbCBhXG4gICAqIGZpcnN0IHBhc3MgZ290IHdyb25nIGJ5IGp1ZGdpbmcgdGhlIGZpZWxkJ3MgbmFtZSBpbnN0ZWFkIG9mIGl0cyBjb250ZW50LiBJdFxuICAgKiBoYWQgYmVlbiBsZWZ0IGluIGEgZnJvbnRlbmQgYFJlY29yZDxEaXNwbGF5ZWRRdW90YVR5cGUsIHN0cmluZz5gIG9uIHRoZVxuICAgKiByZWFzb25pbmcgdGhhdCBkZXNjcmlwdGlvbnMgYXJlIGEgcGFnZSdzIG93biB2b2ljZS4gVGhleSBhcmUgbm90OiBlYWNoIG9mXG4gICAqIHRoZXNlIGFuc3dlcnMgYSBxdWVzdGlvbiB3aG9zZSBhbnN3ZXIgaXMgdGhlIHNhbWUgb24gZXZlcnkgc3VyZmFjZSwgYW5kIHR3b1xuICAgKiBzdXJmYWNlcyBkaXNhZ3JlZWluZyBpcyBhIGJ1Zywgbm90IGEgZGlmZmVyZW5jZSBpbiB0b25lLiBUd28gb2YgdGhlIGZpdmVcbiAgICogbG9jYWwgY29waWVzIGhhZCBhbHJlYWR5IGRyaWZ0ZWQg4oCUIGBhdXRvbWF0aW9uQ2FsbHNgIG9taXR0ZWQgcGVyc29uYWxcbiAgICogYWNjZXNzIGtleXMgKHNvIGEgUEFUIGhvbGRlciByZWFkIHRoZSBwYWdlIGFuZCBjb25jbHVkZWQgdGhleSB3ZXJlIHNwZW5kaW5nXG4gICAqIGBhcGlDYWxsc2ApLCBhbmQgYHBsdWdpbnNgL2BwaXBlbGluZXNgIHdlcmUgcGhyYXNlZCBhcyBzdG9ja3MuXG4gICAqXG4gICAqIFRoZSB0ZXN0IGlzIHdoZXRoZXIgYGRvY3MvcXVvdGFzLm1kYCBhbmQgdGhpcyBzdHJpbmcgY2FuIGxlZ2l0aW1hdGVseVxuICAgKiBkaWZmZXIuIEZvciB0aGVzZSwgbm8uIEZvciBhIHRpZXIncyBwb3NpdGlvbmluZyBsaW5lIChcIlN0YXJ0ZXIgdGllclwiKSBvciB0aGVcbiAgICogcXVvdGFzIHBhZ2UncyBwb29saW5nIGV4cGxhbmF0aW9uLCB5ZXMg4oCUIHRob3NlIHN0YXkgaW4gdGhlIHBhZ2UsIGV4YWN0bHkgYXNcbiAgICogYFRJRVJfTUVUQWAgYWxyZWFkeSBzcGxpdHMgYSB0aWVyJ3MgbGFiZWwgZnJvbSBpdHMgc2FsZXMgY29weS5cbiAgICpcbiAgICogUGhyYXNpbmcgZm9sbG93cyB0aGUgU0VNQU5USUNTIGluIHtAbGluayBRdW90YVR5cGV9J3Mgb3duIGRvY3VtZW50YXRpb24sIG5vdFxuICAgKiBhIGhvdXNlIHN0eWxlOiBcInRoaXMgcGVyaW9kXCIgZm9yIHRoZSBwZXItcGVyaW9kIGZsb3dzLCBcImN1cnJlbnRseVwiIGZvciB0aGVcbiAgICogY3JlYXRlLXRpbWUgY291bnRzIHRoYXQgZGVjcmVtZW50IG9uIGRlbGV0ZSwgYW5kIG5laXRoZXIgZm9yIHRoZSBtZWFzdXJlZFxuICAgKiBzdG9yYWdlIHRvdGFsLlxuICAgKi9cbiAgZGVzY3JpcHRpb246IHN0cmluZztcbiAgLyoqXG4gICAqIFRoZSB1bml0IGFuIEFERC1PTiBQQUNLIG9mIHRoaXMgcXVvdGEgaXMgdHlwZWQgaW4sIGFuZCB0aGUgZGl2aXNvciBmcm9tIHRoZVxuICAgKiByYXcgZ3JhbnQgdG8gdGhhdCB1bml0IOKAlCBzbyBhICsxMCBHQiBzdG9yYWdlIHBhY2sgb2ZmZXJzIGEgR0Igc3RlcCByYXRoZXJcbiAgICogdGhhbiBhIHBhY2sgY291bnQuXG4gICAqXG4gICAqIEFic2VudCBtZWFucyBcInR5cGUgcGFjayBjb3VudHNcIiwgd2hpY2ggaXMgcmlnaHQgd2hlcmUgdGhlIHF1b3RhIGhhcyBub1xuICAgKiBuYXR1cmFsIHVuaXQ6IFwiMSBwYWNrIG9mIEFQSSBjYWxsc1wiIHJlYWRzIGJldHRlciB0aGFuIFwiMjUwMDAgY2FsbHNcIi4gVGhlXG4gICAqIHN0ZXAgaXRzZWxmIGlzIGFsd2F5cyBkZXJpdmVkIGZyb20gdGhlIGJ1bmRsZSdzIG93biBgZ3JhbnRzYCwgbmV2ZXIgZnJvbSBhXG4gICAqIGNvbnN0YW50IGhlcmUsIGJlY2F1c2UgYSBncmFudCBpcyBlbnYtb3ZlcnJpZGFibGUuXG4gICAqL1xuICBwYWNrVW5pdD86IHsgbmFtZTogc3RyaW5nOyBwZXI6IG51bWJlciB9O1xufVxuXG5jb25zdCBHQiA9IDEwMjQgKiogMztcblxuLyoqXG4gKiBEaXNwbGF5IG1ldGFkYXRhIGZvciBldmVyeSBxdW90YSB0eXBlLiBMYWJlbHMgYXJlIHRoZSBjYW5vbmljYWwgb25lcyDigJQgdGhlXG4gKiBzdHJpbmdzIGhlcmUgYXJlIHVzZXItZmFjaW5nIGFuZCBhcmUgcmVsaWVkIG9uIGJ5IDQyOSBib2RpZXMgYW5kIG5vdGljZXMsIHNvXG4gKiB0aGV5IGNoYW5nZSBvbmx5IGRlbGliZXJhdGVseS5cbiAqL1xuZXhwb3J0IGNvbnN0IFFVT1RBX0NBVEFMT0c6IFJlYWRvbmx5PFJlY29yZDxRdW90YVR5cGUsIFF1b3RhVHlwZU1ldGE+PiA9IHtcbiAgLy8gUGVyLXBlcmlvZCBGTE9XUyDigJQgYSBjcmVhdGlvbiByYXRlLCBub3QgYSBzdG9jay4gRGVsZXRpbmcgdGhlIGVudGl0eSBkb2VzXG4gIC8vIG5vdCBnaXZlIHRoZSBzbG90IGJhY2sgKGBkZWNyZW1lbnQtb24tZGVsZXRlYCB3YXMgY29uc2lkZXJlZCBhbmQgcmVqZWN0ZWQpLFxuICAvLyBzbyB0aGUgd29yZGluZyBzYXlzIFwidGhpcyBwZXJpb2RcIiB0byBzdG9wIHRoZSBzdG9jayByZWFkaW5nLlxuICBwbHVnaW5zOiB7IGxhYmVsOiAnUGx1Z2luIGJ1aWxkJywgbGFiZWxQbHVyYWw6ICdQbHVnaW5zJywgcmVuZGVyOiAnY291bnQnLCBhY2NydWFsOiAnZmxvdycsIG9ic2VydmVkOiAnbWV0ZXJlZCcsIGRlc2NyaXB0aW9uOiAnUGx1Z2luIHZlcnNpb25zIGRlcGxveWVkIHRoaXMgcGVyaW9kJyB9LFxuICBwaXBlbGluZXM6IHsgbGFiZWw6ICdQaXBlbGluZSBjcmVhdGlvbicsIGxhYmVsUGx1cmFsOiAnUGlwZWxpbmVzJywgcmVuZGVyOiAnY291bnQnLCBhY2NydWFsOiAnZmxvdycsIG9ic2VydmVkOiAnbWV0ZXJlZCcsIGRlc2NyaXB0aW9uOiAnUGlwZWxpbmVzIGNyZWF0ZWQgdGhpcyBwZXJpb2QnIH0sXG4gIC8vIFRoZSBjYWxsIGJ1ZGdldHMuIFdISUNIIE9ORSBhIHJlcXVlc3Qgc3BlbmRzIGlzIGRlY2lkZWQgYnlcbiAgLy8gYG1ldGVyZWRRdW90YVR5cGVGb3JgLCBuZXZlciBieSB0aGUgcm91dGU6IGEgc2VydmljZS1hY2NvdW50IGtleSwgYSBQRVJTT05BTFxuICAvLyBBQ0NFU1Mga2V5IChgdG9rZW5fdXNlOiAnYXBpX2tleSdgKSBvciBhbnl0aGluZyBtaW50ZWQgd2hpbGUgaG9sZGluZyBvbmVcbiAgLy8gKGBwYXJlbnRLZXlJZGApIHNwZW5kcyBgYXV0b21hdGlvbkNhbGxzYCBJTlNURUFEIG9mIGBhcGlDYWxsc2AuXG4gIGFwaUNhbGxzOiB7IGxhYmVsOiAnQVBJIGNhbGwnLCBsYWJlbFBsdXJhbDogJ0FQSSBjYWxscycsIHJlbmRlcjogJ2NvdW50JywgYWNjcnVhbDogJ2Zsb3cnLCBvYnNlcnZlZDogJ21ldGVyZWQnLCBkZXNjcmlwdGlvbjogJ1JlcXVlc3RzIG1hZGUgYnkgcGVvcGxlIGluIGJyb3dzZXIgc2Vzc2lvbnMgdGhpcyBwZXJpb2QnIH0sXG4gIGF1dG9tYXRpb25DYWxsczogeyBsYWJlbDogJ0F1dG9tYXRpb24gY2FsbCcsIGxhYmVsUGx1cmFsOiAnQXV0b21hdGlvbiBjYWxscycsIHJlbmRlcjogJ2NvdW50JywgYWNjcnVhbDogJ2Zsb3cnLCBvYnNlcnZlZDogJ21ldGVyZWQnLCBkZXNjcmlwdGlvbjogJ1JlcXVlc3RzIG1hZGUgd2l0aCBzZXJ2aWNlLWFjY291bnQgb3IgcGVyc29uYWwgYWNjZXNzIGtleXMgdGhpcyBwZXJpb2QnIH0sXG4gIGFpQ2FsbHM6IHsgbGFiZWw6ICdBSSBjYWxsJywgbGFiZWxQbHVyYWw6ICdBSSBjYWxscycsIHJlbmRlcjogJ2NvdW50JywgYWNjcnVhbDogJ2Zsb3cnLCBvYnNlcnZlZDogJ21ldGVyZWQnLCBkZXNjcmlwdGlvbjogJ0FJIGdlbmVyYXRpb24gaW52b2NhdGlvbnMgdGhpcyBwZXJpb2QnIH0sXG4gIC8vIFRoZSBvbmUgZmxvdyBxdW90YSBvbiBhIHBlcmlvZCBvZiBpdHMgT1dOIOKAlCBhIE1PTlRILCB3aGVyZSBldmVyeSBvdGhlciBmbG93XG4gIC8vIHJvbGxzIG9uIFFVT1RBX1JFU0VUX0RBWVMgKDMgZGF5cykuIFJlbmRlcmVkIGFzIGEgQ09VTlQgYW5kIG5ldmVyIGFzIGFcbiAgLy8gZHVyYXRpb246IHRoZSB1bml0IGlzIGFuIGFic3RyYWN0IGNyZWRpdCBwcmVjaXNlbHkgc28gaXQgY2Fubm90IGJlIG1pc3JlYWQgYXNcbiAgLy8gd2FsbC1jbG9jayAoXCIxMSwyNTAgYnVpbGQgbWludXRlc1wiIHJlYWRzIGFzIDE4NyBob3VycyB3aGVuIGl0IGJ1eXMgMTI1KSwgYW5kXG4gIC8vIGZvcm1hdHRpbmcgaXQgYmFjayBpbnRvIFwiMTg3aCAzMG1cIiB3b3VsZCB1bmRvIHRoYXQuIFRoZSB3YWxsLWNsb2NrIGNvbnZlcnNpb25cbiAgLy8gYmVsb25ncyBpbiB0aGUgcHJlLWJ1aWxkIGNvc3QgaGludCwgd2hlcmUgaXQgZXhwbGFpbnMgcmF0aGVyIHRoYW4gbWlzbGVhZHMuXG4gIGNwdUNyZWRpdHM6IHsgbGFiZWw6ICdDUFUgY3JlZGl0JywgbGFiZWxQbHVyYWw6ICdDUFUgY3JlZGl0cycsIHJlbmRlcjogJ2NvdW50JywgYWNjcnVhbDogJ2Zsb3cnLCBvYnNlcnZlZDogJ21ldGVyZWQnLCBkZXNjcmlwdGlvbjogJ3ZDUFUtbWludXRlcyBvZiBidWlsZCBjb21wdXRlIHVzZWQgdGhpcyBtb250aCcsIHBhY2tVbml0OiB7IG5hbWU6ICdjcmVkaXRzJywgcGVyOiAxIH0gfSxcbiAgLy8gQSBNRUFTVVJFRCB0b3RhbCwgcmVjb21wdXRlZCBvbiBkZW1hbmQgcmF0aGVyIHRoYW4gY291bnRlZCDigJQgdGhlIG9uZSBxdW90YVxuICAvLyB0aGF0IGlzIG5laXRoZXIgYSBwZXJpb2QgZmxvdyBub3IgYSBjcmVhdGUtdGltZSBjb3VudC5cbiAgc3RvcmFnZUJ5dGVzOiB7IGxhYmVsOiAnUmVnaXN0cnkgc3RvcmFnZScsIGxhYmVsUGx1cmFsOiAnUmVnaXN0cnkgc3RvcmFnZScsIHJlbmRlcjogJ2J5dGVzJywgYWNjcnVhbDogJ3N0b2NrJywgb2JzZXJ2ZWQ6ICdtZWFzdXJlZCcsIGRlc2NyaXB0aW9uOiAnUmVnaXN0cnkgc3RvcmFnZSBjdXJyZW50bHkgaW4gdXNlJywgcGFja1VuaXQ6IHsgbmFtZTogJ0dCJywgcGVyOiBHQiB9IH0sXG4gIC8vIFJlc291cmNlLWNvdW50IGNhcHM6IGNvdW50ZWQgYXQgY3JlYXRlIHRpbWUgYW5kIERFQ1JFTUVOVEVEIG9uIGRlbGV0ZSwgc29cbiAgLy8gdGhlc2UgcmVhbGx5IGFyZSBzdG9ja3MgYW5kIFwidGhpcyBwZXJpb2RcIiB3b3VsZCBiZSB3cm9uZy5cbiAgZGFzaGJvYXJkczogeyBsYWJlbDogJ0Rhc2hib2FyZCcsIGxhYmVsUGx1cmFsOiAnRGFzaGJvYXJkcycsIHJlbmRlcjogJ2NvdW50JywgYWNjcnVhbDogJ3N0b2NrJywgb2JzZXJ2ZWQ6ICdtZXRlcmVkJywgZGVzY3JpcHRpb246ICdEYXNoYm9hcmRzIGN1cnJlbnRseSBkZWZpbmVkJyB9LFxuICBhbGVydFJ1bGVzOiB7IGxhYmVsOiAnQWxlcnQgcnVsZScsIGxhYmVsUGx1cmFsOiAnQWxlcnQgcnVsZXMnLCByZW5kZXI6ICdjb3VudCcsIGFjY3J1YWw6ICdzdG9jaycsIG9ic2VydmVkOiAnbWV0ZXJlZCcsIGRlc2NyaXB0aW9uOiAnQWxlcnQgcnVsZXMgY3VycmVudGx5IGRlZmluZWQnIH0sXG4gIGFsZXJ0RGVzdGluYXRpb25zOiB7IGxhYmVsOiAnQWxlcnQgZGVzdGluYXRpb24nLCBsYWJlbFBsdXJhbDogJ0FsZXJ0IGRlc3RpbmF0aW9ucycsIHJlbmRlcjogJ2NvdW50JywgYWNjcnVhbDogJ3N0b2NrJywgb2JzZXJ2ZWQ6ICdtZXRlcmVkJywgZGVzY3JpcHRpb246ICdBbGVydCBkZXN0aW5hdGlvbnMgY3VycmVudGx5IGRlZmluZWQnIH0sXG4gIGlkcENvbmZpZ3M6IHsgbGFiZWw6ICdJZFAgY29uZmlndXJhdGlvbicsIGxhYmVsUGx1cmFsOiAnSWRQIGNvbmZpZ3VyYXRpb25zJywgcmVuZGVyOiAnY291bnQnLCBhY2NydWFsOiAnc3RvY2snLCBvYnNlcnZlZDogJ21ldGVyZWQnLCBkZXNjcmlwdGlvbjogJ0lkZW50aXR5LXByb3ZpZGVyIGNvbmZpZ3VyYXRpb25zIGN1cnJlbnRseSBkZWZpbmVkJyB9LFxuICBsaXN0aW5nczogeyBsYWJlbDogJ1BsdWdpbiBsaXN0aW5nJywgbGFiZWxQbHVyYWw6ICdQbHVnaW4gbGlzdGluZ3MnLCByZW5kZXI6ICdjb3VudCcsIGFjY3J1YWw6ICdzdG9jaycsIG9ic2VydmVkOiAnbWV0ZXJlZCcsIGRlc2NyaXB0aW9uOiAnQWN0aXZlIHBsdWdpbiBsaXN0aW5ncyB5b3UgcHVibGlzaCcgfSxcbn07XG5cbi8qKlxuICogQ29uc3VtYWJsZXMgdGhhdCBhcmUgTk9UIHF1b3RhIHR5cGVzIGJ1dCByZW5kZXIgYmVzaWRlIHRoZW0g4oCUIHNhbWUgbGFiZWwsXG4gKiBzYW1lIGJhciwgc2FtZSBjYXJkLlxuICpcbiAqIGBzZWF0c2AgaXMgdGhlIG9ubHkgb25lOiBpdCBpcyBhIFNUT0NLIChob3cgbWFueSBwZW9wbGUgbWF5IGJlIG9uIHRoZVxuICogYWNjb3VudCkgcmF0aGVyIHRoYW4gYSBwZXItcGVyaW9kIGZsb3csIHNvIGl0IGlzIGRlbGliZXJhdGVseSBvdXRzaWRlXG4gKiBgUXVvdGFUeXBlYCBhbmQgaXMgc291cmNlZCBzZXBhcmF0ZWx5IGZyb20gdGhlIHF1b3RhIHJvbGx1cC4gQnV0IHRoZSBiaWxsaW5nXG4gKiB1c2FnZSBjYXJkIGRyYXdzIGl0IGluIHRoZSBzYW1lIGxpc3QsIGFuZCBiZWZvcmUgdGhpcyBlbnRyeSBleGlzdGVkIHRoYXQgb25lXG4gKiByb3cgY2FycmllZCBpdHMgb3duIGlubGluZSBcIlNlYXRzXCIgbGFiZWwgd2hpbGUgdGhlIGZpdmUgYmVzaWRlIGl0IGNhbWUgZnJvbVxuICogdGhlIGNhdGFsb2cg4oCUIHRoZSBwcmVjaXNlIHNwbGl0LWZhY3QgZHJpZnQgdGhpcyBmaWxlIGV4aXN0cyB0byByZW1vdmUuXG4gKlxuICogS2VlcCB0aGlzIGZvciB0aGluZ3MgcmVuZGVyZWQgQVMgYSBjb25zdW1hYmxlLiBJdCBpcyBub3QgYSBiYWNrIGRvb3IgZm9yXG4gKiBxdW90YSB0eXBlczogYSByZWFsIHF1b3RhIGJlbG9uZ3MgaW4ge0BsaW5rIFFVT1RBX0NBVEFMT0d9LCB3aGVyZVxuICogYFJlY29yZDxRdW90YVR5cGUsIOKApj5gIGZvcmNlcyB0aGUgZW50cnkuXG4gKi9cbmV4cG9ydCBjb25zdCBOT05fUVVPVEFfQ09OU1VNQUJMRV9DQVRBTE9HOiBSZWFkb25seTxSZWNvcmQ8c3RyaW5nLCBRdW90YVR5cGVNZXRhPj4gPSB7XG4gIHNlYXRzOiB7IGxhYmVsOiAnU2VhdCcsIGxhYmVsUGx1cmFsOiAnU2VhdHMnLCByZW5kZXI6ICdjb3VudCcsIGFjY3J1YWw6ICdzdG9jaycsIG9ic2VydmVkOiAnbWVhc3VyZWQnLCBkZXNjcmlwdGlvbjogJ01lbWJlcnMgdGhpcyBhY2NvdW50IG1heSBoYXZlJyB9LFxufTtcblxuLyoqXG4gKiBFdmVyeSBkZXNjcmliZWQgY29uc3VtYWJsZSwgd2lkZW5lZCBmb3IgbG9va3VwcyBieSBhIHZhbHVlIHRoYXQgY2FtZSBvZmYgdGhlXG4gKiB3aXJlLlxuICovXG5jb25zdCBieUtleTogUmVjb3JkPHN0cmluZywgUXVvdGFUeXBlTWV0YSB8IHVuZGVmaW5lZD4gPSB7XG4gIC4uLk5PTl9RVU9UQV9DT05TVU1BQkxFX0NBVEFMT0csXG4gIC4uLlFVT1RBX0NBVEFMT0csXG59O1xuXG4vKipcbiAqIFRoaXMgdHlwZSdzIHNpbmd1bGFyIGxhYmVsLCBvciB0aGUgcmF3IGtleSBmb3IgYSB2YWx1ZSBvdXRzaWRlIHRoZSBjYXRhbG9nLlxuICpcbiAqIFRoZSBhY2Nlc3NvcnMgdGFrZSBgc3RyaW5nYCBiZWNhdXNlIHRoZSBxdW90YSB0eXBlIG9mdGVuIGFycml2ZXMgZnJvbSB0aGUgd2lyZVxuICogKGEgNDI5IGJvZHksIGEgcXVvdGEgcmVzcG9uc2UpIHdoZXJlIGFuIHVua25vd24gdmFsdWUgbXVzdCByZW5kZXIgYXMgc29tZXRoaW5nXG4gKiByYXRoZXIgdGhhbiBjcmFzaCBhIHBhZ2UuIFRoZSBgUXVvdGFUeXBlYCBvdmVybG9hZCBpcyBmb3IgaW50ZXJuYWwgY2FsbGVyczogaXRcbiAqIG1ha2VzIGEgdHlwbyBhIGNvbXBpbGUgZXJyb3IgaW5zdGVhZCBvZiBhIGtleSByZW5kZXJlZCBhcyBhIGxhYmVsLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcXVvdGFMYWJlbCh0eXBlOiBRdW90YVR5cGUpOiBzdHJpbmc7XG5leHBvcnQgZnVuY3Rpb24gcXVvdGFMYWJlbCh0eXBlOiBzdHJpbmcpOiBzdHJpbmc7XG5leHBvcnQgZnVuY3Rpb24gcXVvdGFMYWJlbCh0eXBlOiBzdHJpbmcpOiBzdHJpbmcge1xuICByZXR1cm4gYnlLZXlbdHlwZV0/LmxhYmVsID8/IHR5cGU7XG59XG5cbi8qKiBUaGlzIHR5cGUncyBQTFVSQUwgbGFiZWwsIG9yIHRoZSByYXcga2V5IGZvciBhIHZhbHVlIG91dHNpZGUgdGhlIGNhdGFsb2cuICovXG5leHBvcnQgZnVuY3Rpb24gcXVvdGFMYWJlbFBsdXJhbCh0eXBlOiBRdW90YVR5cGUpOiBzdHJpbmc7XG5leHBvcnQgZnVuY3Rpb24gcXVvdGFMYWJlbFBsdXJhbCh0eXBlOiBzdHJpbmcpOiBzdHJpbmc7XG5leHBvcnQgZnVuY3Rpb24gcXVvdGFMYWJlbFBsdXJhbCh0eXBlOiBzdHJpbmcpOiBzdHJpbmcge1xuICByZXR1cm4gYnlLZXlbdHlwZV0/LmxhYmVsUGx1cmFsID8/IHR5cGU7XG59XG5cbi8qKlxuICogSG93IHRvIHJlbmRlciB0aGlzIHR5cGUncyB2YWx1ZXMuIGBjb3VudGAgZm9yIGFueXRoaW5nIHRoZSBjYXRhbG9nIGRvZXMgbm90XG4gKiBrbm93LCBzbyBhbiB1bmZhbWlsaWFyIHdpcmUgdmFsdWUgcmVuZGVycyBhcyBhIHBsYWluIG51bWJlci5cbiAqXG4gKiBSZWFkIFRISVMgcmF0aGVyIHRoYW4gYXNraW5nIFwiaXMgaXQgYnl0ZXM/XCIuIEEgYm9vbGVhbiBoZWxwZXIgZGVncmFkZXNcbiAqIHNpbGVudGx5IHRoZSBtb21lbnQge0BsaW5rIFF1b3RhUmVuZGVyfSBncm93cyBhIHRoaXJkIG1lbWJlciDigJQgYSBuZXcgbW9kZVxuICogd291bGQgc2ltcGx5IHJlbmRlciBhcyBhIGNvdW50LCB3aXRoIG5vdGhpbmcgZmFpbGluZyDigJQgd2hlcmVhcyBhIGNhbGxlciB0aGF0XG4gKiBzd2l0Y2hlcyBvbiB0aGlzIHZhbHVlIGV4aGF1c3RpdmVseSBzdG9wcyBjb21waWxpbmcgdW50aWwgaXQgaGFuZGxlcyB0aGUgbmV3XG4gKiBtb2RlLiBUaGF0IGlzIHRoZSB3aG9sZSBwb2ludCBvZiB0aGUgY2F0YWxvZywgYXBwbGllZCB0byBpdHMgb3duIGFjY2Vzc29ycy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHF1b3RhUmVuZGVyKHR5cGU6IFF1b3RhVHlwZSk6IFF1b3RhUmVuZGVyO1xuZXhwb3J0IGZ1bmN0aW9uIHF1b3RhUmVuZGVyKHR5cGU6IHN0cmluZyk6IFF1b3RhUmVuZGVyO1xuZXhwb3J0IGZ1bmN0aW9uIHF1b3RhUmVuZGVyKHR5cGU6IHN0cmluZyk6IFF1b3RhUmVuZGVyIHtcbiAgcmV0dXJuIGJ5S2V5W3R5cGVdPy5yZW5kZXIgPz8gJ2NvdW50Jztcbn1cblxuY29uc3QgQllURV9VTklUUyA9IFsnQicsICdLQicsICdNQicsICdHQicsICdUQiddIGFzIGNvbnN0O1xuXG4vKipcbiAqIEEgYnl0ZSBjb3VudCBhcyBhIGh1bWFuIGFtb3VudCDigJQgYDEuNSBHQmAsIGA1MTIgTUJgLCBgMCBCYC5cbiAqXG4gKiBUaGUgT05FIGltcGxlbWVudGF0aW9uOiB0aGUgZnJvbnRlbmQncyBgZm9ybWF0Qnl0ZXNgIGRlbGVnYXRlcyBoZXJlIHJhdGhlclxuICogdGhhbiBrZWVwaW5nIGl0cyBvd24gY29weSwgc28gYSBzdG9yYWdlIGZpZ3VyZSByZWFkcyBpZGVudGljYWxseSBpbiBhIHF1b3RhXG4gKiBub3RpY2UsIGEgNDAzIGV4cGxhbmF0aW9uIGFuZCB0aGUgZGFzaGJvYXJkLiBCaW5hcnkgdW5pdHMgKDEwMjQpLCBvbmUgZGVjaW1hbFxuICogYmVsb3cgMTAgYW5kIHJvdW5kZWQgYWJvdmUsIHdoaWNoIGlzIHdoYXQgdGhlIGRhc2hib2FyZCBhbHJlYWR5IHJlbmRlcmVkIOKAlFxuICogdGhpcyBtb3ZlZCB0aGUgYWxnb3JpdGhtLCBpdCBkaWQgbm90IGNoYW5nZSBpdC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdEJ5dGVBbW91bnQoYnl0ZXM6IG51bWJlcik6IHN0cmluZyB7XG4gIGlmICghTnVtYmVyLmlzRmluaXRlKGJ5dGVzKSB8fCBieXRlcyA8IDApIHJldHVybiAnMCBCJztcbiAgbGV0IHZhbHVlID0gYnl0ZXM7XG4gIGxldCBpID0gMDtcbiAgd2hpbGUgKHZhbHVlID49IDEwMjQgJiYgaSA8IEJZVEVfVU5JVFMubGVuZ3RoIC0gMSkge1xuICAgIHZhbHVlIC89IDEwMjQ7XG4gICAgaSsrO1xuICB9XG4gIHJldHVybiB2YWx1ZSA8IDEwID8gYCR7dmFsdWUudG9GaXhlZCgxKX0gJHtCWVRFX1VOSVRTW2ldfWAgOiBgJHtNYXRoLnJvdW5kKHZhbHVlKX0gJHtCWVRFX1VOSVRTW2ldfWA7XG59XG5cbi8qKlxuICogQSByYXcgcXVvdGEgdmFsdWUgYXMgdGhlIGFtb3VudCBhIEhVTUFOIHNob3VsZCByZWFkLCBwZXIgdGhpcyB0eXBlJ3NcbiAqIHtAbGluayBRdW90YVJlbmRlcn0g4oCUIGJ5dGVzIGFzIGAyNSBHQmAsIGNvdW50cyBncm91cGVkIGFzIGA2MCwwMDBgLlxuICpcbiAqIEZvciBzZXJ2ZXItc2lkZSBjb3B5IChxdW90YSBub3RpY2VzLCBhdWRpdCByZWFzb25zLCA0MDMgZXhwbGFuYXRpb25zKSwgd2hpY2hcbiAqIGlzIHdoeSBpdCBpcyBoZXJlIHJhdGhlciB0aGFuIGluIHRoZSBmcm9udGVuZDogYSBzdG9yYWdlIG5vdGljZSB0aGF0XG4gKiBpbnRlcnBvbGF0ZWQgdGhlIHJhdyBudW1iZXIgd291bGQgcmVhZCBcInVzZWQgYWxsIDI2ODQzNTQ1NjAwIG9mIGl0cyByZWdpc3RyeVxuICogc3RvcmFnZVwiLiBUaGUgZnJvbnRlbmQgaGFzIGl0cyBvd24gdGhpbiB3cmFwcGVyIGJlY2F1c2UgaXQgcmVuZGVycyBhblxuICogdW5saW1pdGVkICgtMSkgbGltaXQgYXMgYOKInmAsIHdoaWNoIGlzIGEgZGlzcGxheSBjaG9pY2Ugbm8gc2VydmVyIGNvcHkgbWFrZXNcbiAqICh1bmxpbWl0ZWQgb3JncyBhcmUgc2tpcHBlZCBiZWZvcmUgYW55IGNvcHkgaXMgZ2VuZXJhdGVkKS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdFF1b3RhQW1vdW50KHR5cGU6IHN0cmluZywgdmFsdWU6IG51bWJlcik6IHN0cmluZyB7XG4gIHJldHVybiBxdW90YVJlbmRlcih0eXBlKSA9PT0gJ2J5dGVzJyA/IGZvcm1hdEJ5dGVBbW91bnQodmFsdWUpIDogdmFsdWUudG9Mb2NhbGVTdHJpbmcoJ2VuLVVTJyk7XG59XG5cbi8qKlxuICogSG93IHRoaXMgcXVvdGEncyB1c2FnZSBpcyBvYnNlcnZlZDogYG1ldGVyZWRgIChpbmNyZW1lbnRlZCkgb3IgYG1lYXN1cmVkYFxuICogKHJlYWQgb24gZGVtYW5kKS4gVW5rbm93biB3aXJlIHZhbHVlcyByZWFkIGFzIGBtZXRlcmVkYCwgbWF0Y2hpbmcgZXZlcnlcbiAqIHF1b3RhIGJ1dCBzdG9yYWdlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcXVvdGFPYnNlcnZhdGlvbih0eXBlOiBRdW90YVR5cGUpOiBRdW90YU9ic2VydmF0aW9uO1xuZXhwb3J0IGZ1bmN0aW9uIHF1b3RhT2JzZXJ2YXRpb24odHlwZTogc3RyaW5nKTogUXVvdGFPYnNlcnZhdGlvbjtcbmV4cG9ydCBmdW5jdGlvbiBxdW90YU9ic2VydmF0aW9uKHR5cGU6IHN0cmluZyk6IFF1b3RhT2JzZXJ2YXRpb24ge1xuICByZXR1cm4gYnlLZXlbdHlwZV0/Lm9ic2VydmVkID8/ICdtZXRlcmVkJztcbn1cblxuLyoqXG4gKiBXaGV0aGVyIHRoaXMgcXVvdGEgcmVmaWxscyBvbiB0aGUgcGVyaW9kIChgZmxvd2ApIG9yIHBlcnNpc3RzIChgc3RvY2tgKS5cbiAqIFVua25vd24gd2lyZSB2YWx1ZXMgYXJlIHRyZWF0ZWQgYXMgYGZsb3dgLCBtYXRjaGluZyBldmVyeSBtZXRlcmVkIHF1b3RhLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcXVvdGFBY2NydWFsKHR5cGU6IFF1b3RhVHlwZSk6IFF1b3RhQWNjcnVhbDtcbmV4cG9ydCBmdW5jdGlvbiBxdW90YUFjY3J1YWwodHlwZTogc3RyaW5nKTogUXVvdGFBY2NydWFsO1xuZXhwb3J0IGZ1bmN0aW9uIHF1b3RhQWNjcnVhbCh0eXBlOiBzdHJpbmcpOiBRdW90YUFjY3J1YWwge1xuICByZXR1cm4gYnlLZXlbdHlwZV0/LmFjY3J1YWwgPz8gJ2Zsb3cnO1xufVxuXG4vKiogV2hhdCB0aGlzIHR5cGUgY291bnRzLCBvciB0aGUgZW1wdHkgc3RyaW5nIGZvciBhIHZhbHVlIG91dHNpZGUgdGhlIGNhdGFsb2cuICovXG5leHBvcnQgZnVuY3Rpb24gcXVvdGFEZXNjcmlwdGlvbih0eXBlOiBRdW90YVR5cGUpOiBzdHJpbmc7XG5leHBvcnQgZnVuY3Rpb24gcXVvdGFEZXNjcmlwdGlvbih0eXBlOiBzdHJpbmcpOiBzdHJpbmc7XG5leHBvcnQgZnVuY3Rpb24gcXVvdGFEZXNjcmlwdGlvbih0eXBlOiBzdHJpbmcpOiBzdHJpbmcge1xuICByZXR1cm4gYnlLZXlbdHlwZV0/LmRlc2NyaXB0aW9uID8/ICcnO1xufVxuXG4vKiogVGhlIHBhY2sgdW5pdCBmb3IgdGhpcyB0eXBlLCBvciBgbnVsbGAgdG8gdHlwZSBwYWNrIGNvdW50cyBpbnN0ZWFkLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHF1b3RhUGFja1VuaXQodHlwZTogUXVvdGFUeXBlKTogeyBuYW1lOiBzdHJpbmc7IHBlcjogbnVtYmVyIH0gfCBudWxsO1xuZXhwb3J0IGZ1bmN0aW9uIHF1b3RhUGFja1VuaXQodHlwZTogc3RyaW5nKTogeyBuYW1lOiBzdHJpbmc7IHBlcjogbnVtYmVyIH0gfCBudWxsO1xuZXhwb3J0IGZ1bmN0aW9uIHF1b3RhUGFja1VuaXQodHlwZTogc3RyaW5nKTogeyBuYW1lOiBzdHJpbmc7IHBlcjogbnVtYmVyIH0gfCBudWxsIHtcbiAgcmV0dXJuIGJ5S2V5W3R5cGVdPy5wYWNrVW5pdCA/PyBudWxsO1xufVxuIl19
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { QuotaType } from './common.js';
|
|
1
2
|
/** Available quota tier identifiers.
|
|
2
3
|
*
|
|
3
4
|
* `unlimited` is a special tier: every quota is `-1` (uncapped) and it includes
|
|
@@ -10,6 +11,12 @@ export interface QuotaTierLimits {
|
|
|
10
11
|
plugins: number;
|
|
11
12
|
pipelines: number;
|
|
12
13
|
apiCalls: number;
|
|
14
|
+
/**
|
|
15
|
+
* Metered API calls made by ORG SERVICE ACCOUNTS (automation), counted
|
|
16
|
+
* instead of `apiCalls` so pipelines can't starve the org's people. See
|
|
17
|
+
* {@link AUTOMATION_CALLS_SIZING} for how the defaults are derived.
|
|
18
|
+
*/
|
|
19
|
+
automationCalls: number;
|
|
13
20
|
aiCalls: number;
|
|
14
21
|
/**
|
|
15
22
|
* Aggregate registry storage cap in BYTES. Counted across every repo
|
|
@@ -18,6 +25,23 @@ export interface QuotaTierLimits {
|
|
|
18
25
|
* `push` scope when the org's measured usage exceeds the limit.
|
|
19
26
|
*/
|
|
20
27
|
storageBytes: number;
|
|
28
|
+
/**
|
|
29
|
+
* vCPU-MINUTES of plugin-build compute per MONTH — the one quota on a period
|
|
30
|
+
* of its own (every other flow quota rolls on `QUOTA_RESET_DAYS`, 3 days).
|
|
31
|
+
*
|
|
32
|
+
* Derived from the `plugins` cap rather than guessed: `plugins x 10` builds a
|
|
33
|
+
* month, at a flat 30 wall-minutes a build and 1.5 vCPU of buildkitd, so
|
|
34
|
+
* `credits = builds x 45`. The flat per-build figure is deliberate — scaling
|
|
35
|
+
* BOTH the build count and the per-build cost by tier made credits
|
|
36
|
+
* superlinear in tier on the strength of an unmeasured claim, and it made a
|
|
37
|
+
* pack's credit grant tier-dependent, which the catalogue's grant shape cannot
|
|
38
|
+
* express.
|
|
39
|
+
*
|
|
40
|
+
* This is the first dimension with genuine per-unit marginal cost: every other
|
|
41
|
+
* quota is a rate limit protecting shared infrastructure, this one is compute
|
|
42
|
+
* the platform pays for. See `docs/plans/cpu-credits.md`.
|
|
43
|
+
*/
|
|
44
|
+
cpuCredits: number;
|
|
21
45
|
/** Count-quotas on the user-editable feature tables added to close per-org
|
|
22
46
|
* DoS via spam. -1 means unlimited. */
|
|
23
47
|
dashboards: number;
|
|
@@ -36,6 +60,38 @@ export interface QuotaTierLimits {
|
|
|
36
60
|
* when active members + pending invites would exceed this).
|
|
37
61
|
*/
|
|
38
62
|
seats: number;
|
|
63
|
+
/**
|
|
64
|
+
* Max designated SUPPORT CONTACTS — the members authorised to open support
|
|
65
|
+
* cases. -1 = unlimited. Base is **0 on every tier**: support is an add-on at
|
|
66
|
+
* every tier and included in none, so an org with no support bundle has no
|
|
67
|
+
* contacts and cannot open a case. That makes the support paywall a quota
|
|
68
|
+
* check rather than a feature flag.
|
|
69
|
+
*
|
|
70
|
+
* Like `seats` this is a tier LIMIT, not a tracked counter — enforced live at
|
|
71
|
+
* designation time against current designations across the org subtree.
|
|
72
|
+
* Extended per unit by the `support_standard` / `support_priority` BASELINE
|
|
73
|
+
* bundles, which are priced per contact.
|
|
74
|
+
*/
|
|
75
|
+
supportContacts: number;
|
|
76
|
+
/**
|
|
77
|
+
* How many of those contacts may be UPGRADED to premium (24×7, 8-hour
|
|
78
|
+
* response, and the only ones who may declare `urgent`).
|
|
79
|
+
*
|
|
80
|
+
* A second dimension rather than a third baseline level, because premium is an
|
|
81
|
+
* UPGRADE and not an alternative. The shape it encodes: an org buys N baseline
|
|
82
|
+
* contacts and upgrades M of them, M ≤ N. That is what lets a 100-seat
|
|
83
|
+
* customer give business-hours support to its three named contacts and 24×7
|
|
84
|
+
* to the one who carries the pager — the case the mutually-exclusive ladder
|
|
85
|
+
* could not express at all, where buying "99 standard + 1 premium" charged for
|
|
86
|
+
* 100 units and granted one contact.
|
|
87
|
+
*
|
|
88
|
+
* ADDITIVE, deliberately. A premium contact costs the baseline unit PLUS the
|
|
89
|
+
* upgrade ($19.99 + $99.99), because it is served during business hours
|
|
90
|
+
* through the baseline and overnight through the upgrade; pricing the upgrade
|
|
91
|
+
* as a delta would pretend the baseline were free. Base 0 on every tier, like
|
|
92
|
+
* `supportContacts`, so the paywall stays a quota check.
|
|
93
|
+
*/
|
|
94
|
+
supportPremiumContacts: number;
|
|
39
95
|
/**
|
|
40
96
|
* Reporting retention in DAYS — NOT a flow quota (never decremented, absent
|
|
41
97
|
* from `VALID_QUOTA_TYPES`): these ride the tier-baseline + bundle-grant math
|
|
@@ -54,6 +110,78 @@ export interface QuotaTierPreset {
|
|
|
54
110
|
label: string;
|
|
55
111
|
limits: QuotaTierLimits;
|
|
56
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* How the `automationCalls` defaults are sized — written down so the next
|
|
115
|
+
* person to touch them argues with the arithmetic, not with the numbers.
|
|
116
|
+
*
|
|
117
|
+
* Every call an org's automation keys make in one pipeline RUN, itemized (the
|
|
118
|
+
* earlier sizing counted only the plugin lookups and hand-waved the rest as
|
|
119
|
+
* "×2"):
|
|
120
|
+
*
|
|
121
|
+
* plugin lookups 12 one per plugin step (self-mutating pipeline)
|
|
122
|
+
* synth 1 the synth step fetching the pipeline config
|
|
123
|
+
* runtime check 1 the pre-deploy runtime-check call
|
|
124
|
+
* security-report upload 1 the scan step posting its report
|
|
125
|
+
* registry register 1 pipeline-register stamping the deployed version
|
|
126
|
+
* reporting ingest batches 12 the event forwarder posts ~one batch per action
|
|
127
|
+
* --
|
|
128
|
+
* 28 calls per run
|
|
129
|
+
*
|
|
130
|
+
* Per default period (`QUOTA_RESET_DAYS`, 3 days) one active pipeline running
|
|
131
|
+
* hourly makes 28 × 24 × 3 = 2,016 calls. An org keeps far more pipelines alive
|
|
132
|
+
* than it CREATES in a period (`pipelines` is a creation rate), so active
|
|
133
|
+
* pipelines are taken as 4× the tier's `pipelines`. On top sits a per-ORG
|
|
134
|
+
* constant that does not scale with pipelines: the ingest-health heartbeat
|
|
135
|
+
* (≤ 1/minute → 1,440/day → 4,320/period). The sum gets 25% headroom (retries,
|
|
136
|
+
* registry pushes, ad-hoc CI scripts on keys) and rounds UP to the next 10,000:
|
|
137
|
+
*
|
|
138
|
+
* developer 2×4×2016 + 4320 = 20,448 × 1.25 = 25,560 → 30,000
|
|
139
|
+
* pro 5×4×2016 + 4320 = 44,640 × 1.25 = 55,800 → 60,000
|
|
140
|
+
* team 10×4×2016 + 4320 = 84,960 × 1.25 = 106,200 → 110,000
|
|
141
|
+
* business 20×4×2016 + 4320 = 165,600 × 1.25 = 207,000 → 210,000
|
|
142
|
+
* enterprise 30×4×2016 + 4320 = 246,240 × 1.25 = 307,800 → 310,000
|
|
143
|
+
*
|
|
144
|
+
* {@link automationCallsFor} computes exactly this, and the tier tests hold the
|
|
145
|
+
* table to it. Over the cap the 429 is SOFT for a band ({@link quotaGracePct}):
|
|
146
|
+
* a self-mutating pipeline that hard-stopped at 100% stayed stopped until the
|
|
147
|
+
* period reset, up to 3 days. The limits are PER PERIOD: an operator who
|
|
148
|
+
* lengthens `QUOTA_RESET_DAYS` should scale `QUOTA_TIER_<TIER>_AUTOMATION_CALLS`
|
|
149
|
+
* by the same factor.
|
|
150
|
+
*/
|
|
151
|
+
export declare const AUTOMATION_CALLS_SIZING: {
|
|
152
|
+
readonly callsPerRun: {
|
|
153
|
+
readonly pluginLookups: 12;
|
|
154
|
+
readonly synth: 1;
|
|
155
|
+
readonly runtimeCheck: 1;
|
|
156
|
+
readonly securityReportUpload: 1;
|
|
157
|
+
readonly registryRegister: 1;
|
|
158
|
+
readonly ingestBatches: 12;
|
|
159
|
+
};
|
|
160
|
+
readonly runsPerDay: 24;
|
|
161
|
+
readonly activePipelinesPerCreation: 4;
|
|
162
|
+
/** Ingest-health heartbeats per org per day (≤ one a minute). */
|
|
163
|
+
readonly heartbeatsPerDay: 1440;
|
|
164
|
+
/** Multiplier on the itemized total. */
|
|
165
|
+
readonly headroom: 1.25;
|
|
166
|
+
/** Defaults round UP to a multiple of this. */
|
|
167
|
+
readonly roundTo: 10000;
|
|
168
|
+
};
|
|
169
|
+
/** The {@link AUTOMATION_CALLS_SIZING} formula for a tier creating `pipelines` per period. */
|
|
170
|
+
export declare function automationCallsFor(pipelines: number, periodDays?: number): number;
|
|
171
|
+
/** The soft band for `quotaType` in percent, or 0 for a type that stops hard. */
|
|
172
|
+
export declare function quotaGracePct(quotaType: string): number;
|
|
173
|
+
/**
|
|
174
|
+
* The limit enforcement actually binds on for `quotaType`: the nominal limit,
|
|
175
|
+
* plus the type's {@link quotaGracePct} band where it has one. Unlimited (-1)
|
|
176
|
+
* and zero ("none") limits are returned unchanged — a 0 cap is a deliberate
|
|
177
|
+
* refusal, not a budget to overrun.
|
|
178
|
+
*
|
|
179
|
+
* {@link QUOTA_GRACE_PCT} decides WHETHER a type has a band; `gracePct` only
|
|
180
|
+
* sizes it. So an explicit percentage passed for an unbanded type still yields
|
|
181
|
+
* the nominal limit, rather than quietly granting a band the type is not meant
|
|
182
|
+
* to have.
|
|
183
|
+
*/
|
|
184
|
+
export declare function enforcedQuotaLimit(quotaType: string, limit: number, gracePct?: number): number;
|
|
57
185
|
export declare const QUOTA_TIERS: Record<QuotaTier, QuotaTierPreset>;
|
|
58
186
|
/** All valid tier names (incl. `unlimited`). Used for validation of a *stored*
|
|
59
187
|
* tier — `unlimited` is a valid value even though it's never offered for sale. */
|
|
@@ -104,4 +232,26 @@ export declare function getTierLimits(tier: string): QuotaTierLimits;
|
|
|
104
232
|
*/
|
|
105
233
|
export declare const QUOTA_RESET_DAYS: number;
|
|
106
234
|
/** The next quota reset: local midnight `days` from `now`. */
|
|
235
|
+
/**
|
|
236
|
+
* The `cpuCredits` period, in days. A MONTH rather than the global three-day
|
|
237
|
+
* window, because build demand is bursty in a way API traffic is not: a full
|
|
238
|
+
* catalogue rebuild is a day's work that arrives on one day, and a three-day
|
|
239
|
+
* window refuses it while the account holds most of a month's budget unspent.
|
|
240
|
+
*/
|
|
241
|
+
export declare const CPU_CREDITS_PERIOD_DAYS: number;
|
|
242
|
+
/**
|
|
243
|
+
* The usage period for ONE quota type, in days.
|
|
244
|
+
*
|
|
245
|
+
* Every flow quota but `cpuCredits` rolls on the global {@link QUOTA_RESET_DAYS}.
|
|
246
|
+
* Credits are monthly, which makes the period a PER-TYPE fact — so anything that
|
|
247
|
+
* computes a reset date, a "resets in N days" line or a burn-rate forecast has
|
|
248
|
+
* to ask for the type's own period rather than reading the global constant. A
|
|
249
|
+
* forecast against the wrong window is wrong by a factor of ten.
|
|
250
|
+
*
|
|
251
|
+
* Stocks (`storageBytes` and the resource counts) have no period at all; this
|
|
252
|
+
* returns the global window for them because callers gate on
|
|
253
|
+
* `quotaAccrual(type) === 'stock'` before using it, and returning a figure is
|
|
254
|
+
* less disruptive than making every caller handle a null. Check the accrual.
|
|
255
|
+
*/
|
|
256
|
+
export declare function quotaPeriodDays(type: QuotaType): number;
|
|
107
257
|
export declare function nextQuotaResetDate(days?: number, now?: Date): Date;
|