@pithy-sh/support 0.1.6 → 0.2.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.
@@ -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,CA+FvE;AAED,2GAA2G;AAC3G,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,UAAU,GAAG,UAAU,IAAI,iBAAiB,CAE3F"}
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,CAmGvE;AAED,2GAA2G;AAC3G,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,UAAU,GAAG,UAAU,IAAI,iBAAiB,CAE3F"}
@@ -4,7 +4,7 @@ import { resolveReplies } from "./reply/snippets.js";
4
4
  import { SupportConfig, supportNeedsBucket } from "./config/config.js";
5
5
  import { resolveCategories } from "./data/categories.js";
6
6
  import { supportTables } from "./data/tables.js";
7
- import { supportSecretsRegistry } from "./secret/registry.js";
7
+ import { SUPPORT_BUCKET_BINDING, inapplicableAttachmentSecrets, supportSecretsRegistry } from "./secret/registry.js";
8
8
  import { supportWorkflows } from "./workflows/specs.js";
9
9
  import { makeResolveDeps } from "./http/resolve.js";
10
10
  import { supportAdminRoutes } from "./http/scopes.js";
@@ -133,7 +133,7 @@ function support(options = {}) {
133
133
  },
134
134
  ...supportNeedsBucket(resolved) ? [{
135
135
  type: "r2",
136
- name: "SUPPORT_BUCKET",
136
+ name: SUPPORT_BUCKET_BINDING,
137
137
  optional: true
138
138
  }] : [],
139
139
  ...workflowBindings(supportWorkflows)
@@ -143,6 +143,7 @@ function support(options = {}) {
143
143
  version: PACKAGE_VERSION,
144
144
  dependsOn: ["secrets"],
145
145
  secretRegistry: supportSecretsRegistry,
146
+ inapplicableSecrets: inapplicableAttachmentSecrets(resolved),
146
147
  workflows: supportWorkflows,
147
148
  requiredBindings,
148
149
  ciPermissions: ["email:routing"],
@@ -1,5 +1,6 @@
1
1
  // SPDX-FileCopyrightText: 2026 Pithy
2
2
  // SPDX-License-Identifier: MIT
3
+ import { type SupportConfig } from "../config/config";
3
4
  /**
4
5
  * The secrets support reads — exactly one, and it is not support's own shape.
5
6
  *
@@ -15,6 +16,35 @@
15
16
  */
16
17
  /** The name support's R2 credential bundle is stored and resolved under, per environment. */
17
18
  export declare const SUPPORT_R2_SECRET = "support-r2-credentials";
19
+ /**
20
+ * The bucket binding it presigns — support's one optional binding, and the one an adopter declines when
21
+ * attachments are off.
22
+ *
23
+ * **Declared, because the relation was prose and a command cannot read prose (#541).** With attachments
24
+ * declined, `pithy doctor` printed `SUPPORT_BUCKET (r2) declined in pithy.config.ts` and then asked for
25
+ * `support-r2-credentials` in the same run. The binding is stated on the entry now, so the secrets side
26
+ * reads the decline the bindings side already reports.
27
+ */
28
+ export declare const SUPPORT_BUCKET_BINDING = "SUPPORT_BUCKET";
18
29
  /** The support capability's secret-registry slice — aggregated into the shared accessor at startup. */
19
30
  export declare const supportSecretsRegistry: Record<"support-r2-credentials", import("@pithy-sh/storage/src/secret/registry").R2CredentialsEntry>;
31
+ /**
32
+ * **The credential this composition cannot reach**, as the `inapplicableSecrets` contribution (#541).
33
+ *
34
+ * The headline case of that issue, and the one the binding field alone does not close. Joining a
35
+ * credential to a **declined** binding only answers for a project that wrote a `declinedBindings` entry;
36
+ * a project that simply turned attachments off never wrote one, because the binding was never declared
37
+ * to decline — {@link supportNeedsBucket} suppresses it at composition. So `pithy doctor` kept asking
38
+ * for `support-r2-credentials` on exactly the configuration the issue opens with.
39
+ *
40
+ * **The predicate already existed.** Support has answered *would this configuration ever put a byte in
41
+ * `SUPPORT_BUCKET`* since #440, in one place, for the binding and the provisioner both. This is the
42
+ * third caller of the same answer rather than a fourth reading of the three settings behind it — which
43
+ * is the arrangement #440 landed precisely so that two callers could not disagree.
44
+ *
45
+ * The reason names the settings an operator would edit, all three of them, because any one of them
46
+ * turns the bucket back on and a sentence naming only `attachments.enabled` would send a reader to the
47
+ * wrong line.
48
+ */
49
+ export declare function inapplicableAttachmentSecrets(config: SupportConfig): Record<string, string>;
20
50
  //# sourceMappingURL=registry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/secret/registry.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;GAYG;AAEH,6FAA6F;AAC7F,eAAO,MAAM,iBAAiB,2BAA2B,CAAC;AAE1D,uGAAuG;AACvG,eAAO,MAAM,sBAAsB,sGAA2C,CAAC"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/secret/registry.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,aAAa,EAAsB,MAAM,kBAAkB,CAAC;AAE1E;;;;;;;;;;;;GAYG;AAEH,6FAA6F;AAC7F,eAAO,MAAM,iBAAiB,2BAA2B,CAAC;AAE1D;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,mBAAmB,CAAC;AAEvD,uGAAuG;AACvG,eAAO,MAAM,sBAAsB,sGAAmE,CAAC;AAEvG;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAM3F"}
@@ -1,5 +1,6 @@
1
1
  // SPDX-FileCopyrightText: 2026 Pithy
2
2
  // SPDX-License-Identifier: MIT
3
+ import { supportNeedsBucket } from "../config/config.js";
3
4
  import { r2CredentialsRegistry } from "@pithy-sh/storage/src/secret/registry";
4
5
  //#region src/secret/registry.ts
5
6
  /**
@@ -17,7 +18,39 @@ import { r2CredentialsRegistry } from "@pithy-sh/storage/src/secret/registry";
17
18
  */
18
19
  /** The name support's R2 credential bundle is stored and resolved under, per environment. */
19
20
  const SUPPORT_R2_SECRET = "support-r2-credentials";
21
+ /**
22
+ * The bucket binding it presigns — support's one optional binding, and the one an adopter declines when
23
+ * attachments are off.
24
+ *
25
+ * **Declared, because the relation was prose and a command cannot read prose (#541).** With attachments
26
+ * declined, `pithy doctor` printed `SUPPORT_BUCKET (r2) declined in pithy.config.ts` and then asked for
27
+ * `support-r2-credentials` in the same run. The binding is stated on the entry now, so the secrets side
28
+ * reads the decline the bindings side already reports.
29
+ */
30
+ const SUPPORT_BUCKET_BINDING = "SUPPORT_BUCKET";
20
31
  /** The support capability's secret-registry slice — aggregated into the shared accessor at startup. */
21
- const supportSecretsRegistry = r2CredentialsRegistry(SUPPORT_R2_SECRET);
32
+ const supportSecretsRegistry = r2CredentialsRegistry(SUPPORT_R2_SECRET, SUPPORT_BUCKET_BINDING);
33
+ /**
34
+ * **The credential this composition cannot reach**, as the `inapplicableSecrets` contribution (#541).
35
+ *
36
+ * The headline case of that issue, and the one the binding field alone does not close. Joining a
37
+ * credential to a **declined** binding only answers for a project that wrote a `declinedBindings` entry;
38
+ * a project that simply turned attachments off never wrote one, because the binding was never declared
39
+ * to decline — {@link supportNeedsBucket} suppresses it at composition. So `pithy doctor` kept asking
40
+ * for `support-r2-credentials` on exactly the configuration the issue opens with.
41
+ *
42
+ * **The predicate already existed.** Support has answered *would this configuration ever put a byte in
43
+ * `SUPPORT_BUCKET`* since #440, in one place, for the binding and the provisioner both. This is the
44
+ * third caller of the same answer rather than a fourth reading of the three settings behind it — which
45
+ * is the arrangement #440 landed precisely so that two callers could not disagree.
46
+ *
47
+ * The reason names the settings an operator would edit, all three of them, because any one of them
48
+ * turns the bucket back on and a sentence naming only `attachments.enabled` would send a reader to the
49
+ * wrong line.
50
+ */
51
+ function inapplicableAttachmentSecrets(config) {
52
+ if (supportNeedsBucket(config)) return {};
53
+ return { [SUPPORT_R2_SECRET]: "support() stores no attachments — attachments.enabled, attachments.retainRaw and submission.attachments.enabled are all off" };
54
+ }
22
55
  //#endregion
23
- export { SUPPORT_R2_SECRET, supportSecretsRegistry };
56
+ export { SUPPORT_BUCKET_BINDING, SUPPORT_R2_SECRET, inapplicableAttachmentSecrets, supportSecretsRegistry };
@@ -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.1.6";
6
+ export declare const PACKAGE_VERSION = "0.2.0";
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.1.6";
7
+ const PACKAGE_VERSION = "0.2.0";
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.1.6",
3
+ "version": "0.2.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -36,16 +36,16 @@
36
36
  "dependencies": {
37
37
  "@cloudflare/workers-types": "^5.20260729.1",
38
38
  "@hono/zod-validator": "^0.9.0",
39
- "@pithy-sh/core": "^0.3.0",
40
- "@pithy-sh/email": "^0.1.7",
41
- "@pithy-sh/secrets": "^0.1.6",
42
- "@pithy-sh/storage": "^0.1.6",
43
39
  "postal-mime": "^3.0.0",
44
40
  "tldts": "^7.4.10"
45
41
  },
46
42
  "peerDependencies": {
47
- "@pithy-sh/auth": "^0.1.6",
48
- "@pithy-sh/payments": "^0.2.1",
43
+ "@pithy-sh/auth": "^0.2.0",
44
+ "@pithy-sh/core": "^0.4.0",
45
+ "@pithy-sh/email": "^0.2.0",
46
+ "@pithy-sh/payments": "^0.3.0",
47
+ "@pithy-sh/secrets": "^0.2.0",
48
+ "@pithy-sh/storage": "^0.2.0",
49
49
  "hono": "^4.13.2",
50
50
  "kysely": "^0.29.0",
51
51
  "zod": "^4.4.0"
@@ -61,7 +61,11 @@
61
61
  "devDependencies": {
62
62
  "@cloudflare/vitest-plugin": "^1.0.0",
63
63
  "@pithy-sh/auth": "workspace:*",
64
+ "@pithy-sh/core": "workspace:*",
65
+ "@pithy-sh/email": "workspace:*",
64
66
  "@pithy-sh/payments": "workspace:*",
67
+ "@pithy-sh/secrets": "workspace:*",
68
+ "@pithy-sh/storage": "workspace:*",
65
69
  "@pithy-sh/tsconfig": "workspace:*",
66
70
  "@types/node": "^22.15.0",
67
71
  "@vitest/coverage-v8": "^4.1.0",
package/src/capability.ts CHANGED
@@ -26,7 +26,7 @@ import { supportAdminRoutes } from "./http/scopes";
26
26
  import { createSupportEmailHandler } from "./inbound/handler";
27
27
  import { support_0001_threads } from "./migrations/0001_threads";
28
28
  import { resolveReplies, type SupportReplySnippets } from "./reply/snippets";
29
- import { supportSecretsRegistry } from "./secret/registry";
29
+ import { inapplicableAttachmentSecrets, SUPPORT_BUCKET_BINDING, supportSecretsRegistry } from "./secret/registry";
30
30
  import { supportExampleSeed } from "./seeds/example";
31
31
  import { PACKAGE_VERSION } from "./version.generated";
32
32
  import { supportWorkflows } from "./workflows/specs";
@@ -227,7 +227,7 @@ export function support(options: SupportOptions = {}): SupportCapability {
227
227
  // three environments for an inbox with attachments off, and `pithy doctor` call them missing
228
228
  // forever once they were deleted by hand (#440). The three settings behind `supportNeedsBucket`
229
229
  // all default `true`, so nothing changes for a project that has not turned one off.
230
- ...(supportNeedsBucket(resolved) ? [{ type: "r2" as const, name: "SUPPORT_BUCKET", optional: true }] : []),
230
+ ...(supportNeedsBucket(resolved) ? [{ type: "r2" as const, name: SUPPORT_BUCKET_BINDING, optional: true }] : []),
231
231
  // The classification Workflow the inbound handler dispatches to, derived from the spec rather
232
232
  // than listed again — one declaration, so a binding rename cannot leave the two disagreeing.
233
233
  ...workflowBindings(supportWorkflows),
@@ -241,6 +241,10 @@ export function support(options: SupportOptions = {}): SupportCapability {
241
241
  // Attachment presigning reads an R2 credential bundle through @pithy-sh/secrets.
242
242
  dependsOn: ["secrets"],
243
243
  secretRegistry: supportSecretsRegistry,
244
+ // And whether this composition can reach it at all (#541). The binding is suppressed when nothing
245
+ // would ever write to the bucket, so there is no decline for the CLI to join against — the
246
+ // capability is the only thing that knows, and it already computed the answer for the binding.
247
+ inapplicableSecrets: inapplicableAttachmentSecrets(resolved),
244
248
  workflows: supportWorkflows,
245
249
  requiredBindings,
246
250
  // Provisioning creates the routing rule that delivers mail to this Worker.
@@ -2,6 +2,7 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
 
4
4
  import { r2CredentialsRegistry } from "@pithy-sh/storage/src/secret/registry";
5
+ import { type SupportConfig, supportNeedsBucket } from "../config/config";
5
6
 
6
7
  /**
7
8
  * The secrets support reads — exactly one, and it is not support's own shape.
@@ -20,5 +21,42 @@ import { r2CredentialsRegistry } from "@pithy-sh/storage/src/secret/registry";
20
21
  /** The name support's R2 credential bundle is stored and resolved under, per environment. */
21
22
  export const SUPPORT_R2_SECRET = "support-r2-credentials";
22
23
 
24
+ /**
25
+ * The bucket binding it presigns — support's one optional binding, and the one an adopter declines when
26
+ * attachments are off.
27
+ *
28
+ * **Declared, because the relation was prose and a command cannot read prose (#541).** With attachments
29
+ * declined, `pithy doctor` printed `SUPPORT_BUCKET (r2) declined in pithy.config.ts` and then asked for
30
+ * `support-r2-credentials` in the same run. The binding is stated on the entry now, so the secrets side
31
+ * reads the decline the bindings side already reports.
32
+ */
33
+ export const SUPPORT_BUCKET_BINDING = "SUPPORT_BUCKET";
34
+
23
35
  /** The support capability's secret-registry slice — aggregated into the shared accessor at startup. */
24
- export const supportSecretsRegistry = r2CredentialsRegistry(SUPPORT_R2_SECRET);
36
+ export const supportSecretsRegistry = r2CredentialsRegistry(SUPPORT_R2_SECRET, SUPPORT_BUCKET_BINDING);
37
+
38
+ /**
39
+ * **The credential this composition cannot reach**, as the `inapplicableSecrets` contribution (#541).
40
+ *
41
+ * The headline case of that issue, and the one the binding field alone does not close. Joining a
42
+ * credential to a **declined** binding only answers for a project that wrote a `declinedBindings` entry;
43
+ * a project that simply turned attachments off never wrote one, because the binding was never declared
44
+ * to decline — {@link supportNeedsBucket} suppresses it at composition. So `pithy doctor` kept asking
45
+ * for `support-r2-credentials` on exactly the configuration the issue opens with.
46
+ *
47
+ * **The predicate already existed.** Support has answered *would this configuration ever put a byte in
48
+ * `SUPPORT_BUCKET`* since #440, in one place, for the binding and the provisioner both. This is the
49
+ * third caller of the same answer rather than a fourth reading of the three settings behind it — which
50
+ * is the arrangement #440 landed precisely so that two callers could not disagree.
51
+ *
52
+ * The reason names the settings an operator would edit, all three of them, because any one of them
53
+ * turns the bucket back on and a sentence naming only `attachments.enabled` would send a reader to the
54
+ * wrong line.
55
+ */
56
+ export function inapplicableAttachmentSecrets(config: SupportConfig): Record<string, string> {
57
+ if (supportNeedsBucket(config)) return {};
58
+ return {
59
+ [SUPPORT_R2_SECRET]:
60
+ "support() stores no attachments — attachments.enabled, attachments.retainRaw and submission.attachments.enabled are all off",
61
+ };
62
+ }
@@ -13,4 +13,4 @@
13
13
  export const PACKAGE_NAME = "@pithy-sh/support";
14
14
 
15
15
  /** This package's version, stamped from its own package.json at generation time. */
16
- export const PACKAGE_VERSION = "0.1.6";
16
+ export const PACKAGE_VERSION = "0.2.0";