@pithy-sh/support 0.2.6 → 0.2.8
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/dist/capability.d.ts.map +1 -1
- package/dist/capability.js +2 -1
- package/dist/provision/provisionSupport.d.ts +32 -12
- package/dist/provision/provisionSupport.d.ts.map +1 -1
- package/dist/provision/provisionSupport.js +27 -12
- package/dist/version.generated.d.ts +1 -1
- package/dist/version.generated.js +1 -1
- package/package.json +7 -7
- package/src/capability.ts +6 -3
- package/src/provision/provisionSupport.ts +48 -17
- package/src/version.generated.ts +1 -1
package/dist/capability.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capability.d.ts","sourceRoot":"","sources":["../src/capability.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mCAAmC,CAAC;AAGnE,OAAO,EAAE,KAAK,UAAU,EAAoB,MAAM,0CAA0C,CAAC;AAC7F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAE3E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAItE,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,kBAAkB,EAGxB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAqB,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAO9E,OAAO,EAAkB,KAAK,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAI7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAErD;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAE5C,+FAA+F;AAC/F,MAAM,MAAM,cAAc,GAAG,kBAAkB,GAAG;IAChD,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,2BAA2B;IAC3B,MAAM,EAAE,aAAa,CAAC;IACtB,8BAA8B;IAC9B,UAAU,EAAE,iBAAiB,CAAC;IAC9B,0CAA0C;IAC1C,QAAQ,EAAE,oBAAoB,CAAC;IAC/B;;;;;OAKG;IACH,YAAY,EAAE,eAAe,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC;IACrD;;;;;;;;OAQG;IACH,IAAI,EAAE,SAAS,CAAC;CACjB;AA4ED;;;;;GAKG;AACH,MAAM,WAAW,iBACf,SAAQ,UAAU,CAAC,eAAe,EAAE,kBAAkB,EAAE,SAAS,EAAE,OAAO,gBAAgB,CAAC;IAC3F,mCAAmC;IACnC,aAAa,EAAE,aAAa,CAAC;IAC7B,wGAAwG;IACxG,UAAU,EAAE,iBAAiB,CAAC;IAC9B,0CAA0C;IAC1C,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,iFAAiF;IACjF,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,OAAO,CAAC,OAAO,GAAE,cAAmB,GAAG,iBAAiB,
|
|
1
|
+
{"version":3,"file":"capability.d.ts","sourceRoot":"","sources":["../src/capability.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mCAAmC,CAAC;AAGnE,OAAO,EAAE,KAAK,UAAU,EAAoB,MAAM,0CAA0C,CAAC;AAC7F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAE3E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAItE,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,kBAAkB,EAGxB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAqB,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAO9E,OAAO,EAAkB,KAAK,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAI7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAErD;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAE5C,+FAA+F;AAC/F,MAAM,MAAM,cAAc,GAAG,kBAAkB,GAAG;IAChD,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,2BAA2B;IAC3B,MAAM,EAAE,aAAa,CAAC;IACtB,8BAA8B;IAC9B,UAAU,EAAE,iBAAiB,CAAC;IAC9B,0CAA0C;IAC1C,QAAQ,EAAE,oBAAoB,CAAC;IAC/B;;;;;OAKG;IACH,YAAY,EAAE,eAAe,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC;IACrD;;;;;;;;OAQG;IACH,IAAI,EAAE,SAAS,CAAC;CACjB;AA4ED;;;;;GAKG;AACH,MAAM,WAAW,iBACf,SAAQ,UAAU,CAAC,eAAe,EAAE,kBAAkB,EAAE,SAAS,EAAE,OAAO,gBAAgB,CAAC;IAC3F,mCAAmC;IACnC,aAAa,EAAE,aAAa,CAAC;IAC7B,wGAAwG;IACxG,UAAU,EAAE,iBAAiB,CAAC;IAC9B,0CAA0C;IAC1C,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,iFAAiF;IACjF,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,OAAO,CAAC,OAAO,GAAE,cAAmB,GAAG,iBAAiB,CAsGvE;AAED,2GAA2G;AAC3G,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,UAAU,GAAG,UAAU,IAAI,iBAAiB,CAE3F"}
|
package/dist/capability.js
CHANGED
|
@@ -12,7 +12,7 @@ import { registerSupportRoutes } from "./http/routes.js";
|
|
|
12
12
|
import { createSupportEmailHandler } from "./inbound/handler.js";
|
|
13
13
|
import { support_0001_threads } from "./migrations/0001_threads.js";
|
|
14
14
|
import { supportExampleSeed } from "./seeds/example.js";
|
|
15
|
-
import { PACKAGE_VERSION } from "./version.generated.js";
|
|
15
|
+
import { PACKAGE_NAME, PACKAGE_VERSION } from "./version.generated.js";
|
|
16
16
|
import { noopEmit } from "@pithy-sh/core/src/audit/recorder";
|
|
17
17
|
import { defineCapability } from "@pithy-sh/core/src/capability/capability";
|
|
18
18
|
import { workflowBindings } from "@pithy-sh/core/src/workflow/bindings";
|
|
@@ -141,6 +141,7 @@ function support(options = {}) {
|
|
|
141
141
|
const capability = defineCapability({
|
|
142
142
|
name: "support",
|
|
143
143
|
version: PACKAGE_VERSION,
|
|
144
|
+
package: PACKAGE_NAME,
|
|
144
145
|
dependsOn: ["secrets"],
|
|
145
146
|
secretRegistry: supportSecretsRegistry,
|
|
146
147
|
inapplicableSecrets: inapplicableAttachmentSecrets(resolved),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2026 Pithy
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
import type { DeclaredEnvironments } from "@pithy-sh/core/src/naming/environment";
|
|
4
|
-
import { type ManagedEnvironment } from "@pithy-sh/secrets/src/scope";
|
|
4
|
+
import { type DeprovisionTarget, type ManagedEnvironment } from "@pithy-sh/secrets/src/scope";
|
|
5
5
|
/**
|
|
6
6
|
* The provisioning orchestration for the support capability — the live counterpart to
|
|
7
7
|
* `pithy add support`'s config wiring.
|
|
@@ -131,9 +131,17 @@ export interface SupportProvisionResult {
|
|
|
131
131
|
export declare function provisionSupport(provisioner: SupportProvisioner, environments: DeclaredEnvironments | readonly string[]): Promise<SupportProvisionResult>;
|
|
132
132
|
/** The teardown seam — the inverse of {@link SupportProvisioner}. Every step idempotent. */
|
|
133
133
|
export interface SupportDeprovisioner {
|
|
134
|
+
/**
|
|
135
|
+
* Whether the env's classification worker is deployed. Read-only. What a teardown asks of every *other*
|
|
136
|
+
* environment before it removes the bucket or the rule they share (#591).
|
|
137
|
+
*/
|
|
138
|
+
hasWorker(env: ManagedEnvironment): Promise<boolean>;
|
|
134
139
|
/** Delete the env's classification worker. Idempotent. */
|
|
135
140
|
deleteWorker(env: ManagedEnvironment): Promise<void>;
|
|
136
|
-
/**
|
|
141
|
+
/**
|
|
142
|
+
* Remove the inbound routing rule, so mail stops being delivered here. Idempotent. Called only when the teardown
|
|
143
|
+
* was asked to remove it (`removeRouting`).
|
|
144
|
+
*/
|
|
137
145
|
removeRoutingRule(): Promise<{
|
|
138
146
|
removed: boolean;
|
|
139
147
|
}>;
|
|
@@ -145,20 +153,32 @@ export interface SupportDeprovisioner {
|
|
|
145
153
|
}
|
|
146
154
|
/** Teardown options. By default the bucket is **kept**: it holds correspondence, not cache. */
|
|
147
155
|
export interface SupportDeprovisionOptions {
|
|
148
|
-
/** Also delete the R2 bucket and its contents. Off by default. */
|
|
156
|
+
/** Also delete the R2 bucket and its contents. Off by default, and only with the last environment. */
|
|
149
157
|
deleteStorage?: boolean;
|
|
158
|
+
/** Also remove the inbound routing rule. Off by default, and only with the last environment. */
|
|
159
|
+
removeRouting?: boolean;
|
|
160
|
+
}
|
|
161
|
+
/** What a support teardown did. */
|
|
162
|
+
export interface SupportDeprovisionResult {
|
|
163
|
+
/** The one environment torn down. */
|
|
164
|
+
env: ManagedEnvironment;
|
|
165
|
+
/** Whether this run removed the inbound routing rule. */
|
|
166
|
+
routingRuleRemoved: boolean;
|
|
150
167
|
}
|
|
151
168
|
/**
|
|
152
|
-
* Tear down
|
|
169
|
+
* Tear down **one named environment's** support infrastructure, reversing {@link provisionSupport} for it.
|
|
153
170
|
*
|
|
154
|
-
*
|
|
155
|
-
* removing the workers that would have handled it, or messages land in a Worker with no classification
|
|
156
|
-
* host during the teardown. Stored correspondence is preserved unless explicitly requested — losing an
|
|
157
|
-
* adopter's support history to a teardown flag would be unrecoverable.
|
|
171
|
+
* In order, and the order is the contract:
|
|
158
172
|
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
*
|
|
173
|
+
* 1. Resolve the target (`@pithy-sh/secrets`' `deprovisionTarget`) — refused with nothing read when none was
|
|
174
|
+
* named, or one the project does not declare. This used to walk every declared environment, so a run meant
|
|
175
|
+
* for staging took production's classification worker with it (#591).
|
|
176
|
+
* 2. With `removeRouting` or `deleteStorage`: refuse while any other declared environment still runs a
|
|
177
|
+
* classification worker (`assertSharedLeavesLast`). The rule and the bucket are one per project — the bucket
|
|
178
|
+
* is every environment's support history — so they go with the last environment, never a staging teardown.
|
|
179
|
+
* 3. Remove the routing rule, when asked, **first**: stop new mail arriving before removing the worker that would
|
|
180
|
+
* have handled it.
|
|
181
|
+
* 4. Delete the environment's worker, then — only when asked — the bucket and everything in it.
|
|
162
182
|
*/
|
|
163
|
-
export declare function deprovisionSupport(deprovisioner: SupportDeprovisioner,
|
|
183
|
+
export declare function deprovisionSupport(deprovisioner: SupportDeprovisioner, target: DeprovisionTarget, options?: SupportDeprovisionOptions): Promise<SupportDeprovisionResult>;
|
|
164
184
|
//# sourceMappingURL=provisionSupport.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provisionSupport.d.ts","sourceRoot":"","sources":["../../src/provision/provisionSupport.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAIlF,OAAO,
|
|
1
|
+
{"version":3,"file":"provisionSupport.d.ts","sourceRoot":"","sources":["../../src/provision/provisionSupport.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,7 +4,7 @@ import { SUPPORT_CAPABILITY } from "../workflows/specs.js";
|
|
|
4
4
|
import { GLOBAL_SCOPE } from "@pithy-sh/core/src/naming/environment";
|
|
5
5
|
import { resourceName } from "@pithy-sh/core/src/naming/resource";
|
|
6
6
|
import { resourceNames } from "@pithy-sh/core/src/naming/resourceNames";
|
|
7
|
-
import { managedEnvironments } from "@pithy-sh/secrets/src/scope";
|
|
7
|
+
import { assertSharedLeavesLast, deprovisionTarget, managedEnvironments } from "@pithy-sh/secrets/src/scope";
|
|
8
8
|
//#region src/provision/provisionSupport.ts
|
|
9
9
|
/**
|
|
10
10
|
* The provisioning orchestration for the support capability — the live counterpart to
|
|
@@ -99,21 +99,36 @@ async function provisionSupport(provisioner, environments) {
|
|
|
99
99
|
};
|
|
100
100
|
}
|
|
101
101
|
/**
|
|
102
|
-
* Tear down
|
|
102
|
+
* Tear down **one named environment's** support infrastructure, reversing {@link provisionSupport} for it.
|
|
103
103
|
*
|
|
104
|
-
*
|
|
105
|
-
* removing the workers that would have handled it, or messages land in a Worker with no classification
|
|
106
|
-
* host during the teardown. Stored correspondence is preserved unless explicitly requested — losing an
|
|
107
|
-
* adopter's support history to a teardown flag would be unrecoverable.
|
|
104
|
+
* In order, and the order is the contract:
|
|
108
105
|
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
106
|
+
* 1. Resolve the target (`@pithy-sh/secrets`' `deprovisionTarget`) — refused with nothing read when none was
|
|
107
|
+
* named, or one the project does not declare. This used to walk every declared environment, so a run meant
|
|
108
|
+
* for staging took production's classification worker with it (#591).
|
|
109
|
+
* 2. With `removeRouting` or `deleteStorage`: refuse while any other declared environment still runs a
|
|
110
|
+
* classification worker (`assertSharedLeavesLast`). The rule and the bucket are one per project — the bucket
|
|
111
|
+
* is every environment's support history — so they go with the last environment, never a staging teardown.
|
|
112
|
+
* 3. Remove the routing rule, when asked, **first**: stop new mail arriving before removing the worker that would
|
|
113
|
+
* have handled it.
|
|
114
|
+
* 4. Delete the environment's worker, then — only when asked — the bucket and everything in it.
|
|
112
115
|
*/
|
|
113
|
-
async function deprovisionSupport(deprovisioner,
|
|
114
|
-
|
|
115
|
-
|
|
116
|
+
async function deprovisionSupport(deprovisioner, target, options = {}) {
|
|
117
|
+
const env = deprovisionTarget(target);
|
|
118
|
+
await assertSharedLeavesLast(env, target.declared, (other) => deprovisioner.hasWorker(other), [...options.deleteStorage ? [{
|
|
119
|
+
what: "the support bucket",
|
|
120
|
+
flag: "--storage"
|
|
121
|
+
}] : [], ...options.removeRouting ? [{
|
|
122
|
+
what: "the inbound routing rule",
|
|
123
|
+
flag: "--routing-zone"
|
|
124
|
+
}] : []]);
|
|
125
|
+
const routingRuleRemoved = options.removeRouting ? (await deprovisioner.removeRoutingRule()).removed : false;
|
|
126
|
+
await deprovisioner.deleteWorker(env);
|
|
116
127
|
if (options.deleteStorage) await deprovisioner.deleteBucket();
|
|
128
|
+
return {
|
|
129
|
+
env,
|
|
130
|
+
routingRuleRemoved
|
|
131
|
+
};
|
|
117
132
|
}
|
|
118
133
|
//#endregion
|
|
119
134
|
export { deprovisionSupport, provisionSupport, supportRoutingRuleName, supportWorkerName };
|
|
@@ -3,5 +3,5 @@
|
|
|
3
3
|
/** This package's npm name — the join key against a release feed. */
|
|
4
4
|
export declare const PACKAGE_NAME = "@pithy-sh/support";
|
|
5
5
|
/** This package's version, stamped from its own package.json at generation time. */
|
|
6
|
-
export declare const PACKAGE_VERSION = "0.2.
|
|
6
|
+
export declare const PACKAGE_VERSION = "0.2.8";
|
|
7
7
|
//# sourceMappingURL=version.generated.d.ts.map
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
/** This package's npm name — the join key against a release feed. */
|
|
5
5
|
const PACKAGE_NAME = "@pithy-sh/support";
|
|
6
6
|
/** This package's version, stamped from its own package.json at generation time. */
|
|
7
|
-
const PACKAGE_VERSION = "0.2.
|
|
7
|
+
const PACKAGE_VERSION = "0.2.8";
|
|
8
8
|
//#endregion
|
|
9
9
|
export { PACKAGE_NAME, PACKAGE_VERSION };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pithy-sh/support",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.8",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -40,12 +40,12 @@
|
|
|
40
40
|
"tldts": "^7.4.10"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
|
-
"@pithy-sh/auth": "^0.6.
|
|
44
|
-
"@pithy-sh/core": "^0.
|
|
45
|
-
"@pithy-sh/email": "^0.3.
|
|
46
|
-
"@pithy-sh/payments": "^0.3.
|
|
47
|
-
"@pithy-sh/secrets": "^0.2.
|
|
48
|
-
"@pithy-sh/storage": "^0.2.
|
|
43
|
+
"@pithy-sh/auth": "^0.6.3",
|
|
44
|
+
"@pithy-sh/core": "^0.7.3",
|
|
45
|
+
"@pithy-sh/email": "^0.3.7",
|
|
46
|
+
"@pithy-sh/payments": "^0.3.5",
|
|
47
|
+
"@pithy-sh/secrets": "^0.2.6",
|
|
48
|
+
"@pithy-sh/storage": "^0.2.6",
|
|
49
49
|
"hono": "^4.13.2",
|
|
50
50
|
"kysely": "^0.29.0",
|
|
51
51
|
"zod": "^4.4.0"
|
package/src/capability.ts
CHANGED
|
@@ -28,7 +28,7 @@ import { support_0001_threads } from "./migrations/0001_threads";
|
|
|
28
28
|
import { resolveReplies, type SupportReplySnippets } from "./reply/snippets";
|
|
29
29
|
import { inapplicableAttachmentSecrets, SUPPORT_BUCKET_BINDING, supportSecretsRegistry } from "./secret/registry";
|
|
30
30
|
import { supportExampleSeed } from "./seeds/example";
|
|
31
|
-
import { PACKAGE_VERSION } from "./version.generated";
|
|
31
|
+
import { PACKAGE_NAME, PACKAGE_VERSION } from "./version.generated";
|
|
32
32
|
import { supportWorkflows } from "./workflows/specs";
|
|
33
33
|
|
|
34
34
|
/**
|
|
@@ -235,9 +235,12 @@ export function support(options: SupportOptions = {}): SupportCapability {
|
|
|
235
235
|
|
|
236
236
|
const capability = defineCapability({
|
|
237
237
|
name: "support",
|
|
238
|
-
// The package
|
|
239
|
-
// cannot read its own package.json. Reported per capability by
|
|
238
|
+
// The package this capability ships in and the version it ships at, both stamped by
|
|
239
|
+
// `scripts/stampVersions.ts` — a Worker cannot read its own package.json. Reported per capability by
|
|
240
|
+
// the control-plane manifest, and reported together: a release feed is keyed by package name, so the
|
|
241
|
+
// version alone leaves a client guessing the key (#626).
|
|
240
242
|
version: PACKAGE_VERSION,
|
|
243
|
+
package: PACKAGE_NAME,
|
|
241
244
|
// Attachment presigning reads an R2 credential bundle through @pithy-sh/secrets.
|
|
242
245
|
dependsOn: ["secrets"],
|
|
243
246
|
secretRegistry: supportSecretsRegistry,
|
|
@@ -5,7 +5,13 @@ import type { DeclaredEnvironments } from "@pithy-sh/core/src/naming/environment
|
|
|
5
5
|
import { GLOBAL_SCOPE } from "@pithy-sh/core/src/naming/environment";
|
|
6
6
|
import { resourceName } from "@pithy-sh/core/src/naming/resource";
|
|
7
7
|
import { resourceNames } from "@pithy-sh/core/src/naming/resourceNames";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
assertSharedLeavesLast,
|
|
10
|
+
type DeprovisionTarget,
|
|
11
|
+
deprovisionTarget,
|
|
12
|
+
type ManagedEnvironment,
|
|
13
|
+
managedEnvironments,
|
|
14
|
+
} from "@pithy-sh/secrets/src/scope";
|
|
9
15
|
import { SUPPORT_CAPABILITY } from "../workflows/specs";
|
|
10
16
|
|
|
11
17
|
/**
|
|
@@ -146,9 +152,17 @@ export async function provisionSupport(
|
|
|
146
152
|
|
|
147
153
|
/** The teardown seam — the inverse of {@link SupportProvisioner}. Every step idempotent. */
|
|
148
154
|
export interface SupportDeprovisioner {
|
|
155
|
+
/**
|
|
156
|
+
* Whether the env's classification worker is deployed. Read-only. What a teardown asks of every *other*
|
|
157
|
+
* environment before it removes the bucket or the rule they share (#591).
|
|
158
|
+
*/
|
|
159
|
+
hasWorker(env: ManagedEnvironment): Promise<boolean>;
|
|
149
160
|
/** Delete the env's classification worker. Idempotent. */
|
|
150
161
|
deleteWorker(env: ManagedEnvironment): Promise<void>;
|
|
151
|
-
/**
|
|
162
|
+
/**
|
|
163
|
+
* Remove the inbound routing rule, so mail stops being delivered here. Idempotent. Called only when the teardown
|
|
164
|
+
* was asked to remove it (`removeRouting`).
|
|
165
|
+
*/
|
|
152
166
|
removeRoutingRule(): Promise<{ removed: boolean }>;
|
|
153
167
|
/**
|
|
154
168
|
* Delete the R2 bucket **and everything in it** — every attachment and every raw message an adopter's
|
|
@@ -159,30 +173,47 @@ export interface SupportDeprovisioner {
|
|
|
159
173
|
|
|
160
174
|
/** Teardown options. By default the bucket is **kept**: it holds correspondence, not cache. */
|
|
161
175
|
export interface SupportDeprovisionOptions {
|
|
162
|
-
/** Also delete the R2 bucket and its contents. Off by default. */
|
|
176
|
+
/** Also delete the R2 bucket and its contents. Off by default, and only with the last environment. */
|
|
163
177
|
deleteStorage?: boolean;
|
|
178
|
+
/** Also remove the inbound routing rule. Off by default, and only with the last environment. */
|
|
179
|
+
removeRouting?: boolean;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** What a support teardown did. */
|
|
183
|
+
export interface SupportDeprovisionResult {
|
|
184
|
+
/** The one environment torn down. */
|
|
185
|
+
env: ManagedEnvironment;
|
|
186
|
+
/** Whether this run removed the inbound routing rule. */
|
|
187
|
+
routingRuleRemoved: boolean;
|
|
164
188
|
}
|
|
165
189
|
|
|
166
190
|
/**
|
|
167
|
-
* Tear down
|
|
191
|
+
* Tear down **one named environment's** support infrastructure, reversing {@link provisionSupport} for it.
|
|
168
192
|
*
|
|
169
|
-
*
|
|
170
|
-
* removing the workers that would have handled it, or messages land in a Worker with no classification
|
|
171
|
-
* host during the teardown. Stored correspondence is preserved unless explicitly requested — losing an
|
|
172
|
-
* adopter's support history to a teardown flag would be unrecoverable.
|
|
193
|
+
* In order, and the order is the contract:
|
|
173
194
|
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
195
|
+
* 1. Resolve the target (`@pithy-sh/secrets`' `deprovisionTarget`) — refused with nothing read when none was
|
|
196
|
+
* named, or one the project does not declare. This used to walk every declared environment, so a run meant
|
|
197
|
+
* for staging took production's classification worker with it (#591).
|
|
198
|
+
* 2. With `removeRouting` or `deleteStorage`: refuse while any other declared environment still runs a
|
|
199
|
+
* classification worker (`assertSharedLeavesLast`). The rule and the bucket are one per project — the bucket
|
|
200
|
+
* is every environment's support history — so they go with the last environment, never a staging teardown.
|
|
201
|
+
* 3. Remove the routing rule, when asked, **first**: stop new mail arriving before removing the worker that would
|
|
202
|
+
* have handled it.
|
|
203
|
+
* 4. Delete the environment's worker, then — only when asked — the bucket and everything in it.
|
|
177
204
|
*/
|
|
178
205
|
export async function deprovisionSupport(
|
|
179
206
|
deprovisioner: SupportDeprovisioner,
|
|
180
|
-
|
|
207
|
+
target: DeprovisionTarget,
|
|
181
208
|
options: SupportDeprovisionOptions = {},
|
|
182
|
-
): Promise<
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
209
|
+
): Promise<SupportDeprovisionResult> {
|
|
210
|
+
const env = deprovisionTarget(target);
|
|
211
|
+
await assertSharedLeavesLast(env, target.declared, (other) => deprovisioner.hasWorker(other), [
|
|
212
|
+
...(options.deleteStorage ? [{ what: "the support bucket", flag: "--storage" }] : []),
|
|
213
|
+
...(options.removeRouting ? [{ what: "the inbound routing rule", flag: "--routing-zone" }] : []),
|
|
214
|
+
]);
|
|
215
|
+
const routingRuleRemoved = options.removeRouting ? (await deprovisioner.removeRoutingRule()).removed : false;
|
|
216
|
+
await deprovisioner.deleteWorker(env);
|
|
187
217
|
if (options.deleteStorage) await deprovisioner.deleteBucket();
|
|
218
|
+
return { env, routingRuleRemoved };
|
|
188
219
|
}
|
package/src/version.generated.ts
CHANGED