@owlmeans/server-marketing-consent 0.1.18-rc.8 → 0.1.18
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/README.md +182 -1
- package/agent-meta/manifest.json +3 -3
- package/agent-meta/skills/server-marketing-consent/SKILL.md +13 -13
- package/build/handlers/index.d.ts +4 -0
- package/build/handlers/index.d.ts.map +1 -0
- package/build/handlers/index.js +4 -0
- package/build/handlers/index.js.map +1 -0
- package/build/handlers/save.d.ts +9 -0
- package/build/handlers/save.d.ts.map +1 -0
- package/build/handlers/save.js +11 -0
- package/build/handlers/save.js.map +1 -0
- package/build/handlers/status.d.ts +4 -0
- package/build/handlers/status.d.ts.map +1 -0
- package/build/handlers/status.js +12 -0
- package/build/handlers/status.js.map +1 -0
- package/build/handlers/terms.d.ts +8 -0
- package/build/handlers/terms.d.ts.map +1 -0
- package/build/handlers/terms.js +11 -0
- package/build/handlers/terms.js.map +1 -0
- package/build/helper.d.ts +1 -4
- package/build/helper.d.ts.map +1 -1
- package/build/helper.js +1 -1
- package/build/helper.js.map +1 -1
- package/build/index.d.ts +3 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1 -1
- package/build/index.js.map +1 -1
- package/build/model.d.ts +1 -56
- package/build/model.d.ts.map +1 -1
- package/build/model.js.map +1 -1
- package/build/service.d.ts +1 -47
- package/build/service.d.ts.map +1 -1
- package/build/service.js +13 -11
- package/build/service.js.map +1 -1
- package/build/subject/types.d.ts +19 -0
- package/build/subject/types.d.ts.map +1 -0
- package/build/subject/types.js +2 -0
- package/build/subject/types.js.map +1 -0
- package/build/subject.d.ts +3 -20
- package/build/subject.d.ts.map +1 -1
- package/build/subject.js +11 -18
- package/build/subject.js.map +1 -1
- package/build/types.d.ts +119 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +2 -0
- package/build/types.js.map +1 -0
- package/package.json +20 -19
- package/src/handlers/index.ts +3 -0
- package/src/handlers/save.ts +16 -0
- package/src/handlers/status.ts +19 -0
- package/src/handlers/terms.ts +16 -0
- package/src/helper.ts +2 -6
- package/src/index.ts +3 -1
- package/src/model.ts +2 -60
- package/src/service.ts +15 -57
- package/src/subject/types.ts +19 -0
- package/src/subject.ts +14 -24
- package/src/types.ts +114 -0
- package/tests/context.ts +2 -4
- package/tests/handlers.spec.ts +1 -1
- package/tests/service.spec.ts +2 -6
- package/tests/subject.spec.ts +6 -6
- package/build/handlers.d.ts +0 -20
- package/build/handlers.d.ts.map +0 -1
- package/build/handlers.js +0 -23
- package/build/handlers.js.map +0 -1
- package/src/handlers.ts +0 -44
package/README.md
CHANGED
|
@@ -1,3 +1,184 @@
|
|
|
1
|
+
# @owlmeans/server-marketing-consent
|
|
2
|
+
|
|
3
|
+
The server half of `@owlmeans/marketing-consent`: a database-agnostic `MarketingConsentService`
|
|
4
|
+
(status, save, terms acceptance, the `isGranted` send gate, purge, observers) over two resources it
|
|
5
|
+
resolves by alias, plus the `status` / `save` / `terms` handlers for the protocol tree from
|
|
6
|
+
`makeMarketingConsentProtocols`. A backend uses it when people record marketing or data-use consent
|
|
7
|
+
and terms acceptance through the API, or when server code must check a consent before sending or
|
|
8
|
+
sharing. It stores nothing by itself: register the resources with
|
|
9
|
+
`@owlmeans/marketing-consent-mongo` or `@owlmeans/marketing-consent-postgres`. Pure catalogue and
|
|
10
|
+
status logic without a server lives in `@owlmeans/marketing-consent`; the screens are
|
|
11
|
+
`@owlmeans/web-marketing-consent`.
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
bun add @owlmeans/server-marketing-consent@^0.1.18
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Concepts
|
|
20
|
+
|
|
21
|
+
- **Subject** — `MarketingConsentSubject` `{ userId, profileId?, entityId? }`, read from an
|
|
22
|
+
authenticated request. `entityId` comes only from `req.entity?.id`; an app without organization
|
|
23
|
+
entities simply has none.
|
|
24
|
+
- **Subject key** — `` `${entityId ?? ''}|${userId}|${profileId ?? ''}` ``, stored in the state
|
|
25
|
+
record's `subject` field. Records are addressed by `subject`, never by `id` (the backend mints
|
|
26
|
+
`id`).
|
|
27
|
+
- **State record** — `MarketingConsentStateRecord` at `RES_MARKETING_CONSENT_STATE`: one row per
|
|
28
|
+
subject holding the latest decision per key (`decisions` is an array) and the last terms
|
|
29
|
+
acceptance.
|
|
30
|
+
- **Log record** — `MarketingConsentLogRecord` at `RES_MARKETING_CONSENT_LOG`: append-only evidence
|
|
31
|
+
of every decision (`kind: 'consent'`) and terms acceptance (`kind: 'terms'`). Never updated or
|
|
32
|
+
deleted.
|
|
33
|
+
- **Send gate** — `isGranted(subject, key)` trusts only a saved, unrevised (`current`) answer; the
|
|
34
|
+
display `granted` from `status()` defaults unanswered opt-out items to `true` and must not gate a
|
|
35
|
+
send.
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
### Register storage, the service and the handlers
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
import { makeMarketingConsentProtocols } from '@owlmeans/marketing-consent'
|
|
43
|
+
import { appendMarketingConsentPostgres } from '@owlmeans/marketing-consent-postgres'
|
|
44
|
+
import { appendMarketingConsentService, serveMarketingConsentEntrypoints } from '@owlmeans/server-marketing-consent'
|
|
45
|
+
|
|
46
|
+
// shared package
|
|
47
|
+
export const marketingConsentProtocols = makeMarketingConsentProtocols({ parent: appProtocols.account.base })
|
|
48
|
+
|
|
49
|
+
// backend context
|
|
50
|
+
appendMarketingConsentPostgres(context) // the two resources
|
|
51
|
+
appendMarketingConsentService(context, { // no-op when the alias is already registered
|
|
52
|
+
config: { standard: { 'marketing.phone': false } },
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
// API process
|
|
56
|
+
context.registerEntrypoints(serveMarketingConsentEntrypoints(marketingConsentProtocols))
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`serveMarketingConsentEntrypoints` binds `status`, `save` and `terms` only. A `base` declared without
|
|
60
|
+
a `parent` must still be bound by the app, or boot fails with `Entrypoint marketing-consent not found`.
|
|
61
|
+
|
|
62
|
+
### Gate a send on a consent
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
import { MC_EMAIL, MARKETING_CONSENT_SERVICE } from '@owlmeans/marketing-consent'
|
|
66
|
+
import type { MarketingConsentService } from '@owlmeans/server-marketing-consent'
|
|
67
|
+
|
|
68
|
+
const consents = context.service<MarketingConsentService>(MARKETING_CONSENT_SERVICE)
|
|
69
|
+
|
|
70
|
+
if (await consents.isGranted({ userId, profileId, entityId }, MC_EMAIL)) {
|
|
71
|
+
await sendNewsletter(userId)
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Record a decision from server code
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { marketingConsentSubjectHelper } from '@owlmeans/server-marketing-consent'
|
|
79
|
+
|
|
80
|
+
const subject = marketingConsentSubjectHelper.subjectOf(req) // AuthForbidden without a user
|
|
81
|
+
await consents.save(subject, { decisions: [{ key: MC_EMAIL, granted: false }], source: 'api' })
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
`source: 'api'` is accepted only by the service, never by the HTTP `save` schema. It is meant for a
|
|
85
|
+
server withdrawing a consent on a caller's behalf.
|
|
86
|
+
|
|
87
|
+
### React to decisions
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
consents.observe(async ({ subject, decisions }) => {
|
|
91
|
+
await mailingList.sync(subject.userId, decisions)
|
|
92
|
+
})
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Listeners run in registration order after each `save`; a throwing listener is logged and never fails
|
|
96
|
+
the write.
|
|
97
|
+
|
|
98
|
+
### Custom handler options
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
import { saveMarketingConsent } from '@owlmeans/server-marketing-consent'
|
|
102
|
+
import { bind } from '@owlmeans/server-entrypoint'
|
|
103
|
+
|
|
104
|
+
bind(protocols.save, saveMarketingConsent(protocols.save, { allowAccessTokens: true, serviceAlias: 'my-consents' }))
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## API
|
|
108
|
+
|
|
109
|
+
### Functions
|
|
110
|
+
|
|
111
|
+
| Symbol | Purpose |
|
|
112
|
+
|---|---|
|
|
113
|
+
| `makeMarketingConsentService(opts?)` | Builds the lazy `MarketingConsentService` |
|
|
114
|
+
| `appendMarketingConsentService(ctx, opts?)` | Registers it unless the alias already exists |
|
|
115
|
+
| `serveMarketingConsentEntrypoints(protocols, opts?)` | `[bind(status), bind(save), bind(terms)]` |
|
|
116
|
+
| `marketingConsentStatus(protocol, opts?)` | `status` handler; reads `Sec-GPC: 1` as `gpc` |
|
|
117
|
+
| `saveMarketingConsent(protocol, opts?)` | `save` handler; refuses access-token auth unless `allowAccessTokens` |
|
|
118
|
+
| `recordTermsAcceptance(protocol, opts?)` | `terms` handler; refuses access-token auth unless `allowAccessTokens` |
|
|
119
|
+
| `marketingConsentSubjectHelper.subjectOf(req)` | `MarketingConsentSubject`; throws `AuthForbidden` without `req.auth.userId` |
|
|
120
|
+
| `marketingConsentSubjectHelper.subjectKey(subject)` | The `subject` string a state record is addressed by |
|
|
121
|
+
| `createMarketingConsentSubjectHelper()` | Builds a `MarketingConsentSubjectHelper` |
|
|
122
|
+
|
|
123
|
+
### `MarketingConsentService`
|
|
124
|
+
|
|
125
|
+
| Method | Purpose |
|
|
126
|
+
|---|---|
|
|
127
|
+
| `definitions()` | Effective catalogue from `opts.config`, memoized |
|
|
128
|
+
| `status(subject, { gpc? }?)` | `MarketingConsentStatusView` for a screen |
|
|
129
|
+
| `save(subject, request)` | Validates every key (`UnknownMarketingConsentError` on any unknown), appends one log row per decision, replaces those keys in the state record, notifies observers |
|
|
130
|
+
| `recordTerms(subject, acceptance, { source? }?)` | Appends a `terms` log row and sets the state record's `terms`; `source` defaults to `sign-in` |
|
|
131
|
+
| `isGranted(subject, key, { gpc? }?)` | `true` only for a `current` item whose saved answer is granted |
|
|
132
|
+
| `purge(subject)` | Deletes the state record; log rows stay |
|
|
133
|
+
| `observe(listener)` | Adds a `MarketingConsentObserver` |
|
|
134
|
+
|
|
135
|
+
### Constants, schemas and types
|
|
136
|
+
|
|
137
|
+
| Symbol | Kind | Purpose |
|
|
138
|
+
|---|---|---|
|
|
139
|
+
| `RES_MARKETING_CONSENT_STATE` | const | `'marketing-consent-state'` |
|
|
140
|
+
| `RES_MARKETING_CONSENT_LOG` | const | `'marketing-consent-log'` |
|
|
141
|
+
| `MarketingConsentStateSchema`, `MarketingConsentLogSchema` | AJV schema | Shared with the storage packages |
|
|
142
|
+
| `MakeMarketingConsentServiceOptions` | type | `{ alias?, state?, log?, config? }` |
|
|
143
|
+
| `MarketingConsentHandlerOptions` | type | `{ serviceAlias?, allowAccessTokens? }` |
|
|
144
|
+
| `MarketingConsentServedProtocols` | type | `status`, `save`, `terms` of the protocol tree |
|
|
145
|
+
| `MarketingConsentStateRecord`, `MarketingConsentLogRecord` | type | Stored records |
|
|
146
|
+
| `MarketingConsentSubject`, `MarketingConsentSubjectHelper` | type | Who a decision is recorded for |
|
|
147
|
+
| `MarketingConsentService`, `MarketingConsentObserver`, `MarketingConsentContext` | type | Service, listener, `ServerContext<ServerConfig>` |
|
|
148
|
+
|
|
149
|
+
## Testing
|
|
150
|
+
|
|
151
|
+
`bun test ./tests` runs against `@owlmeans/static-resource` stores for both aliases; no database is
|
|
152
|
+
needed. The suites cover the in-place upsert, `isGranted` versus display `granted`, `purge` leaving
|
|
153
|
+
the log intact, a throwing observer, the access-token refusal on `save` / `terms`, and a request
|
|
154
|
+
without an organization entity.
|
|
155
|
+
|
|
156
|
+
## Common pitfalls
|
|
157
|
+
|
|
158
|
+
- Gating a send on `status(...).items[n].granted` instead of `isGranted` — that sends to people who
|
|
159
|
+
opened the settings screen and changed nothing.
|
|
160
|
+
- Storing `decisions` as an object keyed by consent key — dotted keys (`marketing.email`) become
|
|
161
|
+
paths in Mongo and Postgres jsonb queries.
|
|
162
|
+
- Creating a state record with a caller-supplied `id` — Mongo and Postgres refuse it; the service
|
|
163
|
+
addresses rows by `subject`.
|
|
164
|
+
- Expecting `purge` to remove the log — the log is GDPR Art. 7(1) evidence and is never deleted
|
|
165
|
+
here.
|
|
166
|
+
- Reading `entityId` from a slug or `requireEntityKey` — the subject takes `req.entity?.id` only.
|
|
167
|
+
- Forgetting a storage package — the service resolves its resources by alias on first call and fails
|
|
168
|
+
there.
|
|
169
|
+
|
|
170
|
+
The `server-marketing-consent` skill covers these rules in depth; the `marketing-consent` skill
|
|
171
|
+
covers the catalogue and status table.
|
|
172
|
+
|
|
173
|
+
## Related packages
|
|
174
|
+
|
|
175
|
+
- [`@owlmeans/marketing-consent`](../marketing-consent) — catalogue, status resolution, protocol tree, errors
|
|
176
|
+
- [`@owlmeans/marketing-consent-mongo`](../marketing-consent-mongo) — MongoDB resources for the two aliases
|
|
177
|
+
- [`@owlmeans/marketing-consent-postgres`](../marketing-consent-postgres) — PostgreSQL resources for the two aliases
|
|
178
|
+
- [`@owlmeans/web-marketing-consent`](../web-marketing-consent) — the screens that call these handlers
|
|
179
|
+
- [`@owlmeans/server-auth-token`](../server-auth-token) — `refuseTokenAuth`, used by `save` and `terms`
|
|
180
|
+
- [`@owlmeans/server-api`](../server-api) — the `handlers()` builder the handlers use
|
|
181
|
+
|
|
1
182
|
<!-- owlmeans:agent-guidance:start -->
|
|
2
183
|
## Agent guidance
|
|
3
184
|
|
|
@@ -6,7 +187,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
|
|
|
6
187
|
your project's skill store (`.agents/skills/`):
|
|
7
188
|
|
|
8
189
|
```sh
|
|
9
|
-
npx @owlmeans/agent-skills@^0.1.18
|
|
190
|
+
npx @owlmeans/agent-skills@^0.1.18
|
|
10
191
|
```
|
|
11
192
|
|
|
12
193
|
The embedded files are version-matched to this package release. Do not edit them
|
package/agent-meta/manifest.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
-
"schemaVersion":
|
|
2
|
+
"schemaVersion": 3,
|
|
3
3
|
"package": "@owlmeans/server-marketing-consent",
|
|
4
|
-
"version": "0.1.18
|
|
5
|
-
"generatedAt": "2026-
|
|
4
|
+
"version": "0.1.18",
|
|
5
|
+
"generatedAt": "2026-10-28T00:31:34.768Z",
|
|
6
6
|
"canonicalRepo": "https://github.com/owlmeans/common",
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -8,8 +8,8 @@ user-invocable: false
|
|
|
8
8
|
# @owlmeans/server-marketing-consent
|
|
9
9
|
|
|
10
10
|
**Layer:** Server
|
|
11
|
-
**Install:** `"@owlmeans/server-marketing-consent": "^0.1.18
|
|
12
|
-
**Contracts:** `@owlmeans/marketing-consent` — the catalogue, `consentStatus`, the protocol tree, the error family
|
|
11
|
+
**Install:** `"@owlmeans/server-marketing-consent": "^0.1.18"` in `dependencies`
|
|
12
|
+
**Contracts:** `@owlmeans/marketing-consent` — the catalogue, `marketingConsentHelper.consentStatus`, the protocol tree, the error family
|
|
13
13
|
|
|
14
14
|
## Key Exports
|
|
15
15
|
|
|
@@ -17,7 +17,7 @@ user-invocable: false
|
|
|
17
17
|
|--------|-------------|
|
|
18
18
|
| `RES_MARKETING_CONSENT_STATE` (`marketing-consent-state`) · `RES_MARKETING_CONSENT_LOG` (`marketing-consent-log`) | Resource aliases — never rename, a later Mongo/Postgres extension's generated resource file names derive from these exact strings |
|
|
19
19
|
| `MarketingConsentStateRecord` · `MarketingConsentLogRecord` · `MarketingConsentStateSchema` · `MarketingConsentLogSchema` | The two record shapes and their AJV `JSONSchemaType` schemas — shared with the `@owlmeans/marketing-consent-mongo` / `@owlmeans/marketing-consent-postgres` extensions, which import them rather than redeclaring |
|
|
20
|
-
| `subjectOf(req)` ·
|
|
20
|
+
| `marketingConsentSubjectHelper.subjectOf(req)` · `.subjectKey(subject)` · `MarketingConsentSubject` | Who a decision is recorded for, and the `subject` key its one state record is addressed by |
|
|
21
21
|
| `makeMarketingConsentService(opts?)` · `appendMarketingConsentService(ctx, opts?)` | Build/register the `MarketingConsentService` |
|
|
22
22
|
| `MarketingConsentService` | `definitions`, `status`, `save`, `recordTerms`, `isGranted`, `purge`, `observe` |
|
|
23
23
|
| `marketingConsentStatus` · `saveMarketingConsent` · `recordTermsAcceptance` | Handler makers for `makeMarketingConsentProtocols().status/save/terms` |
|
|
@@ -54,43 +54,43 @@ exists under this alias before the first `status`/`save`/`terms` call" does).
|
|
|
54
54
|
|
|
55
55
|
This is the single most important gotcha in this package. `MarketingConsentStateRecord.decisions`
|
|
56
56
|
holds `MarketingConsentDecision[]`, one entry per key, folded down to the latest per key on read
|
|
57
|
-
(`consentStatus`). It is never stored as `{ [key]: MarketingConsentDecision }`, because every
|
|
57
|
+
(`marketingConsentHelper.consentStatus`). It is never stored as `{ [key]: MarketingConsentDecision }`, because every
|
|
58
58
|
standard consent key is DOTTED (`marketing.email`, `data.profiling`) and a dotted key inside
|
|
59
59
|
an object field is read as a PATH by both Mongo's dot-notation queries (`{'decisions.marketing.email':
|
|
60
60
|
...}` reaches into a nested `email` field under a nested `marketing` field, not a literal key) and
|
|
61
61
|
Postgres jsonb path operators (`->` chains on segments). An object-keyed shape works in a quick
|
|
62
62
|
manual test with one key and breaks the moment a second dotted key is added — the two fields
|
|
63
63
|
silently merge into one nested tree instead of staying two siblings. Keep it an array in every
|
|
64
|
-
storage backend; fold by key only in memory, in
|
|
64
|
+
storage backend; fold by key only in memory, in `.consentStatus` or in a resource's own read path.
|
|
65
65
|
|
|
66
|
-
## `subjectOf` never uses the slug
|
|
66
|
+
## `marketingConsentSubjectHelper.subjectOf` never uses the slug
|
|
67
67
|
|
|
68
68
|
```ts
|
|
69
|
-
|
|
69
|
+
const subjectOf = (req: AbstractRequest): MarketingConsentSubject => {
|
|
70
70
|
if (req.auth?.userId == null || req.auth.userId === '') throw new AuthForbidden(...)
|
|
71
71
|
return { userId: req.auth.userId, profileId: req.auth.profileId, entityId: req.entity?.id }
|
|
72
72
|
}
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
`entityId` reads `req.entity?.id` ONLY — never `
|
|
75
|
+
`entityId` reads `req.entity?.id` ONLY — never `makeEntityScope(req).requireEntityKey()`, never a slug off the token.
|
|
76
76
|
A deployment with no organization concept at all (a generated target app serving its own end users,
|
|
77
77
|
say) registers no entity resolver, so `req.entity` stays `undefined` and `entityId` is simply
|
|
78
|
-
absent — a valid, expected shape this package must never throw on. `subjectKey(subject)` —
|
|
78
|
+
absent — a valid, expected shape this package must never throw on. `marketingConsentSubjectHelper.subjectKey(subject)` —
|
|
79
79
|
`` `${entityId ?? ''}|${userId}|${profileId ?? ''}` `` — is the state record's `subject` field, and
|
|
80
80
|
every read and write addresses the record by it (`load({ subject })`, `purge({ subject })`), never
|
|
81
81
|
by `id`. The record's `id` is whatever the backend mints on `create()`: Mongo and Postgres refuse a
|
|
82
82
|
caller-supplied id outright (`RecordExists('id-present')`, whether or not such a row exists), so a
|
|
83
83
|
natural key can only ever live in a field. Each storage extension makes `subject` unique.
|
|
84
84
|
|
|
85
|
-
## `isGranted` is not `consentStatus`'s display `granted`
|
|
85
|
+
## `isGranted` is not `marketingConsentHelper.consentStatus`'s display `granted`
|
|
86
86
|
|
|
87
|
-
`status(subject)` returns what `consentStatus` computes for a settings screen: an unanswered
|
|
87
|
+
`status(subject)` returns what `marketingConsentHelper.consentStatus` computes for a settings screen: an unanswered
|
|
88
88
|
`opt-out` item (e.g. `data.partners`) reads `granted: true` there — the "on until you turn it off"
|
|
89
89
|
copy an unauthenticated-feeling first visit shows. `isGranted(subject, key)` is a DIFFERENT
|
|
90
90
|
question — the SERVER-SIDE gate a send/share actually checks before doing something — and it must
|
|
91
91
|
never honor that display default: an item whose `status` is `'new'` or `'revised'` has never been
|
|
92
92
|
affirmatively answered by this person, so `isGranted` answers `false` for it regardless of what
|
|
93
|
-
`consentStatus
|
|
93
|
+
`marketingConsentHelper.consentStatus`'s `granted` says, and `false` too for a key `definitions()` does not carry at all
|
|
94
94
|
(unknown or disabled). Only a `'current'` (i.e. actually saved, unrevised) item's `granted` is
|
|
95
95
|
trusted. Getting this backwards — wiring a send to `status(...).items.find(...).granted` instead of
|
|
96
96
|
`isGranted(...)` — sends to people who opened the settings screen and changed nothing.
|
|
@@ -151,7 +151,7 @@ directly — the app never calls `context.init()`/`configure()` in this style of
|
|
|
151
151
|
|
|
152
152
|
## Related
|
|
153
153
|
|
|
154
|
-
- `@owlmeans/marketing-consent` — the catalogue, `consentStatus`, the protocol tree (read its skill
|
|
154
|
+
- `@owlmeans/marketing-consent` — the catalogue, `marketingConsentHelper.consentStatus`, the protocol tree (read its skill
|
|
155
155
|
first; this package builds on it and repeats nothing it already documents)
|
|
156
156
|
- `@owlmeans/server-oauth`, `@owlmeans/server-auth-token` — `refuseTokenAuth`, the session-guarded
|
|
157
157
|
handler shape this package's `save`/`terms` copy
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/handlers/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,cAAc,YAAY,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/handlers/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,cAAc,YAAY,CAAA"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type MarketingConsentEntrypoints } from '@owlmeans/marketing-consent';
|
|
2
|
+
import type { MarketingConsentHandlerOptions } from '../types.js';
|
|
3
|
+
export declare const saveMarketingConsent: (protocol: MarketingConsentEntrypoints['save'], opts?: MarketingConsentHandlerOptions) => import("@owlmeans/server-entrypoint").BoundEntrypointHandler<import("@owlmeans/entrypoint").EntrypointProtocol<{
|
|
4
|
+
body: import("@owlmeans/marketing-consent").SaveMarketingConsentRequest;
|
|
5
|
+
}, {
|
|
6
|
+
ok: boolean;
|
|
7
|
+
status: import("@owlmeans/marketing-consent").MarketingConsentStatusView;
|
|
8
|
+
}>>;
|
|
9
|
+
//# sourceMappingURL=save.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"save.d.ts","sourceRoot":"","sources":["../../src/handlers/save.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,2BAA2B,EAAE,MAAM,6BAA6B,CAAA;AAGzG,OAAO,KAAK,EAA2B,8BAA8B,EAA2B,MAAM,aAAa,CAAA;AAMnH,eAAO,MAAM,oBAAoB,aACrB,2BAA2B,CAAC,MAAM,CAAC,SAAQ,8BAA8B;;;;;GAKnF,CAAA"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { MARKETING_CONSENT_SERVICE } from '@owlmeans/marketing-consent';
|
|
2
|
+
import { handlers } from '@owlmeans/server-api';
|
|
3
|
+
import { refuseTokenAuth } from '@owlmeans/server-auth-token';
|
|
4
|
+
import { marketingConsentSubjectHelper } from '../subject.js';
|
|
5
|
+
const serviceOf = (context, opts) => context.service(opts?.serviceAlias ?? MARKETING_CONSENT_SERVICE);
|
|
6
|
+
export const saveMarketingConsent = (protocol, opts = {}) => handlers().body(protocol, async (payload, context, req) => {
|
|
7
|
+
if (opts.allowAccessTokens !== true)
|
|
8
|
+
refuseTokenAuth(req, 'marketing-consent-save');
|
|
9
|
+
return serviceOf(context, opts).save(marketingConsentSubjectHelper.subjectOf(req), payload);
|
|
10
|
+
});
|
|
11
|
+
//# sourceMappingURL=save.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"save.js","sourceRoot":"","sources":["../../src/handlers/save.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAoC,MAAM,6BAA6B,CAAA;AACzG,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAE7D,OAAO,EAAE,6BAA6B,EAAE,MAAM,eAAe,CAAA;AAE7D,MAAM,SAAS,GAAG,CAAC,OAAgC,EAAE,IAAqC,EAA2B,EAAE,CACrH,OAAO,CAAC,OAAO,CAA0B,IAAI,EAAE,YAAY,IAAI,yBAAyB,CAAC,CAAA;AAE3F,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,QAA6C,EAAE,IAAI,GAAmC,EAAE,EACxF,EAAE,CAAC,QAAQ,EAA2B,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE;IACtF,IAAI,IAAI,CAAC,iBAAiB,KAAK,IAAI;QAAE,eAAe,CAAC,GAAG,EAAE,wBAAwB,CAAC,CAAA;IAEnF,OAAO,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,6BAA6B,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAA;AAC7F,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type MarketingConsentEntrypoints } from '@owlmeans/marketing-consent';
|
|
2
|
+
import type { MarketingConsentHandlerOptions } from '../types.js';
|
|
3
|
+
export declare const marketingConsentStatus: (protocol: MarketingConsentEntrypoints['status'], opts?: MarketingConsentHandlerOptions) => import("@owlmeans/server-entrypoint").BoundEntrypointHandler<import("@owlmeans/entrypoint").EntrypointProtocol<{}, import("@owlmeans/marketing-consent").MarketingConsentStatusView>>;
|
|
4
|
+
//# sourceMappingURL=status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/handlers/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,2BAA2B,EAAE,MAAM,6BAA6B,CAAA;AAEzG,OAAO,KAAK,EAA2B,8BAA8B,EAA2B,MAAM,aAAa,CAAA;AAanH,eAAO,MAAM,sBAAsB,aACvB,2BAA2B,CAAC,QAAQ,CAAC,SAAQ,8BAA8B,0LAE4B,CAAA"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { MARKETING_CONSENT_SERVICE } from '@owlmeans/marketing-consent';
|
|
2
|
+
import { handlers } from '@owlmeans/server-api';
|
|
3
|
+
import { marketingConsentSubjectHelper } from '../subject.js';
|
|
4
|
+
const serviceOf = (context, opts) => context.service(opts?.serviceAlias ?? MARKETING_CONSENT_SERVICE);
|
|
5
|
+
/** GPC ("Sec-GPC: 1") read as a plain header — Fastify normalizes header names to lower case, and
|
|
6
|
+
* a repeated header arrives as an array, so only its first value is read. */
|
|
7
|
+
const gpcOf = (headers) => {
|
|
8
|
+
const raw = headers['sec-gpc'];
|
|
9
|
+
return (Array.isArray(raw) ? raw[0] : raw) === '1';
|
|
10
|
+
};
|
|
11
|
+
export const marketingConsentStatus = (protocol, opts = {}) => handlers().request(protocol, async (req, context) => serviceOf(context, opts).status(marketingConsentSubjectHelper.subjectOf(req), { gpc: gpcOf(req.headers ?? {}) }));
|
|
12
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/handlers/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAoC,MAAM,6BAA6B,CAAA;AACzG,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAE/C,OAAO,EAAE,6BAA6B,EAAE,MAAM,eAAe,CAAA;AAE7D,MAAM,SAAS,GAAG,CAAC,OAAgC,EAAE,IAAqC,EAA2B,EAAE,CACrH,OAAO,CAAC,OAAO,CAA0B,IAAI,EAAE,YAAY,IAAI,yBAAyB,CAAC,CAAA;AAE3F;6EAC6E;AAC7E,MAAM,KAAK,GAAG,CAAC,OAAsD,EAAW,EAAE;IAChF,MAAM,GAAG,GAAG,OAAO,CAAC,SAAS,CAAC,CAAA;IAC9B,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,GAAG,CAAA;AACpD,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,sBAAsB,GAAG,CACpC,QAA+C,EAAE,IAAI,GAAmC,EAAE,EAC1F,EAAE,CAAC,QAAQ,EAA2B,CAAC,OAAO,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,CAChF,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,MAAM,CAAC,6BAA6B,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type MarketingConsentEntrypoints } from '@owlmeans/marketing-consent';
|
|
2
|
+
import type { MarketingConsentHandlerOptions } from '../types.js';
|
|
3
|
+
export declare const recordTermsAcceptance: (protocol: MarketingConsentEntrypoints['terms'], opts?: MarketingConsentHandlerOptions) => import("@owlmeans/server-entrypoint").BoundEntrypointHandler<import("@owlmeans/entrypoint").EntrypointProtocol<{
|
|
4
|
+
body: import("@owlmeans/marketing-consent").TermsAcceptance;
|
|
5
|
+
}, {
|
|
6
|
+
ok: boolean;
|
|
7
|
+
}>>;
|
|
8
|
+
//# sourceMappingURL=terms.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terms.d.ts","sourceRoot":"","sources":["../../src/handlers/terms.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,2BAA2B,EAAE,MAAM,6BAA6B,CAAA;AAGzG,OAAO,KAAK,EAA2B,8BAA8B,EAA2B,MAAM,aAAa,CAAA;AAMnH,eAAO,MAAM,qBAAqB,aACtB,2BAA2B,CAAC,OAAO,CAAC,SAAQ,8BAA8B;;;;GAKpF,CAAA"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { MARKETING_CONSENT_SERVICE } from '@owlmeans/marketing-consent';
|
|
2
|
+
import { handlers } from '@owlmeans/server-api';
|
|
3
|
+
import { refuseTokenAuth } from '@owlmeans/server-auth-token';
|
|
4
|
+
import { marketingConsentSubjectHelper } from '../subject.js';
|
|
5
|
+
const serviceOf = (context, opts) => context.service(opts?.serviceAlias ?? MARKETING_CONSENT_SERVICE);
|
|
6
|
+
export const recordTermsAcceptance = (protocol, opts = {}) => handlers().body(protocol, async (payload, context, req) => {
|
|
7
|
+
if (opts.allowAccessTokens !== true)
|
|
8
|
+
refuseTokenAuth(req, 'marketing-consent-terms');
|
|
9
|
+
return serviceOf(context, opts).recordTerms(marketingConsentSubjectHelper.subjectOf(req), payload);
|
|
10
|
+
});
|
|
11
|
+
//# sourceMappingURL=terms.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terms.js","sourceRoot":"","sources":["../../src/handlers/terms.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAoC,MAAM,6BAA6B,CAAA;AACzG,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAE7D,OAAO,EAAE,6BAA6B,EAAE,MAAM,eAAe,CAAA;AAE7D,MAAM,SAAS,GAAG,CAAC,OAAgC,EAAE,IAAqC,EAA2B,EAAE,CACrH,OAAO,CAAC,OAAO,CAA0B,IAAI,EAAE,YAAY,IAAI,yBAAyB,CAAC,CAAA;AAE3F,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACnC,QAA8C,EAAE,IAAI,GAAmC,EAAE,EACzF,EAAE,CAAC,QAAQ,EAA2B,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE;IACtF,IAAI,IAAI,CAAC,iBAAiB,KAAK,IAAI;QAAE,eAAe,CAAC,GAAG,EAAE,yBAAyB,CAAC,CAAA;IAEpF,OAAO,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,WAAW,CAAC,6BAA6B,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAA;AACpG,CAAC,CAAC,CAAA"}
|
package/build/helper.d.ts
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { MarketingConsentHandlerOptions } from './handlers.js';
|
|
3
|
-
/** The part of a `makeMarketingConsentProtocols` tree that carries a server implementation. */
|
|
4
|
-
export type MarketingConsentServedProtocols = Pick<MarketingConsentEntrypoints, 'status' | 'save' | 'terms'>;
|
|
1
|
+
import type { MarketingConsentHandlerOptions, MarketingConsentServedProtocols } from './types.js';
|
|
5
2
|
/**
|
|
6
3
|
* Bind this package's handlers to a tree declared by `makeMarketingConsentProtocols` — one binding
|
|
7
4
|
* per protocol that needs a server implementation. `base` and `screen` carry no handler of their
|
package/build/helper.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,8BAA8B,EAAE,+BAA+B,EAAE,MAAM,YAAY,CAAA;AAEjG;;;;;;;GAOG;AACH,eAAO,MAAM,gCAAgC,cAChC,+BAA+B,SAAQ,8BAA8B;;;;;;;;;gMAKjF,CAAA"}
|
package/build/helper.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { bind } from '@owlmeans/server-entrypoint';
|
|
2
|
-
import { marketingConsentStatus, recordTermsAcceptance, saveMarketingConsent } from './handlers.js';
|
|
2
|
+
import { marketingConsentStatus, recordTermsAcceptance, saveMarketingConsent } from './handlers/index.js';
|
|
3
3
|
/**
|
|
4
4
|
* Bind this package's handlers to a tree declared by `makeMarketingConsentProtocols` — one binding
|
|
5
5
|
* per protocol that needs a server implementation. `base` and `screen` carry no handler of their
|
package/build/helper.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helper.js","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,6BAA6B,CAAA;
|
|
1
|
+
{"version":3,"file":"helper.js","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,6BAA6B,CAAA;AAClD,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAGzG;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAG,CAC9C,SAA0C,EAAE,IAAI,GAAmC,EAAE,EACrF,EAAE,CAAC;IACH,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,sBAAsB,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACtE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,oBAAoB,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAChE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,qBAAqB,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;CACpE,CAAA"}
|
package/build/index.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
export * from './consts.js';
|
|
2
2
|
export * from './model.js';
|
|
3
|
+
export type * from './types.js';
|
|
4
|
+
export type * from './subject/types.js';
|
|
3
5
|
export * from './subject.js';
|
|
4
6
|
export * from './service.js';
|
|
5
|
-
export * from './handlers.js';
|
|
7
|
+
export * from './handlers/index.js';
|
|
6
8
|
export * from './helper.js';
|
|
7
9
|
//# sourceMappingURL=index.d.ts.map
|
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA;AAC5B,cAAc,cAAc,CAAA;AAC5B,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,mBAAmB,YAAY,CAAA;AAC/B,mBAAmB,oBAAoB,CAAA;AACvC,cAAc,cAAc,CAAA;AAC5B,cAAc,cAAc,CAAA;AAC5B,cAAc,qBAAqB,CAAA;AACnC,cAAc,aAAa,CAAA"}
|
package/build/index.js
CHANGED
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAG1B,cAAc,cAAc,CAAA;AAC5B,cAAc,cAAc,CAAA;AAC5B,cAAc,qBAAqB,CAAA;AACnC,cAAc,aAAa,CAAA"}
|
package/build/model.d.ts
CHANGED
|
@@ -1,60 +1,5 @@
|
|
|
1
1
|
import type { JSONSchemaType } from 'ajv';
|
|
2
|
-
import type {
|
|
3
|
-
import type { MarketingConsentDecision, MarketingConsentMode, MarketingConsentSource, TermsDocumentRef } from '@owlmeans/marketing-consent';
|
|
4
|
-
/**
|
|
5
|
-
* One subject's saved decisions, one record per subject (`id` = `subjectKey(subject)`).
|
|
6
|
-
*
|
|
7
|
-
* `decisions` is an ARRAY, never an object keyed by consent key. A dotted key such as
|
|
8
|
-
* `"marketing.email"` is read as a PATH by both Mongo dot-notation queries and Postgres jsonb path
|
|
9
|
-
* operators — an object-keyed shape breaks the moment a second consent key is added. This is the
|
|
10
|
-
* single most important shape decision in this package; the Mongo/Postgres extensions that will
|
|
11
|
-
* store this record must not "flatten" it into an object for convenience.
|
|
12
|
-
*/
|
|
13
|
-
export interface MarketingConsentStateRecord extends ResourceRecord {
|
|
14
|
-
id: string;
|
|
15
|
-
/** `subjectKey(subjectOf(req))` — see `./subject.js`. Also this record's own `id`. */
|
|
16
|
-
subject: string;
|
|
17
|
-
userId: string;
|
|
18
|
-
profileId?: string;
|
|
19
|
-
entityId?: string;
|
|
20
|
-
decisions: MarketingConsentDecision[];
|
|
21
|
-
terms?: {
|
|
22
|
-
documents: TermsDocumentRef[];
|
|
23
|
-
notices?: TermsDocumentRef[];
|
|
24
|
-
version: string;
|
|
25
|
-
locale?: string;
|
|
26
|
-
acceptedAt: string;
|
|
27
|
-
};
|
|
28
|
-
gpc?: boolean;
|
|
29
|
-
createdAt: string;
|
|
30
|
-
updatedAt: string;
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Append-only evidence of every decision and terms acceptance ever recorded — GDPR Art. 7(1)
|
|
34
|
-
* "demonstrate consent" material. Never updated or deleted, including on `purge()`: only the
|
|
35
|
-
* current-state record is cleared there.
|
|
36
|
-
*/
|
|
37
|
-
export interface MarketingConsentLogRecord extends ResourceRecord {
|
|
38
|
-
id: string;
|
|
39
|
-
subject: string;
|
|
40
|
-
userId: string;
|
|
41
|
-
profileId?: string;
|
|
42
|
-
entityId?: string;
|
|
43
|
-
kind: 'consent' | 'terms';
|
|
44
|
-
/** Present when `kind === 'consent'`. */
|
|
45
|
-
key?: string;
|
|
46
|
-
granted?: boolean;
|
|
47
|
-
revisedAt?: string;
|
|
48
|
-
mode?: MarketingConsentMode;
|
|
49
|
-
/** Present when `kind === 'terms'`. */
|
|
50
|
-
documents?: TermsDocumentRef[];
|
|
51
|
-
notices?: TermsDocumentRef[];
|
|
52
|
-
version?: string;
|
|
53
|
-
decidedAt: string;
|
|
54
|
-
source: MarketingConsentSource;
|
|
55
|
-
locale?: string;
|
|
56
|
-
gpc?: boolean;
|
|
57
|
-
}
|
|
2
|
+
import type { MarketingConsentLogRecord, MarketingConsentStateRecord } from './types.js';
|
|
58
3
|
/**
|
|
59
4
|
* Shared with the Mongo/Postgres extension packages this schema is built for — imported by them,
|
|
60
5
|
* never duplicated.
|
package/build/model.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,KAAK,CAAA;
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,KAAK,CAAA;AAEzC,OAAO,KAAK,EAAE,yBAAyB,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAA;AA8BxF;;;GAGG;AACH,eAAO,MAAM,2BAA2B,EAAE,cAAc,CAAC,2BAA2B,CA4BnF,CAAA;AAED,eAAO,MAAM,yBAAyB,EAAE,cAAc,CAAC,yBAAyB,CAuB/E,CAAA"}
|
package/build/model.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model.js","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"model.js","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"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"}
|
package/build/service.d.ts
CHANGED
|
@@ -1,50 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { MarketingConsentConfig, MarketingConsentDecision, MarketingConsentDefinition, MarketingConsentSource, MarketingConsentStatusView, SaveMarketingConsentRequest, TermsAcceptance } from '@owlmeans/marketing-consent';
|
|
3
|
-
import type { ServerConfig, ServerContext } from '@owlmeans/server-context';
|
|
4
|
-
import type { MarketingConsentSubject } from './subject.js';
|
|
5
|
-
export type MarketingConsentContext = ServerContext<ServerConfig>;
|
|
6
|
-
export type MarketingConsentObserver = (event: {
|
|
7
|
-
subject: MarketingConsentSubject;
|
|
8
|
-
decisions: MarketingConsentDecision[];
|
|
9
|
-
}) => void | Promise<void>;
|
|
10
|
-
export interface MarketingConsentService extends LazyService {
|
|
11
|
-
/** The effective catalogue (`resolveMarketingConsents`), computed once and memoized. */
|
|
12
|
-
definitions(): MarketingConsentDefinition[];
|
|
13
|
-
status(subject: MarketingConsentSubject, opts?: {
|
|
14
|
-
gpc?: boolean;
|
|
15
|
-
}): Promise<MarketingConsentStatusView>;
|
|
16
|
-
save(subject: MarketingConsentSubject, request: SaveMarketingConsentRequest | (Omit<SaveMarketingConsentRequest, 'source'> & {
|
|
17
|
-
source: 'api';
|
|
18
|
-
})): Promise<{
|
|
19
|
-
ok: true;
|
|
20
|
-
status: MarketingConsentStatusView;
|
|
21
|
-
}>;
|
|
22
|
-
recordTerms(subject: MarketingConsentSubject, acceptance: TermsAcceptance, opts?: {
|
|
23
|
-
source?: MarketingConsentSource;
|
|
24
|
-
}): Promise<{
|
|
25
|
-
ok: true;
|
|
26
|
-
}>;
|
|
27
|
-
/**
|
|
28
|
-
* The SERVER-SIDE gate a send/share checks — the SAVED, CONFIRMED answer only. An item still
|
|
29
|
-
* `'new'` or `'revised'` has never been affirmatively answered by this person, so it reads as
|
|
30
|
-
* NOT granted here even where `consentStatus`'s own `granted` defaults an opt-out item to `true`
|
|
31
|
-
* for DISPLAY purposes.
|
|
32
|
-
*/
|
|
33
|
-
isGranted(subject: MarketingConsentSubject, key: string, opts?: {
|
|
34
|
-
gpc?: boolean;
|
|
35
|
-
}): Promise<boolean>;
|
|
36
|
-
/** Clears the current-state record. Log rows stay — they are the append-only compliance evidence. */
|
|
37
|
-
purge(subject: MarketingConsentSubject): Promise<void>;
|
|
38
|
-
observe(listener: MarketingConsentObserver): void;
|
|
39
|
-
}
|
|
40
|
-
export interface MakeMarketingConsentServiceOptions {
|
|
41
|
-
alias?: string;
|
|
42
|
-
/** Resource alias holding one current-state record per subject. Defaults to `RES_MARKETING_CONSENT_STATE`. */
|
|
43
|
-
state?: string;
|
|
44
|
-
/** Resource alias holding the append-only log. Defaults to `RES_MARKETING_CONSENT_LOG`. */
|
|
45
|
-
log?: string;
|
|
46
|
-
config?: MarketingConsentConfig;
|
|
47
|
-
}
|
|
1
|
+
import type { MakeMarketingConsentServiceOptions, MarketingConsentContext, MarketingConsentService } from './types.js';
|
|
48
2
|
/**
|
|
49
3
|
* Build the `MarketingConsentService`. Database-agnostic: both resources are resolved BY ALIAS
|
|
50
4
|
* from the context lazily, inside each method call — never imported or constructed here. A
|
package/build/service.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAmF,kCAAkC,EAAE,uBAAuB,EAA4B,uBAAuB,EAAE,MAAM,YAAY,CAAA;AAQjO;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,UAChC,kCAAkC,KACvC,uBA+JF,CAAA;AAED,gGAAgG;AAChG,eAAO,MAAM,6BAA6B,YAC/B,uBAAuB,SAAQ,kCAAkC,KACzE,IAKF,CAAA"}
|