@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.cjs
CHANGED
|
@@ -54,18 +54,18 @@ const api_namespaceObject = require("@opentelemetry/api");
|
|
|
54
54
|
let cachedConfig = null;
|
|
55
55
|
let cachedDefaultAttributes = {};
|
|
56
56
|
function create_telemetry_options_isTelemetryEnabled(options) {
|
|
57
|
-
if (options) return options.
|
|
57
|
+
if (options) return options.telemetry?.enabled === true;
|
|
58
58
|
return cachedConfig?.enabled === true;
|
|
59
59
|
}
|
|
60
60
|
const create_telemetry_options_getTracer = (options)=>{
|
|
61
61
|
if (!create_telemetry_options_isTelemetryEnabled(options)) return api_namespaceObject.trace.getTracer('c15t-noop');
|
|
62
|
-
const tracer = options?.
|
|
62
|
+
const tracer = options?.telemetry?.tracer ?? cachedConfig?.tracer;
|
|
63
63
|
if (tracer) return tracer;
|
|
64
64
|
return api_namespaceObject.trace.getTracer(options?.appName ?? 'c15t');
|
|
65
65
|
};
|
|
66
66
|
const getMeter = (options)=>{
|
|
67
67
|
if (!create_telemetry_options_isTelemetryEnabled(options)) return api_namespaceObject.metrics.getMeter('c15t-noop');
|
|
68
|
-
const meter = options?.
|
|
68
|
+
const meter = options?.telemetry?.meter ?? cachedConfig?.meter;
|
|
69
69
|
if (meter) return meter;
|
|
70
70
|
return api_namespaceObject.metrics.getMeter(options?.appName ?? 'c15t');
|
|
71
71
|
};
|
|
@@ -470,7 +470,7 @@ async function executeWithSpan(span, operation) {
|
|
|
470
470
|
}
|
|
471
471
|
}
|
|
472
472
|
function resolveDefaultAttributes(options) {
|
|
473
|
-
return options?.
|
|
473
|
+
return options?.telemetry?.defaultAttributes || getDefaultAttributes();
|
|
474
474
|
}
|
|
475
475
|
async function withExternalSpan(attributes, operation, options) {
|
|
476
476
|
if (!create_telemetry_options_isTelemetryEnabled(options)) return operation();
|
|
@@ -646,6 +646,124 @@ function createGVLResolver(options) {
|
|
|
646
646
|
}
|
|
647
647
|
};
|
|
648
648
|
}
|
|
649
|
+
const external_jose_namespaceObject = require("jose");
|
|
650
|
+
const POLICY_SNAPSHOT_JWT_HEADER = {
|
|
651
|
+
alg: 'HS256',
|
|
652
|
+
typ: 'JWT'
|
|
653
|
+
};
|
|
654
|
+
const DEFAULT_POLICY_SNAPSHOT_ISSUER = 'c15t';
|
|
655
|
+
const DEFAULT_POLICY_SNAPSHOT_AUDIENCE = 'c15t-policy-snapshot';
|
|
656
|
+
function resolveSnapshotIssuer(options) {
|
|
657
|
+
return options?.issuer?.trim() || DEFAULT_POLICY_SNAPSHOT_ISSUER;
|
|
658
|
+
}
|
|
659
|
+
function resolveSnapshotAudience(params) {
|
|
660
|
+
const configuredAudience = params.options?.audience?.trim();
|
|
661
|
+
if (configuredAudience) return configuredAudience;
|
|
662
|
+
return params.tenantId ? `${DEFAULT_POLICY_SNAPSHOT_AUDIENCE}:${params.tenantId}` : DEFAULT_POLICY_SNAPSHOT_AUDIENCE;
|
|
663
|
+
}
|
|
664
|
+
function getSigningKey(secret) {
|
|
665
|
+
return new TextEncoder().encode(secret);
|
|
666
|
+
}
|
|
667
|
+
function isPolicySnapshotPayload(payload) {
|
|
668
|
+
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;
|
|
669
|
+
}
|
|
670
|
+
async function createPolicySnapshotToken(params) {
|
|
671
|
+
const { options } = params;
|
|
672
|
+
if (!options?.signingKey) return;
|
|
673
|
+
const iat = Math.floor(Date.now() / 1000);
|
|
674
|
+
const ttlSeconds = options.ttlSeconds ?? 1800;
|
|
675
|
+
const exp = iat + ttlSeconds;
|
|
676
|
+
const iss = resolveSnapshotIssuer(options);
|
|
677
|
+
const aud = resolveSnapshotAudience({
|
|
678
|
+
options,
|
|
679
|
+
tenantId: params.tenantId
|
|
680
|
+
});
|
|
681
|
+
const payload = {
|
|
682
|
+
iss,
|
|
683
|
+
aud,
|
|
684
|
+
sub: params.policyId,
|
|
685
|
+
tenantId: params.tenantId,
|
|
686
|
+
policyId: params.policyId,
|
|
687
|
+
fingerprint: params.fingerprint,
|
|
688
|
+
matchedBy: params.matchedBy,
|
|
689
|
+
country: params.country,
|
|
690
|
+
region: params.region,
|
|
691
|
+
jurisdiction: params.jurisdiction,
|
|
692
|
+
language: params.language,
|
|
693
|
+
model: params.model,
|
|
694
|
+
policyI18n: params.policyI18n,
|
|
695
|
+
expiryDays: params.expiryDays,
|
|
696
|
+
scopeMode: params.scopeMode,
|
|
697
|
+
uiMode: params.uiMode,
|
|
698
|
+
bannerUi: params.bannerUi,
|
|
699
|
+
dialogUi: params.dialogUi,
|
|
700
|
+
categories: params.categories,
|
|
701
|
+
preselectedCategories: params.preselectedCategories,
|
|
702
|
+
gpc: params.gpc,
|
|
703
|
+
proofConfig: params.proofConfig,
|
|
704
|
+
iat,
|
|
705
|
+
exp
|
|
706
|
+
};
|
|
707
|
+
const token = await new external_jose_namespaceObject.SignJWT(payload).setProtectedHeader(POLICY_SNAPSHOT_JWT_HEADER).setIssuedAt(iat).setExpirationTime(exp).sign(getSigningKey(options.signingKey));
|
|
708
|
+
return {
|
|
709
|
+
token,
|
|
710
|
+
payload
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
async function verifyPolicySnapshotToken(params) {
|
|
714
|
+
const { token, options, tenantId } = params;
|
|
715
|
+
if (!options?.signingKey) return {
|
|
716
|
+
valid: false,
|
|
717
|
+
reason: 'missing'
|
|
718
|
+
};
|
|
719
|
+
if (!token) return {
|
|
720
|
+
valid: false,
|
|
721
|
+
reason: 'missing'
|
|
722
|
+
};
|
|
723
|
+
if (3 !== token.split('.').length) return {
|
|
724
|
+
valid: false,
|
|
725
|
+
reason: 'malformed'
|
|
726
|
+
};
|
|
727
|
+
try {
|
|
728
|
+
const { payload, protectedHeader } = await (0, external_jose_namespaceObject.jwtVerify)(token, getSigningKey(options.signingKey), {
|
|
729
|
+
issuer: resolveSnapshotIssuer(options),
|
|
730
|
+
audience: resolveSnapshotAudience({
|
|
731
|
+
options,
|
|
732
|
+
tenantId
|
|
733
|
+
})
|
|
734
|
+
});
|
|
735
|
+
const header = protectedHeader;
|
|
736
|
+
if ('HS256' !== header.alg || 'JWT' !== header.typ) return {
|
|
737
|
+
valid: false,
|
|
738
|
+
reason: 'invalid'
|
|
739
|
+
};
|
|
740
|
+
if (!isPolicySnapshotPayload(payload)) return {
|
|
741
|
+
valid: false,
|
|
742
|
+
reason: 'invalid'
|
|
743
|
+
};
|
|
744
|
+
if (payload.sub !== payload.policyId) return {
|
|
745
|
+
valid: false,
|
|
746
|
+
reason: 'invalid'
|
|
747
|
+
};
|
|
748
|
+
if ((tenantId ?? void 0) !== (payload.tenantId ?? void 0)) return {
|
|
749
|
+
valid: false,
|
|
750
|
+
reason: 'invalid'
|
|
751
|
+
};
|
|
752
|
+
return {
|
|
753
|
+
valid: true,
|
|
754
|
+
payload
|
|
755
|
+
};
|
|
756
|
+
} catch (error) {
|
|
757
|
+
if (error instanceof external_jose_namespaceObject.errors.JWTExpired) return {
|
|
758
|
+
valid: false,
|
|
759
|
+
reason: 'expired'
|
|
760
|
+
};
|
|
761
|
+
return {
|
|
762
|
+
valid: false,
|
|
763
|
+
reason: 'invalid'
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
}
|
|
649
767
|
function geo_normalizeHeader(value) {
|
|
650
768
|
if (!value) return null;
|
|
651
769
|
return Array.isArray(value) ? value[0] ?? null : value;
|
|
@@ -787,7 +905,7 @@ function checkJurisdiction(countryCode, regionCode) {
|
|
|
787
905
|
return jurisdiction;
|
|
788
906
|
}
|
|
789
907
|
async function getLocation(request, options) {
|
|
790
|
-
if (options.
|
|
908
|
+
if (options.disableGeoLocation) return {
|
|
791
909
|
countryCode: null,
|
|
792
910
|
regionCode: null
|
|
793
911
|
};
|
|
@@ -798,30 +916,253 @@ async function getLocation(request, options) {
|
|
|
798
916
|
};
|
|
799
917
|
}
|
|
800
918
|
function getJurisdiction(location, options) {
|
|
801
|
-
if (options.
|
|
919
|
+
if (options.disableGeoLocation) return 'GDPR';
|
|
802
920
|
return checkJurisdiction(location.countryCode, location.regionCode);
|
|
803
921
|
}
|
|
922
|
+
const schema_types_namespaceObject = require("@c15t/schema/types");
|
|
923
|
+
async function resolvePolicyDecision(params) {
|
|
924
|
+
return (0, schema_types_namespaceObject.resolvePolicyDecision)({
|
|
925
|
+
policies: params.policies,
|
|
926
|
+
countryCode: params.countryCode,
|
|
927
|
+
regionCode: params.regionCode,
|
|
928
|
+
jurisdiction: params.jurisdiction,
|
|
929
|
+
iabEnabled: params.iabEnabled
|
|
930
|
+
});
|
|
931
|
+
}
|
|
804
932
|
const translations_namespaceObject = require("@c15t/translations");
|
|
933
|
+
const all_namespaceObject = require("@c15t/translations/all");
|
|
934
|
+
const DEFAULT_PROFILE = 'default';
|
|
935
|
+
const warnedKeys = new Set();
|
|
805
936
|
function isSupportedBaseLanguage(lang) {
|
|
806
|
-
return lang in
|
|
937
|
+
return lang in all_namespaceObject.baseTranslations;
|
|
938
|
+
}
|
|
939
|
+
function warnOnce(logger, key, message, metadata) {
|
|
940
|
+
if (!logger || warnedKeys.has(key)) return;
|
|
941
|
+
warnedKeys.add(key);
|
|
942
|
+
logger.warn(message, metadata);
|
|
807
943
|
}
|
|
808
|
-
function
|
|
809
|
-
|
|
810
|
-
const
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
944
|
+
function normalizeLanguage(value) {
|
|
945
|
+
if (!value) return;
|
|
946
|
+
const normalized = value.split(',')[0]?.split(';')[0]?.trim().toLowerCase();
|
|
947
|
+
if (!normalized) return;
|
|
948
|
+
return normalized.split('-')[0] ?? void 0;
|
|
949
|
+
}
|
|
950
|
+
function normalizeProfiles(params) {
|
|
951
|
+
const profiles = params.i18n?.messages;
|
|
952
|
+
const legacy = params.customTranslations;
|
|
953
|
+
if (profiles && Object.keys(profiles).length > 0) {
|
|
954
|
+
if (legacy && Object.keys(legacy).length > 0) warnOnce(params.logger, 'i18n.customTranslations.ignored', '`customTranslations` is deprecated and ignored when `i18n.messages` is configured.');
|
|
955
|
+
return profiles;
|
|
956
|
+
}
|
|
957
|
+
if (legacy && Object.keys(legacy).length > 0) {
|
|
958
|
+
warnOnce(params.logger, 'i18n.customTranslations.deprecated', '`customTranslations` is deprecated. Use `i18n.messages` instead.');
|
|
959
|
+
return {
|
|
960
|
+
[DEFAULT_PROFILE]: {
|
|
961
|
+
translations: legacy
|
|
962
|
+
}
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
return {};
|
|
966
|
+
}
|
|
967
|
+
function buildCandidates(input) {
|
|
968
|
+
const raw = [
|
|
969
|
+
{
|
|
970
|
+
language: input.language,
|
|
971
|
+
reason: 'profile_language'
|
|
972
|
+
},
|
|
973
|
+
{
|
|
974
|
+
language: input.fallbackLanguage,
|
|
975
|
+
reason: 'profile_fallback'
|
|
976
|
+
}
|
|
814
977
|
];
|
|
815
|
-
const
|
|
978
|
+
const dedupe = new Set();
|
|
979
|
+
return raw.filter((candidate)=>{
|
|
980
|
+
const key = candidate.language;
|
|
981
|
+
if (dedupe.has(key)) return false;
|
|
982
|
+
dedupe.add(key);
|
|
983
|
+
return true;
|
|
984
|
+
});
|
|
985
|
+
}
|
|
986
|
+
function getProfileLanguages(profiles, profile) {
|
|
987
|
+
return Object.keys(profiles[profile]?.translations ?? {}).sort();
|
|
988
|
+
}
|
|
989
|
+
function getSelectableLanguages(input) {
|
|
990
|
+
return getProfileLanguages(input.profiles, input.profile);
|
|
991
|
+
}
|
|
992
|
+
function resolveFallbackLanguage(input) {
|
|
993
|
+
const configuredFallbackLanguage = normalizeLanguage(input.profile?.fallbackLanguage) ?? 'en';
|
|
994
|
+
const profileLanguages = Object.keys(input.profile?.translations ?? {}).sort();
|
|
995
|
+
if (profileLanguages.includes(configuredFallbackLanguage)) return configuredFallbackLanguage;
|
|
996
|
+
if (profileLanguages.includes('en')) return 'en';
|
|
997
|
+
return profileLanguages[0] ?? configuredFallbackLanguage;
|
|
998
|
+
}
|
|
999
|
+
function resolveActiveProfile(input) {
|
|
1000
|
+
const requestedProfile = input.policyProfile ?? input.defaultProfile;
|
|
1001
|
+
if (input.profiles[requestedProfile]) return requestedProfile;
|
|
1002
|
+
if (input.policyProfile) warnOnce(input.logger, `i18n.profile.missing:${requestedProfile}`, `Policy i18n profile '${requestedProfile}' does not exist. Falling back to default profile '${input.defaultProfile}'.`);
|
|
1003
|
+
return input.defaultProfile;
|
|
1004
|
+
}
|
|
1005
|
+
function translations_getTranslationsData(acceptLanguage, customTranslations, options) {
|
|
1006
|
+
const profiles = normalizeProfiles({
|
|
1007
|
+
customTranslations,
|
|
1008
|
+
i18n: options?.i18n,
|
|
1009
|
+
logger: options?.logger
|
|
1010
|
+
});
|
|
1011
|
+
const defaultProfile = options?.i18n?.defaultProfile ?? DEFAULT_PROFILE;
|
|
1012
|
+
const profile = resolveActiveProfile({
|
|
1013
|
+
profiles,
|
|
1014
|
+
defaultProfile,
|
|
1015
|
+
policyProfile: options?.policyI18n?.messageProfile,
|
|
1016
|
+
logger: options?.logger
|
|
1017
|
+
});
|
|
1018
|
+
const configuredLanguages = Object.keys(profiles).length > 0 ? getSelectableLanguages({
|
|
1019
|
+
profiles,
|
|
1020
|
+
profile
|
|
1021
|
+
}) : Object.keys(all_namespaceObject.baseTranslations);
|
|
1022
|
+
const fallbackLanguage = Object.keys(profiles).length > 0 ? resolveFallbackLanguage({
|
|
1023
|
+
profile: profiles[profile]
|
|
1024
|
+
}) : 'en';
|
|
1025
|
+
const policyLanguage = normalizeLanguage(options?.policyI18n?.language);
|
|
1026
|
+
const requestedLanguage = policyLanguage ?? (0, translations_namespaceObject.selectLanguage)(configuredLanguages, {
|
|
816
1027
|
header: acceptLanguage,
|
|
817
|
-
fallback:
|
|
1028
|
+
fallback: fallbackLanguage
|
|
1029
|
+
});
|
|
1030
|
+
const candidates = buildCandidates({
|
|
1031
|
+
language: requestedLanguage,
|
|
1032
|
+
fallbackLanguage
|
|
1033
|
+
});
|
|
1034
|
+
const selectedCandidate = candidates.find((candidate)=>!!profiles[profile]?.translations[candidate.language]);
|
|
1035
|
+
if (selectedCandidate && 'profile_language' !== selectedCandidate.reason) warnOnce(options?.logger, `i18n.fallback:${profile}:${requestedLanguage}:${selectedCandidate.language}`, `Policy translation fallback used (${selectedCandidate.reason}).`, {
|
|
1036
|
+
requestedProfile: profile,
|
|
1037
|
+
requestedLanguage,
|
|
1038
|
+
resolvedProfile: profile,
|
|
1039
|
+
resolvedLanguage: selectedCandidate.language
|
|
818
1040
|
});
|
|
819
|
-
|
|
820
|
-
|
|
1041
|
+
let language = selectedCandidate?.language ?? requestedLanguage;
|
|
1042
|
+
if (!selectedCandidate && !isSupportedBaseLanguage(language)) {
|
|
1043
|
+
warnOnce(options?.logger, `i18n.base-fallback:${language}`, `No translation found for '${language}'. Falling back to base English translations.`);
|
|
1044
|
+
language = 'en';
|
|
1045
|
+
}
|
|
1046
|
+
const base = isSupportedBaseLanguage(language) ? all_namespaceObject.baseTranslations[language] : all_namespaceObject.baseTranslations.en;
|
|
1047
|
+
const custom = selectedCandidate ? profiles[profile]?.translations[selectedCandidate.language] : void 0;
|
|
821
1048
|
const translations = custom ? (0, translations_namespaceObject.deepMergeTranslations)(base, custom) : base;
|
|
822
1049
|
return {
|
|
823
1050
|
translations: translations,
|
|
824
|
-
language
|
|
1051
|
+
language
|
|
1052
|
+
};
|
|
1053
|
+
}
|
|
1054
|
+
function stripIabTranslations(translations) {
|
|
1055
|
+
const { iab: _iab, ...rest } = translations;
|
|
1056
|
+
return rest;
|
|
1057
|
+
}
|
|
1058
|
+
function resolveNoPolicyFallback() {
|
|
1059
|
+
return {
|
|
1060
|
+
id: 'no_banner',
|
|
1061
|
+
model: 'none',
|
|
1062
|
+
ui: {
|
|
1063
|
+
mode: 'none'
|
|
1064
|
+
}
|
|
1065
|
+
};
|
|
1066
|
+
}
|
|
1067
|
+
async function resolveInitPayload(request, options, logger) {
|
|
1068
|
+
const acceptLanguage = request.headers.get('accept-language') || 'en';
|
|
1069
|
+
const location = await getLocation(request, options);
|
|
1070
|
+
const jurisdiction = getJurisdiction(location, options);
|
|
1071
|
+
const hasExplicitPolicyPack = void 0 !== options.policyPacks;
|
|
1072
|
+
const isExplicitEmptyPolicyPack = hasExplicitPolicyPack && (options.policyPacks?.length ?? 0) === 0;
|
|
1073
|
+
const policyDecision = isExplicitEmptyPolicyPack ? void 0 : await resolvePolicyDecision({
|
|
1074
|
+
policies: options.policyPacks,
|
|
1075
|
+
countryCode: location.countryCode,
|
|
1076
|
+
regionCode: location.regionCode,
|
|
1077
|
+
jurisdiction,
|
|
1078
|
+
iabEnabled: options.iab?.enabled === true
|
|
1079
|
+
});
|
|
1080
|
+
if (hasExplicitPolicyPack && !isExplicitEmptyPolicyPack && !policyDecision) logger?.warn('Policy packs configured but no policy matched', {
|
|
1081
|
+
country: location.countryCode,
|
|
1082
|
+
region: location.regionCode
|
|
1083
|
+
});
|
|
1084
|
+
const resolvedPolicy = hasExplicitPolicyPack ? policyDecision?.policy ?? resolveNoPolicyFallback() : void 0;
|
|
1085
|
+
const iabOptions = options.iab;
|
|
1086
|
+
const shouldIncludeIabPayload = iabOptions?.enabled === true && (!hasExplicitPolicyPack || resolvedPolicy?.model === 'iab');
|
|
1087
|
+
const translationsResult = translations_getTranslationsData(acceptLanguage, options.customTranslations, {
|
|
1088
|
+
i18n: options.i18n,
|
|
1089
|
+
policyI18n: resolvedPolicy?.i18n,
|
|
1090
|
+
logger
|
|
1091
|
+
});
|
|
1092
|
+
const responseTranslations = shouldIncludeIabPayload ? translationsResult : {
|
|
1093
|
+
...translationsResult,
|
|
1094
|
+
translations: stripIabTranslations(translationsResult.translations)
|
|
1095
|
+
};
|
|
1096
|
+
let gvl = null;
|
|
1097
|
+
if (shouldIncludeIabPayload && iabOptions) {
|
|
1098
|
+
const language = translationsResult.language.split('-')[0] || 'en';
|
|
1099
|
+
const gvlResolver = createGVLResolver({
|
|
1100
|
+
appName: options.appName || 'c15t',
|
|
1101
|
+
bundled: iabOptions.bundled,
|
|
1102
|
+
cacheAdapter: options.cache?.adapter,
|
|
1103
|
+
vendorIds: iabOptions.vendorIds,
|
|
1104
|
+
endpoint: iabOptions.endpoint
|
|
1105
|
+
});
|
|
1106
|
+
gvl = await gvlResolver.get(language);
|
|
1107
|
+
}
|
|
1108
|
+
const customVendors = shouldIncludeIabPayload ? iabOptions?.customVendors : void 0;
|
|
1109
|
+
const snapshot = policyDecision ? await createPolicySnapshotToken({
|
|
1110
|
+
options: options.policySnapshot,
|
|
1111
|
+
tenantId: options.tenantId,
|
|
1112
|
+
policyId: policyDecision.policy.id,
|
|
1113
|
+
fingerprint: policyDecision.fingerprint,
|
|
1114
|
+
matchedBy: policyDecision.matchedBy,
|
|
1115
|
+
country: location?.countryCode ?? null,
|
|
1116
|
+
region: location?.regionCode ?? null,
|
|
1117
|
+
jurisdiction,
|
|
1118
|
+
language: translationsResult.language,
|
|
1119
|
+
model: policyDecision.policy.model,
|
|
1120
|
+
policyI18n: policyDecision.policy.i18n,
|
|
1121
|
+
expiryDays: policyDecision.policy.consent?.expiryDays,
|
|
1122
|
+
scopeMode: policyDecision.policy.consent?.scopeMode,
|
|
1123
|
+
uiMode: policyDecision.policy.ui?.mode,
|
|
1124
|
+
bannerUi: policyDecision.policy.ui?.banner,
|
|
1125
|
+
dialogUi: policyDecision.policy.ui?.dialog,
|
|
1126
|
+
categories: policyDecision.policy.consent?.categories,
|
|
1127
|
+
preselectedCategories: policyDecision.policy.consent?.preselectedCategories,
|
|
1128
|
+
gpc: policyDecision.policy.consent?.gpc,
|
|
1129
|
+
proofConfig: policyDecision.policy.proof
|
|
1130
|
+
}) : void 0;
|
|
1131
|
+
const gpc = '1' === request.headers.get('sec-gpc');
|
|
1132
|
+
getMetrics()?.recordInit({
|
|
1133
|
+
jurisdiction,
|
|
1134
|
+
country: location?.countryCode ?? void 0,
|
|
1135
|
+
region: location?.regionCode ?? void 0,
|
|
1136
|
+
gpc
|
|
1137
|
+
});
|
|
1138
|
+
return {
|
|
1139
|
+
jurisdiction,
|
|
1140
|
+
location,
|
|
1141
|
+
translations: responseTranslations,
|
|
1142
|
+
branding: options.branding || 'c15t',
|
|
1143
|
+
...shouldIncludeIabPayload && {
|
|
1144
|
+
gvl,
|
|
1145
|
+
customVendors
|
|
1146
|
+
},
|
|
1147
|
+
...resolvedPolicy && {
|
|
1148
|
+
policy: resolvedPolicy
|
|
1149
|
+
},
|
|
1150
|
+
...policyDecision && {
|
|
1151
|
+
policyDecision: {
|
|
1152
|
+
policyId: policyDecision.policy.id,
|
|
1153
|
+
fingerprint: policyDecision.fingerprint,
|
|
1154
|
+
matchedBy: policyDecision.matchedBy,
|
|
1155
|
+
country: location.countryCode,
|
|
1156
|
+
region: location.regionCode,
|
|
1157
|
+
jurisdiction
|
|
1158
|
+
}
|
|
1159
|
+
},
|
|
1160
|
+
...snapshot?.token && {
|
|
1161
|
+
policySnapshotToken: snapshot.token
|
|
1162
|
+
},
|
|
1163
|
+
...shouldIncludeIabPayload && iabOptions?.cmpId != null && {
|
|
1164
|
+
cmpId: iabOptions.cmpId
|
|
1165
|
+
}
|
|
825
1166
|
};
|
|
826
1167
|
}
|
|
827
1168
|
const createInitRoute = (options)=>{
|
|
@@ -834,7 +1175,7 @@ const createInitRoute = (options)=>{
|
|
|
834
1175
|
- **Location** – User's location (null if geo-location is disabled)
|
|
835
1176
|
- **Translations** – Consent manager copy (from \`Accept-Language\` header)
|
|
836
1177
|
- **Branding** – Configured branding key
|
|
837
|
-
- **GVL** – Global Vendor List when
|
|
1178
|
+
- **GVL** – Global Vendor List when IAB is active for the request
|
|
838
1179
|
|
|
839
1180
|
Use for geo-targeted consent banners and regional compliance.`,
|
|
840
1181
|
tags: [
|
|
@@ -851,46 +1192,13 @@ Use for geo-targeted consent banners and regional compliance.`,
|
|
|
851
1192
|
}
|
|
852
1193
|
}
|
|
853
1194
|
}), async (c)=>{
|
|
854
|
-
const
|
|
855
|
-
const
|
|
856
|
-
|
|
857
|
-
const jurisdiction = getJurisdiction(location, options);
|
|
858
|
-
const translationsResult = translations_getTranslationsData(acceptLanguage, options.advanced?.customTranslations);
|
|
859
|
-
let gvl = null;
|
|
860
|
-
if (options.advanced?.iab?.enabled) {
|
|
861
|
-
const language = translationsResult.language.split('-')[0] || 'en';
|
|
862
|
-
const gvlResolver = createGVLResolver({
|
|
863
|
-
appName: options.appName || 'c15t',
|
|
864
|
-
bundled: options.advanced.iab.bundled,
|
|
865
|
-
cacheAdapter: options.advanced.cache?.adapter,
|
|
866
|
-
vendorIds: options.advanced.iab.vendorIds,
|
|
867
|
-
endpoint: options.advanced.iab.endpoint
|
|
868
|
-
});
|
|
869
|
-
gvl = await gvlResolver.get(language);
|
|
870
|
-
}
|
|
871
|
-
const customVendors = options.advanced?.iab?.customVendors;
|
|
872
|
-
const gpc = '1' === request.headers.get('sec-gpc');
|
|
873
|
-
getMetrics()?.recordInit({
|
|
874
|
-
jurisdiction,
|
|
875
|
-
country: location?.countryCode ?? void 0,
|
|
876
|
-
region: location?.regionCode ?? void 0,
|
|
877
|
-
gpc
|
|
878
|
-
});
|
|
879
|
-
return c.json({
|
|
880
|
-
jurisdiction,
|
|
881
|
-
location,
|
|
882
|
-
translations: translationsResult,
|
|
883
|
-
branding: options.advanced?.branding || 'c15t',
|
|
884
|
-
gvl,
|
|
885
|
-
customVendors,
|
|
886
|
-
...options.advanced?.iab?.cmpId != null && {
|
|
887
|
-
cmpId: options.advanced.iab.cmpId
|
|
888
|
-
}
|
|
889
|
-
});
|
|
1195
|
+
const ctx = c.get('c15tContext');
|
|
1196
|
+
const payload = await resolveInitPayload(c.req.raw, options, ctx?.logger);
|
|
1197
|
+
return c.json(payload);
|
|
890
1198
|
});
|
|
891
1199
|
return app;
|
|
892
1200
|
};
|
|
893
|
-
const version_version = '2.0.0-rc.
|
|
1201
|
+
const version_version = '2.0.0-rc.5';
|
|
894
1202
|
function getHeaders(headers) {
|
|
895
1203
|
if (!headers) return {
|
|
896
1204
|
countryCode: null,
|
|
@@ -1246,6 +1554,119 @@ const patchSubjectHandler = async (c)=>{
|
|
|
1246
1554
|
});
|
|
1247
1555
|
}
|
|
1248
1556
|
};
|
|
1557
|
+
function buildRuntimeDecisionDedupeKey(input) {
|
|
1558
|
+
return [
|
|
1559
|
+
input.tenantId ?? 'default',
|
|
1560
|
+
input.fingerprint,
|
|
1561
|
+
input.matchedBy,
|
|
1562
|
+
input.countryCode ?? 'none',
|
|
1563
|
+
input.regionCode ?? 'none',
|
|
1564
|
+
input.jurisdiction,
|
|
1565
|
+
input.language ?? 'none'
|
|
1566
|
+
].join('|');
|
|
1567
|
+
}
|
|
1568
|
+
function buildDecisionPayload(params) {
|
|
1569
|
+
const { tenantId, snapshot, decision, location, jurisdiction, language, proofConfig } = params;
|
|
1570
|
+
if (snapshot?.valid && snapshot.payload) {
|
|
1571
|
+
const sp = snapshot.payload;
|
|
1572
|
+
return {
|
|
1573
|
+
tenantId,
|
|
1574
|
+
policyId: sp.policyId,
|
|
1575
|
+
fingerprint: sp.fingerprint,
|
|
1576
|
+
matchedBy: sp.matchedBy,
|
|
1577
|
+
countryCode: sp.country,
|
|
1578
|
+
regionCode: sp.region,
|
|
1579
|
+
jurisdiction: sp.jurisdiction,
|
|
1580
|
+
language: sp.language,
|
|
1581
|
+
model: sp.model,
|
|
1582
|
+
policyI18n: sp.policyI18n,
|
|
1583
|
+
uiMode: sp.uiMode,
|
|
1584
|
+
bannerUi: sp.bannerUi,
|
|
1585
|
+
dialogUi: sp.dialogUi,
|
|
1586
|
+
categories: sp.categories,
|
|
1587
|
+
preselectedCategories: sp.preselectedCategories,
|
|
1588
|
+
proofConfig: sp.proofConfig,
|
|
1589
|
+
dedupeKey: buildRuntimeDecisionDedupeKey({
|
|
1590
|
+
tenantId,
|
|
1591
|
+
fingerprint: sp.fingerprint,
|
|
1592
|
+
matchedBy: sp.matchedBy,
|
|
1593
|
+
countryCode: sp.country,
|
|
1594
|
+
regionCode: sp.region,
|
|
1595
|
+
jurisdiction: sp.jurisdiction,
|
|
1596
|
+
language: sp.language
|
|
1597
|
+
}),
|
|
1598
|
+
source: 'snapshot_token'
|
|
1599
|
+
};
|
|
1600
|
+
}
|
|
1601
|
+
if (decision) return {
|
|
1602
|
+
tenantId,
|
|
1603
|
+
policyId: decision.policy.id,
|
|
1604
|
+
fingerprint: decision.fingerprint,
|
|
1605
|
+
matchedBy: decision.matchedBy,
|
|
1606
|
+
countryCode: location.countryCode,
|
|
1607
|
+
regionCode: location.regionCode,
|
|
1608
|
+
jurisdiction,
|
|
1609
|
+
language,
|
|
1610
|
+
model: decision.policy.model,
|
|
1611
|
+
policyI18n: decision.policy.i18n,
|
|
1612
|
+
uiMode: decision.policy.ui?.mode,
|
|
1613
|
+
bannerUi: decision.policy.ui?.banner,
|
|
1614
|
+
dialogUi: decision.policy.ui?.dialog,
|
|
1615
|
+
categories: decision.policy.consent?.categories,
|
|
1616
|
+
preselectedCategories: decision.policy.consent?.preselectedCategories,
|
|
1617
|
+
proofConfig,
|
|
1618
|
+
dedupeKey: buildRuntimeDecisionDedupeKey({
|
|
1619
|
+
tenantId,
|
|
1620
|
+
fingerprint: decision.fingerprint,
|
|
1621
|
+
matchedBy: decision.matchedBy,
|
|
1622
|
+
countryCode: location.countryCode,
|
|
1623
|
+
regionCode: location.regionCode,
|
|
1624
|
+
jurisdiction,
|
|
1625
|
+
language
|
|
1626
|
+
}),
|
|
1627
|
+
source: 'write_time_fallback'
|
|
1628
|
+
};
|
|
1629
|
+
}
|
|
1630
|
+
function parseLanguageFromHeader(header) {
|
|
1631
|
+
if (!header) return;
|
|
1632
|
+
const firstLanguage = header.split(',')[0]?.split(';')[0]?.trim();
|
|
1633
|
+
if (!firstLanguage) return;
|
|
1634
|
+
return firstLanguage.split('-')[0]?.toLowerCase();
|
|
1635
|
+
}
|
|
1636
|
+
function resolveSnapshotFailureMode(ctx) {
|
|
1637
|
+
return ctx.policySnapshot?.onValidationFailure ?? 'reject';
|
|
1638
|
+
}
|
|
1639
|
+
function buildSnapshotHttpException(reason) {
|
|
1640
|
+
switch(reason){
|
|
1641
|
+
case 'missing':
|
|
1642
|
+
return new http_exception_namespaceObject.HTTPException(409, {
|
|
1643
|
+
message: 'Policy snapshot token is required',
|
|
1644
|
+
cause: {
|
|
1645
|
+
code: 'POLICY_SNAPSHOT_REQUIRED'
|
|
1646
|
+
}
|
|
1647
|
+
});
|
|
1648
|
+
case 'expired':
|
|
1649
|
+
return new http_exception_namespaceObject.HTTPException(409, {
|
|
1650
|
+
message: 'Policy snapshot token has expired',
|
|
1651
|
+
cause: {
|
|
1652
|
+
code: 'POLICY_SNAPSHOT_EXPIRED'
|
|
1653
|
+
}
|
|
1654
|
+
});
|
|
1655
|
+
case 'malformed':
|
|
1656
|
+
case 'invalid':
|
|
1657
|
+
return new http_exception_namespaceObject.HTTPException(409, {
|
|
1658
|
+
message: 'Policy snapshot token is invalid',
|
|
1659
|
+
cause: {
|
|
1660
|
+
code: 'POLICY_SNAPSHOT_INVALID'
|
|
1661
|
+
}
|
|
1662
|
+
});
|
|
1663
|
+
default:
|
|
1664
|
+
{
|
|
1665
|
+
const _exhaustive = reason;
|
|
1666
|
+
throw new Error(`Unhandled policy snapshot verification failure reason: ${_exhaustive}`);
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1249
1670
|
const postSubjectHandler = async (c)=>{
|
|
1250
1671
|
const ctx = c.get('c15tContext');
|
|
1251
1672
|
const logger = ctx.logger;
|
|
@@ -1257,9 +1678,6 @@ const postSubjectHandler = async (c)=>{
|
|
|
1257
1678
|
const givenAt = new Date(givenAtEpoch);
|
|
1258
1679
|
const rawConsentAction = 'consentAction' in input ? input.consentAction : void 0;
|
|
1259
1680
|
let derivedConsentAction;
|
|
1260
|
-
if ('all' === rawConsentAction) derivedConsentAction = 'accept_all';
|
|
1261
|
-
else if ('necessary' === rawConsentAction) derivedConsentAction = 'opt-out' === input.jurisdictionModel ? 'opt_out' : 'reject_all';
|
|
1262
|
-
else if ('custom' === rawConsentAction) derivedConsentAction = 'custom';
|
|
1263
1681
|
logger.debug('Request parameters', {
|
|
1264
1682
|
type,
|
|
1265
1683
|
subjectId,
|
|
@@ -1268,6 +1686,50 @@ const postSubjectHandler = async (c)=>{
|
|
|
1268
1686
|
domain
|
|
1269
1687
|
});
|
|
1270
1688
|
try {
|
|
1689
|
+
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.');
|
|
1690
|
+
const request = c.req.raw ?? new Request('https://c15t.local/subjects');
|
|
1691
|
+
const acceptLanguage = request.headers.get('accept-language');
|
|
1692
|
+
const requestLanguage = parseLanguageFromHeader(acceptLanguage);
|
|
1693
|
+
const location = await getLocation(request, ctx);
|
|
1694
|
+
const resolvedJurisdiction = getJurisdiction(location, ctx);
|
|
1695
|
+
const snapshotVerification = await verifyPolicySnapshotToken({
|
|
1696
|
+
token: input.policySnapshotToken,
|
|
1697
|
+
options: ctx.policySnapshot,
|
|
1698
|
+
tenantId: ctx.tenantId
|
|
1699
|
+
});
|
|
1700
|
+
const hasValidSnapshot = snapshotVerification.valid;
|
|
1701
|
+
const snapshotPayload = snapshotVerification.valid ? snapshotVerification.payload : null;
|
|
1702
|
+
const shouldRequireSnapshot = !!ctx.policySnapshot?.signingKey && 'reject' === resolveSnapshotFailureMode(ctx);
|
|
1703
|
+
if (!hasValidSnapshot && shouldRequireSnapshot) throw buildSnapshotHttpException(snapshotVerification.reason);
|
|
1704
|
+
const resolvedPolicyDecision = hasValidSnapshot ? void 0 : await resolvePolicyDecision({
|
|
1705
|
+
policies: ctx.policyPacks,
|
|
1706
|
+
countryCode: location.countryCode,
|
|
1707
|
+
regionCode: location.regionCode,
|
|
1708
|
+
jurisdiction: resolvedJurisdiction,
|
|
1709
|
+
iabEnabled: ctx.iab?.enabled === true
|
|
1710
|
+
});
|
|
1711
|
+
const effectivePolicy = hasValidSnapshot && snapshotPayload ? {
|
|
1712
|
+
id: snapshotPayload.policyId,
|
|
1713
|
+
model: snapshotPayload.model,
|
|
1714
|
+
i18n: snapshotPayload.policyI18n,
|
|
1715
|
+
consent: {
|
|
1716
|
+
expiryDays: snapshotPayload.expiryDays,
|
|
1717
|
+
scopeMode: snapshotPayload.scopeMode,
|
|
1718
|
+
categories: snapshotPayload.categories,
|
|
1719
|
+
preselectedCategories: snapshotPayload.preselectedCategories,
|
|
1720
|
+
gpc: snapshotPayload.gpc
|
|
1721
|
+
},
|
|
1722
|
+
ui: {
|
|
1723
|
+
mode: snapshotPayload.uiMode,
|
|
1724
|
+
banner: snapshotPayload.bannerUi,
|
|
1725
|
+
dialog: snapshotPayload.dialogUi
|
|
1726
|
+
},
|
|
1727
|
+
proof: snapshotPayload.proofConfig
|
|
1728
|
+
} : resolvedPolicyDecision?.policy;
|
|
1729
|
+
const effectiveModel = effectivePolicy?.model ?? ('opt-in' === input.jurisdictionModel || 'opt-out' === input.jurisdictionModel || 'iab' === input.jurisdictionModel ? input.jurisdictionModel : void 0);
|
|
1730
|
+
if ('all' === rawConsentAction) derivedConsentAction = 'accept_all';
|
|
1731
|
+
else if ('necessary' === rawConsentAction) derivedConsentAction = 'opt-out' === effectiveModel ? 'opt_out' : 'reject_all';
|
|
1732
|
+
else if ('custom' === rawConsentAction) derivedConsentAction = 'custom';
|
|
1271
1733
|
const subject = await registry.findOrCreateSubject({
|
|
1272
1734
|
subjectId,
|
|
1273
1735
|
externalSubjectId,
|
|
@@ -1294,6 +1756,7 @@ const postSubjectHandler = async (c)=>{
|
|
|
1294
1756
|
});
|
|
1295
1757
|
let policyId;
|
|
1296
1758
|
let purposeIds = [];
|
|
1759
|
+
let appliedPreferences;
|
|
1297
1760
|
const inputPolicyId = 'policyId' in input ? input.policyId : void 0;
|
|
1298
1761
|
if (inputPolicyId) {
|
|
1299
1762
|
policyId = inputPolicyId;
|
|
@@ -1326,20 +1789,66 @@ const postSubjectHandler = async (c)=>{
|
|
|
1326
1789
|
policyId = policy.id;
|
|
1327
1790
|
}
|
|
1328
1791
|
if (preferences) {
|
|
1329
|
-
const
|
|
1792
|
+
const allowedCategories = effectivePolicy?.consent?.categories;
|
|
1793
|
+
const effectiveScopeMode = effectivePolicy?.consent?.scopeMode ?? 'permissive';
|
|
1794
|
+
const hasWildcardCategoryScope = allowedCategories?.includes('*') === true;
|
|
1795
|
+
const appliedPreferenceEntries = Object.entries(preferences);
|
|
1796
|
+
let filteredAppliedPreferenceEntries = appliedPreferenceEntries;
|
|
1797
|
+
if (allowedCategories && allowedCategories.length > 0 && !hasWildcardCategoryScope) {
|
|
1798
|
+
const disallowed = appliedPreferenceEntries.map(([purpose])=>purpose).filter((purpose)=>!allowedCategories.includes(purpose));
|
|
1799
|
+
filteredAppliedPreferenceEntries = appliedPreferenceEntries.filter(([purpose])=>allowedCategories.includes(purpose));
|
|
1800
|
+
if (disallowed.length > 0 && 'strict' === effectiveScopeMode) throw new http_exception_namespaceObject.HTTPException(400, {
|
|
1801
|
+
message: 'Preferences include categories not allowed by policy',
|
|
1802
|
+
cause: {
|
|
1803
|
+
code: 'PURPOSE_NOT_ALLOWED',
|
|
1804
|
+
disallowed
|
|
1805
|
+
}
|
|
1806
|
+
});
|
|
1807
|
+
}
|
|
1808
|
+
appliedPreferences = Object.fromEntries(filteredAppliedPreferenceEntries);
|
|
1809
|
+
const filteredConsentedPurposeCodes = filteredAppliedPreferenceEntries.filter(([_, isConsented])=>isConsented).map(([purposeCode])=>purposeCode);
|
|
1330
1810
|
logger.debug('Consented purposes', {
|
|
1331
|
-
consentedPurposes
|
|
1811
|
+
consentedPurposes: filteredConsentedPurposeCodes
|
|
1332
1812
|
});
|
|
1333
|
-
const purposesRaw = await Promise.all(
|
|
1813
|
+
const purposesRaw = await Promise.all(filteredConsentedPurposeCodes.map((purposeCode)=>registry.findOrCreateConsentPurposeByCode(purposeCode)));
|
|
1334
1814
|
const purposes = purposesRaw.map((purpose)=>purpose?.id ?? null).filter((id)=>Boolean(id));
|
|
1335
1815
|
logger.debug('Filtered purposes', {
|
|
1336
1816
|
purposes
|
|
1337
1817
|
});
|
|
1338
1818
|
if (0 === purposes.length) logger.warn('No valid purpose IDs found after filtering. Using empty list.', {
|
|
1339
|
-
consentedPurposes
|
|
1819
|
+
consentedPurposes: filteredConsentedPurposeCodes
|
|
1340
1820
|
});
|
|
1341
1821
|
purposeIds = purposes;
|
|
1342
1822
|
}
|
|
1823
|
+
const expiryDays = effectivePolicy?.consent?.expiryDays;
|
|
1824
|
+
const validUntil = 'number' == typeof expiryDays && Number.isFinite(expiryDays) ? new Date(givenAt.getTime() + 86400000 * Math.max(0, expiryDays)) : void 0;
|
|
1825
|
+
const proofConfig = effectivePolicy?.proof;
|
|
1826
|
+
const shouldStoreIp = proofConfig?.storeIp ?? true;
|
|
1827
|
+
const shouldStoreUserAgent = proofConfig?.storeUserAgent ?? true;
|
|
1828
|
+
const shouldStoreLanguage = proofConfig?.storeLanguage ?? false;
|
|
1829
|
+
const effectiveLanguage = (snapshotPayload?.language && hasValidSnapshot ? snapshotPayload.language : requestLanguage) ?? void 0;
|
|
1830
|
+
const metadataWithPolicy = {
|
|
1831
|
+
...metadata ?? {},
|
|
1832
|
+
...shouldStoreLanguage && effectiveLanguage ? {
|
|
1833
|
+
policyLanguage: effectiveLanguage
|
|
1834
|
+
} : {}
|
|
1835
|
+
};
|
|
1836
|
+
const effectiveJurisdiction = hasValidSnapshot && snapshotPayload ? snapshotPayload.jurisdiction : resolvedJurisdiction;
|
|
1837
|
+
const decisionPayload = buildDecisionPayload({
|
|
1838
|
+
tenantId: ctx.tenantId,
|
|
1839
|
+
snapshot: hasValidSnapshot && snapshotPayload ? {
|
|
1840
|
+
valid: true,
|
|
1841
|
+
payload: snapshotPayload
|
|
1842
|
+
} : null,
|
|
1843
|
+
decision: resolvedPolicyDecision,
|
|
1844
|
+
location: {
|
|
1845
|
+
countryCode: location.countryCode,
|
|
1846
|
+
regionCode: location.regionCode
|
|
1847
|
+
},
|
|
1848
|
+
jurisdiction: resolvedJurisdiction,
|
|
1849
|
+
language: effectiveLanguage,
|
|
1850
|
+
proofConfig
|
|
1851
|
+
});
|
|
1343
1852
|
const existingConsent = await db.findFirst('consent', {
|
|
1344
1853
|
where: (b)=>b.and(b('subjectId', '=', subject.id), b('domainId', '=', domainRecord.id), b('policyId', '=', policyId), b('givenAt', '=', givenAt))
|
|
1345
1854
|
});
|
|
@@ -1354,6 +1863,7 @@ const postSubjectHandler = async (c)=>{
|
|
|
1354
1863
|
domain: domainRecord.name,
|
|
1355
1864
|
type,
|
|
1356
1865
|
metadata,
|
|
1866
|
+
appliedPreferences,
|
|
1357
1867
|
uiSource: input.uiSource,
|
|
1358
1868
|
givenAt: existingConsent.givenAt
|
|
1359
1869
|
});
|
|
@@ -1365,6 +1875,42 @@ const postSubjectHandler = async (c)=>{
|
|
|
1365
1875
|
policyId,
|
|
1366
1876
|
purposeIds
|
|
1367
1877
|
});
|
|
1878
|
+
const runtimePolicyDecision = decisionPayload ? await tx.findFirst('runtimePolicyDecision', {
|
|
1879
|
+
where: (b)=>b('dedupeKey', '=', decisionPayload.dedupeKey)
|
|
1880
|
+
}) ?? await tx.create('runtimePolicyDecision', {
|
|
1881
|
+
id: `rpd_${crypto.randomUUID().replaceAll('-', '')}`,
|
|
1882
|
+
tenantId: decisionPayload.tenantId,
|
|
1883
|
+
policyId: decisionPayload.policyId,
|
|
1884
|
+
fingerprint: decisionPayload.fingerprint,
|
|
1885
|
+
matchedBy: decisionPayload.matchedBy,
|
|
1886
|
+
countryCode: decisionPayload.countryCode,
|
|
1887
|
+
regionCode: decisionPayload.regionCode,
|
|
1888
|
+
jurisdiction: decisionPayload.jurisdiction,
|
|
1889
|
+
language: decisionPayload.language,
|
|
1890
|
+
model: decisionPayload.model,
|
|
1891
|
+
policyI18n: decisionPayload.policyI18n ? {
|
|
1892
|
+
json: decisionPayload.policyI18n
|
|
1893
|
+
} : void 0,
|
|
1894
|
+
uiMode: decisionPayload.uiMode,
|
|
1895
|
+
bannerUi: decisionPayload.bannerUi ? {
|
|
1896
|
+
json: decisionPayload.bannerUi
|
|
1897
|
+
} : void 0,
|
|
1898
|
+
dialogUi: decisionPayload.dialogUi ? {
|
|
1899
|
+
json: decisionPayload.dialogUi
|
|
1900
|
+
} : void 0,
|
|
1901
|
+
categories: decisionPayload.categories ? {
|
|
1902
|
+
json: decisionPayload.categories
|
|
1903
|
+
} : void 0,
|
|
1904
|
+
preselectedCategories: decisionPayload.preselectedCategories ? {
|
|
1905
|
+
json: decisionPayload.preselectedCategories
|
|
1906
|
+
} : void 0,
|
|
1907
|
+
proofConfig: decisionPayload.proofConfig ? {
|
|
1908
|
+
json: decisionPayload.proofConfig
|
|
1909
|
+
} : void 0,
|
|
1910
|
+
dedupeKey: decisionPayload.dedupeKey
|
|
1911
|
+
}).catch(async ()=>tx.findFirst('runtimePolicyDecision', {
|
|
1912
|
+
where: (b)=>b('dedupeKey', '=', decisionPayload.dedupeKey)
|
|
1913
|
+
})) : void 0;
|
|
1368
1914
|
const consentRecord = await tx.create('consent', {
|
|
1369
1915
|
id: await generateUniqueId(tx, 'consent', ctx),
|
|
1370
1916
|
subjectId: subject.id,
|
|
@@ -1373,17 +1919,20 @@ const postSubjectHandler = async (c)=>{
|
|
|
1373
1919
|
purposeIds: {
|
|
1374
1920
|
json: purposeIds
|
|
1375
1921
|
},
|
|
1376
|
-
metadata:
|
|
1377
|
-
json:
|
|
1922
|
+
metadata: Object.keys(metadataWithPolicy).length > 0 ? {
|
|
1923
|
+
json: metadataWithPolicy
|
|
1378
1924
|
} : void 0,
|
|
1379
|
-
ipAddress: ctx.ipAddress,
|
|
1380
|
-
userAgent: ctx.userAgent,
|
|
1381
|
-
jurisdiction:
|
|
1382
|
-
jurisdictionModel:
|
|
1925
|
+
ipAddress: shouldStoreIp ? ctx.ipAddress : null,
|
|
1926
|
+
userAgent: shouldStoreUserAgent ? ctx.userAgent : null,
|
|
1927
|
+
jurisdiction: effectiveJurisdiction,
|
|
1928
|
+
jurisdictionModel: effectiveModel,
|
|
1383
1929
|
tcString: input.tcString,
|
|
1384
1930
|
uiSource: input.uiSource,
|
|
1385
1931
|
consentAction: derivedConsentAction,
|
|
1386
|
-
givenAt
|
|
1932
|
+
givenAt,
|
|
1933
|
+
validUntil,
|
|
1934
|
+
runtimePolicyDecisionId: runtimePolicyDecision?.id,
|
|
1935
|
+
runtimePolicySource: decisionPayload?.source
|
|
1387
1936
|
});
|
|
1388
1937
|
logger.debug('Created consent', {
|
|
1389
1938
|
consentRecord: consentRecord.id
|
|
@@ -1402,7 +1951,7 @@ const postSubjectHandler = async (c)=>{
|
|
|
1402
1951
|
});
|
|
1403
1952
|
const metrics = getMetrics();
|
|
1404
1953
|
if (metrics) {
|
|
1405
|
-
const jurisdiction =
|
|
1954
|
+
const jurisdiction = effectiveJurisdiction;
|
|
1406
1955
|
metrics.recordConsentCreated({
|
|
1407
1956
|
type,
|
|
1408
1957
|
jurisdiction
|
|
@@ -1424,6 +1973,7 @@ const postSubjectHandler = async (c)=>{
|
|
|
1424
1973
|
domain: domainRecord.name,
|
|
1425
1974
|
type,
|
|
1426
1975
|
metadata,
|
|
1976
|
+
appliedPreferences,
|
|
1427
1977
|
uiSource: input.uiSource,
|
|
1428
1978
|
givenAt: result.consent.givenAt
|
|
1429
1979
|
});
|