@heroku/sdk 0.12.2 → 0.12.3

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.
@@ -7,7 +7,6 @@ export type DatabaseCredentialOptions = {
7
7
  export { create, type CreateDatabaseCredentialResult } from './create.js';
8
8
  export { destroy, type DestroyDatabaseCredentialResult } from './destroy.js';
9
9
  export { type GetDatabaseCredentialUrlResult, getUrl, } from './get-url.js';
10
- export { NotAdvancedDatabaseError } from './list.js';
11
10
  export { list, type ListDatabaseCredentialsResult } from './list.js';
12
11
  export { rotate, type RotateAllDatabaseCredentialsResult, type RotateDatabaseCredentialOptions, type RotateDatabaseCredentialOpts, type RotateDatabaseCredentialPreview, type RotateDatabaseCredentialResult, } from './rotate.js';
13
12
  export declare const databaseCredentialExtensions: import("../../../core/extend-resource.js").ResourceExtension<"data", "databaseCredential", {
@@ -7,7 +7,6 @@ import { rotate } from './rotate.js';
7
7
  export { create } from './create.js';
8
8
  export { destroy } from './destroy.js';
9
9
  export { getUrl, } from './get-url.js';
10
- export { NotAdvancedDatabaseError } from './list.js';
11
10
  export { list } from './list.js';
12
11
  export { rotate, } from './rotate.js';
13
12
  export const databaseCredentialExtensions = extendResource('data', 'databaseCredential', ctx => {
@@ -2,21 +2,6 @@ import type { PostgresCredentialListResult } from '@heroku/types/data';
2
2
  import type { ResourceCtx } from '../../../core/extend-resource.js';
3
3
  import type { ResolvedAddOn } from '../../platform/add-on/types.js';
4
4
  import { DatabaseCredentialOptions } from './index.js';
5
- /**
6
- * Thrown by `list` when the resolved add-on is not an Advanced-tier
7
- * database. Carries the resolved addon so callers that need its identity
8
- * (e.g. to point the user at the classic-tier equivalent) don't need a
9
- * second resolve call.
10
- */
11
- export declare class NotAdvancedDatabaseError extends Error {
12
- readonly addon: ResolvedAddOn;
13
- readonly id = "not_advanced_database";
14
- constructor(addon: ResolvedAddOn);
15
- get body(): {
16
- id: string;
17
- message: string;
18
- };
19
- }
20
5
  export type ListDatabaseCredentialsResult = {
21
6
  addon: ResolvedAddOn;
22
7
  credentials: PostgresCredentialListResult;
@@ -1,24 +1,6 @@
1
1
  import { withSignal } from '../../../core/with-signal.js';
2
2
  import { resolveAddonInternal } from '../../platform/add-on/resolve.js';
3
- import { getPostgresServiceName, isAdvancedDatabase } from '../internal/postgres-utils.js';
4
- /**
5
- * Thrown by `list` when the resolved add-on is not an Advanced-tier
6
- * database. Carries the resolved addon so callers that need its identity
7
- * (e.g. to point the user at the classic-tier equivalent) don't need a
8
- * second resolve call.
9
- */
10
- export class NotAdvancedDatabaseError extends Error {
11
- addon;
12
- id = 'not_advanced_database';
13
- constructor(addon) {
14
- super(`You can only list credentials on Advanced-tier databases. ${addon.name} isn't an Advanced-tier database.`);
15
- this.addon = addon;
16
- this.name = 'NotAdvancedDatabaseError';
17
- }
18
- get body() {
19
- return { id: this.id, message: this.message };
20
- }
21
- }
3
+ import { getPostgresServiceName, isAdvancedDatabase, NotAdvancedDatabaseError } from '../internal/postgres-utils.js';
22
4
  /**
23
5
  * Lists credentials on an Advanced-tier Postgres database. Classic-tier
24
6
  * databases don't support this endpoint; use `postgresDatabase.listCredentials`
@@ -36,7 +18,7 @@ export async function list(ctx, addonIdentity, options = {}) {
36
18
  addonService: getPostgresServiceName(), appIdentity,
37
19
  });
38
20
  if (!isAdvancedDatabase(addon)) {
39
- throw new NotAdvancedDatabaseError(addon);
21
+ throw new NotAdvancedDatabaseError(addon, `You can only list credentials on Advanced-tier databases. ${addon.name} isn't an Advanced-tier database.`);
40
22
  }
41
23
  const data = withSignal(ctx.data, signal);
42
24
  const credentials = await data.postgresCredential.list(addon.id);
@@ -1,9 +1,8 @@
1
- import type { PostgresDatabaseInfoCredentialResult as NonAdvancedCredentialInfo, PostgresCredentialListResult, PostgresDatabaseRotateCredentialResult, PostgresDatabaseRotateCredentialsResult, PostgresRotateCredentialsResult } from '@heroku/types/data';
1
+ import type { PostgresCredentialInfoResult as AdvancedCredentialInfo, PostgresDatabaseInfoCredentialResult as NonAdvancedCredentialInfo, PostgresDatabaseRotateCredentialResult, PostgresDatabaseRotateCredentialsResult, PostgresRotateCredentialsResult } from '@heroku/types/data';
2
2
  import { AddOnAttachment } from '@heroku/types/3.sdk';
3
3
  import type { ResourceCtx } from '../../../core/extend-resource.js';
4
4
  import type { ResolvedAddOn } from '../../platform/add-on/types.js';
5
5
  import type { DatabaseCredentialOptions } from './index.js';
6
- type AdvancedCredentialInfo = PostgresCredentialListResult['items'][number];
7
6
  export type RotateDatabaseCredentialOptions = DatabaseCredentialOptions & {
8
7
  /**
9
8
  * Fires before the rotate request, allowing the caller to perform any
@@ -53,4 +52,3 @@ export declare function rotate(ctx: Pick<ResourceCtx, 'data' | 'platform'>, addo
53
52
  all: true;
54
53
  }, options?: RotateDatabaseCredentialOptions): Promise<RotateAllDatabaseCredentialsResult>;
55
54
  export declare function rotate(ctx: Pick<ResourceCtx, 'data' | 'platform'>, addonIdentity: string, body?: RotateDatabaseCredentialOpts, options?: RotateDatabaseCredentialOptions): Promise<RotateDatabaseCredentialResult>;
56
- export {};
@@ -1,20 +1,29 @@
1
1
  import type { AddOn } from '@heroku/types/3.sdk';
2
2
  import { PostgresCredentialInfoResult as AdvancedCredentialInfo } from '@heroku/types/data';
3
3
  import type { ResourceCtx } from '../../../core/extend-resource.js';
4
- import type { AddOnAttachmentWithInclusions } from '../../platform/add-on-attachment/resolve.js';
4
+ import type { ResolvedAddOnAttachment } from '../../platform/add-on-attachment/resolve.js';
5
5
  import { CredentialInfo as NonAdvancedCredentialInfo } from '../postgres-database.js';
6
+ /**
7
+ * Thrown when an add-on is not an Advanced-tier database. Carries the add-on
8
+ * so callers that need its identity don't need a second resolve call.
9
+ */
10
+ export declare class NotAdvancedDatabaseError extends Error {
11
+ readonly addon: AddOn | ResolvedAddOnAttachment['addon'];
12
+ readonly id = "not_advanced_database";
13
+ constructor(addon: AddOn | ResolvedAddOnAttachment['addon'], message: string);
14
+ get body(): {
15
+ id: string;
16
+ message: string;
17
+ };
18
+ }
6
19
  /**
7
20
  * Returns the first legacy database add-on on the app.
8
21
  */
9
22
  export declare function findLegacyDatabase(ctx: Pick<ResourceCtx, 'platform'>, appIdentity: string): Promise<AddOn>;
10
23
  export declare function isAdvancedCredential(credential: AdvancedCredentialInfo | NonAdvancedCredentialInfo): credential is AdvancedCredentialInfo;
11
24
  export declare const getPostgresServiceName: () => string;
12
- /**
13
- * `AddOnAttachmentWithInclusions` is an internal type. The public `isPostgresAddon` takes `AddOn` only.
14
- */
15
- export declare const isPostgresAddonInternal: (addon: AddOn | AddOnAttachmentWithInclusions["addon"]) => boolean;
16
- export declare const isPostgresAddon: (addon: AddOn) => boolean;
17
- export declare const isAdvancedDatabase: (addon: AddOn) => boolean;
25
+ export declare const isPostgresAddon: (addon: AddOn | ResolvedAddOnAttachment["addon"]) => boolean;
26
+ export declare const isAdvancedDatabase: (addon: AddOn | ResolvedAddOnAttachment["addon"]) => boolean;
18
27
  export declare const isLegacyDatabase: (addon: AddOn) => boolean;
19
28
  export declare const isLegacyEssentialDatabase: (addon: AddOn) => boolean;
20
29
  export declare const isEssentialDatabase: (addon: AddOn) => boolean;
@@ -1,5 +1,21 @@
1
1
  import createDebug from 'debug';
2
2
  const debug = createDebug('heroku:sdk:resources:postgres-database');
3
+ /**
4
+ * Thrown when an add-on is not an Advanced-tier database. Carries the add-on
5
+ * so callers that need its identity don't need a second resolve call.
6
+ */
7
+ export class NotAdvancedDatabaseError extends Error {
8
+ addon;
9
+ id = 'not_advanced_database';
10
+ constructor(addon, message) {
11
+ super(message);
12
+ this.addon = addon;
13
+ this.name = 'NotAdvancedDatabaseError';
14
+ }
15
+ get body() {
16
+ return { id: this.id, message: this.message };
17
+ }
18
+ }
3
19
  /**
4
20
  * Returns the first legacy database add-on on the app.
5
21
  */
@@ -15,13 +31,9 @@ export function isAdvancedCredential(credential) {
15
31
  return 'roles' in credential;
16
32
  }
17
33
  export const getPostgresServiceName = () => process.env.HEROKU_POSTGRESQL_ADDON_NAME || process.env.HEROKU_DATA_SERVICE || 'heroku-postgresql';
18
- /**
19
- * `AddOnAttachmentWithInclusions` is an internal type. The public `isPostgresAddon` takes `AddOn` only.
20
- */
21
- export const isPostgresAddonInternal = (addon) => addon.plan?.name?.split(':', 2)[0] === getPostgresServiceName();
22
- export const isPostgresAddon = (addon) => isPostgresAddonInternal(addon);
23
- export const isAdvancedDatabase = (addon) => isPostgresAddonInternal(addon) && /^(advanced|performance)/.test(addon.plan?.name?.split(':', 2)[1] ?? '');
24
- export const isLegacyDatabase = (addon) => isPostgresAddonInternal(addon) && !isAdvancedDatabase(addon);
34
+ export const isPostgresAddon = (addon) => addon.plan?.name?.split(':', 2)[0] === getPostgresServiceName();
35
+ export const isAdvancedDatabase = (addon) => isPostgresAddon(addon) && /^(advanced|performance)/.test(addon.plan?.name?.split(':', 2)[1] ?? '');
36
+ export const isLegacyDatabase = (addon) => isPostgresAddon(addon) && !isAdvancedDatabase(addon);
25
37
  export const isLegacyEssentialDatabase = (addon) => isLegacyDatabase(addon) && /^(dev|basic|mini)/.test(addon.plan?.name?.split(':', 2)[1] ?? '');
26
38
  export const isEssentialDatabase = (addon) => isLegacyDatabase(addon) && (addon.plan?.name?.split(':', 2)[1] ?? '').startsWith('essential');
27
39
  export const isEssentialTier = (addon) => isEssentialDatabase(addon) || isLegacyEssentialDatabase(addon);
@@ -2,7 +2,7 @@ import { NotFoundError } from '@heroku/heroku-fetch';
2
2
  import createDebug from 'debug';
3
3
  import { withSignal } from '../../../core/with-signal.js';
4
4
  import { AddonAttachmentAmbiguousError, AddonAttachmentNotFoundError, resolveAddonAttachment } from '../../platform/add-on-attachment/resolve.js';
5
- import { getPostgresServiceName, isPostgresAddonInternal } from './postgres-utils.js';
5
+ import { getPostgresServiceName, isPostgresAddon } from './postgres-utils.js';
6
6
  const debug = createDebug('heroku:sdk:resources:database');
7
7
  const DEFAULT_PG_ATTACHMENT = 'DATABASE_URL';
8
8
  /**
@@ -118,7 +118,7 @@ async function listPostgresAttachments(ctx, appIdentity, attachmentId, error, si
118
118
  debug('listing attachments failed while building not-found error: %O', listError);
119
119
  throw new PgDatabaseNotFoundError(`Could not resolve ${stripUrlSuffix(attachmentId)} on ${appIdentity}`, { cause: error });
120
120
  }
121
- return attachments.filter(attachment => isPostgresAddonInternal(attachment.addon));
121
+ return attachments.filter(attachment => isPostgresAddon(attachment.addon));
122
122
  }
123
123
  /**
124
124
  * Used by `attachmentResolutionFallback` to resolve attachments via app config vars
@@ -1,7 +1,30 @@
1
+ import type { AddOnAttachment, AddOnAttachmentCreateOpts } from '@heroku/types/3.sdk';
1
2
  import type { ResourceCtx } from '../../core/extend-resource.js';
3
+ import type { ResolvedAddOn } from '../platform/add-on/types.js';
2
4
  export type ListCredentialsOptions = {
3
5
  signal?: AbortSignal;
4
6
  };
7
+ export type DeleteAttachmentOptions = {
8
+ beforeDelete?: (preview: {
9
+ addon: ResolvedAddOn;
10
+ attachment: AddOnAttachment;
11
+ }) => Promise<void> | void;
12
+ signal?: AbortSignal;
13
+ };
14
+ export type DeleteAttachmentResult = {
15
+ addon: ResolvedAddOn;
16
+ attachment: AddOnAttachment;
17
+ };
18
+ export type CreateAttachmentBody = Omit<AddOnAttachmentCreateOpts, 'addon' | 'app'>;
19
+ export type CreateAttachmentOptions = {
20
+ onAttached?: (attachment: AddOnAttachment) => Promise<void> | void;
21
+ signal?: AbortSignal;
22
+ };
23
+ export type CreateAttachmentResult = {
24
+ addon: ResolvedAddOn;
25
+ attachment: AddOnAttachment;
26
+ releaseVersion?: number;
27
+ };
5
28
  export type CredentialInfo = {
6
29
  credentials: Array<{
7
30
  connections?: null | number;
@@ -17,8 +40,32 @@ export type CredentialInfo = {
17
40
  uuid: string;
18
41
  };
19
42
  export declare function listCredentials(ctx: Pick<ResourceCtx, 'data' | 'platform'>, appIdentity: string, addonIdentity?: string, options?: ListCredentialsOptions): Promise<CredentialInfo[]>;
43
+ /**
44
+ * Looks up a Postgres attachment by name under an app, resolves its add-on,
45
+ * and deletes the attachment.
46
+ *
47
+ * Throws `NotAdvancedDatabaseError` (carrying the resolved add-on) if the
48
+ * attachment's database isn't an Advanced-tier database.
49
+ *
50
+ * If provided, `beforeDelete` runs after resolution/tier-checking but before
51
+ * the delete request fires. An abort during the hook prevents the delete.
52
+ */
53
+ export declare function deleteAttachment(ctx: Pick<ResourceCtx, 'platform'>, appIdentity: string, attachmentName: string, options?: DeleteAttachmentOptions): Promise<DeleteAttachmentResult>;
54
+ /**
55
+ * Resolves a source Postgres database by add-on identity, attaches it
56
+ * to an app, and fetches the latest release version.
57
+ *
58
+ * Throws `NotAdvancedDatabaseError` (carrying the resolved add-on) if the
59
+ * database isn't an Advanced-tier database.
60
+ *
61
+ * If provided, `onAttached` fires once the attachment is created but before
62
+ * the release version fetch.
63
+ */
64
+ export declare function createAttachment(ctx: Pick<ResourceCtx, 'platform'>, appIdentity: string, addonIdentity: string, body?: CreateAttachmentBody, options?: CreateAttachmentOptions): Promise<CreateAttachmentResult>;
20
65
  export declare const postgresDatabaseExtensions: import("../../core/extend-resource.js").ResourceExtension<"data", "postgresDatabase", {
66
+ createAttachment: (appIdentity: string, addonIdentity: string, body?: CreateAttachmentBody, options?: CreateAttachmentOptions) => Promise<CreateAttachmentResult>;
67
+ deleteAttachment: (appIdentity: string, attachmentName: string, options?: DeleteAttachmentOptions) => Promise<DeleteAttachmentResult>;
21
68
  listCredentials: (appIdentity: string, addonIdentity?: string, options?: ListCredentialsOptions) => Promise<CredentialInfo[]>;
22
69
  }>;
23
70
  export { type AttachmentFactors, parseAttachmentFactors, } from './internal/parse-attachment-factors.js';
24
- export { findLegacyDatabase, getPostgresServiceName, isAdvancedCredential, isAdvancedDatabase, isEssentialDatabase, isEssentialTier, isLegacyDatabase, isLegacyEssentialDatabase, isPostgresAddon, } from './internal/postgres-utils.js';
71
+ export { findLegacyDatabase, getPostgresServiceName, isAdvancedCredential, isAdvancedDatabase, isEssentialDatabase, isEssentialTier, isLegacyDatabase, isLegacyEssentialDatabase, isPostgresAddon, NotAdvancedDatabaseError, } from './internal/postgres-utils.js';
@@ -1,5 +1,9 @@
1
+ import { HerokuApiError } from '@heroku/heroku-fetch';
1
2
  import { extendResource } from '../../core/extend-resource.js';
2
3
  import { withSignal } from '../../core/with-signal.js';
4
+ import { AddonConfirmationRequiredError, AddonNotFoundError } from '../platform/add-on/errors.js';
5
+ import { resolveAddonInternal } from '../platform/add-on/resolve.js';
6
+ import { getPostgresServiceName, isAdvancedDatabase, NotAdvancedDatabaseError } from './internal/postgres-utils.js';
3
7
  import { resolvePgDatabase } from './internal/resolve-pg-database.js';
4
8
  export async function listCredentials(ctx, appIdentity, addonIdentity, options = {}) {
5
9
  options.signal?.throwIfAborted();
@@ -8,8 +12,87 @@ export async function listCredentials(ctx, appIdentity, addonIdentity, options =
8
12
  const data = withSignal(ctx.data, options.signal);
9
13
  return data.postgresDatabase.listCredentials(addon.id);
10
14
  }
15
+ /**
16
+ * Looks up a Postgres attachment by name under an app, resolves its add-on,
17
+ * and deletes the attachment.
18
+ *
19
+ * Throws `NotAdvancedDatabaseError` (carrying the resolved add-on) if the
20
+ * attachment's database isn't an Advanced-tier database.
21
+ *
22
+ * If provided, `beforeDelete` runs after resolution/tier-checking but before
23
+ * the delete request fires. An abort during the hook prevents the delete.
24
+ */
25
+ export async function deleteAttachment(ctx, appIdentity, attachmentName, options = {}) {
26
+ const { beforeDelete, signal } = options;
27
+ signal?.throwIfAborted();
28
+ // Bind the signal so the lookup and delete requests abort in flight.
29
+ const platform = withSignal(ctx.platform, signal);
30
+ const attachment = await platform.addOnAttachment.infoByApp(appIdentity, attachmentName);
31
+ const addon = await resolveAddonInternal(platform, attachment.addon.name, { addonService: getPostgresServiceName() });
32
+ if (!isAdvancedDatabase(addon)) {
33
+ throw new NotAdvancedDatabaseError(addon, `You can only detach Advanced-tier databases with postgresDatabase.deleteAttachment(). ${addon.name} isn't an Advanced-tier database.`);
34
+ }
35
+ await beforeDelete?.({ addon, attachment });
36
+ signal?.throwIfAborted();
37
+ const deletedAttachment = await platform.addOnAttachment.delete(attachment.id);
38
+ return { addon, attachment: deletedAttachment };
39
+ }
40
+ /**
41
+ * Resolves a source Postgres database by add-on identity, attaches it
42
+ * to an app, and fetches the latest release version.
43
+ *
44
+ * Throws `NotAdvancedDatabaseError` (carrying the resolved add-on) if the
45
+ * database isn't an Advanced-tier database.
46
+ *
47
+ * If provided, `onAttached` fires once the attachment is created but before
48
+ * the release version fetch.
49
+ */
50
+ export async function createAttachment(ctx, appIdentity, addonIdentity, body = {}, options = {}) {
51
+ const { onAttached, signal } = options;
52
+ signal?.throwIfAborted();
53
+ // Bind the signal so the resolve, create, and release-list requests abort in flight.
54
+ const platform = withSignal(ctx.platform, signal);
55
+ // Resolve scoped to the target app first. If the target app has no match, retry
56
+ // unscoped so attaching a database owned by a *different* app still resolves by
57
+ // its globally-unique name.
58
+ let addon;
59
+ try {
60
+ addon = await resolveAddonInternal(platform, addonIdentity, { addonService: getPostgresServiceName(), appIdentity });
61
+ }
62
+ catch (error) {
63
+ if (!(error instanceof AddonNotFoundError)) {
64
+ throw error;
65
+ }
66
+ addon = await resolveAddonInternal(platform, addonIdentity, { addonService: getPostgresServiceName() });
67
+ }
68
+ if (!isAdvancedDatabase(addon)) {
69
+ throw new NotAdvancedDatabaseError(addon, `You can only attach Advanced-tier databases with postgresDatabase.createAttachment(). ${addon.name} isn't an Advanced-tier database.`);
70
+ }
71
+ let attachment;
72
+ try {
73
+ attachment = await platform.addOnAttachment.create({
74
+ ...body,
75
+ addon: addon.name,
76
+ app: appIdentity,
77
+ });
78
+ }
79
+ catch (error) {
80
+ if (error instanceof HerokuApiError && error.id === 'confirmation_required') {
81
+ throw new AddonConfirmationRequiredError(error.message);
82
+ }
83
+ throw error;
84
+ }
85
+ await onAttached?.(attachment);
86
+ signal?.throwIfAborted();
87
+ const releases = await platform
88
+ .withHeaders({ Range: 'version ..; max=1, order=desc' })
89
+ .release.list(appIdentity);
90
+ return { addon, attachment, releaseVersion: releases[0]?.version };
91
+ }
11
92
  export const postgresDatabaseExtensions = extendResource('data', 'postgresDatabase', ctx => ({
93
+ createAttachment: (appIdentity, addonIdentity, body, options) => createAttachment(ctx, appIdentity, addonIdentity, body, options),
94
+ deleteAttachment: (appIdentity, attachmentName, options) => deleteAttachment(ctx, appIdentity, attachmentName, options),
12
95
  listCredentials: (appIdentity, addonIdentity, options) => listCredentials(ctx, appIdentity, addonIdentity, options),
13
96
  }));
14
97
  export { parseAttachmentFactors, } from './internal/parse-attachment-factors.js';
15
- export { findLegacyDatabase, getPostgresServiceName, isAdvancedCredential, isAdvancedDatabase, isEssentialDatabase, isEssentialTier, isLegacyDatabase, isLegacyEssentialDatabase, isPostgresAddon, } from './internal/postgres-utils.js';
98
+ export { findLegacyDatabase, getPostgresServiceName, isAdvancedCredential, isAdvancedDatabase, isEssentialDatabase, isEssentialTier, isLegacyDatabase, isLegacyEssentialDatabase, isPostgresAddon, NotAdvancedDatabaseError, } from './internal/postgres-utils.js';
@@ -0,0 +1,45 @@
1
+ import type { PostgresQuotaInfoResult, PostgresQuotaListResult, PostgresQuotaUpdateOpts, PostgresQuotaUpdateResult } from '@heroku/types/data';
2
+ import type { ResourceCtx } from '../../core/extend-resource.js';
3
+ import type { ResolvedAddOn } from '../platform/add-on/types.js';
4
+ export type PostgresQuotaOptions = {
5
+ appIdentity?: string;
6
+ signal?: AbortSignal;
7
+ };
8
+ export type ListPostgresQuotasResult = {
9
+ addon: ResolvedAddOn;
10
+ quotas: PostgresQuotaListResult;
11
+ };
12
+ export type GetPostgresQuotaResult = {
13
+ addon: ResolvedAddOn;
14
+ quota: PostgresQuotaInfoResult;
15
+ };
16
+ export type UpdatePostgresQuotaResult = {
17
+ addon: ResolvedAddOn;
18
+ quota: PostgresQuotaUpdateResult;
19
+ };
20
+ /**
21
+ * Lists quotas on an Advanced-tier Postgres database.
22
+ *
23
+ * Throws `NotAdvancedDatabaseError` (carrying the resolved addon) if
24
+ * `addonIdentity` doesn't resolve to an Advanced-tier database.
25
+ */
26
+ export declare function list(ctx: Pick<ResourceCtx, 'data' | 'platform'>, addonIdentity: string, options?: PostgresQuotaOptions): Promise<ListPostgresQuotasResult>;
27
+ /**
28
+ * Looks up a single quota on an Advanced-tier Postgres database.
29
+ *
30
+ * Throws `NotAdvancedDatabaseError` (carrying the resolved addon) if
31
+ * `addonIdentity` doesn't resolve to an Advanced-tier database.
32
+ */
33
+ export declare function info(ctx: Pick<ResourceCtx, 'data' | 'platform'>, addonIdentity: string, quotaType: string, options?: PostgresQuotaOptions): Promise<GetPostgresQuotaResult>;
34
+ /**
35
+ * Updates a quota's settings on an Advanced-tier Postgres database.
36
+ *
37
+ * Throws `NotAdvancedDatabaseError` (carrying the resolved addon) if
38
+ * `addonIdentity` doesn't resolve to an Advanced-tier database.
39
+ */
40
+ export declare function update(ctx: Pick<ResourceCtx, 'data' | 'platform'>, addonIdentity: string, quotaType: string, body: PostgresQuotaUpdateOpts, options?: PostgresQuotaOptions): Promise<UpdatePostgresQuotaResult>;
41
+ export declare const postgresQuotaExtensions: import("../../core/extend-resource.js").ResourceExtension<"data", "postgresQuota", {
42
+ info: (addonIdentity: string, quotaType: string, options?: PostgresQuotaOptions) => Promise<GetPostgresQuotaResult>;
43
+ list: (addonIdentity: string, options?: PostgresQuotaOptions) => Promise<ListPostgresQuotasResult>;
44
+ update: (addonIdentity: string, quotaType: string, body: PostgresQuotaUpdateOpts, options?: PostgresQuotaOptions) => Promise<UpdatePostgresQuotaResult>;
45
+ }>;
@@ -0,0 +1,61 @@
1
+ import { extendResource } from '../../core/extend-resource.js';
2
+ import { withSignal } from '../../core/with-signal.js';
3
+ import { resolveAddonInternal } from '../platform/add-on/resolve.js';
4
+ import { getPostgresServiceName, isAdvancedDatabase, NotAdvancedDatabaseError } from './internal/postgres-utils.js';
5
+ async function resolveTieredAddon(ctx, addonIdentity, options) {
6
+ const { appIdentity, signal } = options;
7
+ signal?.throwIfAborted();
8
+ // Bind the signal so the resolve request aborts in flight.
9
+ const platform = withSignal(ctx.platform, signal);
10
+ const addon = await resolveAddonInternal(platform, addonIdentity, {
11
+ addonService: getPostgresServiceName(), appIdentity,
12
+ });
13
+ if (!isAdvancedDatabase(addon)) {
14
+ throw new NotAdvancedDatabaseError(addon, `You can only manage quotas on Advanced-tier databases. ${addon.name} isn't an Advanced-tier database.`);
15
+ }
16
+ return addon;
17
+ }
18
+ /**
19
+ * Lists quotas on an Advanced-tier Postgres database.
20
+ *
21
+ * Throws `NotAdvancedDatabaseError` (carrying the resolved addon) if
22
+ * `addonIdentity` doesn't resolve to an Advanced-tier database.
23
+ */
24
+ export async function list(ctx, addonIdentity, options = {}) {
25
+ const addon = await resolveTieredAddon(ctx, addonIdentity, options);
26
+ // Bind the signal so the list request aborts in flight.
27
+ const data = withSignal(ctx.data, options.signal);
28
+ const quotas = await data.postgresQuota.list(addon.id);
29
+ return { addon, quotas };
30
+ }
31
+ /**
32
+ * Looks up a single quota on an Advanced-tier Postgres database.
33
+ *
34
+ * Throws `NotAdvancedDatabaseError` (carrying the resolved addon) if
35
+ * `addonIdentity` doesn't resolve to an Advanced-tier database.
36
+ */
37
+ export async function info(ctx, addonIdentity, quotaType, options = {}) {
38
+ const addon = await resolveTieredAddon(ctx, addonIdentity, options);
39
+ // Bind the signal so the info request aborts in flight.
40
+ const data = withSignal(ctx.data, options.signal);
41
+ const quota = await data.postgresQuota.info(addon.id, quotaType);
42
+ return { addon, quota };
43
+ }
44
+ /**
45
+ * Updates a quota's settings on an Advanced-tier Postgres database.
46
+ *
47
+ * Throws `NotAdvancedDatabaseError` (carrying the resolved addon) if
48
+ * `addonIdentity` doesn't resolve to an Advanced-tier database.
49
+ */
50
+ export async function update(ctx, addonIdentity, quotaType, body, options = {}) {
51
+ const addon = await resolveTieredAddon(ctx, addonIdentity, options);
52
+ // Bind the signal so the update request aborts in flight.
53
+ const data = withSignal(ctx.data, options.signal);
54
+ const quota = await data.postgresQuota.update(addon.id, quotaType, body);
55
+ return { addon, quota };
56
+ }
57
+ export const postgresQuotaExtensions = extendResource('data', 'postgresQuota', ctx => ({
58
+ info: (addonIdentity, quotaType, options) => info(ctx, addonIdentity, quotaType, options),
59
+ list: (addonIdentity, options) => list(ctx, addonIdentity, options),
60
+ update: (addonIdentity, quotaType, body, options) => update(ctx, addonIdentity, quotaType, body, options),
61
+ }));
@@ -0,0 +1,27 @@
1
+ import type { PostgresRunUpgradeOpts, PostgresRunUpgradeResult } from '@heroku/types/data';
2
+ import type { ResourceCtx } from '../../core/extend-resource.js';
3
+ import type { ResolvedPgDatabase } from './internal/resolve-pg-database.js';
4
+ export type RunUpgradeOptions = {
5
+ beforeRunUpgrade?: (preview: {
6
+ addon: ResolvedPgDatabase;
7
+ currentVersion: string;
8
+ }) => Promise<void> | void;
9
+ signal?: AbortSignal;
10
+ };
11
+ export type RunUpgradeResult = {
12
+ addon: ResolvedPgDatabase;
13
+ result: PostgresRunUpgradeResult;
14
+ };
15
+ /**
16
+ * Resolves a Postgres database by attachment identity and runs a version upgrade.
17
+ *
18
+ * Throws `NotAdvancedDatabaseError` (carrying the resolved add-on) if the
19
+ * database isn't an Advanced-tier database. If provided, `beforeRunUpgrade`
20
+ * runs after resolution/tier-checking (and the current-version lookup) but
21
+ * before the upgrade request fires; an abort during the hook prevents the
22
+ * upgrade.
23
+ */
24
+ export declare function runUpgrade(ctx: Pick<ResourceCtx, 'data' | 'platform'>, appIdentity: string, addonIdentity?: string, body?: PostgresRunUpgradeOpts, options?: RunUpgradeOptions): Promise<RunUpgradeResult>;
25
+ export declare const postgresExtensions: import("../../core/extend-resource.js").ResourceExtension<"data", "postgres", {
26
+ runUpgrade: (appIdentity: string, addonIdentity?: string, body?: PostgresRunUpgradeOpts, options?: RunUpgradeOptions) => Promise<RunUpgradeResult>;
27
+ }>;
@@ -0,0 +1,31 @@
1
+ import { extendResource } from '../../core/extend-resource.js';
2
+ import { withSignal } from '../../core/with-signal.js';
3
+ import { isAdvancedDatabase, NotAdvancedDatabaseError } from './internal/postgres-utils.js';
4
+ import { resolvePgDatabase } from './internal/resolve-pg-database.js';
5
+ /**
6
+ * Resolves a Postgres database by attachment identity and runs a version upgrade.
7
+ *
8
+ * Throws `NotAdvancedDatabaseError` (carrying the resolved add-on) if the
9
+ * database isn't an Advanced-tier database. If provided, `beforeRunUpgrade`
10
+ * runs after resolution/tier-checking (and the current-version lookup) but
11
+ * before the upgrade request fires; an abort during the hook prevents the
12
+ * upgrade.
13
+ */
14
+ export async function runUpgrade(ctx, appIdentity, addonIdentity, body = {}, options = {}) {
15
+ const { beforeRunUpgrade, signal } = options;
16
+ signal?.throwIfAborted();
17
+ const addon = await resolvePgDatabase(ctx, appIdentity, { input: addonIdentity, signal });
18
+ if (!isAdvancedDatabase(addon)) {
19
+ throw new NotAdvancedDatabaseError(addon, `You can only use postgres.runUpgrade() on Advanced-tier databases. ${addon.name} isn't an Advanced-tier database.`);
20
+ }
21
+ // Bind the signal so the info and upgrade requests abort in flight.
22
+ const data = withSignal(ctx.data, signal);
23
+ const { version: currentVersion } = await data.postgres.info(addon.id);
24
+ await beforeRunUpgrade?.({ addon, currentVersion });
25
+ signal?.throwIfAborted();
26
+ const result = await data.postgres.runUpgrade(addon.id, body);
27
+ return { addon, result };
28
+ }
29
+ export const postgresExtensions = extendResource('data', 'postgres', ctx => ({
30
+ runUpgrade: (appIdentity, addonIdentity, body, options) => runUpgrade(ctx, appIdentity, addonIdentity, body, options),
31
+ }));
@@ -2,6 +2,8 @@ export { databaseCredentialExtensions } from '../data/database-credential/index.
2
2
  export { databaseExtensions } from '../data/database.js';
3
3
  export { maintenanceExtensions } from '../data/maintenance.js';
4
4
  export { postgresDatabaseExtensions } from '../data/postgres-database.js';
5
+ export { postgresQuotaExtensions } from '../data/postgres-quota.js';
6
+ export { postgresExtensions } from '../data/postgres.js';
5
7
  export { redisExtensions } from '../data/redis/index.js';
6
8
  export { type TransferSchedule, transferScheduleExtensions } from '../data/transfer-schedule.js';
7
9
  export { backupExtensions, type CaptureAndWaitOptions, type RestoreAndWaitOptions, restoreExtensions, transferExtensions, TransferFailedError, TransferTimeoutError, type WaitForTransferOptions, } from '../data/transfer/index.js';
@@ -2,6 +2,8 @@ export { databaseCredentialExtensions } from '../data/database-credential/index.
2
2
  export { databaseExtensions } from '../data/database.js';
3
3
  export { maintenanceExtensions } from '../data/maintenance.js';
4
4
  export { postgresDatabaseExtensions } from '../data/postgres-database.js';
5
+ export { postgresQuotaExtensions } from '../data/postgres-quota.js';
6
+ export { postgresExtensions } from '../data/postgres.js';
5
7
  export { redisExtensions } from '../data/redis/index.js';
6
8
  export { transferScheduleExtensions } from '../data/transfer-schedule.js';
7
9
  export { backupExtensions, restoreExtensions, transferExtensions, TransferFailedError, TransferTimeoutError, } from '../data/transfer/index.js';
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const SDK_VERSION = "0.12.2";
1
+ export declare const SDK_VERSION = "0.12.3";
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated from package.json by scripts/write-version.mjs (build prebuild). Do not edit.
2
- export const SDK_VERSION = '0.12.2';
2
+ export const SDK_VERSION = '0.12.3';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heroku/sdk",
3
- "version": "0.12.2",
3
+ "version": "0.12.3",
4
4
  "description": "Heroku SDK",
5
5
  "author": "Heroku",
6
6
  "type": "module",