@c15t/backend 2.0.0-rc.4 → 2.0.0-rc.6
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/302.js +473 -0
- package/dist/364.js +1140 -0
- package/dist/583.js +540 -0
- package/dist/cache.cjs +1 -1
- package/dist/cache.js +4 -415
- package/dist/core.cjs +849 -96
- package/dist/core.js +147 -1817
- package/dist/db/adapters/drizzle.cjs +1 -1
- package/dist/db/adapters/drizzle.js +1 -2
- package/dist/db/adapters/kysely.cjs +1 -1
- package/dist/db/adapters/kysely.js +1 -2
- package/dist/db/adapters/mongo.cjs +1 -1
- package/dist/db/adapters/mongo.js +1 -2
- package/dist/db/adapters/prisma.cjs +1 -1
- package/dist/db/adapters/prisma.js +1 -2
- package/dist/db/adapters/typeorm.cjs +1 -1
- package/dist/db/adapters/typeorm.js +1 -2
- package/dist/db/adapters.cjs +1 -1
- package/dist/db/migrator.cjs +1 -1
- package/dist/db/schema.cjs +38 -1
- package/dist/db/schema.js +33 -2
- package/dist/define-config.cjs +1 -1
- package/dist/edge.cjs +1106 -0
- package/dist/edge.js +190 -0
- package/dist/router.cjs +629 -81
- package/dist/router.js +1 -1509
- package/dist/types/index.cjs +1 -1
- 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 +2 -3
- 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 +2 -3
- package/{dist → dist-types}/db/schema/2.0.0/consent-policy.d.ts +2 -3
- package/{dist → dist-types}/db/schema/2.0.0/consent-purpose.d.ts +2 -3
- package/{dist → dist-types}/db/schema/2.0.0/consent.d.ts +6 -3
- package/{dist → dist-types}/db/schema/2.0.0/domain.d.ts +2 -3
- 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 +2 -3
- package/{dist → dist-types}/db/schema/index.d.ts +862 -33
- package/{dist → dist-types}/db/tenant-scope.d.ts +0 -1
- package/{dist → dist-types}/define-config.d.ts +0 -1
- 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 +0 -1
- 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 → dist-types}/types/index.d.ts +110 -6
- package/dist-types/utils/background.d.ts +6 -0
- package/{dist → dist-types}/utils/create-telemetry-options.d.ts +0 -1
- 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 +45 -31
- package/.turbo/turbo-build.log +0 -49
- package/CHANGELOG.md +0 -123
- 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.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 -26
- 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.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 -19
- 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 -69
- 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 -122
- package/src/init.ts +0 -88
- 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 -193
- 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 -391
- package/src/utils/create-telemetry-options.test.ts +0 -286
- 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 -183
- package/src/utils/instrumentation.ts +0 -194
- package/src/utils/logger.ts +0 -41
- package/src/utils/metrics.test.ts +0 -311
- package/src/utils/metrics.ts +0 -402
- package/src/utils/telemetry-pii.test.ts +0 -323
- package/src/version.ts +0 -2
- package/tsconfig.json +0 -11
- package/vitest.config.ts +0 -28
- /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
|
@@ -22,7 +22,7 @@ var __webpack_require__ = {};
|
|
|
22
22
|
})();
|
|
23
23
|
(()=>{
|
|
24
24
|
__webpack_require__.r = (exports1)=>{
|
|
25
|
-
if (
|
|
25
|
+
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
26
26
|
value: 'Module'
|
|
27
27
|
});
|
|
28
28
|
Object.defineProperty(exports1, '__esModule', {
|
|
@@ -431,11 +431,7 @@ const createConsentRoutes = ()=>{
|
|
|
431
431
|
const app = new external_hono_namespaceObject.Hono();
|
|
432
432
|
app.get('/check', (0, external_hono_openapi_namespaceObject.describeRoute)({
|
|
433
433
|
summary: 'Check consent by external user ID',
|
|
434
|
-
description:
|
|
435
|
-
|
|
436
|
-
**Query parameters:**
|
|
437
|
-
- \`externalId\` – External user ID to check
|
|
438
|
-
- \`type\` – Consent type(s) to check (comma-separated)`,
|
|
434
|
+
description: "Pre-banner cross-device consent check. Use to avoid showing the banner when the user has already consented on another device.\n\n**Query parameters:**\n- `externalId` – External user ID to check\n- `type` – Consent type(s) to check (comma-separated)",
|
|
439
435
|
tags: [
|
|
440
436
|
'Consent'
|
|
441
437
|
],
|
|
@@ -646,6 +642,124 @@ function createGVLResolver(options) {
|
|
|
646
642
|
}
|
|
647
643
|
};
|
|
648
644
|
}
|
|
645
|
+
const external_jose_namespaceObject = require("jose");
|
|
646
|
+
const POLICY_SNAPSHOT_JWT_HEADER = {
|
|
647
|
+
alg: 'HS256',
|
|
648
|
+
typ: 'JWT'
|
|
649
|
+
};
|
|
650
|
+
const DEFAULT_POLICY_SNAPSHOT_ISSUER = 'c15t';
|
|
651
|
+
const DEFAULT_POLICY_SNAPSHOT_AUDIENCE = 'c15t-policy-snapshot';
|
|
652
|
+
function resolveSnapshotIssuer(options) {
|
|
653
|
+
return options?.issuer?.trim() || DEFAULT_POLICY_SNAPSHOT_ISSUER;
|
|
654
|
+
}
|
|
655
|
+
function resolveSnapshotAudience(params) {
|
|
656
|
+
const configuredAudience = params.options?.audience?.trim();
|
|
657
|
+
if (configuredAudience) return configuredAudience;
|
|
658
|
+
return params.tenantId ? `${DEFAULT_POLICY_SNAPSHOT_AUDIENCE}:${params.tenantId}` : DEFAULT_POLICY_SNAPSHOT_AUDIENCE;
|
|
659
|
+
}
|
|
660
|
+
function getSigningKey(secret) {
|
|
661
|
+
return new TextEncoder().encode(secret);
|
|
662
|
+
}
|
|
663
|
+
function isPolicySnapshotPayload(payload) {
|
|
664
|
+
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;
|
|
665
|
+
}
|
|
666
|
+
async function createPolicySnapshotToken(params) {
|
|
667
|
+
const { options } = params;
|
|
668
|
+
if (!options?.signingKey) return;
|
|
669
|
+
const iat = Math.floor(Date.now() / 1000);
|
|
670
|
+
const ttlSeconds = options.ttlSeconds ?? 1800;
|
|
671
|
+
const exp = iat + ttlSeconds;
|
|
672
|
+
const iss = resolveSnapshotIssuer(options);
|
|
673
|
+
const aud = resolveSnapshotAudience({
|
|
674
|
+
options,
|
|
675
|
+
tenantId: params.tenantId
|
|
676
|
+
});
|
|
677
|
+
const payload = {
|
|
678
|
+
iss,
|
|
679
|
+
aud,
|
|
680
|
+
sub: params.policyId,
|
|
681
|
+
tenantId: params.tenantId,
|
|
682
|
+
policyId: params.policyId,
|
|
683
|
+
fingerprint: params.fingerprint,
|
|
684
|
+
matchedBy: params.matchedBy,
|
|
685
|
+
country: params.country,
|
|
686
|
+
region: params.region,
|
|
687
|
+
jurisdiction: params.jurisdiction,
|
|
688
|
+
language: params.language,
|
|
689
|
+
model: params.model,
|
|
690
|
+
policyI18n: params.policyI18n,
|
|
691
|
+
expiryDays: params.expiryDays,
|
|
692
|
+
scopeMode: params.scopeMode,
|
|
693
|
+
uiMode: params.uiMode,
|
|
694
|
+
bannerUi: params.bannerUi,
|
|
695
|
+
dialogUi: params.dialogUi,
|
|
696
|
+
categories: params.categories,
|
|
697
|
+
preselectedCategories: params.preselectedCategories,
|
|
698
|
+
gpc: params.gpc,
|
|
699
|
+
proofConfig: params.proofConfig,
|
|
700
|
+
iat,
|
|
701
|
+
exp
|
|
702
|
+
};
|
|
703
|
+
const token = await new external_jose_namespaceObject.SignJWT(payload).setProtectedHeader(POLICY_SNAPSHOT_JWT_HEADER).setIssuedAt(iat).setExpirationTime(exp).sign(getSigningKey(options.signingKey));
|
|
704
|
+
return {
|
|
705
|
+
token,
|
|
706
|
+
payload
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
async function verifyPolicySnapshotToken(params) {
|
|
710
|
+
const { token, options, tenantId } = params;
|
|
711
|
+
if (!options?.signingKey) return {
|
|
712
|
+
valid: false,
|
|
713
|
+
reason: 'missing'
|
|
714
|
+
};
|
|
715
|
+
if (!token) return {
|
|
716
|
+
valid: false,
|
|
717
|
+
reason: 'missing'
|
|
718
|
+
};
|
|
719
|
+
if (3 !== token.split('.').length) return {
|
|
720
|
+
valid: false,
|
|
721
|
+
reason: 'malformed'
|
|
722
|
+
};
|
|
723
|
+
try {
|
|
724
|
+
const { payload, protectedHeader } = await (0, external_jose_namespaceObject.jwtVerify)(token, getSigningKey(options.signingKey), {
|
|
725
|
+
issuer: resolveSnapshotIssuer(options),
|
|
726
|
+
audience: resolveSnapshotAudience({
|
|
727
|
+
options,
|
|
728
|
+
tenantId
|
|
729
|
+
})
|
|
730
|
+
});
|
|
731
|
+
const header = protectedHeader;
|
|
732
|
+
if ('HS256' !== header.alg || 'JWT' !== header.typ) return {
|
|
733
|
+
valid: false,
|
|
734
|
+
reason: 'invalid'
|
|
735
|
+
};
|
|
736
|
+
if (!isPolicySnapshotPayload(payload)) return {
|
|
737
|
+
valid: false,
|
|
738
|
+
reason: 'invalid'
|
|
739
|
+
};
|
|
740
|
+
if (payload.sub !== payload.policyId) return {
|
|
741
|
+
valid: false,
|
|
742
|
+
reason: 'invalid'
|
|
743
|
+
};
|
|
744
|
+
if ((tenantId ?? void 0) !== (payload.tenantId ?? void 0)) return {
|
|
745
|
+
valid: false,
|
|
746
|
+
reason: 'invalid'
|
|
747
|
+
};
|
|
748
|
+
return {
|
|
749
|
+
valid: true,
|
|
750
|
+
payload
|
|
751
|
+
};
|
|
752
|
+
} catch (error) {
|
|
753
|
+
if (error instanceof external_jose_namespaceObject.errors.JWTExpired) return {
|
|
754
|
+
valid: false,
|
|
755
|
+
reason: 'expired'
|
|
756
|
+
};
|
|
757
|
+
return {
|
|
758
|
+
valid: false,
|
|
759
|
+
reason: 'invalid'
|
|
760
|
+
};
|
|
761
|
+
}
|
|
762
|
+
}
|
|
649
763
|
function geo_normalizeHeader(value) {
|
|
650
764
|
if (!value) return null;
|
|
651
765
|
return Array.isArray(value) ? value[0] ?? null : value;
|
|
@@ -801,28 +915,250 @@ function getJurisdiction(location, options) {
|
|
|
801
915
|
if (options.disableGeoLocation) return 'GDPR';
|
|
802
916
|
return checkJurisdiction(location.countryCode, location.regionCode);
|
|
803
917
|
}
|
|
918
|
+
const schema_types_namespaceObject = require("@c15t/schema/types");
|
|
919
|
+
async function resolvePolicyDecision(params) {
|
|
920
|
+
return (0, schema_types_namespaceObject.resolvePolicyDecision)({
|
|
921
|
+
policies: params.policies,
|
|
922
|
+
countryCode: params.countryCode,
|
|
923
|
+
regionCode: params.regionCode,
|
|
924
|
+
jurisdiction: params.jurisdiction,
|
|
925
|
+
iabEnabled: params.iabEnabled
|
|
926
|
+
});
|
|
927
|
+
}
|
|
804
928
|
const translations_namespaceObject = require("@c15t/translations");
|
|
805
929
|
const all_namespaceObject = require("@c15t/translations/all");
|
|
930
|
+
const DEFAULT_PROFILE = 'default';
|
|
931
|
+
const warnedKeys = new Set();
|
|
806
932
|
function isSupportedBaseLanguage(lang) {
|
|
807
933
|
return lang in all_namespaceObject.baseTranslations;
|
|
808
934
|
}
|
|
809
|
-
function
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
935
|
+
function warnOnce(logger, key, message, metadata) {
|
|
936
|
+
if (!logger || warnedKeys.has(key)) return;
|
|
937
|
+
warnedKeys.add(key);
|
|
938
|
+
logger.warn(message, metadata);
|
|
939
|
+
}
|
|
940
|
+
function normalizeLanguage(value) {
|
|
941
|
+
if (!value) return;
|
|
942
|
+
const normalized = value.split(',')[0]?.split(';')[0]?.trim().toLowerCase();
|
|
943
|
+
if (!normalized) return;
|
|
944
|
+
return normalized.split('-')[0] ?? void 0;
|
|
945
|
+
}
|
|
946
|
+
function normalizeProfiles(params) {
|
|
947
|
+
const profiles = params.i18n?.messages;
|
|
948
|
+
const legacy = params.customTranslations;
|
|
949
|
+
if (profiles && Object.keys(profiles).length > 0) {
|
|
950
|
+
if (legacy && Object.keys(legacy).length > 0) warnOnce(params.logger, 'i18n.customTranslations.ignored', '`customTranslations` is deprecated and ignored when `i18n.messages` is configured.');
|
|
951
|
+
return profiles;
|
|
952
|
+
}
|
|
953
|
+
if (legacy && Object.keys(legacy).length > 0) {
|
|
954
|
+
warnOnce(params.logger, 'i18n.customTranslations.deprecated', '`customTranslations` is deprecated. Use `i18n.messages` instead.');
|
|
955
|
+
return {
|
|
956
|
+
[DEFAULT_PROFILE]: {
|
|
957
|
+
translations: legacy
|
|
958
|
+
}
|
|
959
|
+
};
|
|
960
|
+
}
|
|
961
|
+
return {};
|
|
962
|
+
}
|
|
963
|
+
function buildCandidates(input) {
|
|
964
|
+
const raw = [
|
|
965
|
+
{
|
|
966
|
+
language: input.language,
|
|
967
|
+
reason: 'profile_language'
|
|
968
|
+
},
|
|
969
|
+
{
|
|
970
|
+
language: input.fallbackLanguage,
|
|
971
|
+
reason: 'profile_fallback'
|
|
972
|
+
}
|
|
815
973
|
];
|
|
816
|
-
const
|
|
974
|
+
const dedupe = new Set();
|
|
975
|
+
return raw.filter((candidate)=>{
|
|
976
|
+
const key = candidate.language;
|
|
977
|
+
if (dedupe.has(key)) return false;
|
|
978
|
+
dedupe.add(key);
|
|
979
|
+
return true;
|
|
980
|
+
});
|
|
981
|
+
}
|
|
982
|
+
function getProfileLanguages(profiles, profile) {
|
|
983
|
+
return Object.keys(profiles[profile]?.translations ?? {}).sort();
|
|
984
|
+
}
|
|
985
|
+
function getSelectableLanguages(input) {
|
|
986
|
+
return getProfileLanguages(input.profiles, input.profile);
|
|
987
|
+
}
|
|
988
|
+
function resolveFallbackLanguage(input) {
|
|
989
|
+
const configuredFallbackLanguage = normalizeLanguage(input.profile?.fallbackLanguage) ?? 'en';
|
|
990
|
+
const profileLanguages = Object.keys(input.profile?.translations ?? {}).sort();
|
|
991
|
+
if (profileLanguages.includes(configuredFallbackLanguage)) return configuredFallbackLanguage;
|
|
992
|
+
if (profileLanguages.includes('en')) return 'en';
|
|
993
|
+
return profileLanguages[0] ?? configuredFallbackLanguage;
|
|
994
|
+
}
|
|
995
|
+
function resolveActiveProfile(input) {
|
|
996
|
+
const requestedProfile = input.policyProfile ?? input.defaultProfile;
|
|
997
|
+
if (input.profiles[requestedProfile]) return requestedProfile;
|
|
998
|
+
if (input.policyProfile) warnOnce(input.logger, `i18n.profile.missing:${requestedProfile}`, `Policy i18n profile '${requestedProfile}' does not exist. Falling back to default profile '${input.defaultProfile}'.`);
|
|
999
|
+
return input.defaultProfile;
|
|
1000
|
+
}
|
|
1001
|
+
function translations_getTranslationsData(acceptLanguage, customTranslations, options) {
|
|
1002
|
+
const profiles = normalizeProfiles({
|
|
1003
|
+
customTranslations,
|
|
1004
|
+
i18n: options?.i18n,
|
|
1005
|
+
logger: options?.logger
|
|
1006
|
+
});
|
|
1007
|
+
const defaultProfile = options?.i18n?.defaultProfile ?? DEFAULT_PROFILE;
|
|
1008
|
+
const profile = resolveActiveProfile({
|
|
1009
|
+
profiles,
|
|
1010
|
+
defaultProfile,
|
|
1011
|
+
policyProfile: options?.policyI18n?.messageProfile,
|
|
1012
|
+
logger: options?.logger
|
|
1013
|
+
});
|
|
1014
|
+
const configuredLanguages = Object.keys(profiles).length > 0 ? getSelectableLanguages({
|
|
1015
|
+
profiles,
|
|
1016
|
+
profile
|
|
1017
|
+
}) : Object.keys(all_namespaceObject.baseTranslations);
|
|
1018
|
+
const fallbackLanguage = Object.keys(profiles).length > 0 ? resolveFallbackLanguage({
|
|
1019
|
+
profile: profiles[profile]
|
|
1020
|
+
}) : 'en';
|
|
1021
|
+
const policyLanguage = normalizeLanguage(options?.policyI18n?.language);
|
|
1022
|
+
const requestedLanguage = policyLanguage ?? (0, translations_namespaceObject.selectLanguage)(configuredLanguages, {
|
|
817
1023
|
header: acceptLanguage,
|
|
818
|
-
fallback:
|
|
1024
|
+
fallback: fallbackLanguage
|
|
1025
|
+
});
|
|
1026
|
+
const candidates = buildCandidates({
|
|
1027
|
+
language: requestedLanguage,
|
|
1028
|
+
fallbackLanguage
|
|
819
1029
|
});
|
|
820
|
-
const
|
|
821
|
-
|
|
1030
|
+
const selectedCandidate = candidates.find((candidate)=>!!profiles[profile]?.translations[candidate.language]);
|
|
1031
|
+
if (selectedCandidate && 'profile_language' !== selectedCandidate.reason) warnOnce(options?.logger, `i18n.fallback:${profile}:${requestedLanguage}:${selectedCandidate.language}`, `Policy translation fallback used (${selectedCandidate.reason}).`, {
|
|
1032
|
+
requestedProfile: profile,
|
|
1033
|
+
requestedLanguage,
|
|
1034
|
+
resolvedProfile: profile,
|
|
1035
|
+
resolvedLanguage: selectedCandidate.language
|
|
1036
|
+
});
|
|
1037
|
+
let language = selectedCandidate?.language ?? requestedLanguage;
|
|
1038
|
+
if (!selectedCandidate && !isSupportedBaseLanguage(language)) {
|
|
1039
|
+
warnOnce(options?.logger, `i18n.base-fallback:${language}`, `No translation found for '${language}'. Falling back to base English translations.`);
|
|
1040
|
+
language = 'en';
|
|
1041
|
+
}
|
|
1042
|
+
const base = isSupportedBaseLanguage(language) ? all_namespaceObject.baseTranslations[language] : all_namespaceObject.baseTranslations.en;
|
|
1043
|
+
const custom = selectedCandidate ? profiles[profile]?.translations[selectedCandidate.language] : void 0;
|
|
822
1044
|
const translations = custom ? (0, translations_namespaceObject.deepMergeTranslations)(base, custom) : base;
|
|
823
1045
|
return {
|
|
824
1046
|
translations: translations,
|
|
825
|
-
language
|
|
1047
|
+
language
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
function stripIabTranslations(translations) {
|
|
1051
|
+
const { iab: _iab, ...rest } = translations;
|
|
1052
|
+
return rest;
|
|
1053
|
+
}
|
|
1054
|
+
function resolveNoPolicyFallback() {
|
|
1055
|
+
return {
|
|
1056
|
+
id: 'no_banner',
|
|
1057
|
+
model: 'none',
|
|
1058
|
+
ui: {
|
|
1059
|
+
mode: 'none'
|
|
1060
|
+
}
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
1063
|
+
async function resolveInitPayload(request, options, logger) {
|
|
1064
|
+
const acceptLanguage = request.headers.get('accept-language') || 'en';
|
|
1065
|
+
const location = await getLocation(request, options);
|
|
1066
|
+
const jurisdiction = getJurisdiction(location, options);
|
|
1067
|
+
const hasExplicitPolicyPack = void 0 !== options.policyPacks;
|
|
1068
|
+
const isExplicitEmptyPolicyPack = hasExplicitPolicyPack && (options.policyPacks?.length ?? 0) === 0;
|
|
1069
|
+
const policyDecision = isExplicitEmptyPolicyPack ? void 0 : await resolvePolicyDecision({
|
|
1070
|
+
policies: options.policyPacks,
|
|
1071
|
+
countryCode: location.countryCode,
|
|
1072
|
+
regionCode: location.regionCode,
|
|
1073
|
+
jurisdiction,
|
|
1074
|
+
iabEnabled: options.iab?.enabled === true
|
|
1075
|
+
});
|
|
1076
|
+
if (hasExplicitPolicyPack && !isExplicitEmptyPolicyPack && !policyDecision) logger?.warn('Policy packs configured but no policy matched', {
|
|
1077
|
+
country: location.countryCode,
|
|
1078
|
+
region: location.regionCode
|
|
1079
|
+
});
|
|
1080
|
+
const resolvedPolicy = hasExplicitPolicyPack ? policyDecision?.policy ?? resolveNoPolicyFallback() : void 0;
|
|
1081
|
+
const iabOptions = options.iab;
|
|
1082
|
+
const shouldIncludeIabPayload = iabOptions?.enabled === true && (!hasExplicitPolicyPack || resolvedPolicy?.model === 'iab');
|
|
1083
|
+
const translationsResult = translations_getTranslationsData(acceptLanguage, options.customTranslations, {
|
|
1084
|
+
i18n: options.i18n,
|
|
1085
|
+
policyI18n: resolvedPolicy?.i18n,
|
|
1086
|
+
logger
|
|
1087
|
+
});
|
|
1088
|
+
const responseTranslations = shouldIncludeIabPayload ? translationsResult : {
|
|
1089
|
+
...translationsResult,
|
|
1090
|
+
translations: stripIabTranslations(translationsResult.translations)
|
|
1091
|
+
};
|
|
1092
|
+
let gvl = null;
|
|
1093
|
+
if (shouldIncludeIabPayload && iabOptions) {
|
|
1094
|
+
const language = translationsResult.language.split('-')[0] || 'en';
|
|
1095
|
+
const gvlResolver = createGVLResolver({
|
|
1096
|
+
appName: options.appName || 'c15t',
|
|
1097
|
+
bundled: iabOptions.bundled,
|
|
1098
|
+
cacheAdapter: options.cache?.adapter,
|
|
1099
|
+
vendorIds: iabOptions.vendorIds,
|
|
1100
|
+
endpoint: iabOptions.endpoint
|
|
1101
|
+
});
|
|
1102
|
+
gvl = await gvlResolver.get(language);
|
|
1103
|
+
}
|
|
1104
|
+
const customVendors = shouldIncludeIabPayload ? iabOptions?.customVendors : void 0;
|
|
1105
|
+
const snapshot = policyDecision ? await createPolicySnapshotToken({
|
|
1106
|
+
options: options.policySnapshot,
|
|
1107
|
+
tenantId: options.tenantId,
|
|
1108
|
+
policyId: policyDecision.policy.id,
|
|
1109
|
+
fingerprint: policyDecision.fingerprint,
|
|
1110
|
+
matchedBy: policyDecision.matchedBy,
|
|
1111
|
+
country: location?.countryCode ?? null,
|
|
1112
|
+
region: location?.regionCode ?? null,
|
|
1113
|
+
jurisdiction,
|
|
1114
|
+
language: translationsResult.language,
|
|
1115
|
+
model: policyDecision.policy.model,
|
|
1116
|
+
policyI18n: policyDecision.policy.i18n,
|
|
1117
|
+
expiryDays: policyDecision.policy.consent?.expiryDays,
|
|
1118
|
+
scopeMode: policyDecision.policy.consent?.scopeMode,
|
|
1119
|
+
uiMode: policyDecision.policy.ui?.mode,
|
|
1120
|
+
bannerUi: policyDecision.policy.ui?.banner,
|
|
1121
|
+
dialogUi: policyDecision.policy.ui?.dialog,
|
|
1122
|
+
categories: policyDecision.policy.consent?.categories,
|
|
1123
|
+
preselectedCategories: policyDecision.policy.consent?.preselectedCategories,
|
|
1124
|
+
gpc: policyDecision.policy.consent?.gpc,
|
|
1125
|
+
proofConfig: policyDecision.policy.proof
|
|
1126
|
+
}) : void 0;
|
|
1127
|
+
const gpc = '1' === request.headers.get('sec-gpc');
|
|
1128
|
+
getMetrics()?.recordInit({
|
|
1129
|
+
jurisdiction,
|
|
1130
|
+
country: location?.countryCode ?? void 0,
|
|
1131
|
+
region: location?.regionCode ?? void 0,
|
|
1132
|
+
gpc
|
|
1133
|
+
});
|
|
1134
|
+
return {
|
|
1135
|
+
jurisdiction,
|
|
1136
|
+
location,
|
|
1137
|
+
translations: responseTranslations,
|
|
1138
|
+
branding: options.branding || 'c15t',
|
|
1139
|
+
...shouldIncludeIabPayload && {
|
|
1140
|
+
gvl,
|
|
1141
|
+
customVendors
|
|
1142
|
+
},
|
|
1143
|
+
...resolvedPolicy && {
|
|
1144
|
+
policy: resolvedPolicy
|
|
1145
|
+
},
|
|
1146
|
+
...policyDecision && {
|
|
1147
|
+
policyDecision: {
|
|
1148
|
+
policyId: policyDecision.policy.id,
|
|
1149
|
+
fingerprint: policyDecision.fingerprint,
|
|
1150
|
+
matchedBy: policyDecision.matchedBy,
|
|
1151
|
+
country: location.countryCode,
|
|
1152
|
+
region: location.regionCode,
|
|
1153
|
+
jurisdiction
|
|
1154
|
+
}
|
|
1155
|
+
},
|
|
1156
|
+
...snapshot?.token && {
|
|
1157
|
+
policySnapshotToken: snapshot.token
|
|
1158
|
+
},
|
|
1159
|
+
...shouldIncludeIabPayload && iabOptions?.cmpId != null && {
|
|
1160
|
+
cmpId: iabOptions.cmpId
|
|
1161
|
+
}
|
|
826
1162
|
};
|
|
827
1163
|
}
|
|
828
1164
|
const createInitRoute = (options)=>{
|
|
@@ -835,7 +1171,7 @@ const createInitRoute = (options)=>{
|
|
|
835
1171
|
- **Location** – User's location (null if geo-location is disabled)
|
|
836
1172
|
- **Translations** – Consent manager copy (from \`Accept-Language\` header)
|
|
837
1173
|
- **Branding** – Configured branding key
|
|
838
|
-
- **GVL** – Global Vendor List when
|
|
1174
|
+
- **GVL** – Global Vendor List when IAB is active for the request
|
|
839
1175
|
|
|
840
1176
|
Use for geo-targeted consent banners and regional compliance.`,
|
|
841
1177
|
tags: [
|
|
@@ -852,46 +1188,13 @@ Use for geo-targeted consent banners and regional compliance.`,
|
|
|
852
1188
|
}
|
|
853
1189
|
}
|
|
854
1190
|
}), async (c)=>{
|
|
855
|
-
const
|
|
856
|
-
const
|
|
857
|
-
|
|
858
|
-
const jurisdiction = getJurisdiction(location, options);
|
|
859
|
-
const translationsResult = translations_getTranslationsData(acceptLanguage, options.customTranslations);
|
|
860
|
-
let gvl = null;
|
|
861
|
-
if (options.iab?.enabled) {
|
|
862
|
-
const language = translationsResult.language.split('-')[0] || 'en';
|
|
863
|
-
const gvlResolver = createGVLResolver({
|
|
864
|
-
appName: options.appName || 'c15t',
|
|
865
|
-
bundled: options.iab.bundled,
|
|
866
|
-
cacheAdapter: options.cache?.adapter,
|
|
867
|
-
vendorIds: options.iab.vendorIds,
|
|
868
|
-
endpoint: options.iab.endpoint
|
|
869
|
-
});
|
|
870
|
-
gvl = await gvlResolver.get(language);
|
|
871
|
-
}
|
|
872
|
-
const customVendors = options.iab?.customVendors;
|
|
873
|
-
const gpc = '1' === request.headers.get('sec-gpc');
|
|
874
|
-
getMetrics()?.recordInit({
|
|
875
|
-
jurisdiction,
|
|
876
|
-
country: location?.countryCode ?? void 0,
|
|
877
|
-
region: location?.regionCode ?? void 0,
|
|
878
|
-
gpc
|
|
879
|
-
});
|
|
880
|
-
return c.json({
|
|
881
|
-
jurisdiction,
|
|
882
|
-
location,
|
|
883
|
-
translations: translationsResult,
|
|
884
|
-
branding: options.branding || 'c15t',
|
|
885
|
-
gvl,
|
|
886
|
-
customVendors,
|
|
887
|
-
...options.iab?.cmpId != null && {
|
|
888
|
-
cmpId: options.iab.cmpId
|
|
889
|
-
}
|
|
890
|
-
});
|
|
1191
|
+
const ctx = c.get('c15tContext');
|
|
1192
|
+
const payload = await resolveInitPayload(c.req.raw, options, ctx?.logger);
|
|
1193
|
+
return c.json(payload);
|
|
891
1194
|
});
|
|
892
1195
|
return app;
|
|
893
1196
|
};
|
|
894
|
-
const version_version = '2.0.0-rc.
|
|
1197
|
+
const version_version = '2.0.0-rc.6';
|
|
895
1198
|
function getHeaders(headers) {
|
|
896
1199
|
if (!headers) return {
|
|
897
1200
|
countryCode: null,
|
|
@@ -978,6 +1281,12 @@ const getSubjectHandler = async (c)=>{
|
|
|
978
1281
|
const subjectId = c.req.param('id');
|
|
979
1282
|
const type = c.req.query('type');
|
|
980
1283
|
const typeFilter = type?.split(',').map((t)=>t.trim()) || [];
|
|
1284
|
+
if (!subjectId) throw new http_exception_namespaceObject.HTTPException(400, {
|
|
1285
|
+
message: 'Subject ID is required',
|
|
1286
|
+
cause: {
|
|
1287
|
+
code: 'SUBJECT_ID_REQUIRED'
|
|
1288
|
+
}
|
|
1289
|
+
});
|
|
981
1290
|
logger.debug('Request parameters', {
|
|
982
1291
|
subjectId,
|
|
983
1292
|
typeFilter
|
|
@@ -1171,6 +1480,12 @@ const patchSubjectHandler = async (c)=>{
|
|
|
1171
1480
|
const subjectId = c.req.param('id');
|
|
1172
1481
|
const body = await c.req.json();
|
|
1173
1482
|
const { externalId, identityProvider = 'external' } = body;
|
|
1483
|
+
if (!subjectId) throw new http_exception_namespaceObject.HTTPException(400, {
|
|
1484
|
+
message: 'Subject ID is required',
|
|
1485
|
+
cause: {
|
|
1486
|
+
code: 'SUBJECT_ID_REQUIRED'
|
|
1487
|
+
}
|
|
1488
|
+
});
|
|
1174
1489
|
logger.debug('Request parameters', {
|
|
1175
1490
|
subjectId,
|
|
1176
1491
|
externalId,
|
|
@@ -1247,6 +1562,119 @@ const patchSubjectHandler = async (c)=>{
|
|
|
1247
1562
|
});
|
|
1248
1563
|
}
|
|
1249
1564
|
};
|
|
1565
|
+
function buildRuntimeDecisionDedupeKey(input) {
|
|
1566
|
+
return [
|
|
1567
|
+
input.tenantId ?? 'default',
|
|
1568
|
+
input.fingerprint,
|
|
1569
|
+
input.matchedBy,
|
|
1570
|
+
input.countryCode ?? 'none',
|
|
1571
|
+
input.regionCode ?? 'none',
|
|
1572
|
+
input.jurisdiction,
|
|
1573
|
+
input.language ?? 'none'
|
|
1574
|
+
].join('|');
|
|
1575
|
+
}
|
|
1576
|
+
function buildDecisionPayload(params) {
|
|
1577
|
+
const { tenantId, snapshot, decision, location, jurisdiction, language, proofConfig } = params;
|
|
1578
|
+
if (snapshot?.valid && snapshot.payload) {
|
|
1579
|
+
const sp = snapshot.payload;
|
|
1580
|
+
return {
|
|
1581
|
+
tenantId,
|
|
1582
|
+
policyId: sp.policyId,
|
|
1583
|
+
fingerprint: sp.fingerprint,
|
|
1584
|
+
matchedBy: sp.matchedBy,
|
|
1585
|
+
countryCode: sp.country,
|
|
1586
|
+
regionCode: sp.region,
|
|
1587
|
+
jurisdiction: sp.jurisdiction,
|
|
1588
|
+
language: sp.language,
|
|
1589
|
+
model: sp.model,
|
|
1590
|
+
policyI18n: sp.policyI18n,
|
|
1591
|
+
uiMode: sp.uiMode,
|
|
1592
|
+
bannerUi: sp.bannerUi,
|
|
1593
|
+
dialogUi: sp.dialogUi,
|
|
1594
|
+
categories: sp.categories,
|
|
1595
|
+
preselectedCategories: sp.preselectedCategories,
|
|
1596
|
+
proofConfig: sp.proofConfig,
|
|
1597
|
+
dedupeKey: buildRuntimeDecisionDedupeKey({
|
|
1598
|
+
tenantId,
|
|
1599
|
+
fingerprint: sp.fingerprint,
|
|
1600
|
+
matchedBy: sp.matchedBy,
|
|
1601
|
+
countryCode: sp.country,
|
|
1602
|
+
regionCode: sp.region,
|
|
1603
|
+
jurisdiction: sp.jurisdiction,
|
|
1604
|
+
language: sp.language
|
|
1605
|
+
}),
|
|
1606
|
+
source: 'snapshot_token'
|
|
1607
|
+
};
|
|
1608
|
+
}
|
|
1609
|
+
if (decision) return {
|
|
1610
|
+
tenantId,
|
|
1611
|
+
policyId: decision.policy.id,
|
|
1612
|
+
fingerprint: decision.fingerprint,
|
|
1613
|
+
matchedBy: decision.matchedBy,
|
|
1614
|
+
countryCode: location.countryCode,
|
|
1615
|
+
regionCode: location.regionCode,
|
|
1616
|
+
jurisdiction,
|
|
1617
|
+
language,
|
|
1618
|
+
model: decision.policy.model,
|
|
1619
|
+
policyI18n: decision.policy.i18n,
|
|
1620
|
+
uiMode: decision.policy.ui?.mode,
|
|
1621
|
+
bannerUi: decision.policy.ui?.banner,
|
|
1622
|
+
dialogUi: decision.policy.ui?.dialog,
|
|
1623
|
+
categories: decision.policy.consent?.categories,
|
|
1624
|
+
preselectedCategories: decision.policy.consent?.preselectedCategories,
|
|
1625
|
+
proofConfig,
|
|
1626
|
+
dedupeKey: buildRuntimeDecisionDedupeKey({
|
|
1627
|
+
tenantId,
|
|
1628
|
+
fingerprint: decision.fingerprint,
|
|
1629
|
+
matchedBy: decision.matchedBy,
|
|
1630
|
+
countryCode: location.countryCode,
|
|
1631
|
+
regionCode: location.regionCode,
|
|
1632
|
+
jurisdiction,
|
|
1633
|
+
language
|
|
1634
|
+
}),
|
|
1635
|
+
source: 'write_time_fallback'
|
|
1636
|
+
};
|
|
1637
|
+
}
|
|
1638
|
+
function parseLanguageFromHeader(header) {
|
|
1639
|
+
if (!header) return;
|
|
1640
|
+
const firstLanguage = header.split(',')[0]?.split(';')[0]?.trim();
|
|
1641
|
+
if (!firstLanguage) return;
|
|
1642
|
+
return firstLanguage.split('-')[0]?.toLowerCase();
|
|
1643
|
+
}
|
|
1644
|
+
function resolveSnapshotFailureMode(ctx) {
|
|
1645
|
+
return ctx.policySnapshot?.onValidationFailure ?? 'reject';
|
|
1646
|
+
}
|
|
1647
|
+
function buildSnapshotHttpException(reason) {
|
|
1648
|
+
switch(reason){
|
|
1649
|
+
case 'missing':
|
|
1650
|
+
return new http_exception_namespaceObject.HTTPException(409, {
|
|
1651
|
+
message: 'Policy snapshot token is required',
|
|
1652
|
+
cause: {
|
|
1653
|
+
code: 'POLICY_SNAPSHOT_REQUIRED'
|
|
1654
|
+
}
|
|
1655
|
+
});
|
|
1656
|
+
case 'expired':
|
|
1657
|
+
return new http_exception_namespaceObject.HTTPException(409, {
|
|
1658
|
+
message: 'Policy snapshot token has expired',
|
|
1659
|
+
cause: {
|
|
1660
|
+
code: 'POLICY_SNAPSHOT_EXPIRED'
|
|
1661
|
+
}
|
|
1662
|
+
});
|
|
1663
|
+
case 'malformed':
|
|
1664
|
+
case 'invalid':
|
|
1665
|
+
return new http_exception_namespaceObject.HTTPException(409, {
|
|
1666
|
+
message: 'Policy snapshot token is invalid',
|
|
1667
|
+
cause: {
|
|
1668
|
+
code: 'POLICY_SNAPSHOT_INVALID'
|
|
1669
|
+
}
|
|
1670
|
+
});
|
|
1671
|
+
default:
|
|
1672
|
+
{
|
|
1673
|
+
const _exhaustive = reason;
|
|
1674
|
+
throw new Error(`Unhandled policy snapshot verification failure reason: ${_exhaustive}`);
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1250
1678
|
const postSubjectHandler = async (c)=>{
|
|
1251
1679
|
const ctx = c.get('c15tContext');
|
|
1252
1680
|
const logger = ctx.logger;
|
|
@@ -1258,9 +1686,6 @@ const postSubjectHandler = async (c)=>{
|
|
|
1258
1686
|
const givenAt = new Date(givenAtEpoch);
|
|
1259
1687
|
const rawConsentAction = 'consentAction' in input ? input.consentAction : void 0;
|
|
1260
1688
|
let derivedConsentAction;
|
|
1261
|
-
if ('all' === rawConsentAction) derivedConsentAction = 'accept_all';
|
|
1262
|
-
else if ('necessary' === rawConsentAction) derivedConsentAction = 'opt-out' === input.jurisdictionModel ? 'opt_out' : 'reject_all';
|
|
1263
|
-
else if ('custom' === rawConsentAction) derivedConsentAction = 'custom';
|
|
1264
1689
|
logger.debug('Request parameters', {
|
|
1265
1690
|
type,
|
|
1266
1691
|
subjectId,
|
|
@@ -1269,6 +1694,50 @@ const postSubjectHandler = async (c)=>{
|
|
|
1269
1694
|
domain
|
|
1270
1695
|
});
|
|
1271
1696
|
try {
|
|
1697
|
+
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.');
|
|
1698
|
+
const request = c.req.raw ?? new Request('https://c15t.local/subjects');
|
|
1699
|
+
const acceptLanguage = request.headers.get('accept-language');
|
|
1700
|
+
const requestLanguage = parseLanguageFromHeader(acceptLanguage);
|
|
1701
|
+
const location = await getLocation(request, ctx);
|
|
1702
|
+
const resolvedJurisdiction = getJurisdiction(location, ctx);
|
|
1703
|
+
const snapshotVerification = await verifyPolicySnapshotToken({
|
|
1704
|
+
token: input.policySnapshotToken,
|
|
1705
|
+
options: ctx.policySnapshot,
|
|
1706
|
+
tenantId: ctx.tenantId
|
|
1707
|
+
});
|
|
1708
|
+
const hasValidSnapshot = snapshotVerification.valid;
|
|
1709
|
+
const snapshotPayload = snapshotVerification.valid ? snapshotVerification.payload : null;
|
|
1710
|
+
const shouldRequireSnapshot = !!ctx.policySnapshot?.signingKey && 'reject' === resolveSnapshotFailureMode(ctx);
|
|
1711
|
+
if (!hasValidSnapshot && shouldRequireSnapshot) throw buildSnapshotHttpException(snapshotVerification.reason);
|
|
1712
|
+
const resolvedPolicyDecision = hasValidSnapshot ? void 0 : await resolvePolicyDecision({
|
|
1713
|
+
policies: ctx.policyPacks,
|
|
1714
|
+
countryCode: location.countryCode,
|
|
1715
|
+
regionCode: location.regionCode,
|
|
1716
|
+
jurisdiction: resolvedJurisdiction,
|
|
1717
|
+
iabEnabled: ctx.iab?.enabled === true
|
|
1718
|
+
});
|
|
1719
|
+
const effectivePolicy = hasValidSnapshot && snapshotPayload ? {
|
|
1720
|
+
id: snapshotPayload.policyId,
|
|
1721
|
+
model: snapshotPayload.model,
|
|
1722
|
+
i18n: snapshotPayload.policyI18n,
|
|
1723
|
+
consent: {
|
|
1724
|
+
expiryDays: snapshotPayload.expiryDays,
|
|
1725
|
+
scopeMode: snapshotPayload.scopeMode,
|
|
1726
|
+
categories: snapshotPayload.categories,
|
|
1727
|
+
preselectedCategories: snapshotPayload.preselectedCategories,
|
|
1728
|
+
gpc: snapshotPayload.gpc
|
|
1729
|
+
},
|
|
1730
|
+
ui: {
|
|
1731
|
+
mode: snapshotPayload.uiMode,
|
|
1732
|
+
banner: snapshotPayload.bannerUi,
|
|
1733
|
+
dialog: snapshotPayload.dialogUi
|
|
1734
|
+
},
|
|
1735
|
+
proof: snapshotPayload.proofConfig
|
|
1736
|
+
} : resolvedPolicyDecision?.policy;
|
|
1737
|
+
const effectiveModel = effectivePolicy?.model ?? ('opt-in' === input.jurisdictionModel || 'opt-out' === input.jurisdictionModel || 'iab' === input.jurisdictionModel ? input.jurisdictionModel : void 0);
|
|
1738
|
+
if ('all' === rawConsentAction) derivedConsentAction = 'accept_all';
|
|
1739
|
+
else if ('necessary' === rawConsentAction) derivedConsentAction = 'opt-out' === effectiveModel ? 'opt_out' : 'reject_all';
|
|
1740
|
+
else if ('custom' === rawConsentAction) derivedConsentAction = 'custom';
|
|
1272
1741
|
const subject = await registry.findOrCreateSubject({
|
|
1273
1742
|
subjectId,
|
|
1274
1743
|
externalSubjectId,
|
|
@@ -1295,6 +1764,7 @@ const postSubjectHandler = async (c)=>{
|
|
|
1295
1764
|
});
|
|
1296
1765
|
let policyId;
|
|
1297
1766
|
let purposeIds = [];
|
|
1767
|
+
let appliedPreferences;
|
|
1298
1768
|
const inputPolicyId = 'policyId' in input ? input.policyId : void 0;
|
|
1299
1769
|
if (inputPolicyId) {
|
|
1300
1770
|
policyId = inputPolicyId;
|
|
@@ -1327,20 +1797,66 @@ const postSubjectHandler = async (c)=>{
|
|
|
1327
1797
|
policyId = policy.id;
|
|
1328
1798
|
}
|
|
1329
1799
|
if (preferences) {
|
|
1330
|
-
const
|
|
1800
|
+
const allowedCategories = effectivePolicy?.consent?.categories;
|
|
1801
|
+
const effectiveScopeMode = effectivePolicy?.consent?.scopeMode ?? 'permissive';
|
|
1802
|
+
const hasWildcardCategoryScope = allowedCategories?.includes('*') === true;
|
|
1803
|
+
const appliedPreferenceEntries = Object.entries(preferences);
|
|
1804
|
+
let filteredAppliedPreferenceEntries = appliedPreferenceEntries;
|
|
1805
|
+
if (allowedCategories && allowedCategories.length > 0 && !hasWildcardCategoryScope) {
|
|
1806
|
+
const disallowed = appliedPreferenceEntries.map(([purpose])=>purpose).filter((purpose)=>!allowedCategories.includes(purpose));
|
|
1807
|
+
filteredAppliedPreferenceEntries = appliedPreferenceEntries.filter(([purpose])=>allowedCategories.includes(purpose));
|
|
1808
|
+
if (disallowed.length > 0 && 'strict' === effectiveScopeMode) throw new http_exception_namespaceObject.HTTPException(400, {
|
|
1809
|
+
message: 'Preferences include categories not allowed by policy',
|
|
1810
|
+
cause: {
|
|
1811
|
+
code: 'PURPOSE_NOT_ALLOWED',
|
|
1812
|
+
disallowed
|
|
1813
|
+
}
|
|
1814
|
+
});
|
|
1815
|
+
}
|
|
1816
|
+
appliedPreferences = Object.fromEntries(filteredAppliedPreferenceEntries);
|
|
1817
|
+
const filteredConsentedPurposeCodes = filteredAppliedPreferenceEntries.filter(([_, isConsented])=>isConsented).map(([purposeCode])=>purposeCode);
|
|
1331
1818
|
logger.debug('Consented purposes', {
|
|
1332
|
-
consentedPurposes
|
|
1819
|
+
consentedPurposes: filteredConsentedPurposeCodes
|
|
1333
1820
|
});
|
|
1334
|
-
const purposesRaw = await Promise.all(
|
|
1821
|
+
const purposesRaw = await Promise.all(filteredConsentedPurposeCodes.map((purposeCode)=>registry.findOrCreateConsentPurposeByCode(purposeCode)));
|
|
1335
1822
|
const purposes = purposesRaw.map((purpose)=>purpose?.id ?? null).filter((id)=>Boolean(id));
|
|
1336
1823
|
logger.debug('Filtered purposes', {
|
|
1337
1824
|
purposes
|
|
1338
1825
|
});
|
|
1339
1826
|
if (0 === purposes.length) logger.warn('No valid purpose IDs found after filtering. Using empty list.', {
|
|
1340
|
-
consentedPurposes
|
|
1827
|
+
consentedPurposes: filteredConsentedPurposeCodes
|
|
1341
1828
|
});
|
|
1342
1829
|
purposeIds = purposes;
|
|
1343
1830
|
}
|
|
1831
|
+
const expiryDays = effectivePolicy?.consent?.expiryDays;
|
|
1832
|
+
const validUntil = 'number' == typeof expiryDays && Number.isFinite(expiryDays) ? new Date(givenAt.getTime() + 86400000 * Math.max(0, expiryDays)) : void 0;
|
|
1833
|
+
const proofConfig = effectivePolicy?.proof;
|
|
1834
|
+
const shouldStoreIp = proofConfig?.storeIp ?? true;
|
|
1835
|
+
const shouldStoreUserAgent = proofConfig?.storeUserAgent ?? true;
|
|
1836
|
+
const shouldStoreLanguage = proofConfig?.storeLanguage ?? false;
|
|
1837
|
+
const effectiveLanguage = (snapshotPayload?.language && hasValidSnapshot ? snapshotPayload.language : requestLanguage) ?? void 0;
|
|
1838
|
+
const metadataWithPolicy = {
|
|
1839
|
+
...metadata ?? {},
|
|
1840
|
+
...shouldStoreLanguage && effectiveLanguage ? {
|
|
1841
|
+
policyLanguage: effectiveLanguage
|
|
1842
|
+
} : {}
|
|
1843
|
+
};
|
|
1844
|
+
const effectiveJurisdiction = hasValidSnapshot && snapshotPayload ? snapshotPayload.jurisdiction : resolvedJurisdiction;
|
|
1845
|
+
const decisionPayload = buildDecisionPayload({
|
|
1846
|
+
tenantId: ctx.tenantId,
|
|
1847
|
+
snapshot: hasValidSnapshot && snapshotPayload ? {
|
|
1848
|
+
valid: true,
|
|
1849
|
+
payload: snapshotPayload
|
|
1850
|
+
} : null,
|
|
1851
|
+
decision: resolvedPolicyDecision,
|
|
1852
|
+
location: {
|
|
1853
|
+
countryCode: location.countryCode,
|
|
1854
|
+
regionCode: location.regionCode
|
|
1855
|
+
},
|
|
1856
|
+
jurisdiction: resolvedJurisdiction,
|
|
1857
|
+
language: effectiveLanguage,
|
|
1858
|
+
proofConfig
|
|
1859
|
+
});
|
|
1344
1860
|
const existingConsent = await db.findFirst('consent', {
|
|
1345
1861
|
where: (b)=>b.and(b('subjectId', '=', subject.id), b('domainId', '=', domainRecord.id), b('policyId', '=', policyId), b('givenAt', '=', givenAt))
|
|
1346
1862
|
});
|
|
@@ -1355,6 +1871,7 @@ const postSubjectHandler = async (c)=>{
|
|
|
1355
1871
|
domain: domainRecord.name,
|
|
1356
1872
|
type,
|
|
1357
1873
|
metadata,
|
|
1874
|
+
appliedPreferences,
|
|
1358
1875
|
uiSource: input.uiSource,
|
|
1359
1876
|
givenAt: existingConsent.givenAt
|
|
1360
1877
|
});
|
|
@@ -1366,6 +1883,42 @@ const postSubjectHandler = async (c)=>{
|
|
|
1366
1883
|
policyId,
|
|
1367
1884
|
purposeIds
|
|
1368
1885
|
});
|
|
1886
|
+
const runtimePolicyDecision = decisionPayload ? await tx.findFirst('runtimePolicyDecision', {
|
|
1887
|
+
where: (b)=>b('dedupeKey', '=', decisionPayload.dedupeKey)
|
|
1888
|
+
}) ?? await tx.create('runtimePolicyDecision', {
|
|
1889
|
+
id: `rpd_${crypto.randomUUID().replaceAll('-', '')}`,
|
|
1890
|
+
tenantId: decisionPayload.tenantId,
|
|
1891
|
+
policyId: decisionPayload.policyId,
|
|
1892
|
+
fingerprint: decisionPayload.fingerprint,
|
|
1893
|
+
matchedBy: decisionPayload.matchedBy,
|
|
1894
|
+
countryCode: decisionPayload.countryCode,
|
|
1895
|
+
regionCode: decisionPayload.regionCode,
|
|
1896
|
+
jurisdiction: decisionPayload.jurisdiction,
|
|
1897
|
+
language: decisionPayload.language,
|
|
1898
|
+
model: decisionPayload.model,
|
|
1899
|
+
policyI18n: decisionPayload.policyI18n ? {
|
|
1900
|
+
json: decisionPayload.policyI18n
|
|
1901
|
+
} : void 0,
|
|
1902
|
+
uiMode: decisionPayload.uiMode,
|
|
1903
|
+
bannerUi: decisionPayload.bannerUi ? {
|
|
1904
|
+
json: decisionPayload.bannerUi
|
|
1905
|
+
} : void 0,
|
|
1906
|
+
dialogUi: decisionPayload.dialogUi ? {
|
|
1907
|
+
json: decisionPayload.dialogUi
|
|
1908
|
+
} : void 0,
|
|
1909
|
+
categories: decisionPayload.categories ? {
|
|
1910
|
+
json: decisionPayload.categories
|
|
1911
|
+
} : void 0,
|
|
1912
|
+
preselectedCategories: decisionPayload.preselectedCategories ? {
|
|
1913
|
+
json: decisionPayload.preselectedCategories
|
|
1914
|
+
} : void 0,
|
|
1915
|
+
proofConfig: decisionPayload.proofConfig ? {
|
|
1916
|
+
json: decisionPayload.proofConfig
|
|
1917
|
+
} : void 0,
|
|
1918
|
+
dedupeKey: decisionPayload.dedupeKey
|
|
1919
|
+
}).catch(async ()=>tx.findFirst('runtimePolicyDecision', {
|
|
1920
|
+
where: (b)=>b('dedupeKey', '=', decisionPayload.dedupeKey)
|
|
1921
|
+
})) : void 0;
|
|
1369
1922
|
const consentRecord = await tx.create('consent', {
|
|
1370
1923
|
id: await generateUniqueId(tx, 'consent', ctx),
|
|
1371
1924
|
subjectId: subject.id,
|
|
@@ -1374,17 +1927,20 @@ const postSubjectHandler = async (c)=>{
|
|
|
1374
1927
|
purposeIds: {
|
|
1375
1928
|
json: purposeIds
|
|
1376
1929
|
},
|
|
1377
|
-
metadata:
|
|
1378
|
-
json:
|
|
1930
|
+
metadata: Object.keys(metadataWithPolicy).length > 0 ? {
|
|
1931
|
+
json: metadataWithPolicy
|
|
1379
1932
|
} : void 0,
|
|
1380
|
-
ipAddress: ctx.ipAddress,
|
|
1381
|
-
userAgent: ctx.userAgent,
|
|
1382
|
-
jurisdiction:
|
|
1383
|
-
jurisdictionModel:
|
|
1933
|
+
ipAddress: shouldStoreIp ? ctx.ipAddress : null,
|
|
1934
|
+
userAgent: shouldStoreUserAgent ? ctx.userAgent : null,
|
|
1935
|
+
jurisdiction: effectiveJurisdiction,
|
|
1936
|
+
jurisdictionModel: effectiveModel,
|
|
1384
1937
|
tcString: input.tcString,
|
|
1385
1938
|
uiSource: input.uiSource,
|
|
1386
1939
|
consentAction: derivedConsentAction,
|
|
1387
|
-
givenAt
|
|
1940
|
+
givenAt,
|
|
1941
|
+
validUntil,
|
|
1942
|
+
runtimePolicyDecisionId: runtimePolicyDecision?.id,
|
|
1943
|
+
runtimePolicySource: decisionPayload?.source
|
|
1388
1944
|
});
|
|
1389
1945
|
logger.debug('Created consent', {
|
|
1390
1946
|
consentRecord: consentRecord.id
|
|
@@ -1403,7 +1959,7 @@ const postSubjectHandler = async (c)=>{
|
|
|
1403
1959
|
});
|
|
1404
1960
|
const metrics = getMetrics();
|
|
1405
1961
|
if (metrics) {
|
|
1406
|
-
const jurisdiction =
|
|
1962
|
+
const jurisdiction = effectiveJurisdiction;
|
|
1407
1963
|
metrics.recordConsentCreated({
|
|
1408
1964
|
type,
|
|
1409
1965
|
jurisdiction
|
|
@@ -1425,6 +1981,7 @@ const postSubjectHandler = async (c)=>{
|
|
|
1425
1981
|
domain: domainRecord.name,
|
|
1426
1982
|
type,
|
|
1427
1983
|
metadata,
|
|
1984
|
+
appliedPreferences,
|
|
1428
1985
|
uiSource: input.uiSource,
|
|
1429
1986
|
givenAt: result.consent.givenAt
|
|
1430
1987
|
});
|
|
@@ -1446,11 +2003,7 @@ const createSubjectRoutes = ()=>{
|
|
|
1446
2003
|
const app = new external_hono_namespaceObject.Hono();
|
|
1447
2004
|
app.get('/:id', (0, external_hono_openapi_namespaceObject.describeRoute)({
|
|
1448
2005
|
summary: 'Get subject consent status',
|
|
1449
|
-
description:
|
|
1450
|
-
|
|
1451
|
-
**Query:** \`type\` – Filter by consent type(s), comma-separated (e.g. \`privacy_policy,cookie_banner\`).
|
|
1452
|
-
|
|
1453
|
-
**Response:** \`subject\`, \`consents\` (matching filter), \`isValid\` (valid consent for requested type(s)).`,
|
|
2006
|
+
description: "Returns the subject's consent status for this device. Use to check if the subject has valid consent for given policy types.\n\n**Query:** `type` – Filter by consent type(s), comma-separated (e.g. `privacy_policy,cookie_banner`).\n\n**Response:** `subject`, `consents` (matching filter), `isValid` (valid consent for requested type(s)).",
|
|
1454
2007
|
tags: [
|
|
1455
2008
|
'Subject',
|
|
1456
2009
|
'Consent'
|
|
@@ -1471,12 +2024,7 @@ const createSubjectRoutes = ()=>{
|
|
|
1471
2024
|
}), (0, external_hono_openapi_namespaceObject.validator)('param', schema_namespaceObject.getSubjectInputSchema), getSubjectHandler);
|
|
1472
2025
|
app.post('/', (0, external_hono_openapi_namespaceObject.describeRoute)({
|
|
1473
2026
|
summary: 'Record consent for a subject',
|
|
1474
|
-
description:
|
|
1475
|
-
|
|
1476
|
-
**Request body by \`type\`:**
|
|
1477
|
-
- \`cookie_banner\` – Requires \`preferences\` object
|
|
1478
|
-
- \`privacy_policy\`, \`dpa\`, \`terms_and_conditions\` – Optional \`policyId\`
|
|
1479
|
-
- \`marketing_communications\`, \`age_verification\`, \`other\` – Optional \`preferences\``,
|
|
2027
|
+
description: "Creates a new consent record (append-only). Creates the subject if it does not exist.\n\n**Request body by `type`:**\n- `cookie_banner` – Requires `preferences` object\n- `privacy_policy`, `dpa`, `terms_and_conditions` – Optional `policyId`\n- `marketing_communications`, `age_verification`, `other` – Optional `preferences`",
|
|
1480
2028
|
tags: [
|
|
1481
2029
|
'Subject',
|
|
1482
2030
|
'Consent'
|