@vllnt/convex-suppression 0.1.0-canary.261f634

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.
Files changed (64) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +138 -0
  3. package/dist/client/index.d.ts +135 -0
  4. package/dist/client/index.d.ts.map +1 -0
  5. package/dist/client/index.js +131 -0
  6. package/dist/client/index.js.map +1 -0
  7. package/dist/client/types.d.ts +70 -0
  8. package/dist/client/types.d.ts.map +1 -0
  9. package/dist/client/types.js +3 -0
  10. package/dist/client/types.js.map +1 -0
  11. package/dist/component/_generated/api.d.ts +38 -0
  12. package/dist/component/_generated/api.d.ts.map +1 -0
  13. package/dist/component/_generated/api.js +31 -0
  14. package/dist/component/_generated/api.js.map +1 -0
  15. package/dist/component/_generated/component.d.ts +67 -0
  16. package/dist/component/_generated/component.d.ts.map +1 -0
  17. package/dist/component/_generated/component.js +11 -0
  18. package/dist/component/_generated/component.js.map +1 -0
  19. package/dist/component/_generated/dataModel.d.ts +46 -0
  20. package/dist/component/_generated/dataModel.d.ts.map +1 -0
  21. package/dist/component/_generated/dataModel.js +11 -0
  22. package/dist/component/_generated/dataModel.js.map +1 -0
  23. package/dist/component/_generated/server.d.ts +121 -0
  24. package/dist/component/_generated/server.d.ts.map +1 -0
  25. package/dist/component/_generated/server.js +78 -0
  26. package/dist/component/_generated/server.js.map +1 -0
  27. package/dist/component/convex.config.d.ts +3 -0
  28. package/dist/component/convex.config.d.ts.map +1 -0
  29. package/dist/component/convex.config.js +7 -0
  30. package/dist/component/convex.config.js.map +1 -0
  31. package/dist/component/mutations.d.ts +49 -0
  32. package/dist/component/mutations.d.ts.map +1 -0
  33. package/dist/component/mutations.js +110 -0
  34. package/dist/component/mutations.js.map +1 -0
  35. package/dist/component/queries.d.ts +46 -0
  36. package/dist/component/queries.d.ts.map +1 -0
  37. package/dist/component/queries.js +112 -0
  38. package/dist/component/queries.js.map +1 -0
  39. package/dist/component/schema.d.ts +51 -0
  40. package/dist/component/schema.d.ts.map +1 -0
  41. package/dist/component/schema.js +39 -0
  42. package/dist/component/schema.js.map +1 -0
  43. package/dist/component/validators.d.ts +50 -0
  44. package/dist/component/validators.d.ts.map +1 -0
  45. package/dist/component/validators.js +40 -0
  46. package/dist/component/validators.js.map +1 -0
  47. package/dist/shared.d.ts +22 -0
  48. package/dist/shared.d.ts.map +1 -0
  49. package/dist/shared.js +26 -0
  50. package/dist/shared.js.map +1 -0
  51. package/package.json +101 -0
  52. package/src/client/index.ts +271 -0
  53. package/src/client/types.ts +82 -0
  54. package/src/component/_generated/api.ts +54 -0
  55. package/src/component/_generated/component.ts +102 -0
  56. package/src/component/_generated/dataModel.ts +60 -0
  57. package/src/component/_generated/server.ts +156 -0
  58. package/src/component/convex.config.ts +9 -0
  59. package/src/component/mutations.ts +118 -0
  60. package/src/component/queries.ts +128 -0
  61. package/src/component/schema.ts +40 -0
  62. package/src/component/validators.ts +49 -0
  63. package/src/shared.ts +31 -0
  64. package/src/test.ts +15 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 bntvllnt
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,138 @@
1
+ <!-- Badges -->
2
+ [![convex-component](https://img.shields.io/badge/convex-component-EE342F.svg)](https://www.convex.dev/components)
3
+ [![npm](https://img.shields.io/npm/v/@vllnt/convex-suppression.svg)](https://www.npmjs.com/package/@vllnt/convex-suppression)
4
+ [![CI](https://github.com/vllnt/convex-suppression/actions/workflows/ci.yml/badge.svg)](https://github.com/vllnt/convex-suppression/actions/workflows/ci.yml)
5
+ [![license](https://img.shields.io/npm/l/@vllnt/convex-suppression.svg)](./LICENSE)
6
+
7
+ # @vllnt/convex-suppression
8
+
9
+ The do-not-contact suppression list + opt-in proof, as a Convex component.
10
+
11
+ ```ts
12
+ const dnc = new Suppression(components.suppression);
13
+ await dnc.suppress(ctx, contactHash, "complaint", { channel: "email" });
14
+ const canSend = await dnc.isEligible(ctx, contactHash, { channel: "email" });
15
+ ```
16
+
17
+ The GDPR opt-out / CAN-SPAM primitive: a global do-not-contact list keyed by an
18
+ opaque **`contactHash`** (never raw PII, so it survives erasure) plus an opt-in
19
+ proof ledger. Before every send the host asks `isEligible`; an unsubscribe / bounce
20
+ / complaint calls `suppress`; a double-opt-in confirmation calls `recordOptIn`.
21
+ Domain-neutral — channels and reasons are the host's.
22
+
23
+ ## Features
24
+
25
+ - **Hash-keyed, erasure-surviving** — stores only the opaque `contactHash`, never raw PII, so a tombstone outlives erasure of the subject.
26
+ - **Channel-aware** — scope a suppression to one channel (`"email"`/`"sms"`/`"push"`), or omit `channel` for a global all-channel tombstone.
27
+ - **One send gate** — `isEligible` answers `¬suppressed [∧ confirmed]` in one call; suppression always blocks, opt-in is per call.
28
+ - **Opt-in proof ledger** — `recordOptIn` / `getOptInProof` store legal proof-of-consent per list, kept separate from any authz store.
29
+ - **Idempotent** — re-suppressing / re-recording on its key tuple updates in place, so a replayed webhook never duplicates a row.
30
+ - **Typed, opaque proof** — `Suppression<TProof>` with an optional `proofValidator` narrows the stored evidence at the boundary.
31
+ - **Server-sourced time** — `createdAt`/`confirmedAt` are stamped from the server clock; a caller can never supply a timestamp.
32
+ - **Mount-safe** — correct under multiple named `app.use` mounts (e.g. marketing vs. transactional lists), each an isolated sandbox.
33
+
34
+ ## Installation
35
+
36
+ ```bash
37
+ pnpm add @vllnt/convex-suppression
38
+ ```
39
+
40
+ Peer dependency: `convex@^1.41.0`.
41
+
42
+ ## Usage
43
+
44
+ ```ts
45
+ // convex/convex.config.ts
46
+ import { defineApp } from "convex/server";
47
+ import suppression from "@vllnt/convex-suppression/convex.config";
48
+
49
+ const app = defineApp();
50
+ app.use(suppression);
51
+ export default app;
52
+ ```
53
+
54
+ ```ts
55
+ // convex/email.ts — host owns auth + hashing; pass an opaque contactHash in.
56
+ import { components } from "./_generated/api";
57
+ import { mutation, query } from "./_generated/server";
58
+ import { v } from "convex/values";
59
+ import { Suppression } from "@vllnt/convex-suppression";
60
+
61
+ const dnc = new Suppression<{ ip: string }>(components.suppression, {
62
+ proofValidator: v.object({ ip: v.string() }).parse,
63
+ });
64
+ const hash = (email: string) => myHash(email.trim().toLowerCase()); // host's hashing + salt policy
65
+
66
+ // The send gate — call before every send.
67
+ export const canEmail = query({
68
+ args: { email: v.string() },
69
+ handler: (ctx, { email }) =>
70
+ dnc.isEligible(ctx, hash(email), { channel: "email", listKey: "newsletter", requireOptIn: true }),
71
+ });
72
+
73
+ // A provider webhook suppresses on complaint; a confirmed double opt-in records proof.
74
+ export const onComplaint = mutation({
75
+ args: { email: v.string() },
76
+ handler: (ctx, { email }) => dnc.suppress(ctx, hash(email), "complaint", { channel: "email" }),
77
+ });
78
+ export const confirmOptIn = mutation({
79
+ args: { email: v.string(), ip: v.string() },
80
+ handler: (ctx, { email, ip }) =>
81
+ dnc.recordOptIn(ctx, hash(email), { listKey: "newsletter", source: "double-opt-in", proof: { ip } }),
82
+ });
83
+ ```
84
+
85
+ Client options: `new Suppression(component, { proofValidator? })`. Omitting `channel`/`listKey` targets the global entry.
86
+
87
+ ## API Reference
88
+
89
+ | Method | Kind | Result |
90
+ |--------|------|--------|
91
+ | `suppress(ctx, contactHash, reason, opts?)` | mutation | `null` (`reason`: `"unsubscribe" \| "bounce" \| "complaint" \| "manual" \| "global"`; `opts`: `{ channel? }`) |
92
+ | `unsuppress(ctx, contactHash, channel?)` | mutation | `boolean` (`true` if an entry was removed) |
93
+ | `recordOptIn(ctx, contactHash, opts)` | mutation | `null` (`opts`: `{ listKey?; source; proof? }`) |
94
+ | `isSuppressed(ctx, contactHash, channel?)` | query | `SuppressionView \| null` |
95
+ | `getOptInProof(ctx, contactHash, listKey?)` | query | `OptInProofView \| null` |
96
+ | `isEligible(ctx, contactHash, opts?)` | query | `boolean` (`opts`: `{ channel?; listKey?; requireOptIn? }`) |
97
+
98
+ Full reference: [docs/API.md](docs/API.md).
99
+
100
+ ## React
101
+
102
+ Backend-only — no `./react` entry. The consumer surface is a server-side
103
+ `isEligible` gate and webhook-driven `suppress` writes; a reactive do-not-contact
104
+ badge, if ever needed, is an ordinary `useQuery` over the host's re-exported
105
+ `isSuppressed` ref.
106
+
107
+ ## Security
108
+
109
+ - **Auth-agnostic** — the host authenticates the caller, decides who may suppress/query a hash, and passes an opaque `contactHash`; tables are sandboxed.
110
+ - **Hash-keyed, never raw PII** — the host hashes and normalizes the contact and owns the salt policy; a suppression survives erasure of the subject.
111
+ - **Server-sourced time** — `createdAt`/`confirmedAt` come from `Date.now()` in each handler, never the caller; the opt-in `proof` is opaque, narrowed by the host validator.
112
+
113
+ See [docs/API.md](docs/API.md).
114
+
115
+ ## Testing
116
+
117
+ ```bash
118
+ pnpm test # single run
119
+ pnpm test:coverage # enforced 100% on covered files
120
+ ```
121
+
122
+ Tests run against the real component runtime via `convex-test` (`@edge-runtime/vm`), not mocks.
123
+
124
+ ## Contributing
125
+
126
+ See [CONTRIBUTING.md](CONTRIBUTING.md).
127
+
128
+ ## Author
129
+
130
+ Built by [bntvllnt](https://github.com/bntvllnt) · [bntvllnt.com](https://bntvllnt.com) · [X @bntvllnt](https://x.com/bntvllnt)
131
+
132
+ Part of the [@vllnt](https://github.com/vllnt) Convex component fleet — [vllnt.com](https://vllnt.com)
133
+
134
+ If this is useful, [sponsor the work](https://github.com/sponsors/bntvllnt).
135
+
136
+ ## License
137
+
138
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,135 @@
1
+ import type { FunctionArgs, FunctionReference, FunctionReturnType } from "convex/server";
2
+ import type { IsEligibleOptions, OptInProofView, Parser, RecordOptInOptions, SuppressionOptions, SuppressionReason, SuppressionView, SuppressOptions } from "./types.js";
3
+ /**
4
+ * The component's raw opt-in proof view, before the client narrows opaque host
5
+ * evidence. `proof` is `unknown` here; the {@link Suppression} client runs the
6
+ * host validator over it at its typed boundary.
7
+ */
8
+ type RawProofView = {
9
+ contactHash: string;
10
+ listKey: string | null;
11
+ source: string;
12
+ proof?: unknown;
13
+ confirmedAt: number;
14
+ };
15
+ /**
16
+ * The suppression component's function references, as exposed on the host via
17
+ * `components.suppression`. The host's stored opt-in `proof` is opaque here
18
+ * (`unknown`); the {@link Suppression} client narrows it at its own typed boundary.
19
+ */
20
+ export interface SuppressionComponent {
21
+ mutations: {
22
+ suppress: FunctionReference<"mutation", "internal", {
23
+ contactHash: string;
24
+ channel: string;
25
+ reason: SuppressionReason;
26
+ }, null>;
27
+ unsuppress: FunctionReference<"mutation", "internal", {
28
+ contactHash: string;
29
+ channel: string;
30
+ }, boolean>;
31
+ recordOptIn: FunctionReference<"mutation", "internal", {
32
+ contactHash: string;
33
+ listKey: string;
34
+ source: string;
35
+ proof?: unknown;
36
+ }, null>;
37
+ };
38
+ queries: {
39
+ isSuppressed: FunctionReference<"query", "internal", {
40
+ contactHash: string;
41
+ channel: string;
42
+ }, SuppressionView | null>;
43
+ getOptInProof: FunctionReference<"query", "internal", {
44
+ contactHash: string;
45
+ listKey: string;
46
+ }, RawProofView | null>;
47
+ isEligible: FunctionReference<"query", "internal", {
48
+ contactHash: string;
49
+ channel: string;
50
+ listKey: string;
51
+ requireOptIn: boolean;
52
+ }, boolean>;
53
+ };
54
+ }
55
+ interface RunQueryCtx {
56
+ runQuery<Q extends FunctionReference<"query", "internal">>(reference: Q, args: FunctionArgs<Q>): Promise<FunctionReturnType<Q>>;
57
+ }
58
+ interface RunMutationCtx {
59
+ runMutation<M extends FunctionReference<"mutation", "internal">>(reference: M, args: FunctionArgs<M>): Promise<FunctionReturnType<M>>;
60
+ }
61
+ /**
62
+ * Consumer-facing client for the do-not-contact suppression gate (GDPR opt-out /
63
+ * CAN-SPAM). The host hashes a contact (`hash(normalize(email|phone))`) and passes
64
+ * the opaque `contactHash`; the component stores a `(contactHash, channel)`
65
+ * anti-membership tombstone that survives erasure of the subject. A sender calls
66
+ * `isEligible` before every send (`¬suppressed [∧ confirmed]`); an unsubscribe /
67
+ * bounce / complaint webhook calls `suppress`; a double-opt-in confirmation calls
68
+ * `recordOptIn`. The host owns meaning and auth — it resolves identity, hashes the
69
+ * contact, and decides the channel/list semantics. Pass `proofValidator` to narrow
70
+ * the opaque opt-in evidence to `TProof` at the boundary — there is no unchecked
71
+ * cast.
72
+ *
73
+ * @typeParam TProof - The host's opt-in proof evidence type (defaults to `unknown`).
74
+ *
75
+ * @example
76
+ * ```ts
77
+ * const dnc = new Suppression(components.suppression, {
78
+ * proofValidator: v.object({ ip: v.string() }).parse,
79
+ * });
80
+ * // a webhook suppresses on complaint:
81
+ * await dnc.suppress(ctx, contactHash, "complaint", { channel: "email" });
82
+ * // a sender gates a marketing send:
83
+ * if (await dnc.isEligible(ctx, contactHash, { channel: "email", listKey: "news", requireOptIn: true })) {
84
+ * // ...send
85
+ * }
86
+ * ```
87
+ */
88
+ export declare class Suppression<TProof = unknown> {
89
+ private readonly component;
90
+ private readonly proofValidator;
91
+ constructor(component: SuppressionComponent, options?: SuppressionOptions<TProof>);
92
+ /** Narrow an opaque value through a host parser; pass `undefined` and unset parsers through. */
93
+ private parse;
94
+ /**
95
+ * Suppress a `(contactHash, channel)` — add it to the do-not-contact list.
96
+ * `opts.channel` scopes the suppression to one channel; omit it for a global
97
+ * (all-channel) suppression. Idempotent on `(contactHash, channel)`. `reason` is
98
+ * recorded for audit.
99
+ */
100
+ suppress(ctx: RunMutationCtx, contactHash: string, reason: SuppressionReason, opts?: SuppressOptions): Promise<null>;
101
+ /**
102
+ * Remove a `(contactHash, channel)` from the do-not-contact list (a rare,
103
+ * audited re-subscribe). Omit `channel` to clear the global entry. Returns `true`
104
+ * if an entry was removed, `false` if none matched.
105
+ */
106
+ unsuppress(ctx: RunMutationCtx, contactHash: string, channel?: string): Promise<boolean>;
107
+ /**
108
+ * The matching suppression for `(contactHash, channel)`, or `null` if the
109
+ * contact is not suppressed on that channel. A global suppression matches every
110
+ * channel and wins. Omit `channel` to check the global entry only.
111
+ */
112
+ isSuppressed(ctx: RunQueryCtx, contactHash: string, channel?: string): Promise<SuppressionView | null>;
113
+ /**
114
+ * Record an opt-in proof for a `(contactHash, listKey)` — the legal evidence of
115
+ * a confirmed opt-in. `opts.listKey` scopes the proof to one list; omit it for a
116
+ * global opt-in. `opts.proof` is opaque host evidence validated against
117
+ * `proofValidator` before storage. Idempotent on `(contactHash, listKey)`.
118
+ */
119
+ recordOptIn(ctx: RunMutationCtx, contactHash: string, opts: RecordOptInOptions<TProof>): Promise<null>;
120
+ /**
121
+ * The opt-in proof for `(contactHash, listKey)`, or `null` if none is recorded.
122
+ * Omit `listKey` to fetch a global opt-in. `proof` is narrowed by the host
123
+ * validator on read.
124
+ */
125
+ getOptInProof(ctx: RunQueryCtx, contactHash: string, listKey?: string): Promise<OptInProofView<TProof> | null>;
126
+ /**
127
+ * The send gate: `true` when the contact may be contacted — NOT suppressed on
128
+ * `opts.channel` (global or channel-specific) and, when `opts.requireOptIn` is
129
+ * set, holding a recorded opt-in proof for `opts.listKey`. Suppression always
130
+ * blocks; the opt-in requirement is per call.
131
+ */
132
+ isEligible(ctx: RunQueryCtx, contactHash: string, opts?: IsEligibleOptions): Promise<boolean>;
133
+ }
134
+ export type { IsEligibleOptions, OptInProofView, Parser, RecordOptInOptions, SuppressionOptions, SuppressionReason, SuppressionView, SuppressOptions, };
135
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,131 @@
1
+ import { GLOBAL_CHANNEL } from "../shared.js";
2
+ /**
3
+ * Consumer-facing client for the do-not-contact suppression gate (GDPR opt-out /
4
+ * CAN-SPAM). The host hashes a contact (`hash(normalize(email|phone))`) and passes
5
+ * the opaque `contactHash`; the component stores a `(contactHash, channel)`
6
+ * anti-membership tombstone that survives erasure of the subject. A sender calls
7
+ * `isEligible` before every send (`¬suppressed [∧ confirmed]`); an unsubscribe /
8
+ * bounce / complaint webhook calls `suppress`; a double-opt-in confirmation calls
9
+ * `recordOptIn`. The host owns meaning and auth — it resolves identity, hashes the
10
+ * contact, and decides the channel/list semantics. Pass `proofValidator` to narrow
11
+ * the opaque opt-in evidence to `TProof` at the boundary — there is no unchecked
12
+ * cast.
13
+ *
14
+ * @typeParam TProof - The host's opt-in proof evidence type (defaults to `unknown`).
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * const dnc = new Suppression(components.suppression, {
19
+ * proofValidator: v.object({ ip: v.string() }).parse,
20
+ * });
21
+ * // a webhook suppresses on complaint:
22
+ * await dnc.suppress(ctx, contactHash, "complaint", { channel: "email" });
23
+ * // a sender gates a marketing send:
24
+ * if (await dnc.isEligible(ctx, contactHash, { channel: "email", listKey: "news", requireOptIn: true })) {
25
+ * // ...send
26
+ * }
27
+ * ```
28
+ */
29
+ export class Suppression {
30
+ component;
31
+ proofValidator;
32
+ constructor(component, options = {}) {
33
+ this.component = component;
34
+ this.proofValidator = options.proofValidator;
35
+ }
36
+ /** Narrow an opaque value through a host parser; pass `undefined` and unset parsers through. */
37
+ parse(value) {
38
+ if (value === undefined) {
39
+ return undefined;
40
+ }
41
+ if (this.proofValidator === undefined) {
42
+ return value;
43
+ }
44
+ return this.proofValidator(value);
45
+ }
46
+ /**
47
+ * Suppress a `(contactHash, channel)` — add it to the do-not-contact list.
48
+ * `opts.channel` scopes the suppression to one channel; omit it for a global
49
+ * (all-channel) suppression. Idempotent on `(contactHash, channel)`. `reason` is
50
+ * recorded for audit.
51
+ */
52
+ suppress(ctx, contactHash, reason, opts = {}) {
53
+ return ctx.runMutation(this.component.mutations.suppress, {
54
+ contactHash,
55
+ channel: opts.channel ?? GLOBAL_CHANNEL,
56
+ reason,
57
+ });
58
+ }
59
+ /**
60
+ * Remove a `(contactHash, channel)` from the do-not-contact list (a rare,
61
+ * audited re-subscribe). Omit `channel` to clear the global entry. Returns `true`
62
+ * if an entry was removed, `false` if none matched.
63
+ */
64
+ unsuppress(ctx, contactHash, channel) {
65
+ return ctx.runMutation(this.component.mutations.unsuppress, {
66
+ contactHash,
67
+ channel: channel ?? GLOBAL_CHANNEL,
68
+ });
69
+ }
70
+ /**
71
+ * The matching suppression for `(contactHash, channel)`, or `null` if the
72
+ * contact is not suppressed on that channel. A global suppression matches every
73
+ * channel and wins. Omit `channel` to check the global entry only.
74
+ */
75
+ isSuppressed(ctx, contactHash, channel) {
76
+ return ctx.runQuery(this.component.queries.isSuppressed, {
77
+ contactHash,
78
+ channel: channel ?? GLOBAL_CHANNEL,
79
+ });
80
+ }
81
+ /**
82
+ * Record an opt-in proof for a `(contactHash, listKey)` — the legal evidence of
83
+ * a confirmed opt-in. `opts.listKey` scopes the proof to one list; omit it for a
84
+ * global opt-in. `opts.proof` is opaque host evidence validated against
85
+ * `proofValidator` before storage. Idempotent on `(contactHash, listKey)`.
86
+ */
87
+ recordOptIn(ctx, contactHash, opts) {
88
+ return ctx.runMutation(this.component.mutations.recordOptIn, {
89
+ contactHash,
90
+ listKey: opts.listKey ?? GLOBAL_CHANNEL,
91
+ source: opts.source,
92
+ proof: opts.proof === undefined ? undefined : this.parse(opts.proof),
93
+ });
94
+ }
95
+ /**
96
+ * The opt-in proof for `(contactHash, listKey)`, or `null` if none is recorded.
97
+ * Omit `listKey` to fetch a global opt-in. `proof` is narrowed by the host
98
+ * validator on read.
99
+ */
100
+ async getOptInProof(ctx, contactHash, listKey) {
101
+ const raw = await ctx.runQuery(this.component.queries.getOptInProof, {
102
+ contactHash,
103
+ listKey: listKey ?? GLOBAL_CHANNEL,
104
+ });
105
+ if (raw === null) {
106
+ return null;
107
+ }
108
+ return {
109
+ contactHash: raw.contactHash,
110
+ listKey: raw.listKey,
111
+ source: raw.source,
112
+ proof: this.parse(raw.proof),
113
+ confirmedAt: raw.confirmedAt,
114
+ };
115
+ }
116
+ /**
117
+ * The send gate: `true` when the contact may be contacted — NOT suppressed on
118
+ * `opts.channel` (global or channel-specific) and, when `opts.requireOptIn` is
119
+ * set, holding a recorded opt-in proof for `opts.listKey`. Suppression always
120
+ * blocks; the opt-in requirement is per call.
121
+ */
122
+ isEligible(ctx, contactHash, opts = {}) {
123
+ return ctx.runQuery(this.component.queries.isEligible, {
124
+ contactHash,
125
+ channel: opts.channel ?? GLOBAL_CHANNEL,
126
+ listKey: opts.listKey ?? GLOBAL_CHANNEL,
127
+ requireOptIn: opts.requireOptIn ?? false,
128
+ });
129
+ }
130
+ }
131
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ /** Public TypeScript surface for the suppression client. */
2
+ /** The five standard reasons a `(contactHash, channel)` is suppressed. */
3
+ export type SuppressionReason = "unsubscribe" | "bounce" | "complaint" | "manual" | "global";
4
+ /**
5
+ * Validates and narrows opaque host opt-in evidence to a host type `T` at the
6
+ * client boundary. Receives the raw value the component returned (`unknown`) and
7
+ * MUST return a typed `T` or throw. A `convex/values` validator's `.parse` (or a
8
+ * Zod `.parse`) fits directly; omit it to keep the value unvalidated.
9
+ *
10
+ * @typeParam T - The host's stored opt-in `proof` type.
11
+ */
12
+ export type Parser<T> = (value: unknown) => T;
13
+ /** The public view of a suppression returned by {@link Suppression.isSuppressed}. */
14
+ export interface SuppressionView {
15
+ /** The host's opaque contact hash — never a raw email/phone. */
16
+ contactHash: string;
17
+ /** The channel this entry applies to, or `null` for a global (all-channel) suppression. */
18
+ channel: string | null;
19
+ /** Why the contact was suppressed (audit only — never changes the effect). */
20
+ reason: SuppressionReason;
21
+ /** Absolute ms timestamp the suppression was recorded. */
22
+ createdAt: number;
23
+ }
24
+ /** The public view of an opt-in proof returned by {@link Suppression.getOptInProof}. */
25
+ export interface OptInProofView<TProof = unknown> {
26
+ /** The host's opaque contact hash. */
27
+ contactHash: string;
28
+ /** The list/purpose the opt-in applies to, or `null` for a global opt-in. */
29
+ listKey: string | null;
30
+ /** How the opt-in was captured (e.g. `"double-opt-in"`, `"checkbox"`). */
31
+ source: string;
32
+ /** The opaque host evidence (narrowed if a `proofValidator` is set). */
33
+ proof?: TProof;
34
+ /** Absolute ms timestamp the opt-in was confirmed. */
35
+ confirmedAt: number;
36
+ }
37
+ /** Per-call options for {@link Suppression.suppress}. */
38
+ export interface SuppressOptions {
39
+ /** The channel to suppress (`"email"`/`"sms"`/…). Omit for a global suppression. */
40
+ channel?: string;
41
+ }
42
+ /** Per-call options for {@link Suppression.recordOptIn}. */
43
+ export interface RecordOptInOptions<TProof> {
44
+ /** The list/purpose the opt-in applies to. Omit for a global opt-in. */
45
+ listKey?: string;
46
+ /** How the opt-in was captured. */
47
+ source: string;
48
+ /** Opaque host evidence (validated against `proofValidator` before storage). */
49
+ proof?: TProof;
50
+ }
51
+ /** Per-call options for {@link Suppression.isEligible}. */
52
+ export interface IsEligibleOptions {
53
+ /** The channel to check (`"email"`/`"sms"`/…). Omit to check the global gate only. */
54
+ channel?: string;
55
+ /** The list/purpose to require an opt-in for (used only when `requireOptIn`). */
56
+ listKey?: string;
57
+ /** When `true`, also require a recorded opt-in proof for `listKey`. */
58
+ requireOptIn?: boolean;
59
+ }
60
+ /** Construction options for the {@link Suppression} client. */
61
+ export interface SuppressionOptions<TProof> {
62
+ /**
63
+ * Validates/narrows opaque opt-in `proof` to `TProof` at the boundary — applied
64
+ * to the `proof` passed into `recordOptIn` (before storage) and the `proof`
65
+ * returned by `getOptInProof` (on read). Throws on a mismatch. Omit to leave
66
+ * proof evidence unvalidated.
67
+ */
68
+ proofValidator?: Parser<TProof>;
69
+ }
70
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/client/types.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAE5D,0EAA0E;AAC1E,MAAM,MAAM,iBAAiB,GACzB,aAAa,GACb,QAAQ,GACR,WAAW,GACX,QAAQ,GACR,QAAQ,CAAC;AAEb;;;;;;;GAOG;AACH,MAAM,MAAM,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,KAAK,CAAC,CAAC;AAE9C,qFAAqF;AACrF,MAAM,WAAW,eAAe;IAC9B,gEAAgE;IAChE,WAAW,EAAE,MAAM,CAAC;IACpB,2FAA2F;IAC3F,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,8EAA8E;IAC9E,MAAM,EAAE,iBAAiB,CAAC;IAC1B,0DAA0D;IAC1D,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wFAAwF;AACxF,MAAM,WAAW,cAAc,CAAC,MAAM,GAAG,OAAO;IAC9C,sCAAsC;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,0EAA0E;IAC1E,MAAM,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sDAAsD;IACtD,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,yDAAyD;AACzD,MAAM,WAAW,eAAe;IAC9B,oFAAoF;IACpF,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,4DAA4D;AAC5D,MAAM,WAAW,kBAAkB,CAAC,MAAM;IACxC,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mCAAmC;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,2DAA2D;AAC3D,MAAM,WAAW,iBAAiB;IAChC,sFAAsF;IACtF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iFAAiF;IACjF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uEAAuE;IACvE,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,+DAA+D;AAC/D,MAAM,WAAW,kBAAkB,CAAC,MAAM;IACxC;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;CACjC"}
@@ -0,0 +1,3 @@
1
+ /** Public TypeScript surface for the suppression client. */
2
+ export {};
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/client/types.ts"],"names":[],"mappings":"AAAA,4DAA4D"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Generated `api` utility.
3
+ *
4
+ * THIS CODE IS AUTOMATICALLY GENERATED.
5
+ *
6
+ * To regenerate, run `npx convex dev`.
7
+ * @module
8
+ */
9
+ import type * as mutations from "../mutations.js";
10
+ import type * as queries from "../queries.js";
11
+ import type * as validators from "../validators.js";
12
+ import type { ApiFromModules, FilterApi, FunctionReference } from "convex/server";
13
+ declare const fullApi: ApiFromModules<{
14
+ mutations: typeof mutations;
15
+ queries: typeof queries;
16
+ validators: typeof validators;
17
+ }>;
18
+ /**
19
+ * A utility for referencing Convex functions in your app's public API.
20
+ *
21
+ * Usage:
22
+ * ```js
23
+ * const myFunctionReference = api.myModule.myFunction;
24
+ * ```
25
+ */
26
+ export declare const api: FilterApi<typeof fullApi, FunctionReference<any, "public">>;
27
+ /**
28
+ * A utility for referencing Convex functions in your app's internal API.
29
+ *
30
+ * Usage:
31
+ * ```js
32
+ * const myFunctionReference = internal.myModule.myFunction;
33
+ * ```
34
+ */
35
+ export declare const internal: FilterApi<typeof fullApi, FunctionReference<any, "internal">>;
36
+ export declare const components: {};
37
+ export {};
38
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/component/_generated/api.ts"],"names":[],"mappings":"AACA;;;;;;;GAOG;AAEH,OAAO,KAAK,KAAK,SAAS,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,KAAK,OAAO,MAAM,eAAe,CAAC;AAC9C,OAAO,KAAK,KAAK,UAAU,MAAM,kBAAkB,CAAC;AAEpD,OAAO,KAAK,EACV,cAAc,EACd,SAAS,EACT,iBAAiB,EAClB,MAAM,eAAe,CAAC;AAGvB,QAAA,MAAM,OAAO,EAAE,cAAc,CAAC;IAC5B,SAAS,EAAE,OAAO,SAAS,CAAC;IAC5B,OAAO,EAAE,OAAO,OAAO,CAAC;IACxB,UAAU,EAAE,OAAO,UAAU,CAAC;CAC/B,CAAiB,CAAC;AAEnB;;;;;;;GAOG;AACH,eAAO,MAAM,GAAG,EAAE,SAAS,CACzB,OAAO,OAAO,EACd,iBAAiB,CAAC,GAAG,EAAE,QAAQ,CAAC,CACjB,CAAC;AAElB;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ,EAAE,SAAS,CAC9B,OAAO,OAAO,EACd,iBAAiB,CAAC,GAAG,EAAE,UAAU,CAAC,CACnB,CAAC;AAElB,eAAO,MAAM,UAAU,EAAqC,EAAE,CAAC"}
@@ -0,0 +1,31 @@
1
+ /* eslint-disable */
2
+ /**
3
+ * Generated `api` utility.
4
+ *
5
+ * THIS CODE IS AUTOMATICALLY GENERATED.
6
+ *
7
+ * To regenerate, run `npx convex dev`.
8
+ * @module
9
+ */
10
+ import { anyApi, componentsGeneric } from "convex/server";
11
+ const fullApi = anyApi;
12
+ /**
13
+ * A utility for referencing Convex functions in your app's public API.
14
+ *
15
+ * Usage:
16
+ * ```js
17
+ * const myFunctionReference = api.myModule.myFunction;
18
+ * ```
19
+ */
20
+ export const api = anyApi;
21
+ /**
22
+ * A utility for referencing Convex functions in your app's internal API.
23
+ *
24
+ * Usage:
25
+ * ```js
26
+ * const myFunctionReference = internal.myModule.myFunction;
27
+ * ```
28
+ */
29
+ export const internal = anyApi;
30
+ export const components = componentsGeneric();
31
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../../src/component/_generated/api.ts"],"names":[],"mappings":"AAAA,oBAAoB;AACpB;;;;;;;GAOG;AAWH,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAE1D,MAAM,OAAO,GAIR,MAAa,CAAC;AAEnB;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,GAAG,GAGZ,MAAa,CAAC;AAElB;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,QAAQ,GAGjB,MAAa,CAAC;AAElB,MAAM,CAAC,MAAM,UAAU,GAAG,iBAAiB,EAAmB,CAAC"}