@heroku/sdk 0.1.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +207 -0
- package/README.md +94 -0
- package/dist/compositions/add-on.d.ts +7 -0
- package/dist/compositions/add-on.js +6 -0
- package/dist/compositions/app.d.ts +8 -0
- package/dist/compositions/app.js +11 -0
- package/dist/compositions/dyno.d.ts +17 -0
- package/dist/compositions/dyno.js +23 -0
- package/dist/compositions/pg.d.ts +15 -0
- package/dist/compositions/pg.js +56 -0
- package/dist/compositions/pipeline.d.ts +33 -0
- package/dist/compositions/pipeline.js +146 -0
- package/dist/core/create-client.d.ts +32 -0
- package/dist/core/create-client.js +48 -0
- package/dist/core/dispatcher.d.ts +3 -0
- package/dist/core/dispatcher.js +80 -0
- package/dist/core/extend-resource.d.ts +24 -0
- package/dist/core/extend-resource.js +3 -0
- package/dist/core/extensions-proxy.d.ts +2 -0
- package/dist/core/extensions-proxy.js +56 -0
- package/dist/core/heroku-sdk.d.ts +14 -0
- package/dist/core/heroku-sdk.js +54 -0
- package/dist/core/interpolate-path.d.ts +3 -0
- package/dist/core/interpolate-path.js +13 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/resources/data/backup.d.ts +9 -0
- package/dist/resources/data/backup.js +10 -0
- package/dist/resources/data/database.d.ts +54 -0
- package/dist/resources/data/database.js +44 -0
- package/dist/resources/data/internal/resolve-addon-id.d.ts +2 -0
- package/dist/resources/data/internal/resolve-addon-id.js +13 -0
- package/dist/resources/data/internal/resolve-pg-database.d.ts +23 -0
- package/dist/resources/data/internal/resolve-pg-database.js +52 -0
- package/dist/resources/data/maintenance.d.ts +9 -0
- package/dist/resources/data/maintenance.js +10 -0
- package/dist/resources/data/postgres-credential.d.ts +11 -0
- package/dist/resources/data/postgres-credential.js +16 -0
- package/dist/resources/data/postgres-database.d.ts +32 -0
- package/dist/resources/data/postgres-database.js +14 -0
- package/dist/resources/data/postgres.d.ts +9 -0
- package/dist/resources/data/postgres.js +10 -0
- package/dist/resources/data/transfer-schedule.d.ts +9 -0
- package/dist/resources/data/transfer-schedule.js +10 -0
- package/dist/resources/data/transfer.d.ts +49 -0
- package/dist/resources/data/transfer.js +14 -0
- package/dist/resources/extensions/data.d.ts +4 -0
- package/dist/resources/extensions/data.js +4 -0
- package/dist/resources/extensions/platform.d.ts +8 -0
- package/dist/resources/extensions/platform.js +8 -0
- package/dist/resources/platform/add-on/create-and-wait.d.ts +28 -0
- package/dist/resources/platform/add-on/create-and-wait.js +63 -0
- package/dist/resources/platform/add-on/debug.d.ts +2 -0
- package/dist/resources/platform/add-on/debug.js +2 -0
- package/dist/resources/platform/add-on/describe.d.ts +20 -0
- package/dist/resources/platform/add-on/describe.js +51 -0
- package/dist/resources/platform/add-on/errors.d.ts +51 -0
- package/dist/resources/platform/add-on/errors.js +61 -0
- package/dist/resources/platform/add-on/index.d.ts +23 -0
- package/dist/resources/platform/add-on/index.js +25 -0
- package/dist/resources/platform/add-on/list-plans.d.ts +34 -0
- package/dist/resources/platform/add-on/list-plans.js +53 -0
- package/dist/resources/platform/add-on/pricing.d.ts +72 -0
- package/dist/resources/platform/add-on/pricing.js +100 -0
- package/dist/resources/platform/add-on/resolve.d.ts +43 -0
- package/dist/resources/platform/add-on/resolve.js +103 -0
- package/dist/resources/platform/add-on/types.d.ts +49 -0
- package/dist/resources/platform/add-on/types.js +1 -0
- package/dist/resources/platform/add-on/upgrade.d.ts +21 -0
- package/dist/resources/platform/add-on/upgrade.js +36 -0
- package/dist/resources/platform/add-on.d.ts +211 -0
- package/dist/resources/platform/add-on.js +354 -0
- package/dist/resources/platform/app/index.d.ts +58 -0
- package/dist/resources/platform/app/index.js +53 -0
- package/dist/resources/platform/app/info.d.ts +30 -0
- package/dist/resources/platform/app/info.js +27 -0
- package/dist/resources/platform/app.d.ts +56 -0
- package/dist/resources/platform/app.js +50 -0
- package/dist/resources/platform/dyno/index.d.ts +45 -0
- package/dist/resources/platform/dyno/index.js +26 -0
- package/dist/resources/platform/dyno.d.ts +45 -0
- package/dist/resources/platform/dyno.js +26 -0
- package/dist/resources/platform/log-session/index.d.ts +102 -0
- package/dist/resources/platform/log-session/index.js +223 -0
- package/dist/resources/platform/log-session/parser.d.ts +74 -0
- package/dist/resources/platform/log-session/parser.js +71 -0
- package/dist/resources/platform/log-session.d.ts +81 -0
- package/dist/resources/platform/log-session.js +167 -0
- package/dist/resources/platform/pipeline/coupling.d.ts +27 -0
- package/dist/resources/platform/pipeline/coupling.js +55 -0
- package/dist/resources/platform/pipeline/index.d.ts +53 -0
- package/dist/resources/platform/pipeline/index.js +75 -0
- package/dist/resources/platform/pipeline/promotion.d.ts +21 -0
- package/dist/resources/platform/pipeline/promotion.js +109 -0
- package/dist/resources/platform/pipeline-coupling.d.ts +27 -0
- package/dist/resources/platform/pipeline-coupling.js +55 -0
- package/dist/resources/platform/pipeline-promotion.d.ts +21 -0
- package/dist/resources/platform/pipeline-promotion.js +128 -0
- package/dist/resources/platform/pipeline.d.ts +53 -0
- package/dist/resources/platform/pipeline.js +75 -0
- package/dist/resources/platform/shield.d.ts +2 -0
- package/dist/resources/platform/shield.js +6 -0
- package/dist/services/data.d.ts +5 -0
- package/dist/services/data.js +5 -0
- package/dist/services/platform.d.ts +5 -0
- package/dist/services/platform.js +5 -0
- package/dist/utils/wait.d.ts +16 -0
- package/dist/utils/wait.js +35 -0
- package/package.json +54 -0
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import { HerokuApiError, NotFoundError } from '@heroku/heroku-fetch';
|
|
2
|
+
import createDebug from 'debug';
|
|
3
|
+
import { extendResource } from '../../core/extend-resource.js';
|
|
4
|
+
const debug = createDebug('heroku:sdk:resources:add-on');
|
|
5
|
+
export class AddonNotFoundError extends Error {
|
|
6
|
+
resource;
|
|
7
|
+
id = 'not_found';
|
|
8
|
+
statusCode = 404;
|
|
9
|
+
constructor(resource = 'addon') {
|
|
10
|
+
super(`Couldn't find that ${resource}.`);
|
|
11
|
+
this.resource = resource;
|
|
12
|
+
this.name = 'AddonNotFoundError';
|
|
13
|
+
}
|
|
14
|
+
get body() {
|
|
15
|
+
return { id: this.id, message: this.message, resource: this.resource };
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export class AddonAmbiguousError extends Error {
|
|
19
|
+
matches;
|
|
20
|
+
id = 'multiple_matches';
|
|
21
|
+
statusCode = 422;
|
|
22
|
+
constructor(matches) {
|
|
23
|
+
super(`Ambiguous identifier; multiple matching add-ons found: ${matches.map(m => m.name).join(', ')}.`);
|
|
24
|
+
this.matches = matches;
|
|
25
|
+
this.name = 'AddonAmbiguousError';
|
|
26
|
+
}
|
|
27
|
+
get body() {
|
|
28
|
+
return { id: this.id, message: this.message };
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Thrown by `createAndWait` when the Platform returns 423
|
|
33
|
+
* `confirmation_required`. Callers that want to prompt the user should
|
|
34
|
+
* catch this, gather a confirmation value, and retry the call with
|
|
35
|
+
* `body.confirm` set.
|
|
36
|
+
*/
|
|
37
|
+
export class AddonConfirmationRequiredError extends Error {
|
|
38
|
+
platformMessage;
|
|
39
|
+
id = 'confirmation_required';
|
|
40
|
+
statusCode = 423;
|
|
41
|
+
constructor(platformMessage) {
|
|
42
|
+
super(platformMessage);
|
|
43
|
+
this.platformMessage = platformMessage;
|
|
44
|
+
this.name = 'AddonConfirmationRequiredError';
|
|
45
|
+
}
|
|
46
|
+
get body() {
|
|
47
|
+
return { id: this.id, message: this.message };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Thrown by `createAndWait` when the Platform reports the add-on
|
|
52
|
+
* settled into a non-provisioned terminal state (e.g. `deprovisioned`).
|
|
53
|
+
*/
|
|
54
|
+
export class AddonProvisioningFailedError extends Error {
|
|
55
|
+
addon;
|
|
56
|
+
id = 'provisioning_failed';
|
|
57
|
+
constructor(addon) {
|
|
58
|
+
super(`The add-on was unable to be created, with status ${addon.state}.`);
|
|
59
|
+
this.addon = addon;
|
|
60
|
+
this.name = 'AddonProvisioningFailedError';
|
|
61
|
+
}
|
|
62
|
+
get body() {
|
|
63
|
+
return { id: this.id, message: this.message };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Change the plan of an add-on.
|
|
68
|
+
*
|
|
69
|
+
* Resolves the add-on first so the caller can pass any identifier
|
|
70
|
+
* (UUID, globally unique name, or namespaced `service::name`). When
|
|
71
|
+
* `appIdentity` is provided, the resolve is scoped to that app and
|
|
72
|
+
* falls back to a global resolve if the platform returns 404 add_on.
|
|
73
|
+
*
|
|
74
|
+
* If `plan` is unqualified (no `:`), it's prefixed with the resolved
|
|
75
|
+
* add-on's `addon_service.name` — so callers can pass `hobby` rather
|
|
76
|
+
* than `heroku-redis:hobby` if they don't already know the service.
|
|
77
|
+
*
|
|
78
|
+
* `options.onResolved` fires after the resolve and before the update,
|
|
79
|
+
* receiving the resolved add-on. Useful for surfacing pre-update
|
|
80
|
+
* state (the add-on's current plan, app, etc.) without an extra
|
|
81
|
+
* round-trip.
|
|
82
|
+
*/
|
|
83
|
+
export async function upgrade(ctx, addonIdentity, plan, options = {}) {
|
|
84
|
+
options.signal?.throwIfAborted();
|
|
85
|
+
const addon = await resolveAddonInternal(ctx.platform, addonIdentity, {
|
|
86
|
+
addonService: options.addonService,
|
|
87
|
+
appIdentity: options.appIdentity,
|
|
88
|
+
});
|
|
89
|
+
await options.onResolved?.(addon);
|
|
90
|
+
options.signal?.throwIfAborted();
|
|
91
|
+
const qualifiedPlan = plan.includes(':')
|
|
92
|
+
? plan
|
|
93
|
+
: `${addon.addon_service?.name}:${plan}`;
|
|
94
|
+
if (qualifiedPlan !== plan) {
|
|
95
|
+
debug('upgrade plan qualified plan=%s qualified=%s', plan, qualifiedPlan);
|
|
96
|
+
}
|
|
97
|
+
debug('upgrade addon=%s app=%s plan=%s', addon.id, addon.app.id, qualifiedPlan);
|
|
98
|
+
return ctx.platform.addOn.update(addon.app.id, addon.id, { plan: qualifiedPlan });
|
|
99
|
+
}
|
|
100
|
+
const DEFAULT_CREATE_WAIT_INTERVAL_MS = 5000;
|
|
101
|
+
/**
|
|
102
|
+
* Create an add-on and optionally wait for provisioning to complete.
|
|
103
|
+
*
|
|
104
|
+
* Wraps `addOn.create` with three pieces of orchestration:
|
|
105
|
+
*
|
|
106
|
+
* - 423 `confirmation_required` from the platform is converted to a
|
|
107
|
+
* typed `AddonConfirmationRequiredError`. Callers should catch
|
|
108
|
+
* this, prompt the user for confirmation, and retry the call with
|
|
109
|
+
* `body.confirm` set.
|
|
110
|
+
* - When `wait: true` is set, polls `addOn.infoByApp` on a
|
|
111
|
+
* `waitIntervalMs` cadence until the add-on's `state` is no longer
|
|
112
|
+
* `provisioning`. Throws `AddonProvisioningFailedError` if the
|
|
113
|
+
* terminal state is `deprovisioned`.
|
|
114
|
+
* - `options.onProvisioning` fires once after the create response
|
|
115
|
+
* when polling is about to begin, letting callers surface a
|
|
116
|
+
* two-phase status display ("Created" → "Waiting").
|
|
117
|
+
*
|
|
118
|
+
* Both the create and poll calls are issued with `Accept-Expansion:
|
|
119
|
+
* addon_service,plan` so the response includes the full plan shape
|
|
120
|
+
* (price, unit, etc.). The create call additionally sets
|
|
121
|
+
* `X-Heroku-Legacy-Provider-Messages: true` to opt into
|
|
122
|
+
* provider-supplied `provision_message` text on the response — without
|
|
123
|
+
* it the platform silently omits that field.
|
|
124
|
+
*/
|
|
125
|
+
export async function createAndWait(ctx, appIdentity, body, options = {}) {
|
|
126
|
+
options.signal?.throwIfAborted();
|
|
127
|
+
const platform = ctx.platform.withHeaders({ 'Accept-Expansion': 'addon_service,plan' });
|
|
128
|
+
let addon;
|
|
129
|
+
try {
|
|
130
|
+
addon = await platform
|
|
131
|
+
.withHeaders({ 'X-Heroku-Legacy-Provider-Messages': 'true' })
|
|
132
|
+
.addOn.create(appIdentity, body);
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
if (error instanceof HerokuApiError && error.id === 'confirmation_required') {
|
|
136
|
+
throw new AddonConfirmationRequiredError(error.message);
|
|
137
|
+
}
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
140
|
+
if (!options.wait || addon.state !== 'provisioning') {
|
|
141
|
+
if (addon.state === 'deprovisioned') {
|
|
142
|
+
throw new AddonProvisioningFailedError(addon);
|
|
143
|
+
}
|
|
144
|
+
return addon;
|
|
145
|
+
}
|
|
146
|
+
await options.onProvisioning?.(addon);
|
|
147
|
+
const intervalMs = options.waitIntervalMs ?? DEFAULT_CREATE_WAIT_INTERVAL_MS;
|
|
148
|
+
while (addon.state === 'provisioning') {
|
|
149
|
+
options.signal?.throwIfAborted();
|
|
150
|
+
// eslint-disable-next-line no-await-in-loop
|
|
151
|
+
await wait(intervalMs, options.signal);
|
|
152
|
+
// eslint-disable-next-line no-await-in-loop
|
|
153
|
+
addon = await platform.addOn.infoByApp(appIdentity, addon.name);
|
|
154
|
+
}
|
|
155
|
+
if (addon.state === 'deprovisioned') {
|
|
156
|
+
throw new AddonProvisioningFailedError(addon);
|
|
157
|
+
}
|
|
158
|
+
return addon;
|
|
159
|
+
}
|
|
160
|
+
function wait(ms, signal) {
|
|
161
|
+
return new Promise((resolve, reject) => {
|
|
162
|
+
function onAbort() {
|
|
163
|
+
clearTimeout(timer);
|
|
164
|
+
reject(signal.reason ?? new Error('Aborted'));
|
|
165
|
+
}
|
|
166
|
+
const timer = setTimeout(() => {
|
|
167
|
+
signal?.removeEventListener('abort', onAbort);
|
|
168
|
+
resolve();
|
|
169
|
+
}, ms);
|
|
170
|
+
if (signal) {
|
|
171
|
+
if (signal.aborted) {
|
|
172
|
+
clearTimeout(timer);
|
|
173
|
+
reject(signal.reason ?? new Error('Aborted'));
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* List the plans available for an add-on service, sorted ascending by
|
|
182
|
+
* `price.cents`. Plans without a price (or with equal prices) are
|
|
183
|
+
* returned later in the list.
|
|
184
|
+
*/
|
|
185
|
+
export async function listPlans(ctx, serviceIdentity, options = {}) {
|
|
186
|
+
options.signal?.throwIfAborted();
|
|
187
|
+
const plans = await ctx.platform.plan.listByAddOn(serviceIdentity);
|
|
188
|
+
return [...plans].sort(byPriceCentsAsc);
|
|
189
|
+
}
|
|
190
|
+
function byPriceCentsAsc(a, b) {
|
|
191
|
+
return sortableCents(a) - sortableCents(b);
|
|
192
|
+
}
|
|
193
|
+
function sortableCents(plan) {
|
|
194
|
+
const cents = plan.price?.cents;
|
|
195
|
+
return typeof cents === 'number' ? cents : Number.MAX_SAFE_INTEGER;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Resolve, describe, and decorate an add-on for display.
|
|
199
|
+
*
|
|
200
|
+
* - Resolves the add-on by identity (optionally scoped to an app).
|
|
201
|
+
* - Loads the add-on's attachments.
|
|
202
|
+
* - Returns a copy with `plan.price` set from `billed_price` (so the
|
|
203
|
+
* value reflects any grandfathered/contract pricing).
|
|
204
|
+
*
|
|
205
|
+
* If `appIdentity` is provided and the platform returns 404 (resource
|
|
206
|
+
* `add_on`), the resolve falls back to a global lookup. This handles
|
|
207
|
+
* the case where the add-on belongs to a different app than the one
|
|
208
|
+
* supplied.
|
|
209
|
+
*
|
|
210
|
+
* Requests the `version=3.sdk` accept variant with `addon_service,plan`
|
|
211
|
+
* expansion so the resolved add-on includes the full `Plan` shape
|
|
212
|
+
* (`price.unit`, etc.) needed to render pricing.
|
|
213
|
+
*/
|
|
214
|
+
export async function describeAddon(ctx, addonIdentity, options = {}) {
|
|
215
|
+
options.signal?.throwIfAborted();
|
|
216
|
+
// Accept-Expansion is scoped to the resolve call (addOn.resolution
|
|
217
|
+
// serializer accepts addon_service + plan expansions). The
|
|
218
|
+
// /addons/:id/addon-attachments endpoint uses Accept-Inclusion, not
|
|
219
|
+
// Accept-Expansion, and rejects an unknown expansion header.
|
|
220
|
+
const resolvingPlatform = ctx.platform.withHeaders({
|
|
221
|
+
Accept: 'application/vnd.heroku+json; version=3.sdk',
|
|
222
|
+
'Accept-Expansion': 'addon_service,plan',
|
|
223
|
+
});
|
|
224
|
+
const addon = await resolveAddonInternal(resolvingPlatform, addonIdentity, {
|
|
225
|
+
addonService: options.addonService,
|
|
226
|
+
appIdentity: options.appIdentity,
|
|
227
|
+
});
|
|
228
|
+
options.signal?.throwIfAborted();
|
|
229
|
+
const attachments = await ctx.platform.addOnAttachment.listByAddOn(addon.id);
|
|
230
|
+
const plan = addon.plan;
|
|
231
|
+
return {
|
|
232
|
+
...addon,
|
|
233
|
+
...(plan && { plan: { ...plan, price: grandfatheredPrice(addon) } }),
|
|
234
|
+
attachments,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Resolve a Platform add-on by identity.
|
|
239
|
+
*
|
|
240
|
+
* The add-on identity may be:
|
|
241
|
+
* - a UUID (`d5e3f2a4-...`)
|
|
242
|
+
* - a globally-unique name (`postgres-curved-12345`)
|
|
243
|
+
* - a namespaced credential reference (`postgres-curved-12345::SECONDARY`)
|
|
244
|
+
*
|
|
245
|
+
* When `appIdentity` is provided, the resolve is scoped to that app first
|
|
246
|
+
* and falls back to a global resolve if the platform returns 404 add_on.
|
|
247
|
+
* Namespaced identities (containing `::`) skip the app-scoped lookup
|
|
248
|
+
* because they are globally unique.
|
|
249
|
+
*
|
|
250
|
+
* `addonService` (e.g. `heroku-postgresql`) is filtered client-side after
|
|
251
|
+
* the resolve. The platform's server-side `addon_service` filter excludes
|
|
252
|
+
* alpha add-ons, so we don't pass it through.
|
|
253
|
+
*
|
|
254
|
+
* Errors:
|
|
255
|
+
* - throws `AddonNotFoundError` if no match is found
|
|
256
|
+
* - throws `AddonAmbiguousError` if multiple matches remain after filtering
|
|
257
|
+
*
|
|
258
|
+
* For attachment-based resolution (e.g. `DATABASE_URL` on a particular
|
|
259
|
+
* app), use `resolveAddonByAttachment` instead.
|
|
260
|
+
*/
|
|
261
|
+
export async function resolveAddon(ctx, addonIdentity, options = {}) {
|
|
262
|
+
options.signal?.throwIfAborted();
|
|
263
|
+
return resolveAddonInternal(ctx.platform, addonIdentity, options);
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Resolve a Platform add-on via one of its attachments on a given app.
|
|
267
|
+
*
|
|
268
|
+
* Use this when you have an attachment name (e.g. `DATABASE_URL`,
|
|
269
|
+
* `HEROKU_POSTGRESQL_GREEN`) on a known app, rather than an add-on
|
|
270
|
+
* identity. Calls `addOnAttachment.resolution` and returns the add-on
|
|
271
|
+
* the matched attachment points to.
|
|
272
|
+
*
|
|
273
|
+
* For resolving by add-on identity, use `resolveAddon`.
|
|
274
|
+
*/
|
|
275
|
+
export async function resolveAddonByAttachment(ctx, appIdentity, attachmentName, options = {}) {
|
|
276
|
+
options.signal?.throwIfAborted();
|
|
277
|
+
debug('resolveByAttachment app=%s attachment=%s', appIdentity, attachmentName);
|
|
278
|
+
const matches = await ctx.platform.addOnAttachment.resolution({
|
|
279
|
+
// eslint-disable-next-line camelcase
|
|
280
|
+
addon_attachment: attachmentName,
|
|
281
|
+
app: appIdentity,
|
|
282
|
+
});
|
|
283
|
+
const attachment = matches[0];
|
|
284
|
+
const addon = attachment?.addon;
|
|
285
|
+
if (!addon?.id || !addon.app?.id) {
|
|
286
|
+
debug('resolveByAttachment matches=%d (no usable add-on returned)', matches.length);
|
|
287
|
+
throw new AddonNotFoundError();
|
|
288
|
+
}
|
|
289
|
+
debug('resolveByAttachment resolved addon=%s app=%s', addon.id, addon.app.id);
|
|
290
|
+
return addon;
|
|
291
|
+
}
|
|
292
|
+
async function resolveAddonInternal(platform, addonIdentity, options = {}) {
|
|
293
|
+
const { addonService, appIdentity } = options;
|
|
294
|
+
const resolveBy = async (app) => {
|
|
295
|
+
const body = app ? { addon: addonIdentity, app } : { addon: addonIdentity };
|
|
296
|
+
debug('resolve addon=%s app=%s service=%s', addonIdentity, app ?? '<global>', addonService ?? '<any>');
|
|
297
|
+
const matches = await platform.addOn.resolution(body);
|
|
298
|
+
const filtered = addonService
|
|
299
|
+
? matches.filter(addon => addon.addon_service?.name === addonService)
|
|
300
|
+
: matches;
|
|
301
|
+
debug('resolve matches=%d filtered=%d (service=%s)', matches.length, filtered.length, addonService ?? '<any>');
|
|
302
|
+
const resolvedAddon = singularize(filtered);
|
|
303
|
+
debug('resolve resolved addon=%s app=%s', resolvedAddon.id, resolvedAddon.app.id);
|
|
304
|
+
return resolvedAddon;
|
|
305
|
+
};
|
|
306
|
+
if (!appIdentity || addonIdentity.includes('::')) {
|
|
307
|
+
debug('resolve scope=global reason=%s', appIdentity ? 'namespaced-identity' : 'no-app');
|
|
308
|
+
return resolveBy();
|
|
309
|
+
}
|
|
310
|
+
try {
|
|
311
|
+
return await resolveBy(appIdentity);
|
|
312
|
+
}
|
|
313
|
+
catch (error) {
|
|
314
|
+
if (isAddOnNotFound(error)) {
|
|
315
|
+
debug('resolve app-scope 404 add_on, falling back to global addon=%s', addonIdentity);
|
|
316
|
+
return resolveBy();
|
|
317
|
+
}
|
|
318
|
+
throw error;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
function isAddOnNotFound(error) {
|
|
322
|
+
return error instanceof NotFoundError && error.resource === 'add_on';
|
|
323
|
+
}
|
|
324
|
+
function singularize(matches) {
|
|
325
|
+
if (matches.length === 0) {
|
|
326
|
+
throw new AddonNotFoundError();
|
|
327
|
+
}
|
|
328
|
+
if (matches.length > 1) {
|
|
329
|
+
throw new AddonAmbiguousError(matches);
|
|
330
|
+
}
|
|
331
|
+
const match = matches[0];
|
|
332
|
+
if (!match.id || !match.app?.id) {
|
|
333
|
+
throw new Error(`Resolved add-on is missing required fields (id=${match.id}, app.id=${match.app?.id})`);
|
|
334
|
+
}
|
|
335
|
+
return match;
|
|
336
|
+
}
|
|
337
|
+
function grandfatheredPrice(addon) {
|
|
338
|
+
const price = addon.plan?.price;
|
|
339
|
+
return {
|
|
340
|
+
...price,
|
|
341
|
+
...(addon.billed_price && {
|
|
342
|
+
cents: addon.billed_price.cents,
|
|
343
|
+
contract: addon.billed_price.contract,
|
|
344
|
+
}),
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
export const addOnExtensions = extendResource('platform', 'addOn', ctx => ({
|
|
348
|
+
createAndWait: (appIdentity, body, options) => createAndWait(ctx, appIdentity, body, options),
|
|
349
|
+
describe: (addonIdentity, options) => describeAddon(ctx, addonIdentity, options),
|
|
350
|
+
listPlans: (serviceIdentity, options) => listPlans(ctx, serviceIdentity, options),
|
|
351
|
+
resolve: (addonIdentity, options) => resolveAddon(ctx, addonIdentity, options),
|
|
352
|
+
resolveByAttachment: (appIdentity, attachmentName, options) => resolveAddonByAttachment(ctx, appIdentity, attachmentName, options),
|
|
353
|
+
upgrade: (addonIdentity, plan, options) => upgrade(ctx, addonIdentity, plan, options),
|
|
354
|
+
}));
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { App } from '@heroku/types/3.sdk';
|
|
2
|
+
import type { ResourceCtx } from '../../../core/extend-resource.js';
|
|
3
|
+
import type { DescribeAppOptions } from './info.js';
|
|
4
|
+
export { type AppInfo, describeApp, type DescribeAppOptions, type PipelineCouplingDetail, } from './info.js';
|
|
5
|
+
export type AppMaintenanceOptions = {
|
|
6
|
+
signal?: AbortSignal;
|
|
7
|
+
};
|
|
8
|
+
export type GenerationKind = 'cedar' | 'fir';
|
|
9
|
+
export type GetGenerationOptions = {
|
|
10
|
+
signal?: AbortSignal;
|
|
11
|
+
};
|
|
12
|
+
export type IsShieldedOptions = {
|
|
13
|
+
signal?: AbortSignal;
|
|
14
|
+
};
|
|
15
|
+
export type GetProcessTierOptions = {
|
|
16
|
+
signal?: AbortSignal;
|
|
17
|
+
};
|
|
18
|
+
export type ProcessTier = 'basic' | 'eco' | 'performance' | 'private' | 'shield' | 'standard' | (string & {});
|
|
19
|
+
export declare function enableMaintenance(ctx: Pick<ResourceCtx, 'platform'>, appIdentity: string, options?: AppMaintenanceOptions): Promise<App>;
|
|
20
|
+
export declare function disableMaintenance(ctx: Pick<ResourceCtx, 'platform'>, appIdentity: string, options?: AppMaintenanceOptions): Promise<App>;
|
|
21
|
+
/**
|
|
22
|
+
* Look up the platform generation (`cedar` or `fir`) of an app.
|
|
23
|
+
*
|
|
24
|
+
* Issues `app.info` with the `version=3.sdk` Accept variant so the
|
|
25
|
+
* response includes the `generation` field, then normalizes the
|
|
26
|
+
* platform's strings to the `'cedar' | 'fir'` union. Returns
|
|
27
|
+
* `undefined` if the platform reports an unrecognized value (e.g. a
|
|
28
|
+
* future generation name we haven't taught the SDK about).
|
|
29
|
+
*/
|
|
30
|
+
export declare function getGeneration(ctx: Pick<ResourceCtx, 'platform'>, appIdentity: string, options?: GetGenerationOptions): Promise<GenerationKind | undefined>;
|
|
31
|
+
type AppInfoResult = App & {
|
|
32
|
+
process_tier?: string;
|
|
33
|
+
space?: null | {
|
|
34
|
+
shield?: boolean;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
type AppInfoPlatform = {
|
|
38
|
+
app: {
|
|
39
|
+
info(appIdentity: string): Promise<AppInfoResult>;
|
|
40
|
+
};
|
|
41
|
+
withOptions(opts: {
|
|
42
|
+
signal: AbortSignal;
|
|
43
|
+
}): AppInfoPlatform;
|
|
44
|
+
};
|
|
45
|
+
export declare function isShielded(ctx: {
|
|
46
|
+
platform: AppInfoPlatform;
|
|
47
|
+
}, appIdentity: string, options?: IsShieldedOptions): Promise<boolean>;
|
|
48
|
+
export declare function getProcessTier(ctx: {
|
|
49
|
+
platform: AppInfoPlatform;
|
|
50
|
+
}, appIdentity: string, options?: GetProcessTierOptions): Promise<ProcessTier | undefined>;
|
|
51
|
+
export declare const appExtensions: import("../../../index.js").ResourceExtension<"platform", "app", {
|
|
52
|
+
describe: (appIdentity: string, options?: DescribeAppOptions) => Promise<import("./info.js").AppInfo>;
|
|
53
|
+
disableMaintenance: (appIdentity: string, options?: AppMaintenanceOptions) => Promise<App>;
|
|
54
|
+
enableMaintenance: (appIdentity: string, options?: AppMaintenanceOptions) => Promise<App>;
|
|
55
|
+
getGeneration: (appIdentity: string, options?: GetGenerationOptions) => Promise<GenerationKind | undefined>;
|
|
56
|
+
getProcessTier: (appIdentity: string, options?: GetProcessTierOptions) => Promise<ProcessTier | undefined>;
|
|
57
|
+
isShielded: (appIdentity: string, options?: IsShieldedOptions) => Promise<boolean>;
|
|
58
|
+
}>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { extendResource } from '../../../core/extend-resource.js';
|
|
2
|
+
import { describeApp } from './info.js';
|
|
3
|
+
export { describeApp, } from './info.js';
|
|
4
|
+
export async function enableMaintenance(ctx, appIdentity, options = {}) {
|
|
5
|
+
options.signal?.throwIfAborted();
|
|
6
|
+
return ctx.platform.app.update(appIdentity, { maintenance: true });
|
|
7
|
+
}
|
|
8
|
+
export async function disableMaintenance(ctx, appIdentity, options = {}) {
|
|
9
|
+
options.signal?.throwIfAborted();
|
|
10
|
+
return ctx.platform.app.update(appIdentity, { maintenance: false });
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Look up the platform generation (`cedar` or `fir`) of an app.
|
|
14
|
+
*
|
|
15
|
+
* Issues `app.info` with the `version=3.sdk` Accept variant so the
|
|
16
|
+
* response includes the `generation` field, then normalizes the
|
|
17
|
+
* platform's strings to the `'cedar' | 'fir'` union. Returns
|
|
18
|
+
* `undefined` if the platform reports an unrecognized value (e.g. a
|
|
19
|
+
* future generation name we haven't taught the SDK about).
|
|
20
|
+
*/
|
|
21
|
+
export async function getGeneration(ctx, appIdentity, options = {}) {
|
|
22
|
+
options.signal?.throwIfAborted();
|
|
23
|
+
const platform = ctx.platform.withHeaders({
|
|
24
|
+
Accept: 'application/vnd.heroku+json; version=3.sdk',
|
|
25
|
+
});
|
|
26
|
+
const app = await platform.app.info(appIdentity);
|
|
27
|
+
return parseGeneration(app.generation);
|
|
28
|
+
}
|
|
29
|
+
function parseGeneration(value) {
|
|
30
|
+
if (value === 'cedar' || value === 'fir')
|
|
31
|
+
return value;
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
export async function isShielded(ctx, appIdentity, options = {}) {
|
|
35
|
+
options.signal?.throwIfAborted();
|
|
36
|
+
const platform = options.signal ? ctx.platform.withOptions({ signal: options.signal }) : ctx.platform;
|
|
37
|
+
const app = await platform.app.info(appIdentity);
|
|
38
|
+
return Boolean(app.space?.shield);
|
|
39
|
+
}
|
|
40
|
+
export async function getProcessTier(ctx, appIdentity, options = {}) {
|
|
41
|
+
options.signal?.throwIfAborted();
|
|
42
|
+
const platform = options.signal ? ctx.platform.withOptions({ signal: options.signal }) : ctx.platform;
|
|
43
|
+
const app = await platform.app.info(appIdentity);
|
|
44
|
+
return app.process_tier;
|
|
45
|
+
}
|
|
46
|
+
export const appExtensions = extendResource('platform', 'app', ctx => ({
|
|
47
|
+
describe: (appIdentity, options) => describeApp(ctx, appIdentity, options),
|
|
48
|
+
disableMaintenance: (appIdentity, options) => disableMaintenance(ctx, appIdentity, options),
|
|
49
|
+
enableMaintenance: (appIdentity, options) => enableMaintenance(ctx, appIdentity, options),
|
|
50
|
+
getGeneration: (appIdentity, options) => getGeneration(ctx, appIdentity, options),
|
|
51
|
+
getProcessTier: (appIdentity, options) => getProcessTier(ctx, appIdentity, options),
|
|
52
|
+
isShielded: (appIdentity, options) => isShielded(ctx, appIdentity, options),
|
|
53
|
+
}));
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { AddOn, App, Collaborator, Dyno, PipelineCoupling } from '@heroku/types/3.sdk';
|
|
2
|
+
import type { ResourceCtx } from '../../../core/extend-resource.js';
|
|
3
|
+
export type DescribeAppOptions = {
|
|
4
|
+
signal?: AbortSignal;
|
|
5
|
+
};
|
|
6
|
+
export type PipelineCouplingDetail = PipelineCoupling & {
|
|
7
|
+
pipeline: {
|
|
8
|
+
id?: string;
|
|
9
|
+
name: string;
|
|
10
|
+
};
|
|
11
|
+
stage: 'development' | 'production' | 'review' | 'staging' | 'test';
|
|
12
|
+
};
|
|
13
|
+
export type AppInfo = {
|
|
14
|
+
addons: AddOn[];
|
|
15
|
+
app: App;
|
|
16
|
+
collaborators: Collaborator[];
|
|
17
|
+
dynos: Dyno[];
|
|
18
|
+
pipelineCoupling: null | PipelineCouplingDetail;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Composite "describe this app" view: parallel-fetches add-ons, the app
|
|
22
|
+
* itself, dynos, collaborators, and the pipeline coupling.
|
|
23
|
+
*
|
|
24
|
+
* Hard-fails on add-ons or app (the response is meaningless without
|
|
25
|
+
* either). Soft-fails on dynos, collaborators, and pipeline coupling —
|
|
26
|
+
* any error collapses to an empty default. The broad .catch is
|
|
27
|
+
* intentional v1 fidelity with the Heroku CLI's apps:info; a future
|
|
28
|
+
* tightening to specific status codes is non-breaking.
|
|
29
|
+
*/
|
|
30
|
+
export declare function describeApp(ctx: Pick<ResourceCtx, 'platform'>, appIdentity: string, options?: DescribeAppOptions): Promise<AppInfo>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Composite "describe this app" view: parallel-fetches add-ons, the app
|
|
3
|
+
* itself, dynos, collaborators, and the pipeline coupling.
|
|
4
|
+
*
|
|
5
|
+
* Hard-fails on add-ons or app (the response is meaningless without
|
|
6
|
+
* either). Soft-fails on dynos, collaborators, and pipeline coupling —
|
|
7
|
+
* any error collapses to an empty default. The broad .catch is
|
|
8
|
+
* intentional v1 fidelity with the Heroku CLI's apps:info; a future
|
|
9
|
+
* tightening to specific status codes is non-breaking.
|
|
10
|
+
*/
|
|
11
|
+
export async function describeApp(ctx, appIdentity, options = {}) {
|
|
12
|
+
options.signal?.throwIfAborted();
|
|
13
|
+
const platform = options.signal
|
|
14
|
+
? ctx.platform.withOptions({ signal: options.signal })
|
|
15
|
+
: ctx.platform;
|
|
16
|
+
const [addons, app, dynos, collaborators, pipelineCoupling] = await Promise.all([
|
|
17
|
+
platform.addOn.listByApp(appIdentity),
|
|
18
|
+
platform.app.info(appIdentity),
|
|
19
|
+
platform.dyno.list(appIdentity).catch(() => []),
|
|
20
|
+
platform.collaborator.list(appIdentity).catch(() => []),
|
|
21
|
+
platform.pipelineCoupling.infoByApp(appIdentity)
|
|
22
|
+
.catch(() => null),
|
|
23
|
+
]);
|
|
24
|
+
return {
|
|
25
|
+
addons, app, collaborators, dynos, pipelineCoupling,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { App } from '@heroku/types/3.sdk';
|
|
2
|
+
import type { ResourceCtx } from '../../core/extend-resource.js';
|
|
3
|
+
export type AppMaintenanceOptions = {
|
|
4
|
+
signal?: AbortSignal;
|
|
5
|
+
};
|
|
6
|
+
export type GenerationKind = 'cedar' | 'fir';
|
|
7
|
+
export type GetGenerationOptions = {
|
|
8
|
+
signal?: AbortSignal;
|
|
9
|
+
};
|
|
10
|
+
export type IsShieldedOptions = {
|
|
11
|
+
signal?: AbortSignal;
|
|
12
|
+
};
|
|
13
|
+
export type GetProcessTierOptions = {
|
|
14
|
+
signal?: AbortSignal;
|
|
15
|
+
};
|
|
16
|
+
export type ProcessTier = 'eco' | 'basic' | 'standard' | 'performance' | 'private' | 'shield' | (string & {});
|
|
17
|
+
export declare function enableMaintenance(ctx: Pick<ResourceCtx, 'platform'>, appIdentity: string, options?: AppMaintenanceOptions): Promise<App>;
|
|
18
|
+
export declare function disableMaintenance(ctx: Pick<ResourceCtx, 'platform'>, appIdentity: string, options?: AppMaintenanceOptions): Promise<App>;
|
|
19
|
+
/**
|
|
20
|
+
* Look up the platform generation (`cedar` or `fir`) of an app.
|
|
21
|
+
*
|
|
22
|
+
* Issues `app.info` with the `version=3.sdk` Accept variant so the
|
|
23
|
+
* response includes the `generation` field, then normalizes the
|
|
24
|
+
* platform's strings to the `'cedar' | 'fir'` union. Returns
|
|
25
|
+
* `undefined` if the platform reports an unrecognized value (e.g. a
|
|
26
|
+
* future generation name we haven't taught the SDK about).
|
|
27
|
+
*/
|
|
28
|
+
export declare function getGeneration(ctx: Pick<ResourceCtx, 'platform'>, appIdentity: string, options?: GetGenerationOptions): Promise<GenerationKind | undefined>;
|
|
29
|
+
type AppInfoResult = App & {
|
|
30
|
+
process_tier?: string;
|
|
31
|
+
space?: {
|
|
32
|
+
shield?: boolean;
|
|
33
|
+
} | null;
|
|
34
|
+
};
|
|
35
|
+
type AppInfoPlatform = {
|
|
36
|
+
app: {
|
|
37
|
+
info(appIdentity: string): Promise<AppInfoResult>;
|
|
38
|
+
};
|
|
39
|
+
withOptions(opts: {
|
|
40
|
+
signal: AbortSignal;
|
|
41
|
+
}): AppInfoPlatform;
|
|
42
|
+
};
|
|
43
|
+
export declare function isShielded(ctx: {
|
|
44
|
+
platform: AppInfoPlatform;
|
|
45
|
+
}, appIdentity: string, options?: IsShieldedOptions): Promise<boolean>;
|
|
46
|
+
export declare function getProcessTier(ctx: {
|
|
47
|
+
platform: AppInfoPlatform;
|
|
48
|
+
}, appIdentity: string, options?: GetProcessTierOptions): Promise<ProcessTier | undefined>;
|
|
49
|
+
export declare const appExtensions: import("../../index.js").ResourceExtension<"platform", "app", {
|
|
50
|
+
disableMaintenance: (appIdentity: string, options?: AppMaintenanceOptions) => Promise<App>;
|
|
51
|
+
enableMaintenance: (appIdentity: string, options?: AppMaintenanceOptions) => Promise<App>;
|
|
52
|
+
getGeneration: (appIdentity: string, options?: GetGenerationOptions) => Promise<GenerationKind | undefined>;
|
|
53
|
+
getProcessTier: (appIdentity: string, options?: GetProcessTierOptions) => Promise<ProcessTier | undefined>;
|
|
54
|
+
isShielded: (appIdentity: string, options?: IsShieldedOptions) => Promise<boolean>;
|
|
55
|
+
}>;
|
|
56
|
+
export {};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { extendResource } from '../../core/extend-resource.js';
|
|
2
|
+
export async function enableMaintenance(ctx, appIdentity, options = {}) {
|
|
3
|
+
options.signal?.throwIfAborted();
|
|
4
|
+
return ctx.platform.app.update(appIdentity, { maintenance: true });
|
|
5
|
+
}
|
|
6
|
+
export async function disableMaintenance(ctx, appIdentity, options = {}) {
|
|
7
|
+
options.signal?.throwIfAborted();
|
|
8
|
+
return ctx.platform.app.update(appIdentity, { maintenance: false });
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Look up the platform generation (`cedar` or `fir`) of an app.
|
|
12
|
+
*
|
|
13
|
+
* Issues `app.info` with the `version=3.sdk` Accept variant so the
|
|
14
|
+
* response includes the `generation` field, then normalizes the
|
|
15
|
+
* platform's strings to the `'cedar' | 'fir'` union. Returns
|
|
16
|
+
* `undefined` if the platform reports an unrecognized value (e.g. a
|
|
17
|
+
* future generation name we haven't taught the SDK about).
|
|
18
|
+
*/
|
|
19
|
+
export async function getGeneration(ctx, appIdentity, options = {}) {
|
|
20
|
+
options.signal?.throwIfAborted();
|
|
21
|
+
const platform = ctx.platform.withHeaders({
|
|
22
|
+
Accept: 'application/vnd.heroku+json; version=3.sdk',
|
|
23
|
+
});
|
|
24
|
+
const app = await platform.app.info(appIdentity);
|
|
25
|
+
return parseGeneration(app.generation);
|
|
26
|
+
}
|
|
27
|
+
function parseGeneration(value) {
|
|
28
|
+
if (value === 'cedar' || value === 'fir')
|
|
29
|
+
return value;
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
export async function isShielded(ctx, appIdentity, options = {}) {
|
|
33
|
+
options.signal?.throwIfAborted();
|
|
34
|
+
const platform = options.signal ? ctx.platform.withOptions({ signal: options.signal }) : ctx.platform;
|
|
35
|
+
const app = await platform.app.info(appIdentity);
|
|
36
|
+
return Boolean(app.space?.shield);
|
|
37
|
+
}
|
|
38
|
+
export async function getProcessTier(ctx, appIdentity, options = {}) {
|
|
39
|
+
options.signal?.throwIfAborted();
|
|
40
|
+
const platform = options.signal ? ctx.platform.withOptions({ signal: options.signal }) : ctx.platform;
|
|
41
|
+
const app = await platform.app.info(appIdentity);
|
|
42
|
+
return app.process_tier;
|
|
43
|
+
}
|
|
44
|
+
export const appExtensions = extendResource('platform', 'app', ctx => ({
|
|
45
|
+
disableMaintenance: (appIdentity, options) => disableMaintenance(ctx, appIdentity, options),
|
|
46
|
+
enableMaintenance: (appIdentity, options) => enableMaintenance(ctx, appIdentity, options),
|
|
47
|
+
getGeneration: (appIdentity, options) => getGeneration(ctx, appIdentity, options),
|
|
48
|
+
getProcessTier: (appIdentity, options) => getProcessTier(ctx, appIdentity, options),
|
|
49
|
+
isShielded: (appIdentity, options) => isShielded(ctx, appIdentity, options),
|
|
50
|
+
}));
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { Formation } from '@heroku/types/3.sdk';
|
|
2
|
+
import type { StickyRouteOptions } from '../../../core/create-client.js';
|
|
3
|
+
import type { ResourceCtx } from '../../../core/extend-resource.js';
|
|
4
|
+
export type DynoOptions = {
|
|
5
|
+
signal?: AbortSignal;
|
|
6
|
+
};
|
|
7
|
+
export type ScaleDynosUpdate = {
|
|
8
|
+
dyno_size?: {
|
|
9
|
+
id?: string;
|
|
10
|
+
name?: string;
|
|
11
|
+
};
|
|
12
|
+
quantity?: number | string;
|
|
13
|
+
size?: string;
|
|
14
|
+
type: string;
|
|
15
|
+
};
|
|
16
|
+
export type RestartDynosTarget = {
|
|
17
|
+
dyno: string;
|
|
18
|
+
} | {
|
|
19
|
+
type: string;
|
|
20
|
+
};
|
|
21
|
+
type FormationClient = {
|
|
22
|
+
batchUpdate(appIdentity: string, body: {
|
|
23
|
+
updates: ScaleDynosUpdate[];
|
|
24
|
+
}): Promise<Formation[]>;
|
|
25
|
+
update(appIdentity: string, type: string, body: Omit<ScaleDynosUpdate, 'type'>): Promise<Formation>;
|
|
26
|
+
};
|
|
27
|
+
type ScalePlatform = {
|
|
28
|
+
formation: FormationClient;
|
|
29
|
+
withOptions(opts: StickyRouteOptions): ScalePlatform;
|
|
30
|
+
};
|
|
31
|
+
export declare function scaleDynos(ctx: {
|
|
32
|
+
platform: ScalePlatform;
|
|
33
|
+
}, appIdentity: string, updates: ScaleDynosUpdate, options?: DynoOptions): Promise<Formation>;
|
|
34
|
+
export declare function scaleDynos(ctx: {
|
|
35
|
+
platform: ScalePlatform;
|
|
36
|
+
}, appIdentity: string, updates: ScaleDynosUpdate[], options?: DynoOptions): Promise<Formation[]>;
|
|
37
|
+
export declare function restartDynos(ctx: Pick<ResourceCtx, 'platform'>, appIdentity: string, target?: RestartDynosTarget, options?: DynoOptions): Promise<void>;
|
|
38
|
+
export declare const dynoExtensions: import("../../../index.js").ResourceExtension<"platform", "dyno", {
|
|
39
|
+
restart: (appIdentity: string, target?: RestartDynosTarget, options?: DynoOptions) => Promise<void>;
|
|
40
|
+
scale: {
|
|
41
|
+
(appIdentity: string, updates: ScaleDynosUpdate, options?: DynoOptions): Promise<Formation>;
|
|
42
|
+
(appIdentity: string, updates: ScaleDynosUpdate[], options?: DynoOptions): Promise<Formation[]>;
|
|
43
|
+
};
|
|
44
|
+
}>;
|
|
45
|
+
export {};
|