@c15t/backend 2.0.0-rc.3 → 2.0.0-rc.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cache.cjs +4 -4
- package/dist/cache.js +4 -4
- package/dist/core.cjs +845 -87
- package/dist/core.js +821 -87
- package/dist/db/schema.cjs +37 -0
- package/dist/db/schema.js +33 -2
- package/dist/edge.cjs +1106 -0
- package/dist/edge.js +1069 -0
- package/dist/router.cjs +621 -71
- package/dist/router.js +621 -71
- package/{dist → dist-types}/cache/adapters/cloudflare-kv.d.ts +0 -1
- package/{dist → dist-types}/cache/adapters/index.d.ts +0 -1
- package/{dist → dist-types}/cache/adapters/memory.d.ts +0 -1
- package/{dist → dist-types}/cache/adapters/upstash-redis.d.ts +0 -1
- package/{dist → dist-types}/cache/gvl-resolver.d.ts +1 -2
- package/{dist → dist-types}/cache/index.d.ts +0 -1
- package/{dist → dist-types}/cache/keys.d.ts +0 -1
- package/{dist → dist-types}/cache/types.d.ts +0 -1
- package/{dist → dist-types}/core.d.ts +8 -1
- package/{dist → dist-types}/db/migrator/index.d.ts +0 -1
- package/{dist → dist-types}/db/registry/consent-policy.d.ts +0 -1
- package/{dist → dist-types}/db/registry/consent-purpose.d.ts +0 -1
- package/{dist → dist-types}/db/registry/domain.d.ts +0 -1
- package/{dist → dist-types}/db/registry/index.d.ts +22 -2
- package/dist-types/db/registry/runtime-policy-decision.d.ts +60 -0
- package/{dist → dist-types}/db/registry/subject.d.ts +0 -1
- package/{dist → dist-types}/db/registry/types.d.ts +1 -2
- package/{dist → dist-types}/db/registry/utils/generate-id.d.ts +0 -1
- package/{dist → dist-types}/db/registry/utils.d.ts +0 -1
- package/{dist → dist-types}/db/schema/1.0.0/audit-log.d.ts +0 -1
- package/{dist → dist-types}/db/schema/1.0.0/consent-policy.d.ts +0 -1
- package/{dist → dist-types}/db/schema/1.0.0/consent-purpose.d.ts +0 -1
- package/{dist → dist-types}/db/schema/1.0.0/consent-record.d.ts +0 -1
- package/{dist → dist-types}/db/schema/1.0.0/consent.d.ts +1 -2
- package/{dist → dist-types}/db/schema/1.0.0/domain.d.ts +0 -1
- package/{dist → dist-types}/db/schema/1.0.0/index.d.ts +0 -1
- package/{dist → dist-types}/db/schema/1.0.0/subject.d.ts +0 -1
- package/{dist → dist-types}/db/schema/2.0.0/audit-log.d.ts +1 -2
- package/{dist → dist-types}/db/schema/2.0.0/consent-policy.d.ts +1 -2
- package/{dist → dist-types}/db/schema/2.0.0/consent-purpose.d.ts +1 -2
- package/{dist → dist-types}/db/schema/2.0.0/consent.d.ts +5 -2
- package/{dist → dist-types}/db/schema/2.0.0/domain.d.ts +1 -2
- package/{dist → dist-types}/db/schema/2.0.0/index.d.ts +432 -17
- package/dist-types/db/schema/2.0.0/runtime-policy-decision.d.ts +23 -0
- package/{dist → dist-types}/db/schema/2.0.0/subject.d.ts +1 -2
- package/{dist → dist-types}/db/schema/index.d.ts +862 -33
- package/{dist → dist-types}/db/tenant-scope.d.ts +0 -1
- package/dist-types/define-config.d.ts +17 -0
- package/dist-types/edge/index.d.ts +5 -0
- package/dist-types/edge/init-handler.d.ts +38 -0
- package/dist-types/edge/resolve-consent.d.ts +80 -0
- package/dist-types/edge/types.d.ts +13 -0
- package/{dist → dist-types}/handlers/consent/check.handler.d.ts +0 -1
- package/{src/handlers/consent/index.ts → dist-types/handlers/consent/index.d.ts} +0 -1
- package/{dist → dist-types}/handlers/init/geo.d.ts +2 -3
- package/{dist → dist-types}/handlers/init/index.d.ts +4 -5
- package/dist-types/handlers/init/policy.d.ts +26 -0
- package/dist-types/handlers/init/resolve-init.d.ts +44 -0
- package/dist-types/handlers/init/translations.d.ts +48 -0
- package/dist-types/handlers/policy/snapshot.d.ts +99 -0
- package/{src/handlers/status/index.ts → dist-types/handlers/status/index.d.ts} +0 -1
- package/{dist → dist-types}/handlers/status/status.handler.d.ts +0 -1
- package/{dist → dist-types}/handlers/subject/get.handler.d.ts +0 -1
- package/{src/handlers/subject/index.ts → dist-types/handlers/subject/index.d.ts} +0 -1
- package/{dist → dist-types}/handlers/subject/list.handler.d.ts +0 -1
- package/{dist → dist-types}/handlers/subject/patch.handler.d.ts +0 -1
- package/{dist → dist-types}/handlers/subject/post.handler.d.ts +12 -1
- package/{dist → dist-types}/handlers/utils/consent-enrichment.d.ts +0 -1
- package/{dist → dist-types}/init.d.ts +4 -7
- package/{dist → dist-types}/middleware/auth/index.d.ts +0 -1
- package/{dist → dist-types}/middleware/auth/validate-api-key.d.ts +0 -1
- package/{dist → dist-types}/middleware/cors/cors.d.ts +0 -1
- package/{src/middleware/cors/index.ts → dist-types/middleware/cors/index.d.ts} +0 -1
- package/{dist → dist-types}/middleware/cors/is-origin-trusted.d.ts +1 -2
- package/{dist → dist-types}/middleware/cors/process-cors.d.ts +0 -1
- package/{dist → dist-types}/middleware/openapi/config.d.ts +0 -1
- package/{dist → dist-types}/middleware/openapi/handlers.d.ts +0 -1
- package/{src/middleware/openapi/index.ts → dist-types/middleware/openapi/index.d.ts} +0 -1
- package/{dist → dist-types}/middleware/process-ip/index.d.ts +0 -1
- package/dist-types/policies/builder.d.ts +127 -0
- package/dist-types/policies/defaults.d.ts +2 -0
- package/dist-types/policies/matchers.d.ts +3 -0
- package/{dist → dist-types}/router.d.ts +0 -1
- package/{dist → dist-types}/routes/consent.d.ts +0 -1
- package/{src/routes/index.ts → dist-types/routes/index.d.ts} +0 -1
- package/{dist → dist-types}/routes/init.d.ts +0 -1
- package/{dist → dist-types}/routes/status.d.ts +0 -1
- package/{dist → dist-types}/routes/subject.d.ts +0 -1
- package/{dist → dist-types}/types/api.d.ts +0 -1
- package/dist-types/types/index.d.ts +443 -0
- package/dist-types/utils/background.d.ts +6 -0
- package/{dist → dist-types}/utils/create-telemetry-options.d.ts +1 -2
- package/{dist → dist-types}/utils/env.d.ts +0 -1
- package/{dist → dist-types}/utils/extract-error-message.d.ts +0 -1
- package/{dist → dist-types}/utils/instrumentation.d.ts +0 -1
- package/{dist → dist-types}/utils/logger.d.ts +1 -2
- package/{dist → dist-types}/utils/metrics.d.ts +0 -1
- package/dist-types/version.d.ts +1 -0
- package/docs/README.md +49 -0
- package/docs/api/configuration.md +197 -0
- package/docs/api/endpoints.md +211 -0
- package/docs/guides/caching.md +85 -0
- package/docs/guides/database-setup.md +128 -0
- package/docs/guides/edge-deployment.md +248 -0
- package/docs/guides/framework-integration.md +142 -0
- package/docs/guides/iab-tcf.md +89 -0
- package/docs/guides/observability.md +96 -0
- package/docs/guides/policy-packs.md +396 -0
- package/docs/quickstart.md +129 -0
- package/package.json +37 -23
- package/.turbo/turbo-build.log +0 -49
- package/CHANGELOG.md +0 -115
- package/dist/cache/adapters/cloudflare-kv.d.ts.map +0 -1
- package/dist/cache/adapters/index.d.ts.map +0 -1
- package/dist/cache/adapters/memory.d.ts.map +0 -1
- package/dist/cache/adapters/upstash-redis.d.ts.map +0 -1
- package/dist/cache/gvl-resolver.d.ts.map +0 -1
- package/dist/cache/index.d.ts.map +0 -1
- package/dist/cache/keys.d.ts.map +0 -1
- package/dist/cache/types.d.ts.map +0 -1
- package/dist/core.d.ts.map +0 -1
- package/dist/db/adapters/drizzle.d.ts +0 -2
- package/dist/db/adapters/drizzle.d.ts.map +0 -1
- package/dist/db/adapters/index.d.ts +0 -2
- package/dist/db/adapters/index.d.ts.map +0 -1
- package/dist/db/adapters/kysely.d.ts +0 -2
- package/dist/db/adapters/kysely.d.ts.map +0 -1
- package/dist/db/adapters/mongo.d.ts +0 -2
- package/dist/db/adapters/mongo.d.ts.map +0 -1
- package/dist/db/adapters/prisma.d.ts +0 -2
- package/dist/db/adapters/prisma.d.ts.map +0 -1
- package/dist/db/adapters/typeorm.d.ts +0 -2
- package/dist/db/adapters/typeorm.d.ts.map +0 -1
- package/dist/db/migrator/index.d.ts.map +0 -1
- package/dist/db/registry/consent-policy.d.ts.map +0 -1
- package/dist/db/registry/consent-purpose.d.ts.map +0 -1
- package/dist/db/registry/domain.d.ts.map +0 -1
- package/dist/db/registry/index.d.ts.map +0 -1
- package/dist/db/registry/subject.d.ts.map +0 -1
- package/dist/db/registry/types.d.ts.map +0 -1
- package/dist/db/registry/utils/generate-id.d.ts.map +0 -1
- package/dist/db/registry/utils.d.ts.map +0 -1
- package/dist/db/schema/1.0.0/audit-log.d.ts.map +0 -1
- package/dist/db/schema/1.0.0/consent-policy.d.ts.map +0 -1
- package/dist/db/schema/1.0.0/consent-purpose.d.ts.map +0 -1
- package/dist/db/schema/1.0.0/consent-record.d.ts.map +0 -1
- package/dist/db/schema/1.0.0/consent.d.ts.map +0 -1
- package/dist/db/schema/1.0.0/domain.d.ts.map +0 -1
- package/dist/db/schema/1.0.0/index.d.ts.map +0 -1
- package/dist/db/schema/1.0.0/subject.d.ts.map +0 -1
- package/dist/db/schema/2.0.0/audit-log.d.ts.map +0 -1
- package/dist/db/schema/2.0.0/consent-policy.d.ts.map +0 -1
- package/dist/db/schema/2.0.0/consent-purpose.d.ts.map +0 -1
- package/dist/db/schema/2.0.0/consent.d.ts.map +0 -1
- package/dist/db/schema/2.0.0/domain.d.ts.map +0 -1
- package/dist/db/schema/2.0.0/index.d.ts.map +0 -1
- package/dist/db/schema/2.0.0/subject.d.ts.map +0 -1
- package/dist/db/schema/index.d.ts.map +0 -1
- package/dist/db/tenant-scope.d.ts.map +0 -1
- package/dist/define-config.d.ts +0 -5
- package/dist/define-config.d.ts.map +0 -1
- package/dist/handlers/consent/check.handler.d.ts.map +0 -1
- package/dist/handlers/consent/index.d.ts +0 -12
- package/dist/handlers/consent/index.d.ts.map +0 -1
- package/dist/handlers/init/geo.d.ts.map +0 -1
- package/dist/handlers/init/index.d.ts.map +0 -1
- package/dist/handlers/init/translations.d.ts +0 -28
- package/dist/handlers/init/translations.d.ts.map +0 -1
- package/dist/handlers/status/index.d.ts +0 -7
- package/dist/handlers/status/index.d.ts.map +0 -1
- package/dist/handlers/status/status.handler.d.ts.map +0 -1
- package/dist/handlers/subject/get.handler.d.ts.map +0 -1
- package/dist/handlers/subject/index.d.ts +0 -10
- package/dist/handlers/subject/index.d.ts.map +0 -1
- package/dist/handlers/subject/list.handler.d.ts.map +0 -1
- package/dist/handlers/subject/patch.handler.d.ts.map +0 -1
- package/dist/handlers/subject/post.handler.d.ts.map +0 -1
- package/dist/handlers/utils/consent-enrichment.d.ts.map +0 -1
- package/dist/init.d.ts.map +0 -1
- package/dist/middleware/auth/index.d.ts.map +0 -1
- package/dist/middleware/auth/validate-api-key.d.ts.map +0 -1
- package/dist/middleware/cors/cors.d.ts.map +0 -1
- package/dist/middleware/cors/index.d.ts +0 -30
- package/dist/middleware/cors/index.d.ts.map +0 -1
- package/dist/middleware/cors/is-origin-trusted.d.ts.map +0 -1
- package/dist/middleware/cors/process-cors.d.ts.map +0 -1
- package/dist/middleware/openapi/config.d.ts.map +0 -1
- package/dist/middleware/openapi/handlers.d.ts.map +0 -1
- package/dist/middleware/openapi/index.d.ts +0 -12
- package/dist/middleware/openapi/index.d.ts.map +0 -1
- package/dist/middleware/process-ip/index.d.ts.map +0 -1
- package/dist/router.d.ts.map +0 -1
- package/dist/routes/consent.d.ts.map +0 -1
- package/dist/routes/index.d.ts +0 -10
- package/dist/routes/index.d.ts.map +0 -1
- package/dist/routes/init.d.ts.map +0 -1
- package/dist/routes/status.d.ts.map +0 -1
- package/dist/routes/subject.d.ts.map +0 -1
- package/dist/types/api.d.ts.map +0 -1
- package/dist/types/index.d.ts +0 -263
- package/dist/types/index.d.ts.map +0 -1
- package/dist/utils/create-telemetry-options.d.ts.map +0 -1
- package/dist/utils/env.d.ts.map +0 -1
- package/dist/utils/extract-error-message.d.ts.map +0 -1
- package/dist/utils/index.d.ts +0 -4
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/instrumentation.d.ts.map +0 -1
- package/dist/utils/logger.d.ts.map +0 -1
- package/dist/utils/metrics.d.ts.map +0 -1
- package/dist/version.d.ts +0 -2
- package/dist/version.d.ts.map +0 -1
- package/knip.json +0 -31
- package/rslib.config.ts +0 -93
- package/src/cache/adapters/cloudflare-kv.ts +0 -71
- package/src/cache/adapters/index.ts +0 -22
- package/src/cache/adapters/memory.ts +0 -111
- package/src/cache/adapters/upstash-redis.ts +0 -113
- package/src/cache/gvl-resolver.ts +0 -289
- package/src/cache/index.ts +0 -34
- package/src/cache/keys.ts +0 -68
- package/src/cache/types.ts +0 -66
- package/src/core.ts +0 -369
- package/src/db/migrator/index.ts +0 -80
- package/src/db/registry/consent-policy.test.ts +0 -451
- package/src/db/registry/consent-policy.ts +0 -82
- package/src/db/registry/consent-purpose.test.ts +0 -428
- package/src/db/registry/consent-purpose.ts +0 -61
- package/src/db/registry/domain.test.ts +0 -445
- package/src/db/registry/domain.ts +0 -91
- package/src/db/registry/index.ts +0 -14
- package/src/db/registry/subject.test.ts +0 -371
- package/src/db/registry/subject.ts +0 -126
- package/src/db/registry/types.ts +0 -10
- package/src/db/registry/utils/generate-id.test.ts +0 -216
- package/src/db/registry/utils/generate-id.ts +0 -133
- package/src/db/registry/utils.ts +0 -133
- package/src/db/schema/1.0.0/audit-log.ts +0 -15
- package/src/db/schema/1.0.0/consent-policy.ts +0 -14
- package/src/db/schema/1.0.0/consent-purpose.ts +0 -14
- package/src/db/schema/1.0.0/consent-record.ts +0 -10
- package/src/db/schema/1.0.0/consent.ts +0 -20
- package/src/db/schema/1.0.0/domain.ts +0 -12
- package/src/db/schema/1.0.0/index.ts +0 -48
- package/src/db/schema/1.0.0/subject.ts +0 -11
- package/src/db/schema/2.0.0/audit-log.ts +0 -18
- package/src/db/schema/2.0.0/consent-policy.ts +0 -28
- package/src/db/schema/2.0.0/consent-purpose.ts +0 -12
- package/src/db/schema/2.0.0/consent.ts +0 -28
- package/src/db/schema/2.0.0/domain.ts +0 -12
- package/src/db/schema/2.0.0/index.ts +0 -47
- package/src/db/schema/2.0.0/subject.ts +0 -13
- package/src/db/schema/index.ts +0 -15
- package/src/db/tenant-scope.test.ts +0 -747
- package/src/db/tenant-scope.ts +0 -103
- package/src/define-config.ts +0 -5
- package/src/handlers/consent/check.handler.ts +0 -126
- package/src/handlers/init/geo.test.ts +0 -317
- package/src/handlers/init/geo.ts +0 -195
- package/src/handlers/init/index.test.ts +0 -205
- package/src/handlers/init/index.ts +0 -114
- package/src/handlers/init/translations.test.ts +0 -121
- package/src/handlers/init/translations.ts +0 -72
- package/src/handlers/status/status.handler.test.ts +0 -155
- package/src/handlers/status/status.handler.ts +0 -51
- package/src/handlers/subject/get.handler.ts +0 -92
- package/src/handlers/subject/list.handler.ts +0 -92
- package/src/handlers/subject/patch.handler.ts +0 -119
- package/src/handlers/subject/post.handler.test.ts +0 -294
- package/src/handlers/subject/post.handler.ts +0 -268
- package/src/handlers/utils/consent-enrichment.test.ts +0 -380
- package/src/handlers/utils/consent-enrichment.ts +0 -218
- package/src/init.test.ts +0 -126
- package/src/init.ts +0 -87
- package/src/middleware/auth/index.ts +0 -11
- package/src/middleware/auth/validate-api-key.test.ts +0 -86
- package/src/middleware/auth/validate-api-key.ts +0 -107
- package/src/middleware/cors/cors.test.ts +0 -135
- package/src/middleware/cors/cors.ts +0 -186
- package/src/middleware/cors/is-origin-trusted.test.ts +0 -164
- package/src/middleware/cors/is-origin-trusted.ts +0 -130
- package/src/middleware/cors/process-cors.ts +0 -91
- package/src/middleware/openapi/config.ts +0 -29
- package/src/middleware/openapi/handlers.ts +0 -34
- package/src/middleware/process-ip/index.test.ts +0 -195
- package/src/middleware/process-ip/index.ts +0 -199
- package/src/router.ts +0 -15
- package/src/routes/consent.ts +0 -52
- package/src/routes/init.ts +0 -105
- package/src/routes/status.ts +0 -46
- package/src/routes/subject.ts +0 -152
- package/src/types/api.ts +0 -48
- package/src/types/index.ts +0 -297
- package/src/utils/create-telemetry-options.test.ts +0 -302
- package/src/utils/create-telemetry-options.ts +0 -229
- package/src/utils/env.ts +0 -84
- package/src/utils/extract-error-message.ts +0 -21
- package/src/utils/instrumentation.test.ts +0 -185
- package/src/utils/instrumentation.ts +0 -196
- package/src/utils/logger.ts +0 -41
- package/src/utils/metrics.test.ts +0 -323
- package/src/utils/metrics.ts +0 -402
- package/src/utils/telemetry-pii.test.ts +0 -325
- package/src/version.ts +0 -2
- package/tsconfig.json +0 -11
- package/vitest.config.ts +0 -28
- /package/dist/{types.cjs → types/index.cjs} +0 -0
- /package/dist/{types.js → types/index.js} +0 -0
- /package/{src/db/adapters/drizzle.ts → dist-types/db/adapters/drizzle.d.ts} +0 -0
- /package/{src/db/adapters/index.ts → dist-types/db/adapters/index.d.ts} +0 -0
- /package/{src/db/adapters/kysely.ts → dist-types/db/adapters/kysely.d.ts} +0 -0
- /package/{src/db/adapters/mongo.ts → dist-types/db/adapters/mongo.d.ts} +0 -0
- /package/{src/db/adapters/prisma.ts → dist-types/db/adapters/prisma.d.ts} +0 -0
- /package/{src/db/adapters/typeorm.ts → dist-types/db/adapters/typeorm.d.ts} +0 -0
- /package/{src/utils/index.ts → dist-types/utils/index.d.ts} +0 -0
package/dist/router.js
CHANGED
|
@@ -3,7 +3,10 @@ import { Hono } from "hono";
|
|
|
3
3
|
import { describeRoute, resolver, validator } from "hono-openapi";
|
|
4
4
|
import { HTTPException } from "hono/http-exception";
|
|
5
5
|
import { SpanKind as api_SpanKind, SpanStatusCode as api_SpanStatusCode, context, metrics as api_metrics, trace as api_trace } from "@opentelemetry/api";
|
|
6
|
-
import {
|
|
6
|
+
import { SignJWT, errors, jwtVerify } from "jose";
|
|
7
|
+
import { resolvePolicyDecision } from "@c15t/schema/types";
|
|
8
|
+
import { deepMergeTranslations, selectLanguage } from "@c15t/translations";
|
|
9
|
+
import { baseTranslations } from "@c15t/translations/all";
|
|
7
10
|
import { object, optional, string } from "valibot";
|
|
8
11
|
import base_x from "base-x";
|
|
9
12
|
function extractErrorMessage(error) {
|
|
@@ -14,22 +17,22 @@ function extractErrorMessage(error) {
|
|
|
14
17
|
if (error instanceof Error) return error.message || error.name;
|
|
15
18
|
return String(error);
|
|
16
19
|
}
|
|
17
|
-
const version_version = '2.0.0-rc.
|
|
20
|
+
const version_version = '2.0.0-rc.5';
|
|
18
21
|
let cachedConfig = null;
|
|
19
22
|
let cachedDefaultAttributes = {};
|
|
20
23
|
function create_telemetry_options_isTelemetryEnabled(options) {
|
|
21
|
-
if (options) return options.
|
|
24
|
+
if (options) return options.telemetry?.enabled === true;
|
|
22
25
|
return cachedConfig?.enabled === true;
|
|
23
26
|
}
|
|
24
27
|
const create_telemetry_options_getTracer = (options)=>{
|
|
25
28
|
if (!create_telemetry_options_isTelemetryEnabled(options)) return api_trace.getTracer('c15t-noop');
|
|
26
|
-
const tracer = options?.
|
|
29
|
+
const tracer = options?.telemetry?.tracer ?? cachedConfig?.tracer;
|
|
27
30
|
if (tracer) return tracer;
|
|
28
31
|
return api_trace.getTracer(options?.appName ?? 'c15t');
|
|
29
32
|
};
|
|
30
33
|
const getMeter = (options)=>{
|
|
31
34
|
if (!create_telemetry_options_isTelemetryEnabled(options)) return api_metrics.getMeter('c15t-noop');
|
|
32
|
-
const meter = options?.
|
|
35
|
+
const meter = options?.telemetry?.meter ?? cachedConfig?.meter;
|
|
33
36
|
if (meter) return meter;
|
|
34
37
|
return api_metrics.getMeter(options?.appName ?? 'c15t');
|
|
35
38
|
};
|
|
@@ -434,7 +437,7 @@ async function executeWithSpan(span, operation) {
|
|
|
434
437
|
}
|
|
435
438
|
}
|
|
436
439
|
function resolveDefaultAttributes(options) {
|
|
437
|
-
return options?.
|
|
440
|
+
return options?.telemetry?.defaultAttributes || getDefaultAttributes();
|
|
438
441
|
}
|
|
439
442
|
async function withExternalSpan(attributes, operation, options) {
|
|
440
443
|
if (!create_telemetry_options_isTelemetryEnabled(options)) return operation();
|
|
@@ -610,6 +613,123 @@ function createGVLResolver(options) {
|
|
|
610
613
|
}
|
|
611
614
|
};
|
|
612
615
|
}
|
|
616
|
+
const POLICY_SNAPSHOT_JWT_HEADER = {
|
|
617
|
+
alg: 'HS256',
|
|
618
|
+
typ: 'JWT'
|
|
619
|
+
};
|
|
620
|
+
const DEFAULT_POLICY_SNAPSHOT_ISSUER = 'c15t';
|
|
621
|
+
const DEFAULT_POLICY_SNAPSHOT_AUDIENCE = 'c15t-policy-snapshot';
|
|
622
|
+
function resolveSnapshotIssuer(options) {
|
|
623
|
+
return options?.issuer?.trim() || DEFAULT_POLICY_SNAPSHOT_ISSUER;
|
|
624
|
+
}
|
|
625
|
+
function resolveSnapshotAudience(params) {
|
|
626
|
+
const configuredAudience = params.options?.audience?.trim();
|
|
627
|
+
if (configuredAudience) return configuredAudience;
|
|
628
|
+
return params.tenantId ? `${DEFAULT_POLICY_SNAPSHOT_AUDIENCE}:${params.tenantId}` : DEFAULT_POLICY_SNAPSHOT_AUDIENCE;
|
|
629
|
+
}
|
|
630
|
+
function getSigningKey(secret) {
|
|
631
|
+
return new TextEncoder().encode(secret);
|
|
632
|
+
}
|
|
633
|
+
function isPolicySnapshotPayload(payload) {
|
|
634
|
+
return 'string' == typeof payload.policyId && 'string' == typeof payload.fingerprint && 'string' == typeof payload.matchedBy && 'string' == typeof payload.jurisdiction && 'string' == typeof payload.model && 'string' == typeof payload.iss && 'string' == typeof payload.aud && 'string' == typeof payload.sub && 'number' == typeof payload.iat && 'number' == typeof payload.exp;
|
|
635
|
+
}
|
|
636
|
+
async function createPolicySnapshotToken(params) {
|
|
637
|
+
const { options } = params;
|
|
638
|
+
if (!options?.signingKey) return;
|
|
639
|
+
const iat = Math.floor(Date.now() / 1000);
|
|
640
|
+
const ttlSeconds = options.ttlSeconds ?? 1800;
|
|
641
|
+
const exp = iat + ttlSeconds;
|
|
642
|
+
const iss = resolveSnapshotIssuer(options);
|
|
643
|
+
const aud = resolveSnapshotAudience({
|
|
644
|
+
options,
|
|
645
|
+
tenantId: params.tenantId
|
|
646
|
+
});
|
|
647
|
+
const payload = {
|
|
648
|
+
iss,
|
|
649
|
+
aud,
|
|
650
|
+
sub: params.policyId,
|
|
651
|
+
tenantId: params.tenantId,
|
|
652
|
+
policyId: params.policyId,
|
|
653
|
+
fingerprint: params.fingerprint,
|
|
654
|
+
matchedBy: params.matchedBy,
|
|
655
|
+
country: params.country,
|
|
656
|
+
region: params.region,
|
|
657
|
+
jurisdiction: params.jurisdiction,
|
|
658
|
+
language: params.language,
|
|
659
|
+
model: params.model,
|
|
660
|
+
policyI18n: params.policyI18n,
|
|
661
|
+
expiryDays: params.expiryDays,
|
|
662
|
+
scopeMode: params.scopeMode,
|
|
663
|
+
uiMode: params.uiMode,
|
|
664
|
+
bannerUi: params.bannerUi,
|
|
665
|
+
dialogUi: params.dialogUi,
|
|
666
|
+
categories: params.categories,
|
|
667
|
+
preselectedCategories: params.preselectedCategories,
|
|
668
|
+
gpc: params.gpc,
|
|
669
|
+
proofConfig: params.proofConfig,
|
|
670
|
+
iat,
|
|
671
|
+
exp
|
|
672
|
+
};
|
|
673
|
+
const token = await new SignJWT(payload).setProtectedHeader(POLICY_SNAPSHOT_JWT_HEADER).setIssuedAt(iat).setExpirationTime(exp).sign(getSigningKey(options.signingKey));
|
|
674
|
+
return {
|
|
675
|
+
token,
|
|
676
|
+
payload
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
async function verifyPolicySnapshotToken(params) {
|
|
680
|
+
const { token, options, tenantId } = params;
|
|
681
|
+
if (!options?.signingKey) return {
|
|
682
|
+
valid: false,
|
|
683
|
+
reason: 'missing'
|
|
684
|
+
};
|
|
685
|
+
if (!token) return {
|
|
686
|
+
valid: false,
|
|
687
|
+
reason: 'missing'
|
|
688
|
+
};
|
|
689
|
+
if (3 !== token.split('.').length) return {
|
|
690
|
+
valid: false,
|
|
691
|
+
reason: 'malformed'
|
|
692
|
+
};
|
|
693
|
+
try {
|
|
694
|
+
const { payload, protectedHeader } = await jwtVerify(token, getSigningKey(options.signingKey), {
|
|
695
|
+
issuer: resolveSnapshotIssuer(options),
|
|
696
|
+
audience: resolveSnapshotAudience({
|
|
697
|
+
options,
|
|
698
|
+
tenantId
|
|
699
|
+
})
|
|
700
|
+
});
|
|
701
|
+
const header = protectedHeader;
|
|
702
|
+
if ('HS256' !== header.alg || 'JWT' !== header.typ) return {
|
|
703
|
+
valid: false,
|
|
704
|
+
reason: 'invalid'
|
|
705
|
+
};
|
|
706
|
+
if (!isPolicySnapshotPayload(payload)) return {
|
|
707
|
+
valid: false,
|
|
708
|
+
reason: 'invalid'
|
|
709
|
+
};
|
|
710
|
+
if (payload.sub !== payload.policyId) return {
|
|
711
|
+
valid: false,
|
|
712
|
+
reason: 'invalid'
|
|
713
|
+
};
|
|
714
|
+
if ((tenantId ?? void 0) !== (payload.tenantId ?? void 0)) return {
|
|
715
|
+
valid: false,
|
|
716
|
+
reason: 'invalid'
|
|
717
|
+
};
|
|
718
|
+
return {
|
|
719
|
+
valid: true,
|
|
720
|
+
payload
|
|
721
|
+
};
|
|
722
|
+
} catch (error) {
|
|
723
|
+
if (error instanceof errors.JWTExpired) return {
|
|
724
|
+
valid: false,
|
|
725
|
+
reason: 'expired'
|
|
726
|
+
};
|
|
727
|
+
return {
|
|
728
|
+
valid: false,
|
|
729
|
+
reason: 'invalid'
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
}
|
|
613
733
|
function geo_normalizeHeader(value) {
|
|
614
734
|
if (!value) return null;
|
|
615
735
|
return Array.isArray(value) ? value[0] ?? null : value;
|
|
@@ -751,7 +871,7 @@ function checkJurisdiction(countryCode, regionCode) {
|
|
|
751
871
|
return jurisdiction;
|
|
752
872
|
}
|
|
753
873
|
async function getLocation(request, options) {
|
|
754
|
-
if (options.
|
|
874
|
+
if (options.disableGeoLocation) return {
|
|
755
875
|
countryCode: null,
|
|
756
876
|
regionCode: null
|
|
757
877
|
};
|
|
@@ -762,29 +882,250 @@ async function getLocation(request, options) {
|
|
|
762
882
|
};
|
|
763
883
|
}
|
|
764
884
|
function getJurisdiction(location, options) {
|
|
765
|
-
if (options.
|
|
885
|
+
if (options.disableGeoLocation) return 'GDPR';
|
|
766
886
|
return checkJurisdiction(location.countryCode, location.regionCode);
|
|
767
887
|
}
|
|
888
|
+
async function policy_resolvePolicyDecision(params) {
|
|
889
|
+
return resolvePolicyDecision({
|
|
890
|
+
policies: params.policies,
|
|
891
|
+
countryCode: params.countryCode,
|
|
892
|
+
regionCode: params.regionCode,
|
|
893
|
+
jurisdiction: params.jurisdiction,
|
|
894
|
+
iabEnabled: params.iabEnabled
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
const DEFAULT_PROFILE = 'default';
|
|
898
|
+
const warnedKeys = new Set();
|
|
768
899
|
function isSupportedBaseLanguage(lang) {
|
|
769
900
|
return lang in baseTranslations;
|
|
770
901
|
}
|
|
771
|
-
function
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
902
|
+
function warnOnce(logger, key, message, metadata) {
|
|
903
|
+
if (!logger || warnedKeys.has(key)) return;
|
|
904
|
+
warnedKeys.add(key);
|
|
905
|
+
logger.warn(message, metadata);
|
|
906
|
+
}
|
|
907
|
+
function normalizeLanguage(value) {
|
|
908
|
+
if (!value) return;
|
|
909
|
+
const normalized = value.split(',')[0]?.split(';')[0]?.trim().toLowerCase();
|
|
910
|
+
if (!normalized) return;
|
|
911
|
+
return normalized.split('-')[0] ?? void 0;
|
|
912
|
+
}
|
|
913
|
+
function normalizeProfiles(params) {
|
|
914
|
+
const profiles = params.i18n?.messages;
|
|
915
|
+
const legacy = params.customTranslations;
|
|
916
|
+
if (profiles && Object.keys(profiles).length > 0) {
|
|
917
|
+
if (legacy && Object.keys(legacy).length > 0) warnOnce(params.logger, 'i18n.customTranslations.ignored', '`customTranslations` is deprecated and ignored when `i18n.messages` is configured.');
|
|
918
|
+
return profiles;
|
|
919
|
+
}
|
|
920
|
+
if (legacy && Object.keys(legacy).length > 0) {
|
|
921
|
+
warnOnce(params.logger, 'i18n.customTranslations.deprecated', '`customTranslations` is deprecated. Use `i18n.messages` instead.');
|
|
922
|
+
return {
|
|
923
|
+
[DEFAULT_PROFILE]: {
|
|
924
|
+
translations: legacy
|
|
925
|
+
}
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
return {};
|
|
929
|
+
}
|
|
930
|
+
function buildCandidates(input) {
|
|
931
|
+
const raw = [
|
|
932
|
+
{
|
|
933
|
+
language: input.language,
|
|
934
|
+
reason: 'profile_language'
|
|
935
|
+
},
|
|
936
|
+
{
|
|
937
|
+
language: input.fallbackLanguage,
|
|
938
|
+
reason: 'profile_fallback'
|
|
939
|
+
}
|
|
777
940
|
];
|
|
778
|
-
const
|
|
941
|
+
const dedupe = new Set();
|
|
942
|
+
return raw.filter((candidate)=>{
|
|
943
|
+
const key = candidate.language;
|
|
944
|
+
if (dedupe.has(key)) return false;
|
|
945
|
+
dedupe.add(key);
|
|
946
|
+
return true;
|
|
947
|
+
});
|
|
948
|
+
}
|
|
949
|
+
function getProfileLanguages(profiles, profile) {
|
|
950
|
+
return Object.keys(profiles[profile]?.translations ?? {}).sort();
|
|
951
|
+
}
|
|
952
|
+
function getSelectableLanguages(input) {
|
|
953
|
+
return getProfileLanguages(input.profiles, input.profile);
|
|
954
|
+
}
|
|
955
|
+
function resolveFallbackLanguage(input) {
|
|
956
|
+
const configuredFallbackLanguage = normalizeLanguage(input.profile?.fallbackLanguage) ?? 'en';
|
|
957
|
+
const profileLanguages = Object.keys(input.profile?.translations ?? {}).sort();
|
|
958
|
+
if (profileLanguages.includes(configuredFallbackLanguage)) return configuredFallbackLanguage;
|
|
959
|
+
if (profileLanguages.includes('en')) return 'en';
|
|
960
|
+
return profileLanguages[0] ?? configuredFallbackLanguage;
|
|
961
|
+
}
|
|
962
|
+
function resolveActiveProfile(input) {
|
|
963
|
+
const requestedProfile = input.policyProfile ?? input.defaultProfile;
|
|
964
|
+
if (input.profiles[requestedProfile]) return requestedProfile;
|
|
965
|
+
if (input.policyProfile) warnOnce(input.logger, `i18n.profile.missing:${requestedProfile}`, `Policy i18n profile '${requestedProfile}' does not exist. Falling back to default profile '${input.defaultProfile}'.`);
|
|
966
|
+
return input.defaultProfile;
|
|
967
|
+
}
|
|
968
|
+
function translations_getTranslationsData(acceptLanguage, customTranslations, options) {
|
|
969
|
+
const profiles = normalizeProfiles({
|
|
970
|
+
customTranslations,
|
|
971
|
+
i18n: options?.i18n,
|
|
972
|
+
logger: options?.logger
|
|
973
|
+
});
|
|
974
|
+
const defaultProfile = options?.i18n?.defaultProfile ?? DEFAULT_PROFILE;
|
|
975
|
+
const profile = resolveActiveProfile({
|
|
976
|
+
profiles,
|
|
977
|
+
defaultProfile,
|
|
978
|
+
policyProfile: options?.policyI18n?.messageProfile,
|
|
979
|
+
logger: options?.logger
|
|
980
|
+
});
|
|
981
|
+
const configuredLanguages = Object.keys(profiles).length > 0 ? getSelectableLanguages({
|
|
982
|
+
profiles,
|
|
983
|
+
profile
|
|
984
|
+
}) : Object.keys(baseTranslations);
|
|
985
|
+
const fallbackLanguage = Object.keys(profiles).length > 0 ? resolveFallbackLanguage({
|
|
986
|
+
profile: profiles[profile]
|
|
987
|
+
}) : 'en';
|
|
988
|
+
const policyLanguage = normalizeLanguage(options?.policyI18n?.language);
|
|
989
|
+
const requestedLanguage = policyLanguage ?? selectLanguage(configuredLanguages, {
|
|
779
990
|
header: acceptLanguage,
|
|
780
|
-
fallback:
|
|
991
|
+
fallback: fallbackLanguage
|
|
992
|
+
});
|
|
993
|
+
const candidates = buildCandidates({
|
|
994
|
+
language: requestedLanguage,
|
|
995
|
+
fallbackLanguage
|
|
996
|
+
});
|
|
997
|
+
const selectedCandidate = candidates.find((candidate)=>!!profiles[profile]?.translations[candidate.language]);
|
|
998
|
+
if (selectedCandidate && 'profile_language' !== selectedCandidate.reason) warnOnce(options?.logger, `i18n.fallback:${profile}:${requestedLanguage}:${selectedCandidate.language}`, `Policy translation fallback used (${selectedCandidate.reason}).`, {
|
|
999
|
+
requestedProfile: profile,
|
|
1000
|
+
requestedLanguage,
|
|
1001
|
+
resolvedProfile: profile,
|
|
1002
|
+
resolvedLanguage: selectedCandidate.language
|
|
781
1003
|
});
|
|
782
|
-
|
|
783
|
-
|
|
1004
|
+
let language = selectedCandidate?.language ?? requestedLanguage;
|
|
1005
|
+
if (!selectedCandidate && !isSupportedBaseLanguage(language)) {
|
|
1006
|
+
warnOnce(options?.logger, `i18n.base-fallback:${language}`, `No translation found for '${language}'. Falling back to base English translations.`);
|
|
1007
|
+
language = 'en';
|
|
1008
|
+
}
|
|
1009
|
+
const base = isSupportedBaseLanguage(language) ? baseTranslations[language] : baseTranslations.en;
|
|
1010
|
+
const custom = selectedCandidate ? profiles[profile]?.translations[selectedCandidate.language] : void 0;
|
|
784
1011
|
const translations = custom ? deepMergeTranslations(base, custom) : base;
|
|
785
1012
|
return {
|
|
786
1013
|
translations: translations,
|
|
787
|
-
language
|
|
1014
|
+
language
|
|
1015
|
+
};
|
|
1016
|
+
}
|
|
1017
|
+
function stripIabTranslations(translations) {
|
|
1018
|
+
const { iab: _iab, ...rest } = translations;
|
|
1019
|
+
return rest;
|
|
1020
|
+
}
|
|
1021
|
+
function resolveNoPolicyFallback() {
|
|
1022
|
+
return {
|
|
1023
|
+
id: 'no_banner',
|
|
1024
|
+
model: 'none',
|
|
1025
|
+
ui: {
|
|
1026
|
+
mode: 'none'
|
|
1027
|
+
}
|
|
1028
|
+
};
|
|
1029
|
+
}
|
|
1030
|
+
async function resolveInitPayload(request, options, logger) {
|
|
1031
|
+
const acceptLanguage = request.headers.get('accept-language') || 'en';
|
|
1032
|
+
const location = await getLocation(request, options);
|
|
1033
|
+
const jurisdiction = getJurisdiction(location, options);
|
|
1034
|
+
const hasExplicitPolicyPack = void 0 !== options.policyPacks;
|
|
1035
|
+
const isExplicitEmptyPolicyPack = hasExplicitPolicyPack && (options.policyPacks?.length ?? 0) === 0;
|
|
1036
|
+
const policyDecision = isExplicitEmptyPolicyPack ? void 0 : await policy_resolvePolicyDecision({
|
|
1037
|
+
policies: options.policyPacks,
|
|
1038
|
+
countryCode: location.countryCode,
|
|
1039
|
+
regionCode: location.regionCode,
|
|
1040
|
+
jurisdiction,
|
|
1041
|
+
iabEnabled: options.iab?.enabled === true
|
|
1042
|
+
});
|
|
1043
|
+
if (hasExplicitPolicyPack && !isExplicitEmptyPolicyPack && !policyDecision) logger?.warn('Policy packs configured but no policy matched', {
|
|
1044
|
+
country: location.countryCode,
|
|
1045
|
+
region: location.regionCode
|
|
1046
|
+
});
|
|
1047
|
+
const resolvedPolicy = hasExplicitPolicyPack ? policyDecision?.policy ?? resolveNoPolicyFallback() : void 0;
|
|
1048
|
+
const iabOptions = options.iab;
|
|
1049
|
+
const shouldIncludeIabPayload = iabOptions?.enabled === true && (!hasExplicitPolicyPack || resolvedPolicy?.model === 'iab');
|
|
1050
|
+
const translationsResult = translations_getTranslationsData(acceptLanguage, options.customTranslations, {
|
|
1051
|
+
i18n: options.i18n,
|
|
1052
|
+
policyI18n: resolvedPolicy?.i18n,
|
|
1053
|
+
logger
|
|
1054
|
+
});
|
|
1055
|
+
const responseTranslations = shouldIncludeIabPayload ? translationsResult : {
|
|
1056
|
+
...translationsResult,
|
|
1057
|
+
translations: stripIabTranslations(translationsResult.translations)
|
|
1058
|
+
};
|
|
1059
|
+
let gvl = null;
|
|
1060
|
+
if (shouldIncludeIabPayload && iabOptions) {
|
|
1061
|
+
const language = translationsResult.language.split('-')[0] || 'en';
|
|
1062
|
+
const gvlResolver = createGVLResolver({
|
|
1063
|
+
appName: options.appName || 'c15t',
|
|
1064
|
+
bundled: iabOptions.bundled,
|
|
1065
|
+
cacheAdapter: options.cache?.adapter,
|
|
1066
|
+
vendorIds: iabOptions.vendorIds,
|
|
1067
|
+
endpoint: iabOptions.endpoint
|
|
1068
|
+
});
|
|
1069
|
+
gvl = await gvlResolver.get(language);
|
|
1070
|
+
}
|
|
1071
|
+
const customVendors = shouldIncludeIabPayload ? iabOptions?.customVendors : void 0;
|
|
1072
|
+
const snapshot = policyDecision ? await createPolicySnapshotToken({
|
|
1073
|
+
options: options.policySnapshot,
|
|
1074
|
+
tenantId: options.tenantId,
|
|
1075
|
+
policyId: policyDecision.policy.id,
|
|
1076
|
+
fingerprint: policyDecision.fingerprint,
|
|
1077
|
+
matchedBy: policyDecision.matchedBy,
|
|
1078
|
+
country: location?.countryCode ?? null,
|
|
1079
|
+
region: location?.regionCode ?? null,
|
|
1080
|
+
jurisdiction,
|
|
1081
|
+
language: translationsResult.language,
|
|
1082
|
+
model: policyDecision.policy.model,
|
|
1083
|
+
policyI18n: policyDecision.policy.i18n,
|
|
1084
|
+
expiryDays: policyDecision.policy.consent?.expiryDays,
|
|
1085
|
+
scopeMode: policyDecision.policy.consent?.scopeMode,
|
|
1086
|
+
uiMode: policyDecision.policy.ui?.mode,
|
|
1087
|
+
bannerUi: policyDecision.policy.ui?.banner,
|
|
1088
|
+
dialogUi: policyDecision.policy.ui?.dialog,
|
|
1089
|
+
categories: policyDecision.policy.consent?.categories,
|
|
1090
|
+
preselectedCategories: policyDecision.policy.consent?.preselectedCategories,
|
|
1091
|
+
gpc: policyDecision.policy.consent?.gpc,
|
|
1092
|
+
proofConfig: policyDecision.policy.proof
|
|
1093
|
+
}) : void 0;
|
|
1094
|
+
const gpc = '1' === request.headers.get('sec-gpc');
|
|
1095
|
+
getMetrics()?.recordInit({
|
|
1096
|
+
jurisdiction,
|
|
1097
|
+
country: location?.countryCode ?? void 0,
|
|
1098
|
+
region: location?.regionCode ?? void 0,
|
|
1099
|
+
gpc
|
|
1100
|
+
});
|
|
1101
|
+
return {
|
|
1102
|
+
jurisdiction,
|
|
1103
|
+
location,
|
|
1104
|
+
translations: responseTranslations,
|
|
1105
|
+
branding: options.branding || 'c15t',
|
|
1106
|
+
...shouldIncludeIabPayload && {
|
|
1107
|
+
gvl,
|
|
1108
|
+
customVendors
|
|
1109
|
+
},
|
|
1110
|
+
...resolvedPolicy && {
|
|
1111
|
+
policy: resolvedPolicy
|
|
1112
|
+
},
|
|
1113
|
+
...policyDecision && {
|
|
1114
|
+
policyDecision: {
|
|
1115
|
+
policyId: policyDecision.policy.id,
|
|
1116
|
+
fingerprint: policyDecision.fingerprint,
|
|
1117
|
+
matchedBy: policyDecision.matchedBy,
|
|
1118
|
+
country: location.countryCode,
|
|
1119
|
+
region: location.regionCode,
|
|
1120
|
+
jurisdiction
|
|
1121
|
+
}
|
|
1122
|
+
},
|
|
1123
|
+
...snapshot?.token && {
|
|
1124
|
+
policySnapshotToken: snapshot.token
|
|
1125
|
+
},
|
|
1126
|
+
...shouldIncludeIabPayload && iabOptions?.cmpId != null && {
|
|
1127
|
+
cmpId: iabOptions.cmpId
|
|
1128
|
+
}
|
|
788
1129
|
};
|
|
789
1130
|
}
|
|
790
1131
|
const createInitRoute = (options)=>{
|
|
@@ -797,7 +1138,7 @@ const createInitRoute = (options)=>{
|
|
|
797
1138
|
- **Location** – User's location (null if geo-location is disabled)
|
|
798
1139
|
- **Translations** – Consent manager copy (from \`Accept-Language\` header)
|
|
799
1140
|
- **Branding** – Configured branding key
|
|
800
|
-
- **GVL** – Global Vendor List when
|
|
1141
|
+
- **GVL** – Global Vendor List when IAB is active for the request
|
|
801
1142
|
|
|
802
1143
|
Use for geo-targeted consent banners and regional compliance.`,
|
|
803
1144
|
tags: [
|
|
@@ -814,42 +1155,9 @@ Use for geo-targeted consent banners and regional compliance.`,
|
|
|
814
1155
|
}
|
|
815
1156
|
}
|
|
816
1157
|
}), async (c)=>{
|
|
817
|
-
const
|
|
818
|
-
const
|
|
819
|
-
|
|
820
|
-
const jurisdiction = getJurisdiction(location, options);
|
|
821
|
-
const translationsResult = translations_getTranslationsData(acceptLanguage, options.advanced?.customTranslations);
|
|
822
|
-
let gvl = null;
|
|
823
|
-
if (options.advanced?.iab?.enabled) {
|
|
824
|
-
const language = translationsResult.language.split('-')[0] || 'en';
|
|
825
|
-
const gvlResolver = createGVLResolver({
|
|
826
|
-
appName: options.appName || 'c15t',
|
|
827
|
-
bundled: options.advanced.iab.bundled,
|
|
828
|
-
cacheAdapter: options.advanced.cache?.adapter,
|
|
829
|
-
vendorIds: options.advanced.iab.vendorIds,
|
|
830
|
-
endpoint: options.advanced.iab.endpoint
|
|
831
|
-
});
|
|
832
|
-
gvl = await gvlResolver.get(language);
|
|
833
|
-
}
|
|
834
|
-
const customVendors = options.advanced?.iab?.customVendors;
|
|
835
|
-
const gpc = '1' === request.headers.get('sec-gpc');
|
|
836
|
-
getMetrics()?.recordInit({
|
|
837
|
-
jurisdiction,
|
|
838
|
-
country: location?.countryCode ?? void 0,
|
|
839
|
-
region: location?.regionCode ?? void 0,
|
|
840
|
-
gpc
|
|
841
|
-
});
|
|
842
|
-
return c.json({
|
|
843
|
-
jurisdiction,
|
|
844
|
-
location,
|
|
845
|
-
translations: translationsResult,
|
|
846
|
-
branding: options.advanced?.branding || 'c15t',
|
|
847
|
-
gvl,
|
|
848
|
-
customVendors,
|
|
849
|
-
...options.advanced?.iab?.cmpId != null && {
|
|
850
|
-
cmpId: options.advanced.iab.cmpId
|
|
851
|
-
}
|
|
852
|
-
});
|
|
1158
|
+
const ctx = c.get('c15tContext');
|
|
1159
|
+
const payload = await resolveInitPayload(c.req.raw, options, ctx?.logger);
|
|
1160
|
+
return c.json(payload);
|
|
853
1161
|
});
|
|
854
1162
|
return app;
|
|
855
1163
|
};
|
|
@@ -1205,6 +1513,119 @@ const patchSubjectHandler = async (c)=>{
|
|
|
1205
1513
|
});
|
|
1206
1514
|
}
|
|
1207
1515
|
};
|
|
1516
|
+
function buildRuntimeDecisionDedupeKey(input) {
|
|
1517
|
+
return [
|
|
1518
|
+
input.tenantId ?? 'default',
|
|
1519
|
+
input.fingerprint,
|
|
1520
|
+
input.matchedBy,
|
|
1521
|
+
input.countryCode ?? 'none',
|
|
1522
|
+
input.regionCode ?? 'none',
|
|
1523
|
+
input.jurisdiction,
|
|
1524
|
+
input.language ?? 'none'
|
|
1525
|
+
].join('|');
|
|
1526
|
+
}
|
|
1527
|
+
function buildDecisionPayload(params) {
|
|
1528
|
+
const { tenantId, snapshot, decision, location, jurisdiction, language, proofConfig } = params;
|
|
1529
|
+
if (snapshot?.valid && snapshot.payload) {
|
|
1530
|
+
const sp = snapshot.payload;
|
|
1531
|
+
return {
|
|
1532
|
+
tenantId,
|
|
1533
|
+
policyId: sp.policyId,
|
|
1534
|
+
fingerprint: sp.fingerprint,
|
|
1535
|
+
matchedBy: sp.matchedBy,
|
|
1536
|
+
countryCode: sp.country,
|
|
1537
|
+
regionCode: sp.region,
|
|
1538
|
+
jurisdiction: sp.jurisdiction,
|
|
1539
|
+
language: sp.language,
|
|
1540
|
+
model: sp.model,
|
|
1541
|
+
policyI18n: sp.policyI18n,
|
|
1542
|
+
uiMode: sp.uiMode,
|
|
1543
|
+
bannerUi: sp.bannerUi,
|
|
1544
|
+
dialogUi: sp.dialogUi,
|
|
1545
|
+
categories: sp.categories,
|
|
1546
|
+
preselectedCategories: sp.preselectedCategories,
|
|
1547
|
+
proofConfig: sp.proofConfig,
|
|
1548
|
+
dedupeKey: buildRuntimeDecisionDedupeKey({
|
|
1549
|
+
tenantId,
|
|
1550
|
+
fingerprint: sp.fingerprint,
|
|
1551
|
+
matchedBy: sp.matchedBy,
|
|
1552
|
+
countryCode: sp.country,
|
|
1553
|
+
regionCode: sp.region,
|
|
1554
|
+
jurisdiction: sp.jurisdiction,
|
|
1555
|
+
language: sp.language
|
|
1556
|
+
}),
|
|
1557
|
+
source: 'snapshot_token'
|
|
1558
|
+
};
|
|
1559
|
+
}
|
|
1560
|
+
if (decision) return {
|
|
1561
|
+
tenantId,
|
|
1562
|
+
policyId: decision.policy.id,
|
|
1563
|
+
fingerprint: decision.fingerprint,
|
|
1564
|
+
matchedBy: decision.matchedBy,
|
|
1565
|
+
countryCode: location.countryCode,
|
|
1566
|
+
regionCode: location.regionCode,
|
|
1567
|
+
jurisdiction,
|
|
1568
|
+
language,
|
|
1569
|
+
model: decision.policy.model,
|
|
1570
|
+
policyI18n: decision.policy.i18n,
|
|
1571
|
+
uiMode: decision.policy.ui?.mode,
|
|
1572
|
+
bannerUi: decision.policy.ui?.banner,
|
|
1573
|
+
dialogUi: decision.policy.ui?.dialog,
|
|
1574
|
+
categories: decision.policy.consent?.categories,
|
|
1575
|
+
preselectedCategories: decision.policy.consent?.preselectedCategories,
|
|
1576
|
+
proofConfig,
|
|
1577
|
+
dedupeKey: buildRuntimeDecisionDedupeKey({
|
|
1578
|
+
tenantId,
|
|
1579
|
+
fingerprint: decision.fingerprint,
|
|
1580
|
+
matchedBy: decision.matchedBy,
|
|
1581
|
+
countryCode: location.countryCode,
|
|
1582
|
+
regionCode: location.regionCode,
|
|
1583
|
+
jurisdiction,
|
|
1584
|
+
language
|
|
1585
|
+
}),
|
|
1586
|
+
source: 'write_time_fallback'
|
|
1587
|
+
};
|
|
1588
|
+
}
|
|
1589
|
+
function parseLanguageFromHeader(header) {
|
|
1590
|
+
if (!header) return;
|
|
1591
|
+
const firstLanguage = header.split(',')[0]?.split(';')[0]?.trim();
|
|
1592
|
+
if (!firstLanguage) return;
|
|
1593
|
+
return firstLanguage.split('-')[0]?.toLowerCase();
|
|
1594
|
+
}
|
|
1595
|
+
function resolveSnapshotFailureMode(ctx) {
|
|
1596
|
+
return ctx.policySnapshot?.onValidationFailure ?? 'reject';
|
|
1597
|
+
}
|
|
1598
|
+
function buildSnapshotHttpException(reason) {
|
|
1599
|
+
switch(reason){
|
|
1600
|
+
case 'missing':
|
|
1601
|
+
return new HTTPException(409, {
|
|
1602
|
+
message: 'Policy snapshot token is required',
|
|
1603
|
+
cause: {
|
|
1604
|
+
code: 'POLICY_SNAPSHOT_REQUIRED'
|
|
1605
|
+
}
|
|
1606
|
+
});
|
|
1607
|
+
case 'expired':
|
|
1608
|
+
return new HTTPException(409, {
|
|
1609
|
+
message: 'Policy snapshot token has expired',
|
|
1610
|
+
cause: {
|
|
1611
|
+
code: 'POLICY_SNAPSHOT_EXPIRED'
|
|
1612
|
+
}
|
|
1613
|
+
});
|
|
1614
|
+
case 'malformed':
|
|
1615
|
+
case 'invalid':
|
|
1616
|
+
return new HTTPException(409, {
|
|
1617
|
+
message: 'Policy snapshot token is invalid',
|
|
1618
|
+
cause: {
|
|
1619
|
+
code: 'POLICY_SNAPSHOT_INVALID'
|
|
1620
|
+
}
|
|
1621
|
+
});
|
|
1622
|
+
default:
|
|
1623
|
+
{
|
|
1624
|
+
const _exhaustive = reason;
|
|
1625
|
+
throw new Error(`Unhandled policy snapshot verification failure reason: ${_exhaustive}`);
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1208
1629
|
const postSubjectHandler = async (c)=>{
|
|
1209
1630
|
const ctx = c.get('c15tContext');
|
|
1210
1631
|
const logger = ctx.logger;
|
|
@@ -1216,9 +1637,6 @@ const postSubjectHandler = async (c)=>{
|
|
|
1216
1637
|
const givenAt = new Date(givenAtEpoch);
|
|
1217
1638
|
const rawConsentAction = 'consentAction' in input ? input.consentAction : void 0;
|
|
1218
1639
|
let derivedConsentAction;
|
|
1219
|
-
if ('all' === rawConsentAction) derivedConsentAction = 'accept_all';
|
|
1220
|
-
else if ('necessary' === rawConsentAction) derivedConsentAction = 'opt-out' === input.jurisdictionModel ? 'opt_out' : 'reject_all';
|
|
1221
|
-
else if ('custom' === rawConsentAction) derivedConsentAction = 'custom';
|
|
1222
1640
|
logger.debug('Request parameters', {
|
|
1223
1641
|
type,
|
|
1224
1642
|
subjectId,
|
|
@@ -1227,6 +1645,50 @@ const postSubjectHandler = async (c)=>{
|
|
|
1227
1645
|
domain
|
|
1228
1646
|
});
|
|
1229
1647
|
try {
|
|
1648
|
+
if ('cookie_banner' === type) logger.warn('`cookie_banner` policy type is deprecated in 2.0 RC and will be removed in 2.0 GA. Use backend runtime `policyPacks` for banner behavior.');
|
|
1649
|
+
const request = c.req.raw ?? new Request('https://c15t.local/subjects');
|
|
1650
|
+
const acceptLanguage = request.headers.get('accept-language');
|
|
1651
|
+
const requestLanguage = parseLanguageFromHeader(acceptLanguage);
|
|
1652
|
+
const location = await getLocation(request, ctx);
|
|
1653
|
+
const resolvedJurisdiction = getJurisdiction(location, ctx);
|
|
1654
|
+
const snapshotVerification = await verifyPolicySnapshotToken({
|
|
1655
|
+
token: input.policySnapshotToken,
|
|
1656
|
+
options: ctx.policySnapshot,
|
|
1657
|
+
tenantId: ctx.tenantId
|
|
1658
|
+
});
|
|
1659
|
+
const hasValidSnapshot = snapshotVerification.valid;
|
|
1660
|
+
const snapshotPayload = snapshotVerification.valid ? snapshotVerification.payload : null;
|
|
1661
|
+
const shouldRequireSnapshot = !!ctx.policySnapshot?.signingKey && 'reject' === resolveSnapshotFailureMode(ctx);
|
|
1662
|
+
if (!hasValidSnapshot && shouldRequireSnapshot) throw buildSnapshotHttpException(snapshotVerification.reason);
|
|
1663
|
+
const resolvedPolicyDecision = hasValidSnapshot ? void 0 : await policy_resolvePolicyDecision({
|
|
1664
|
+
policies: ctx.policyPacks,
|
|
1665
|
+
countryCode: location.countryCode,
|
|
1666
|
+
regionCode: location.regionCode,
|
|
1667
|
+
jurisdiction: resolvedJurisdiction,
|
|
1668
|
+
iabEnabled: ctx.iab?.enabled === true
|
|
1669
|
+
});
|
|
1670
|
+
const effectivePolicy = hasValidSnapshot && snapshotPayload ? {
|
|
1671
|
+
id: snapshotPayload.policyId,
|
|
1672
|
+
model: snapshotPayload.model,
|
|
1673
|
+
i18n: snapshotPayload.policyI18n,
|
|
1674
|
+
consent: {
|
|
1675
|
+
expiryDays: snapshotPayload.expiryDays,
|
|
1676
|
+
scopeMode: snapshotPayload.scopeMode,
|
|
1677
|
+
categories: snapshotPayload.categories,
|
|
1678
|
+
preselectedCategories: snapshotPayload.preselectedCategories,
|
|
1679
|
+
gpc: snapshotPayload.gpc
|
|
1680
|
+
},
|
|
1681
|
+
ui: {
|
|
1682
|
+
mode: snapshotPayload.uiMode,
|
|
1683
|
+
banner: snapshotPayload.bannerUi,
|
|
1684
|
+
dialog: snapshotPayload.dialogUi
|
|
1685
|
+
},
|
|
1686
|
+
proof: snapshotPayload.proofConfig
|
|
1687
|
+
} : resolvedPolicyDecision?.policy;
|
|
1688
|
+
const effectiveModel = effectivePolicy?.model ?? ('opt-in' === input.jurisdictionModel || 'opt-out' === input.jurisdictionModel || 'iab' === input.jurisdictionModel ? input.jurisdictionModel : void 0);
|
|
1689
|
+
if ('all' === rawConsentAction) derivedConsentAction = 'accept_all';
|
|
1690
|
+
else if ('necessary' === rawConsentAction) derivedConsentAction = 'opt-out' === effectiveModel ? 'opt_out' : 'reject_all';
|
|
1691
|
+
else if ('custom' === rawConsentAction) derivedConsentAction = 'custom';
|
|
1230
1692
|
const subject = await registry.findOrCreateSubject({
|
|
1231
1693
|
subjectId,
|
|
1232
1694
|
externalSubjectId,
|
|
@@ -1253,6 +1715,7 @@ const postSubjectHandler = async (c)=>{
|
|
|
1253
1715
|
});
|
|
1254
1716
|
let policyId;
|
|
1255
1717
|
let purposeIds = [];
|
|
1718
|
+
let appliedPreferences;
|
|
1256
1719
|
const inputPolicyId = 'policyId' in input ? input.policyId : void 0;
|
|
1257
1720
|
if (inputPolicyId) {
|
|
1258
1721
|
policyId = inputPolicyId;
|
|
@@ -1285,20 +1748,66 @@ const postSubjectHandler = async (c)=>{
|
|
|
1285
1748
|
policyId = policy.id;
|
|
1286
1749
|
}
|
|
1287
1750
|
if (preferences) {
|
|
1288
|
-
const
|
|
1751
|
+
const allowedCategories = effectivePolicy?.consent?.categories;
|
|
1752
|
+
const effectiveScopeMode = effectivePolicy?.consent?.scopeMode ?? 'permissive';
|
|
1753
|
+
const hasWildcardCategoryScope = allowedCategories?.includes('*') === true;
|
|
1754
|
+
const appliedPreferenceEntries = Object.entries(preferences);
|
|
1755
|
+
let filteredAppliedPreferenceEntries = appliedPreferenceEntries;
|
|
1756
|
+
if (allowedCategories && allowedCategories.length > 0 && !hasWildcardCategoryScope) {
|
|
1757
|
+
const disallowed = appliedPreferenceEntries.map(([purpose])=>purpose).filter((purpose)=>!allowedCategories.includes(purpose));
|
|
1758
|
+
filteredAppliedPreferenceEntries = appliedPreferenceEntries.filter(([purpose])=>allowedCategories.includes(purpose));
|
|
1759
|
+
if (disallowed.length > 0 && 'strict' === effectiveScopeMode) throw new HTTPException(400, {
|
|
1760
|
+
message: 'Preferences include categories not allowed by policy',
|
|
1761
|
+
cause: {
|
|
1762
|
+
code: 'PURPOSE_NOT_ALLOWED',
|
|
1763
|
+
disallowed
|
|
1764
|
+
}
|
|
1765
|
+
});
|
|
1766
|
+
}
|
|
1767
|
+
appliedPreferences = Object.fromEntries(filteredAppliedPreferenceEntries);
|
|
1768
|
+
const filteredConsentedPurposeCodes = filteredAppliedPreferenceEntries.filter(([_, isConsented])=>isConsented).map(([purposeCode])=>purposeCode);
|
|
1289
1769
|
logger.debug('Consented purposes', {
|
|
1290
|
-
consentedPurposes
|
|
1770
|
+
consentedPurposes: filteredConsentedPurposeCodes
|
|
1291
1771
|
});
|
|
1292
|
-
const purposesRaw = await Promise.all(
|
|
1772
|
+
const purposesRaw = await Promise.all(filteredConsentedPurposeCodes.map((purposeCode)=>registry.findOrCreateConsentPurposeByCode(purposeCode)));
|
|
1293
1773
|
const purposes = purposesRaw.map((purpose)=>purpose?.id ?? null).filter((id)=>Boolean(id));
|
|
1294
1774
|
logger.debug('Filtered purposes', {
|
|
1295
1775
|
purposes
|
|
1296
1776
|
});
|
|
1297
1777
|
if (0 === purposes.length) logger.warn('No valid purpose IDs found after filtering. Using empty list.', {
|
|
1298
|
-
consentedPurposes
|
|
1778
|
+
consentedPurposes: filteredConsentedPurposeCodes
|
|
1299
1779
|
});
|
|
1300
1780
|
purposeIds = purposes;
|
|
1301
1781
|
}
|
|
1782
|
+
const expiryDays = effectivePolicy?.consent?.expiryDays;
|
|
1783
|
+
const validUntil = 'number' == typeof expiryDays && Number.isFinite(expiryDays) ? new Date(givenAt.getTime() + 86400000 * Math.max(0, expiryDays)) : void 0;
|
|
1784
|
+
const proofConfig = effectivePolicy?.proof;
|
|
1785
|
+
const shouldStoreIp = proofConfig?.storeIp ?? true;
|
|
1786
|
+
const shouldStoreUserAgent = proofConfig?.storeUserAgent ?? true;
|
|
1787
|
+
const shouldStoreLanguage = proofConfig?.storeLanguage ?? false;
|
|
1788
|
+
const effectiveLanguage = (snapshotPayload?.language && hasValidSnapshot ? snapshotPayload.language : requestLanguage) ?? void 0;
|
|
1789
|
+
const metadataWithPolicy = {
|
|
1790
|
+
...metadata ?? {},
|
|
1791
|
+
...shouldStoreLanguage && effectiveLanguage ? {
|
|
1792
|
+
policyLanguage: effectiveLanguage
|
|
1793
|
+
} : {}
|
|
1794
|
+
};
|
|
1795
|
+
const effectiveJurisdiction = hasValidSnapshot && snapshotPayload ? snapshotPayload.jurisdiction : resolvedJurisdiction;
|
|
1796
|
+
const decisionPayload = buildDecisionPayload({
|
|
1797
|
+
tenantId: ctx.tenantId,
|
|
1798
|
+
snapshot: hasValidSnapshot && snapshotPayload ? {
|
|
1799
|
+
valid: true,
|
|
1800
|
+
payload: snapshotPayload
|
|
1801
|
+
} : null,
|
|
1802
|
+
decision: resolvedPolicyDecision,
|
|
1803
|
+
location: {
|
|
1804
|
+
countryCode: location.countryCode,
|
|
1805
|
+
regionCode: location.regionCode
|
|
1806
|
+
},
|
|
1807
|
+
jurisdiction: resolvedJurisdiction,
|
|
1808
|
+
language: effectiveLanguage,
|
|
1809
|
+
proofConfig
|
|
1810
|
+
});
|
|
1302
1811
|
const existingConsent = await db.findFirst('consent', {
|
|
1303
1812
|
where: (b)=>b.and(b('subjectId', '=', subject.id), b('domainId', '=', domainRecord.id), b('policyId', '=', policyId), b('givenAt', '=', givenAt))
|
|
1304
1813
|
});
|
|
@@ -1313,6 +1822,7 @@ const postSubjectHandler = async (c)=>{
|
|
|
1313
1822
|
domain: domainRecord.name,
|
|
1314
1823
|
type,
|
|
1315
1824
|
metadata,
|
|
1825
|
+
appliedPreferences,
|
|
1316
1826
|
uiSource: input.uiSource,
|
|
1317
1827
|
givenAt: existingConsent.givenAt
|
|
1318
1828
|
});
|
|
@@ -1324,6 +1834,42 @@ const postSubjectHandler = async (c)=>{
|
|
|
1324
1834
|
policyId,
|
|
1325
1835
|
purposeIds
|
|
1326
1836
|
});
|
|
1837
|
+
const runtimePolicyDecision = decisionPayload ? await tx.findFirst('runtimePolicyDecision', {
|
|
1838
|
+
where: (b)=>b('dedupeKey', '=', decisionPayload.dedupeKey)
|
|
1839
|
+
}) ?? await tx.create('runtimePolicyDecision', {
|
|
1840
|
+
id: `rpd_${crypto.randomUUID().replaceAll('-', '')}`,
|
|
1841
|
+
tenantId: decisionPayload.tenantId,
|
|
1842
|
+
policyId: decisionPayload.policyId,
|
|
1843
|
+
fingerprint: decisionPayload.fingerprint,
|
|
1844
|
+
matchedBy: decisionPayload.matchedBy,
|
|
1845
|
+
countryCode: decisionPayload.countryCode,
|
|
1846
|
+
regionCode: decisionPayload.regionCode,
|
|
1847
|
+
jurisdiction: decisionPayload.jurisdiction,
|
|
1848
|
+
language: decisionPayload.language,
|
|
1849
|
+
model: decisionPayload.model,
|
|
1850
|
+
policyI18n: decisionPayload.policyI18n ? {
|
|
1851
|
+
json: decisionPayload.policyI18n
|
|
1852
|
+
} : void 0,
|
|
1853
|
+
uiMode: decisionPayload.uiMode,
|
|
1854
|
+
bannerUi: decisionPayload.bannerUi ? {
|
|
1855
|
+
json: decisionPayload.bannerUi
|
|
1856
|
+
} : void 0,
|
|
1857
|
+
dialogUi: decisionPayload.dialogUi ? {
|
|
1858
|
+
json: decisionPayload.dialogUi
|
|
1859
|
+
} : void 0,
|
|
1860
|
+
categories: decisionPayload.categories ? {
|
|
1861
|
+
json: decisionPayload.categories
|
|
1862
|
+
} : void 0,
|
|
1863
|
+
preselectedCategories: decisionPayload.preselectedCategories ? {
|
|
1864
|
+
json: decisionPayload.preselectedCategories
|
|
1865
|
+
} : void 0,
|
|
1866
|
+
proofConfig: decisionPayload.proofConfig ? {
|
|
1867
|
+
json: decisionPayload.proofConfig
|
|
1868
|
+
} : void 0,
|
|
1869
|
+
dedupeKey: decisionPayload.dedupeKey
|
|
1870
|
+
}).catch(async ()=>tx.findFirst('runtimePolicyDecision', {
|
|
1871
|
+
where: (b)=>b('dedupeKey', '=', decisionPayload.dedupeKey)
|
|
1872
|
+
})) : void 0;
|
|
1327
1873
|
const consentRecord = await tx.create('consent', {
|
|
1328
1874
|
id: await generateUniqueId(tx, 'consent', ctx),
|
|
1329
1875
|
subjectId: subject.id,
|
|
@@ -1332,17 +1878,20 @@ const postSubjectHandler = async (c)=>{
|
|
|
1332
1878
|
purposeIds: {
|
|
1333
1879
|
json: purposeIds
|
|
1334
1880
|
},
|
|
1335
|
-
metadata:
|
|
1336
|
-
json:
|
|
1881
|
+
metadata: Object.keys(metadataWithPolicy).length > 0 ? {
|
|
1882
|
+
json: metadataWithPolicy
|
|
1337
1883
|
} : void 0,
|
|
1338
|
-
ipAddress: ctx.ipAddress,
|
|
1339
|
-
userAgent: ctx.userAgent,
|
|
1340
|
-
jurisdiction:
|
|
1341
|
-
jurisdictionModel:
|
|
1884
|
+
ipAddress: shouldStoreIp ? ctx.ipAddress : null,
|
|
1885
|
+
userAgent: shouldStoreUserAgent ? ctx.userAgent : null,
|
|
1886
|
+
jurisdiction: effectiveJurisdiction,
|
|
1887
|
+
jurisdictionModel: effectiveModel,
|
|
1342
1888
|
tcString: input.tcString,
|
|
1343
1889
|
uiSource: input.uiSource,
|
|
1344
1890
|
consentAction: derivedConsentAction,
|
|
1345
|
-
givenAt
|
|
1891
|
+
givenAt,
|
|
1892
|
+
validUntil,
|
|
1893
|
+
runtimePolicyDecisionId: runtimePolicyDecision?.id,
|
|
1894
|
+
runtimePolicySource: decisionPayload?.source
|
|
1346
1895
|
});
|
|
1347
1896
|
logger.debug('Created consent', {
|
|
1348
1897
|
consentRecord: consentRecord.id
|
|
@@ -1361,7 +1910,7 @@ const postSubjectHandler = async (c)=>{
|
|
|
1361
1910
|
});
|
|
1362
1911
|
const metrics = getMetrics();
|
|
1363
1912
|
if (metrics) {
|
|
1364
|
-
const jurisdiction =
|
|
1913
|
+
const jurisdiction = effectiveJurisdiction;
|
|
1365
1914
|
metrics.recordConsentCreated({
|
|
1366
1915
|
type,
|
|
1367
1916
|
jurisdiction
|
|
@@ -1383,6 +1932,7 @@ const postSubjectHandler = async (c)=>{
|
|
|
1383
1932
|
domain: domainRecord.name,
|
|
1384
1933
|
type,
|
|
1385
1934
|
metadata,
|
|
1935
|
+
appliedPreferences,
|
|
1386
1936
|
uiSource: input.uiSource,
|
|
1387
1937
|
givenAt: result.consent.givenAt
|
|
1388
1938
|
});
|