@c15t/backend 1.5.0 → 1.6.0
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/.turbo/turbo-build.log +63 -37
- package/CHANGELOG.md +4 -209
- package/README.md +86 -164
- package/dist/contracts/consent/index.d.ts +103 -615
- package/dist/contracts/consent/index.d.ts.map +1 -1
- package/dist/contracts/consent/post.contract.d.ts +42 -140
- package/dist/contracts/consent/post.contract.d.ts.map +1 -1
- package/dist/contracts/consent/show-banner.contract.d.ts +28 -376
- package/dist/contracts/consent/show-banner.contract.d.ts.map +1 -1
- package/dist/contracts/consent/verify.contract.d.ts +33 -99
- package/dist/contracts/consent/verify.contract.d.ts.map +1 -1
- package/dist/contracts/index.d.ts +222 -1356
- package/dist/contracts/index.d.ts.map +1 -1
- package/dist/contracts/meta/index.d.ts +8 -63
- package/dist/contracts/meta/index.d.ts.map +1 -1
- package/dist/contracts/meta/status.contract.d.ts +8 -63
- package/dist/contracts/meta/status.contract.d.ts.map +1 -1
- package/dist/contracts/shared/jurisdiction.schema.d.ts +21 -9
- package/dist/contracts/shared/jurisdiction.schema.d.ts.map +1 -1
- package/dist/contracts.cjs +100 -106
- package/dist/contracts.js +100 -106
- package/dist/core.cjs +681 -681
- package/dist/core.d.ts +118 -678
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js +634 -637
- package/dist/handlers/consent/index.d.ts +103 -615
- package/dist/handlers/consent/index.d.ts.map +1 -1
- package/dist/handlers/consent/post.handler.d.ts +42 -140
- package/dist/handlers/consent/post.handler.d.ts.map +1 -1
- package/dist/handlers/consent/show-banner/handler.d.ts +28 -376
- package/dist/handlers/consent/show-banner/handler.d.ts.map +1 -1
- package/dist/handlers/consent/show-banner/translations.d.ts.map +1 -1
- package/dist/handlers/consent/verify.handler.d.ts +33 -99
- package/dist/handlers/consent/verify.handler.d.ts.map +1 -1
- package/dist/handlers/meta/index.d.ts +8 -63
- package/dist/handlers/meta/index.d.ts.map +1 -1
- package/dist/handlers/meta/status.handler.d.ts +8 -63
- package/dist/handlers/meta/status.handler.d.ts.map +1 -1
- package/dist/init.d.ts.map +1 -1
- package/dist/middleware/openapi/index.d.ts +2 -2
- package/dist/middleware/openapi/index.d.ts.map +1 -1
- package/dist/pkgs/data-model/fields/index.cjs +14 -26
- package/dist/pkgs/data-model/fields/index.d.ts +4 -4
- package/dist/pkgs/data-model/fields/index.d.ts.map +1 -1
- package/dist/pkgs/data-model/fields/index.js +14 -26
- package/dist/pkgs/data-model/fields/zod-fields.d.ts +195 -871
- package/dist/pkgs/data-model/fields/zod-fields.d.ts.map +1 -1
- package/dist/pkgs/data-model/hooks/index.d.ts +2 -2
- package/dist/pkgs/data-model/hooks/index.d.ts.map +1 -1
- package/dist/pkgs/data-model/index.cjs +346 -358
- package/dist/pkgs/data-model/index.d.ts +1 -1
- package/dist/pkgs/data-model/index.d.ts.map +1 -1
- package/dist/pkgs/data-model/index.js +345 -357
- package/dist/pkgs/data-model/schema/index.cjs +346 -358
- package/dist/pkgs/data-model/schema/index.d.ts +1 -1
- package/dist/pkgs/data-model/schema/index.d.ts.map +1 -1
- package/dist/pkgs/data-model/schema/index.js +345 -357
- package/dist/pkgs/data-model/schema/schemas.d.ts +2 -2
- package/dist/pkgs/data-model/schema/schemas.d.ts.map +1 -1
- package/dist/pkgs/db-adapters/adapters/drizzle-adapter/drizzle-adapter.d.ts +3 -0
- package/dist/pkgs/db-adapters/adapters/drizzle-adapter/drizzle-adapter.d.ts.map +1 -1
- package/dist/pkgs/db-adapters/adapters/drizzle-adapter/index.cjs +158 -170
- package/dist/pkgs/db-adapters/adapters/drizzle-adapter/index.js +157 -169
- package/dist/pkgs/db-adapters/adapters/index.d.ts +2 -2
- package/dist/pkgs/db-adapters/adapters/index.d.ts.map +1 -1
- package/dist/pkgs/db-adapters/adapters/kysely-adapter/index.cjs +215 -227
- package/dist/pkgs/db-adapters/adapters/kysely-adapter/index.d.ts +2 -2
- package/dist/pkgs/db-adapters/adapters/kysely-adapter/index.d.ts.map +1 -1
- package/dist/pkgs/db-adapters/adapters/kysely-adapter/index.js +213 -225
- package/dist/pkgs/db-adapters/adapters/kysely-adapter/kysely-adapter.d.ts +2 -0
- package/dist/pkgs/db-adapters/adapters/kysely-adapter/kysely-adapter.d.ts.map +1 -1
- package/dist/pkgs/db-adapters/adapters/kysely-adapter/tests/test-utils.d.ts +1 -1
- package/dist/pkgs/db-adapters/adapters/kysely-adapter/tests/test-utils.d.ts.map +1 -1
- package/dist/pkgs/db-adapters/adapters/memory-adapter/index.cjs +158 -170
- package/dist/pkgs/db-adapters/adapters/memory-adapter/index.js +157 -169
- package/dist/pkgs/db-adapters/adapters/memory-adapter/memory-adapter.d.ts +3 -0
- package/dist/pkgs/db-adapters/adapters/memory-adapter/memory-adapter.d.ts.map +1 -1
- package/dist/pkgs/db-adapters/adapters/prisma-adapter/index.cjs +243 -255
- package/dist/pkgs/db-adapters/adapters/prisma-adapter/index.d.ts +1 -1
- package/dist/pkgs/db-adapters/adapters/prisma-adapter/index.d.ts.map +1 -1
- package/dist/pkgs/db-adapters/adapters/prisma-adapter/index.js +241 -253
- package/dist/pkgs/db-adapters/adapters/prisma-adapter/prisma-adapter.d.ts +3 -0
- package/dist/pkgs/db-adapters/adapters/prisma-adapter/prisma-adapter.d.ts.map +1 -1
- package/dist/pkgs/db-adapters/index.cjs +714 -726
- package/dist/pkgs/db-adapters/index.d.ts +6 -6
- package/dist/pkgs/db-adapters/index.d.ts.map +1 -1
- package/dist/pkgs/db-adapters/index.js +708 -720
- package/dist/pkgs/migrations/get-migration.d.ts.map +1 -1
- package/dist/pkgs/migrations/get-schema/get-schema.d.ts.map +1 -1
- package/dist/pkgs/migrations/get-schema/process-tables.d.ts.map +1 -1
- package/dist/pkgs/migrations/index.cjs +236 -248
- package/dist/pkgs/migrations/index.d.ts +4 -4
- package/dist/pkgs/migrations/index.d.ts.map +1 -1
- package/dist/pkgs/migrations/index.js +235 -247
- package/dist/pkgs/results/index.cjs +67 -67
- package/dist/pkgs/results/index.d.ts +5 -5
- package/dist/pkgs/results/index.d.ts.map +1 -1
- package/dist/pkgs/results/index.js +67 -67
- package/dist/pkgs/results/orpc-error-handler.d.ts +1 -1
- package/dist/pkgs/results/orpc-error-handler.d.ts.map +1 -1
- package/dist/pkgs/types/index.d.ts +1 -2
- package/dist/pkgs/types/index.d.ts.map +1 -1
- package/dist/pkgs/types/options.d.ts +9 -2
- package/dist/pkgs/types/options.d.ts.map +1 -1
- package/dist/pkgs/utils/index.d.ts +1 -1
- package/dist/pkgs/utils/index.d.ts.map +1 -1
- package/dist/pkgs/utils/logger.d.ts +1 -1
- package/dist/pkgs/utils/logger.d.ts.map +1 -1
- package/dist/router.cjs +114 -117
- package/dist/router.d.ts +111 -678
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +114 -117
- package/dist/schema/audit-log/schema.d.ts +2 -24
- package/dist/schema/audit-log/schema.d.ts.map +1 -1
- package/dist/schema/audit-log/table.d.ts +2 -24
- package/dist/schema/audit-log/table.d.ts.map +1 -1
- package/dist/schema/consent/registry.d.ts +8 -8
- package/dist/schema/consent/schema.d.ts +9 -33
- package/dist/schema/consent/schema.d.ts.map +1 -1
- package/dist/schema/consent/table.d.ts +9 -33
- package/dist/schema/consent/table.d.ts.map +1 -1
- package/dist/schema/consent-policy/registry.d.ts +20 -20
- package/dist/schema/consent-policy/schema.d.ts +22 -30
- package/dist/schema/consent-policy/schema.d.ts.map +1 -1
- package/dist/schema/consent-policy/table.d.ts +13 -29
- package/dist/schema/consent-policy/table.d.ts.map +1 -1
- package/dist/schema/consent-purpose/registry.d.ts +6 -6
- package/dist/schema/consent-purpose/schema.d.ts +5 -27
- package/dist/schema/consent-purpose/schema.d.ts.map +1 -1
- package/dist/schema/consent-purpose/table.d.ts +5 -27
- package/dist/schema/consent-purpose/table.d.ts.map +1 -1
- package/dist/schema/consent-record/schema.d.ts +3 -19
- package/dist/schema/consent-record/schema.d.ts.map +1 -1
- package/dist/schema/consent-record/table.d.ts +3 -19
- package/dist/schema/consent-record/table.d.ts.map +1 -1
- package/dist/schema/create-registry.d.ts +58 -58
- package/dist/schema/definition.d.ts +42 -176
- package/dist/schema/definition.d.ts.map +1 -1
- package/dist/schema/domain/registry.d.ts +20 -20
- package/dist/schema/domain/schema.d.ts +6 -24
- package/dist/schema/domain/schema.d.ts.map +1 -1
- package/dist/schema/domain/table.d.ts +6 -24
- package/dist/schema/domain/table.d.ts.map +1 -1
- package/dist/schema/index.cjs +426 -438
- package/dist/schema/index.d.ts +12 -12
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +426 -438
- package/dist/schema/schemas.d.ts +42 -176
- package/dist/schema/schemas.d.ts.map +1 -1
- package/dist/schema/subject/registry.d.ts +4 -4
- package/dist/schema/subject/schema.d.ts +4 -20
- package/dist/schema/subject/schema.d.ts.map +1 -1
- package/dist/schema/subject/table.d.ts +4 -20
- package/dist/schema/subject/table.d.ts.map +1 -1
- package/dist/schema/types.d.ts +1 -1
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/testing/contract-testing.d.ts +3 -2
- package/dist/testing/contract-testing.d.ts.map +1 -1
- package/dist/types/index.d.ts +5 -4
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/options.d.ts +2 -2
- package/dist/types/options.d.ts.map +1 -1
- package/dist/v2/contracts/consent/index.d.ts +260 -0
- package/dist/v2/contracts/consent/index.d.ts.map +1 -0
- package/dist/v2/contracts/consent/index.test.d.ts +2 -0
- package/dist/v2/contracts/consent/index.test.d.ts.map +1 -0
- package/dist/v2/contracts/consent/post.contract.d.ts +114 -0
- package/dist/v2/contracts/consent/post.contract.d.ts.map +1 -0
- package/dist/v2/contracts/consent/post.contract.test.d.ts +2 -0
- package/dist/v2/contracts/consent/post.contract.test.d.ts.map +1 -0
- package/dist/v2/contracts/consent/show-banner.contract.d.ts +68 -0
- package/dist/v2/contracts/consent/show-banner.contract.d.ts.map +1 -0
- package/dist/v2/contracts/consent/show-banner.contract.test.d.ts +2 -0
- package/dist/v2/contracts/consent/show-banner.contract.test.d.ts.map +1 -0
- package/dist/v2/contracts/consent/verify.contract.d.ts +81 -0
- package/dist/v2/contracts/consent/verify.contract.d.ts.map +1 -0
- package/dist/v2/contracts/consent/verify.contract.test.d.ts +2 -0
- package/dist/v2/contracts/consent/verify.contract.test.d.ts.map +1 -0
- package/dist/v2/contracts/index.cjs +644 -0
- package/dist/v2/contracts/index.d.ts +563 -0
- package/dist/v2/contracts/index.d.ts.map +1 -0
- package/dist/v2/contracts/index.js +607 -0
- package/dist/v2/contracts/meta/index.d.ts +19 -0
- package/dist/v2/contracts/meta/index.d.ts.map +1 -0
- package/dist/v2/contracts/meta/index.test.d.ts +2 -0
- package/dist/v2/contracts/meta/index.test.d.ts.map +1 -0
- package/dist/v2/contracts/meta/status.contract.d.ts +18 -0
- package/dist/v2/contracts/meta/status.contract.d.ts.map +1 -0
- package/dist/v2/contracts/meta/status.contract.test.d.ts +2 -0
- package/dist/v2/contracts/meta/status.contract.test.d.ts.map +1 -0
- package/dist/v2/contracts/shared/jurisdiction.schema.d.ts +36 -0
- package/dist/v2/contracts/shared/jurisdiction.schema.d.ts.map +1 -0
- package/dist/v2/contracts/test.utils.d.ts +38 -0
- package/dist/v2/contracts/test.utils.d.ts.map +1 -0
- package/dist/v2/core.cjs +2181 -0
- package/dist/v2/core.d.ts +364 -0
- package/dist/v2/core.d.ts.map +1 -0
- package/dist/v2/core.js +2130 -0
- package/dist/v2/db/adapters/drizzle.cjs +36 -0
- package/dist/v2/db/adapters/drizzle.d.ts +2 -0
- package/dist/v2/db/adapters/drizzle.d.ts.map +1 -0
- package/dist/v2/db/adapters/drizzle.js +3 -0
- package/dist/v2/db/adapters/index.cjs +18 -0
- package/dist/v2/db/adapters/index.d.ts +2 -0
- package/dist/v2/db/adapters/index.d.ts.map +1 -0
- package/dist/v2/db/adapters/index.js +0 -0
- package/dist/v2/db/adapters/kysely.cjs +36 -0
- package/dist/v2/db/adapters/kysely.d.ts +2 -0
- package/dist/v2/db/adapters/kysely.d.ts.map +1 -0
- package/dist/v2/db/adapters/kysely.js +3 -0
- package/dist/v2/db/adapters/mongo.cjs +36 -0
- package/dist/v2/db/adapters/mongo.d.ts +2 -0
- package/dist/v2/db/adapters/mongo.d.ts.map +1 -0
- package/dist/v2/db/adapters/mongo.js +3 -0
- package/dist/v2/db/adapters/prisma.cjs +36 -0
- package/dist/v2/db/adapters/prisma.d.ts +2 -0
- package/dist/v2/db/adapters/prisma.d.ts.map +1 -0
- package/dist/v2/db/adapters/prisma.js +3 -0
- package/dist/v2/db/adapters/typeorm.cjs +36 -0
- package/dist/v2/db/adapters/typeorm.d.ts +2 -0
- package/dist/v2/db/adapters/typeorm.d.ts.map +1 -0
- package/dist/v2/db/adapters/typeorm.js +3 -0
- package/dist/v2/db/migrator/index.cjs +61 -0
- package/dist/v2/db/migrator/index.d.ts +29 -0
- package/dist/v2/db/migrator/index.d.ts.map +1 -0
- package/dist/v2/db/migrator/index.js +27 -0
- package/dist/v2/db/registry/audit-log.d.ts +21 -0
- package/dist/v2/db/registry/audit-log.d.ts.map +1 -0
- package/dist/v2/db/registry/audit-log.test.d.ts +2 -0
- package/dist/v2/db/registry/audit-log.test.d.ts.map +1 -0
- package/dist/v2/db/registry/consent-policy.d.ts +29 -0
- package/dist/v2/db/registry/consent-policy.d.ts.map +1 -0
- package/dist/v2/db/registry/consent-policy.test.d.ts +2 -0
- package/dist/v2/db/registry/consent-policy.test.d.ts.map +1 -0
- package/dist/v2/db/registry/consent-purpose.d.ts +16 -0
- package/dist/v2/db/registry/consent-purpose.d.ts.map +1 -0
- package/dist/v2/db/registry/consent-purpose.test.d.ts +2 -0
- package/dist/v2/db/registry/consent-purpose.test.d.ts.map +1 -0
- package/dist/v2/db/registry/consent.d.ts +20 -0
- package/dist/v2/db/registry/consent.d.ts.map +1 -0
- package/dist/v2/db/registry/consent.test.d.ts +2 -0
- package/dist/v2/db/registry/consent.test.d.ts.map +1 -0
- package/dist/v2/db/registry/domain.d.ts +24 -0
- package/dist/v2/db/registry/domain.d.ts.map +1 -0
- package/dist/v2/db/registry/domain.test.d.ts +2 -0
- package/dist/v2/db/registry/domain.test.d.ts.map +1 -0
- package/dist/v2/db/registry/index.d.ts +102 -0
- package/dist/v2/db/registry/index.d.ts.map +1 -0
- package/dist/v2/db/registry/subject.d.ts +18 -0
- package/dist/v2/db/registry/subject.d.ts.map +1 -0
- package/dist/v2/db/registry/subject.test.d.ts +2 -0
- package/dist/v2/db/registry/subject.test.d.ts.map +1 -0
- package/dist/v2/db/registry/types.d.ts +10 -0
- package/dist/v2/db/registry/types.d.ts.map +1 -0
- package/dist/v2/db/registry/utils/generate-id.d.ts +25 -0
- package/dist/v2/db/registry/utils/generate-id.d.ts.map +1 -0
- package/dist/v2/db/registry/utils/generate-id.test.d.ts +2 -0
- package/dist/v2/db/registry/utils/generate-id.test.d.ts.map +1 -0
- package/dist/v2/db/registry/utils.d.ts +25 -0
- package/dist/v2/db/registry/utils.d.ts.map +1 -0
- package/dist/v2/db/schema/1.0.0/audit-log.d.ts +29 -0
- package/dist/v2/db/schema/1.0.0/audit-log.d.ts.map +1 -0
- package/dist/v2/db/schema/1.0.0/consent-policy.d.ts +45 -0
- package/dist/v2/db/schema/1.0.0/consent-policy.d.ts.map +1 -0
- package/dist/v2/db/schema/1.0.0/consent-purpose.d.ts +27 -0
- package/dist/v2/db/schema/1.0.0/consent-purpose.d.ts.map +1 -0
- package/dist/v2/db/schema/1.0.0/consent-record.d.ts +19 -0
- package/dist/v2/db/schema/1.0.0/consent-record.d.ts.map +1 -0
- package/dist/v2/db/schema/1.0.0/consent.d.ts +42 -0
- package/dist/v2/db/schema/1.0.0/consent.d.ts.map +1 -0
- package/dist/v2/db/schema/1.0.0/domain.d.ts +23 -0
- package/dist/v2/db/schema/1.0.0/domain.d.ts.map +1 -0
- package/dist/v2/db/schema/1.0.0/index.d.ts +1513 -0
- package/dist/v2/db/schema/1.0.0/index.d.ts.map +1 -0
- package/dist/v2/db/schema/1.0.0/subject.d.ts +23 -0
- package/dist/v2/db/schema/1.0.0/subject.d.ts.map +1 -0
- package/dist/v2/db/schema/index.cjs +326 -0
- package/dist/v2/db/schema/index.d.ts +1507 -0
- package/dist/v2/db/schema/index.d.ts.map +1 -0
- package/dist/v2/db/schema/index.js +241 -0
- package/dist/v2/define-config.cjs +36 -0
- package/dist/v2/define-config.d.ts +5 -0
- package/dist/v2/define-config.d.ts.map +1 -0
- package/dist/v2/define-config.js +2 -0
- package/dist/v2/handlers/consent/index.d.ts +260 -0
- package/dist/v2/handlers/consent/index.d.ts.map +1 -0
- package/dist/v2/handlers/consent/post.handler.d.ts +136 -0
- package/dist/v2/handlers/consent/post.handler.d.ts.map +1 -0
- package/dist/v2/handlers/consent/show-banner/geo.d.ts +10 -0
- package/dist/v2/handlers/consent/show-banner/geo.d.ts.map +1 -0
- package/dist/v2/handlers/consent/show-banner/geo.test.d.ts +2 -0
- package/dist/v2/handlers/consent/show-banner/geo.test.d.ts.map +1 -0
- package/dist/v2/handlers/consent/show-banner/handler.d.ts +71 -0
- package/dist/v2/handlers/consent/show-banner/handler.d.ts.map +1 -0
- package/dist/v2/handlers/consent/show-banner/handler.test.d.ts +2 -0
- package/dist/v2/handlers/consent/show-banner/handler.test.d.ts.map +1 -0
- package/dist/v2/handlers/consent/show-banner/translations.d.ts +13 -0
- package/dist/v2/handlers/consent/show-banner/translations.d.ts.map +1 -0
- package/dist/v2/handlers/consent/show-banner/translations.test.d.ts +2 -0
- package/dist/v2/handlers/consent/show-banner/translations.test.d.ts.map +1 -0
- package/dist/v2/handlers/consent/verify.handler.d.ts +103 -0
- package/dist/v2/handlers/consent/verify.handler.d.ts.map +1 -0
- package/dist/v2/handlers/meta/index.d.ts +19 -0
- package/dist/v2/handlers/meta/index.d.ts.map +1 -0
- package/dist/v2/handlers/meta/status.handler.d.ts +17 -0
- package/dist/v2/handlers/meta/status.handler.d.ts.map +1 -0
- package/dist/v2/init.d.ts +3 -0
- package/dist/v2/init.d.ts.map +1 -0
- package/dist/v2/init.test.d.ts +2 -0
- package/dist/v2/init.test.d.ts.map +1 -0
- package/dist/v2/middleware/cors/cors.d.ts +37 -0
- package/dist/v2/middleware/cors/cors.d.ts.map +1 -0
- package/dist/v2/middleware/cors/cors.test.d.ts +2 -0
- package/dist/v2/middleware/cors/cors.test.d.ts.map +1 -0
- package/dist/v2/middleware/cors/index.d.ts +30 -0
- package/dist/v2/middleware/cors/index.d.ts.map +1 -0
- package/dist/v2/middleware/cors/is-origin-trusted.d.ts +49 -0
- package/dist/v2/middleware/cors/is-origin-trusted.d.ts.map +1 -0
- package/dist/v2/middleware/cors/is-origin-trusted.test.d.ts +2 -0
- package/dist/v2/middleware/cors/is-origin-trusted.test.d.ts.map +1 -0
- package/dist/v2/middleware/cors/process-cors.d.ts +31 -0
- package/dist/v2/middleware/cors/process-cors.d.ts.map +1 -0
- package/dist/v2/middleware/openapi/config.d.ts +28 -0
- package/dist/v2/middleware/openapi/config.d.ts.map +1 -0
- package/dist/v2/middleware/openapi/handlers.d.ts +29 -0
- package/dist/v2/middleware/openapi/handlers.d.ts.map +1 -0
- package/dist/v2/middleware/openapi/index.d.ts +11 -0
- package/dist/v2/middleware/openapi/index.d.ts.map +1 -0
- package/dist/v2/middleware/process-ip/index.d.ts +3 -0
- package/dist/v2/middleware/process-ip/index.d.ts.map +1 -0
- package/dist/v2/router.cjs +1275 -0
- package/dist/v2/router.d.ts +280 -0
- package/dist/v2/router.d.ts.map +1 -0
- package/dist/v2/router.js +1231 -0
- package/dist/v2/types/api.d.ts +27 -0
- package/dist/v2/types/api.d.ts.map +1 -0
- package/dist/v2/types/index.cjs +40 -0
- package/dist/v2/types/index.d.ts +104 -0
- package/dist/v2/types/index.d.ts.map +1 -0
- package/dist/v2/types/index.js +6 -0
- package/dist/v2/utils/create-telemetry-options.d.ts +28 -0
- package/dist/v2/utils/create-telemetry-options.d.ts.map +1 -0
- package/dist/v2/utils/env.d.ts +60 -0
- package/dist/v2/utils/env.d.ts.map +1 -0
- package/dist/v2/utils/index.d.ts +3 -0
- package/dist/v2/utils/index.d.ts.map +1 -0
- package/dist/v2/utils/logger.d.ts +16 -0
- package/dist/v2/utils/logger.d.ts.map +1 -0
- package/dist/version.d.ts +1 -1
- package/package.json +106 -15
- package/readme.json +30 -0
- package/rslib.config.ts +13 -14
- package/src/__tests__/server.test.ts +1 -1
- package/src/contracts/consent/post.contract.test.ts +3 -8
- package/src/contracts/consent/post.contract.ts +13 -13
- package/src/contracts/consent/show-banner.contract.test.ts +9 -0
- package/src/contracts/consent/show-banner.contract.ts +2 -0
- package/src/contracts/consent/verify.contract.ts +19 -23
- package/src/core.ts +7 -0
- package/src/handlers/consent/show-banner/handler.ts +12 -9
- package/src/handlers/consent/show-banner/translations.ts +2 -2
- package/src/init.ts +9 -6
- package/src/middleware/openapi/index.ts +2 -2
- package/src/pkgs/api-router/hooks/__tests__/processor.test.ts +1 -1
- package/src/pkgs/data-model/fields/index.ts +17 -22
- package/src/pkgs/data-model/fields/zod-fields.ts +14 -26
- package/src/pkgs/data-model/hooks/index.ts +3 -2
- package/src/pkgs/data-model/index.ts +2 -4
- package/src/pkgs/data-model/schema/index.ts +6 -7
- package/src/pkgs/data-model/schema/schemas.ts +3 -3
- package/src/pkgs/db-adapters/adapters/drizzle-adapter/drizzle-adapter.ts +4 -1
- package/src/pkgs/db-adapters/adapters/index.ts +2 -2
- package/src/pkgs/db-adapters/adapters/kysely-adapter/index.ts +4 -4
- package/src/pkgs/db-adapters/adapters/kysely-adapter/kysely-adapter.ts +4 -5
- package/src/pkgs/db-adapters/adapters/kysely-adapter/tests/postgres.test.ts +2 -4
- package/src/pkgs/db-adapters/adapters/kysely-adapter/tests/sqlite.test.ts +2 -3
- package/src/pkgs/db-adapters/adapters/kysely-adapter/tests/test-utils.ts +1 -6
- package/src/pkgs/db-adapters/adapters/memory-adapter/memory-adapter.ts +4 -1
- package/src/pkgs/db-adapters/adapters/prisma-adapter/index.ts +1 -1
- package/src/pkgs/db-adapters/adapters/prisma-adapter/prisma-adapter.ts +5 -2
- package/src/pkgs/db-adapters/index.ts +12 -13
- package/src/pkgs/migrations/get-migration.ts +4 -2
- package/src/pkgs/migrations/get-schema/get-schema.ts +0 -1
- package/src/pkgs/migrations/get-schema/process-fields.ts +1 -1
- package/src/pkgs/migrations/get-schema/process-tables.ts +0 -2
- package/src/pkgs/migrations/index.ts +7 -8
- package/src/pkgs/results/__tests__/error-codes.test.ts +2 -2
- package/src/pkgs/results/index.ts +22 -27
- package/src/pkgs/results/orpc-error-handler.ts +1 -1
- package/src/pkgs/results/results/result-helpers.ts +1 -1
- package/src/pkgs/types/index.ts +4 -4
- package/src/pkgs/types/options.ts +10 -3
- package/src/pkgs/utils/index.ts +1 -1
- package/src/pkgs/utils/logger.ts +1 -1
- package/src/schema/audit-log/schema.ts +3 -3
- package/src/schema/consent/schema.ts +4 -4
- package/src/schema/consent-policy/schema.ts +3 -3
- package/src/schema/consent-purpose/schema.ts +4 -4
- package/src/schema/consent-record/schema.ts +3 -3
- package/src/schema/definition.ts +1 -1
- package/src/schema/domain/schema.ts +5 -5
- package/src/schema/index.ts +14 -17
- package/src/schema/subject/schema.ts +3 -3
- package/src/schema/types.ts +1 -1
- package/src/testing/contract-testing.ts +15 -52
- package/src/types/index.ts +8 -8
- package/src/types/options.ts +2 -3
- package/src/v2/contracts/consent/index.test.ts +5 -0
- package/src/v2/contracts/consent/index.ts +9 -0
- package/src/v2/contracts/consent/post.contract.test.ts +521 -0
- package/src/v2/contracts/consent/post.contract.ts +155 -0
- package/src/v2/contracts/consent/show-banner.contract.test.ts +252 -0
- package/src/v2/contracts/consent/show-banner.contract.ts +73 -0
- package/src/v2/contracts/consent/verify.contract.test.ts +185 -0
- package/src/v2/contracts/consent/verify.contract.ts +122 -0
- package/src/v2/contracts/index.ts +20 -0
- package/src/v2/contracts/meta/index.test.ts +5 -0
- package/src/v2/contracts/meta/index.ts +5 -0
- package/src/v2/contracts/meta/status.contract.test.ts +226 -0
- package/src/v2/contracts/meta/status.contract.ts +34 -0
- package/src/v2/contracts/shared/jurisdiction.schema.ts +30 -0
- package/src/v2/contracts/test.utils.ts +400 -0
- package/src/v2/core.ts +379 -0
- package/src/v2/db/adapters/drizzle.ts +1 -0
- package/src/v2/db/adapters/index.ts +1 -0
- package/src/v2/db/adapters/kysely.ts +1 -0
- package/src/v2/db/adapters/mongo.ts +1 -0
- package/src/v2/db/adapters/prisma.ts +1 -0
- package/src/v2/db/adapters/typeorm.ts +1 -0
- package/src/v2/db/migrator/index.ts +80 -0
- package/src/v2/db/registry/audit-log.test.ts +77 -0
- package/src/v2/db/registry/audit-log.ts +46 -0
- package/src/v2/db/registry/consent-policy.test.ts +778 -0
- package/src/v2/db/registry/consent-policy.ts +74 -0
- package/src/v2/db/registry/consent-purpose.test.ts +485 -0
- package/src/v2/db/registry/consent-purpose.ts +41 -0
- package/src/v2/db/registry/consent.test.ts +843 -0
- package/src/v2/db/registry/consent.ts +42 -0
- package/src/v2/db/registry/domain.test.ts +463 -0
- package/src/v2/db/registry/domain.ts +51 -0
- package/src/v2/db/registry/index.ts +18 -0
- package/src/v2/db/registry/subject.test.ts +497 -0
- package/src/v2/db/registry/subject.ts +101 -0
- package/src/v2/db/registry/types.ts +10 -0
- package/src/v2/db/registry/utils/generate-id.test.ts +217 -0
- package/src/v2/db/registry/utils/generate-id.ts +134 -0
- package/src/v2/db/registry/utils.ts +134 -0
- package/src/v2/db/schema/1.0.0/audit-log.ts +32 -0
- package/src/v2/db/schema/1.0.0/consent-policy.ts +41 -0
- package/src/v2/db/schema/1.0.0/consent-purpose.ts +30 -0
- package/src/v2/db/schema/1.0.0/consent-record.ts +22 -0
- package/src/v2/db/schema/1.0.0/consent.ts +38 -0
- package/src/v2/db/schema/1.0.0/domain.ts +26 -0
- package/src/v2/db/schema/1.0.0/index.ts +56 -0
- package/src/v2/db/schema/1.0.0/subject.ts +26 -0
- package/src/v2/db/schema/index.ts +9 -0
- package/src/v2/define-config.ts +5 -0
- package/src/v2/handlers/consent/index.ts +9 -0
- package/src/v2/handlers/consent/post.handler.ts +254 -0
- package/src/v2/handlers/consent/show-banner/geo.test.ts +281 -0
- package/src/v2/handlers/consent/show-banner/geo.ts +96 -0
- package/src/v2/handlers/consent/show-banner/handler.test.ts +374 -0
- package/src/v2/handlers/consent/show-banner/handler.ts +123 -0
- package/src/v2/handlers/consent/show-banner/translations.test.ts +121 -0
- package/src/v2/handlers/consent/show-banner/translations.ts +79 -0
- package/src/v2/handlers/consent/verify.handler.ts +288 -0
- package/src/v2/handlers/meta/index.ts +5 -0
- package/src/v2/handlers/meta/status.handler.ts +43 -0
- package/src/v2/init.test.ts +114 -0
- package/src/v2/init.ts +126 -0
- package/src/v2/middleware/cors/cors.test.ts +111 -0
- package/src/v2/middleware/cors/cors.ts +192 -0
- package/src/v2/middleware/cors/index.ts +30 -0
- package/src/v2/middleware/cors/is-origin-trusted.test.ts +104 -0
- package/src/v2/middleware/cors/is-origin-trusted.ts +126 -0
- package/src/v2/middleware/cors/process-cors.ts +91 -0
- package/src/v2/middleware/openapi/config.ts +27 -0
- package/src/v2/middleware/openapi/handlers.ts +132 -0
- package/src/v2/middleware/openapi/index.ts +11 -0
- package/src/v2/middleware/process-ip/index.ts +39 -0
- package/src/v2/router.ts +8 -0
- package/src/v2/types/api.ts +32 -0
- package/src/v2/types/index.ts +121 -0
- package/src/v2/utils/create-telemetry-options.ts +115 -0
- package/src/v2/utils/env.ts +84 -0
- package/src/v2/utils/index.ts +2 -0
- package/src/v2/utils/logger.ts +38 -0
- package/src/version.ts +1 -1
- package/vitest.config.ts +11 -2
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type JurisdictionCode,
|
|
3
|
+
JurisdictionMessages,
|
|
4
|
+
} from '~/v2/contracts/shared/jurisdiction.schema';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Determines if a consent banner should be shown based on country code
|
|
8
|
+
* and returns appropriate jurisdiction information
|
|
9
|
+
*/
|
|
10
|
+
export function checkJurisdiction(countryCode: string | null) {
|
|
11
|
+
// Country code sets for different jurisdictions
|
|
12
|
+
const jurisdictions = {
|
|
13
|
+
EU: new Set([
|
|
14
|
+
'AT',
|
|
15
|
+
'BE',
|
|
16
|
+
'BG',
|
|
17
|
+
'HR',
|
|
18
|
+
'CY',
|
|
19
|
+
'CZ',
|
|
20
|
+
'DK',
|
|
21
|
+
'EE',
|
|
22
|
+
'FI',
|
|
23
|
+
'FR',
|
|
24
|
+
'DE',
|
|
25
|
+
'GR',
|
|
26
|
+
'HU',
|
|
27
|
+
'IE',
|
|
28
|
+
'IT',
|
|
29
|
+
'LV',
|
|
30
|
+
'LT',
|
|
31
|
+
'LU',
|
|
32
|
+
'MT',
|
|
33
|
+
'NL',
|
|
34
|
+
'PL',
|
|
35
|
+
'PT',
|
|
36
|
+
'RO',
|
|
37
|
+
'SK',
|
|
38
|
+
'SI',
|
|
39
|
+
'ES',
|
|
40
|
+
'SE',
|
|
41
|
+
]),
|
|
42
|
+
EEA: new Set(['IS', 'NO', 'LI']),
|
|
43
|
+
UK: new Set(['GB']),
|
|
44
|
+
CH: new Set(['CH']),
|
|
45
|
+
BR: new Set(['BR']),
|
|
46
|
+
CA: new Set(['CA']),
|
|
47
|
+
AU: new Set(['AU']),
|
|
48
|
+
JP: new Set(['JP']),
|
|
49
|
+
KR: new Set(['KR']),
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// Default to no jurisdiction, but show banner
|
|
53
|
+
let showConsentBanner = true;
|
|
54
|
+
let jurisdictionCode: JurisdictionCode = 'NONE';
|
|
55
|
+
|
|
56
|
+
// Check country code against jurisdiction sets
|
|
57
|
+
if (countryCode) {
|
|
58
|
+
// Normalize country code to uppercase for case-insensitive comparison
|
|
59
|
+
const normalizedCountryCode = countryCode.toUpperCase();
|
|
60
|
+
|
|
61
|
+
// Default to false as we don't know if it fits any jurisdiction yet
|
|
62
|
+
showConsentBanner = false;
|
|
63
|
+
|
|
64
|
+
// Map jurisdiction sets to their respective codes
|
|
65
|
+
const jurisdictionMap = [
|
|
66
|
+
{
|
|
67
|
+
sets: [jurisdictions.EU, jurisdictions.EEA, jurisdictions.UK],
|
|
68
|
+
code: 'GDPR',
|
|
69
|
+
},
|
|
70
|
+
{ sets: [jurisdictions.CH], code: 'CH' },
|
|
71
|
+
{ sets: [jurisdictions.BR], code: 'BR' },
|
|
72
|
+
{ sets: [jurisdictions.CA], code: 'PIPEDA' },
|
|
73
|
+
{ sets: [jurisdictions.AU], code: 'AU' },
|
|
74
|
+
{ sets: [jurisdictions.JP], code: 'APPI' },
|
|
75
|
+
{ sets: [jurisdictions.KR], code: 'PIPA' },
|
|
76
|
+
] as const;
|
|
77
|
+
|
|
78
|
+
// Find matching jurisdiction
|
|
79
|
+
for (const { sets, code } of jurisdictionMap) {
|
|
80
|
+
if (sets.some((set) => set.has(normalizedCountryCode))) {
|
|
81
|
+
jurisdictionCode = code;
|
|
82
|
+
showConsentBanner = true;
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Get corresponding message from shared schema
|
|
89
|
+
const message = JurisdictionMessages[jurisdictionCode];
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
showConsentBanner,
|
|
93
|
+
jurisdictionCode,
|
|
94
|
+
message,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
import { baseTranslations } from '@c15t/translations';
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
3
|
+
import type { C15TContext } from '~/v2/types';
|
|
4
|
+
import { showConsentBanner } from './handler';
|
|
5
|
+
|
|
6
|
+
// First, mock the oRPC handler
|
|
7
|
+
vi.mock('~/v2/contracts', () => ({
|
|
8
|
+
os: {
|
|
9
|
+
consent: {
|
|
10
|
+
showBanner: {
|
|
11
|
+
handler: (fn: typeof showConsentBanner) => fn,
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
describe('Show Consent Banner Handler', () => {
|
|
18
|
+
// Helper to create mock context with headers
|
|
19
|
+
const createMockContext = (
|
|
20
|
+
headers: Record<string, string>,
|
|
21
|
+
advanced?: Partial<C15TContext['advanced']>
|
|
22
|
+
) => {
|
|
23
|
+
return {
|
|
24
|
+
context: {
|
|
25
|
+
headers: new Headers(headers),
|
|
26
|
+
advanced: {
|
|
27
|
+
...(advanced ?? {}),
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
describe('Header extraction', () => {
|
|
34
|
+
it('extracts country code from cf-ipcountry header', async () => {
|
|
35
|
+
//@ts-expect-error
|
|
36
|
+
const result = await showConsentBanner(
|
|
37
|
+
createMockContext({ 'cf-ipcountry': 'DE' })
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
expect(result.location.countryCode).toBe('DE');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('falls back to alternative country code headers', async () => {
|
|
44
|
+
//@ts-expect-error
|
|
45
|
+
const result = await showConsentBanner(
|
|
46
|
+
createMockContext({ 'x-vercel-ip-country': 'FR' })
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
expect(result.location.countryCode).toBe('FR');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('prioritizes cf-ipcountry over other headers when multiple are present', async () => {
|
|
53
|
+
//@ts-expect-error
|
|
54
|
+
const result = await showConsentBanner(
|
|
55
|
+
createMockContext({
|
|
56
|
+
'cf-ipcountry': 'DE',
|
|
57
|
+
'x-vercel-ip-country': 'FR',
|
|
58
|
+
})
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
expect(result.location.countryCode).toBe('DE');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('extracts region code from headers', async () => {
|
|
65
|
+
//@ts-expect-error
|
|
66
|
+
const result = await showConsentBanner(
|
|
67
|
+
createMockContext({
|
|
68
|
+
'cf-ipcountry': 'US',
|
|
69
|
+
'x-vercel-ip-country-region': 'CA',
|
|
70
|
+
})
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
expect(result.location.countryCode).toBe('US');
|
|
74
|
+
expect(result.location.regionCode).toBe('CA');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('handles missing headers gracefully', async () => {
|
|
78
|
+
//@ts-expect-error
|
|
79
|
+
const result = await showConsentBanner(createMockContext({}));
|
|
80
|
+
|
|
81
|
+
expect(result.location.countryCode).toBeNull();
|
|
82
|
+
expect(result.location.regionCode).toBeNull();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('handles case-insensitive header names', async () => {
|
|
86
|
+
//@ts-expect-error
|
|
87
|
+
const result = await showConsentBanner(
|
|
88
|
+
createMockContext({ 'CF-IPCountry': 'GB' })
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
expect(result.location.countryCode).toBe('GB');
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
describe('Integration and response structure', () => {
|
|
96
|
+
it('returns properly structured response with all required fields', async () => {
|
|
97
|
+
//@ts-expect-error
|
|
98
|
+
const result = await showConsentBanner(
|
|
99
|
+
createMockContext({ 'cf-ipcountry': 'DE' })
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
// Verify structure
|
|
103
|
+
expect(result).toHaveProperty('showConsentBanner');
|
|
104
|
+
expect(result).toHaveProperty('jurisdiction');
|
|
105
|
+
expect(result).toHaveProperty('location');
|
|
106
|
+
expect(result).toHaveProperty('translations');
|
|
107
|
+
|
|
108
|
+
// Verify types
|
|
109
|
+
expect(typeof result.showConsentBanner).toBe('boolean');
|
|
110
|
+
expect(typeof result.jurisdiction.code).toBe('string');
|
|
111
|
+
expect(typeof result.jurisdiction.message).toBe('string');
|
|
112
|
+
expect(result.location.countryCode).toBe('DE');
|
|
113
|
+
expect(typeof result.translations.language).toBe('string');
|
|
114
|
+
expect(typeof result.translations.translations).toBe('object');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('integrates geo logic correctly for regulated countries', async () => {
|
|
118
|
+
//@ts-expect-error
|
|
119
|
+
const result = await showConsentBanner(
|
|
120
|
+
createMockContext({ 'cf-ipcountry': 'DE' })
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
// Should show banner for regulated country
|
|
124
|
+
expect(result.showConsentBanner).toBe(true);
|
|
125
|
+
expect(result.jurisdiction.code).not.toBe('NONE');
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('integrates geo logic correctly for non-regulated countries', async () => {
|
|
129
|
+
//@ts-expect-error
|
|
130
|
+
const result = await showConsentBanner(
|
|
131
|
+
createMockContext({ 'cf-ipcountry': 'US' })
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
// Should not show banner for non-regulated country
|
|
135
|
+
expect(result.showConsentBanner).toBe(false);
|
|
136
|
+
expect(result.jurisdiction.code).toBe('NONE');
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('integrates translations correctly', async () => {
|
|
140
|
+
//@ts-expect-error
|
|
141
|
+
const result = await showConsentBanner(createMockContext({}));
|
|
142
|
+
|
|
143
|
+
expect(result.translations.translations).toStrictEqual(
|
|
144
|
+
baseTranslations.en
|
|
145
|
+
);
|
|
146
|
+
expect(result.translations.language).toBe('en');
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('handles custom translations when provided', async () => {
|
|
150
|
+
const customTranslations = {
|
|
151
|
+
en: { cookieBanner: { title: 'Custom Title' } },
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
//@ts-expect-error
|
|
155
|
+
const result = await showConsentBanner(
|
|
156
|
+
createMockContext({}, { customTranslations })
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
expect(result.translations.translations.cookieBanner.title).toBe(
|
|
160
|
+
'Custom Title'
|
|
161
|
+
);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('maintains consistency between location and jurisdiction', async () => {
|
|
165
|
+
// Test that the country code extracted matches the jurisdiction determination
|
|
166
|
+
//@ts-expect-error
|
|
167
|
+
const result = await showConsentBanner(
|
|
168
|
+
createMockContext({ 'cf-ipcountry': 'CH' })
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
expect(result.location.countryCode).toBe('CH');
|
|
172
|
+
expect(result.jurisdiction.code).toBe('CH');
|
|
173
|
+
expect(result.showConsentBanner).toBe(true);
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
describe('Geo location disabling', () => {
|
|
178
|
+
it('disables geo logic when disableGeoLocation is true for regulated country', async () => {
|
|
179
|
+
//@ts-expect-error
|
|
180
|
+
const result = await showConsentBanner(
|
|
181
|
+
createMockContext(
|
|
182
|
+
{ 'cf-ipcountry': 'DE' }, // Normally would show banner
|
|
183
|
+
{ disableGeoLocation: true }
|
|
184
|
+
)
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
// Should show banner despite being in a regulated country when geo-location is disabled
|
|
188
|
+
expect(result.showConsentBanner).toBe(true);
|
|
189
|
+
expect(result.jurisdiction.code).toBe('NONE');
|
|
190
|
+
expect(result.location.countryCode).toBeNull();
|
|
191
|
+
expect(result.location.regionCode).toBeNull();
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('disables geo logic when disableGeoLocation is true for non-regulated country', async () => {
|
|
195
|
+
//@ts-expect-error
|
|
196
|
+
const result = await showConsentBanner(
|
|
197
|
+
createMockContext(
|
|
198
|
+
{ 'cf-ipcountry': 'US' }, // Normally would not show banner
|
|
199
|
+
{ disableGeoLocation: true }
|
|
200
|
+
)
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
// Should still not show banner and have consistent response
|
|
204
|
+
expect(result.showConsentBanner).toBe(true);
|
|
205
|
+
expect(result.jurisdiction.code).toBe('NONE');
|
|
206
|
+
expect(result.location.countryCode).toBeNull();
|
|
207
|
+
expect(result.location.regionCode).toBeNull();
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('still provides translations when geo location is disabled', async () => {
|
|
211
|
+
//@ts-expect-error
|
|
212
|
+
const result = await showConsentBanner(
|
|
213
|
+
createMockContext(
|
|
214
|
+
{ 'cf-ipcountry': 'DE' },
|
|
215
|
+
{ disableGeoLocation: true }
|
|
216
|
+
)
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
// Translations should still be provided
|
|
220
|
+
expect(result.translations.translations).toStrictEqual(
|
|
221
|
+
baseTranslations.en
|
|
222
|
+
);
|
|
223
|
+
expect(result.translations.language).toBe('en');
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('respects custom translations when geo location is disabled', async () => {
|
|
227
|
+
const customTranslations = {
|
|
228
|
+
en: { cookieBanner: { title: 'Custom Disabled Title' } },
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
//@ts-expect-error
|
|
232
|
+
const result = await showConsentBanner(
|
|
233
|
+
createMockContext(
|
|
234
|
+
{ 'cf-ipcountry': 'DE' },
|
|
235
|
+
{ disableGeoLocation: true, customTranslations }
|
|
236
|
+
)
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
expect(result.translations.translations.cookieBanner.title).toBe(
|
|
240
|
+
'Custom Disabled Title'
|
|
241
|
+
);
|
|
242
|
+
expect(result.showConsentBanner).toBe(true);
|
|
243
|
+
expect(result.jurisdiction.code).toBe('NONE');
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('ignores all geo headers when geo location is disabled', async () => {
|
|
247
|
+
//@ts-expect-error
|
|
248
|
+
const result = await showConsentBanner(
|
|
249
|
+
createMockContext(
|
|
250
|
+
{
|
|
251
|
+
'cf-ipcountry': 'DE',
|
|
252
|
+
'x-vercel-ip-country': 'FR',
|
|
253
|
+
'x-vercel-ip-country-region': 'IDF',
|
|
254
|
+
},
|
|
255
|
+
{ disableGeoLocation: true }
|
|
256
|
+
)
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
// Should ignore all geo headers
|
|
260
|
+
expect(result.location.countryCode).toBeNull();
|
|
261
|
+
expect(result.location.regionCode).toBeNull();
|
|
262
|
+
expect(result.showConsentBanner).toBe(true);
|
|
263
|
+
expect(result.jurisdiction.code).toBe('NONE');
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it('applies normal geo logic when disableGeoLocation is false', async () => {
|
|
267
|
+
//@ts-expect-error
|
|
268
|
+
const result = await showConsentBanner(
|
|
269
|
+
createMockContext(
|
|
270
|
+
{ 'cf-ipcountry': 'DE' },
|
|
271
|
+
{ disableGeoLocation: false }
|
|
272
|
+
)
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
// Should apply normal geo logic
|
|
276
|
+
expect(result.showConsentBanner).toBe(true);
|
|
277
|
+
expect(result.jurisdiction.code).not.toBe('NONE');
|
|
278
|
+
expect(result.location.countryCode).toBe('DE');
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it('applies normal geo logic when disableGeoLocation is undefined', async () => {
|
|
282
|
+
//@ts-expect-error
|
|
283
|
+
const result = await showConsentBanner(
|
|
284
|
+
createMockContext(
|
|
285
|
+
{ 'cf-ipcountry': 'DE' },
|
|
286
|
+
{ disableGeoLocation: undefined }
|
|
287
|
+
)
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
// Should apply normal geo logic when undefined
|
|
291
|
+
expect(result.showConsentBanner).toBe(true);
|
|
292
|
+
expect(result.jurisdiction.code).not.toBe('NONE');
|
|
293
|
+
expect(result.location.countryCode).toBe('DE');
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
it('maintains consistent response structure when geo is disabled', async () => {
|
|
297
|
+
//@ts-expect-error
|
|
298
|
+
const result = await showConsentBanner(
|
|
299
|
+
createMockContext(
|
|
300
|
+
{ 'cf-ipcountry': 'DE' },
|
|
301
|
+
{ disableGeoLocation: true }
|
|
302
|
+
)
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
// Verify the response has the same structure as normal responses
|
|
306
|
+
expect(result).toHaveProperty('showConsentBanner');
|
|
307
|
+
expect(result).toHaveProperty('jurisdiction');
|
|
308
|
+
expect(result).toHaveProperty('location');
|
|
309
|
+
expect(result).toHaveProperty('translations');
|
|
310
|
+
|
|
311
|
+
// Verify specific disabled values
|
|
312
|
+
expect(typeof result.showConsentBanner).toBe('boolean');
|
|
313
|
+
expect(typeof result.jurisdiction.code).toBe('string');
|
|
314
|
+
expect(typeof result.jurisdiction.message).toBe('string');
|
|
315
|
+
expect(result.location.countryCode).toBeNull();
|
|
316
|
+
expect(result.location.regionCode).toBeNull();
|
|
317
|
+
expect(typeof result.translations.language).toBe('string');
|
|
318
|
+
expect(typeof result.translations.translations).toBe('object');
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
describe('Edge cases and error handling', () => {
|
|
323
|
+
it('handles malformed headers gracefully', async () => {
|
|
324
|
+
//@ts-expect-error
|
|
325
|
+
const result = await showConsentBanner(
|
|
326
|
+
createMockContext({
|
|
327
|
+
'cf-ipcountry': '', // Empty string
|
|
328
|
+
'x-vercel-ip-country-region': ' ', // Whitespace
|
|
329
|
+
})
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
// Should handle gracefully without throwing
|
|
333
|
+
expect(result).toHaveProperty('showConsentBanner');
|
|
334
|
+
expect(result).toHaveProperty('jurisdiction');
|
|
335
|
+
expect(result).toHaveProperty('location');
|
|
336
|
+
expect(result).toHaveProperty('translations');
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
it('handles undefined context options gracefully', async () => {
|
|
340
|
+
const contextWithoutOptions = {
|
|
341
|
+
context: {
|
|
342
|
+
headers: new Headers({ 'cf-ipcountry': 'DE' }),
|
|
343
|
+
options: {}, // No advanced options
|
|
344
|
+
},
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
//@ts-expect-error
|
|
348
|
+
const result = await showConsentBanner(contextWithoutOptions);
|
|
349
|
+
|
|
350
|
+
expect(result.translations.translations).toStrictEqual(
|
|
351
|
+
baseTranslations.en
|
|
352
|
+
);
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it('handles malformed headers gracefully when geo is disabled', async () => {
|
|
356
|
+
//@ts-expect-error
|
|
357
|
+
const result = await showConsentBanner(
|
|
358
|
+
createMockContext(
|
|
359
|
+
{
|
|
360
|
+
'cf-ipcountry': '', // Empty string
|
|
361
|
+
'x-vercel-ip-country-region': ' ', // Whitespace
|
|
362
|
+
},
|
|
363
|
+
{ disableGeoLocation: true }
|
|
364
|
+
)
|
|
365
|
+
);
|
|
366
|
+
|
|
367
|
+
// Should handle gracefully and ignore malformed headers
|
|
368
|
+
expect(result.showConsentBanner).toBe(true);
|
|
369
|
+
expect(result.jurisdiction.code).toBe('NONE');
|
|
370
|
+
expect(result.location.countryCode).toBeNull();
|
|
371
|
+
expect(result.location.regionCode).toBeNull();
|
|
372
|
+
});
|
|
373
|
+
});
|
|
374
|
+
});
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import type { Translations } from '@c15t/translations';
|
|
2
|
+
import { os } from '~/v2/contracts';
|
|
3
|
+
import {
|
|
4
|
+
type JurisdictionCode,
|
|
5
|
+
JurisdictionMessages,
|
|
6
|
+
} from '~/v2/contracts/shared/jurisdiction.schema';
|
|
7
|
+
import type { Branding, C15TContext } from '~/v2/types';
|
|
8
|
+
import { checkJurisdiction } from './geo';
|
|
9
|
+
import { getTranslations } from './translations';
|
|
10
|
+
|
|
11
|
+
function getHeaders(headers: Headers | undefined) {
|
|
12
|
+
if (!headers) {
|
|
13
|
+
return {
|
|
14
|
+
countryCode: null,
|
|
15
|
+
regionCode: null,
|
|
16
|
+
acceptLanguage: null,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Add this conversion to ensure headers are always string or null
|
|
21
|
+
const normalizeHeader = (
|
|
22
|
+
value: string | string[] | null | undefined
|
|
23
|
+
): string | null => {
|
|
24
|
+
if (!value) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return Array.isArray(value) ? (value[0] ?? null) : value;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const countryCode =
|
|
32
|
+
normalizeHeader(headers.get('x-c15t-country')) ??
|
|
33
|
+
normalizeHeader(headers.get('cf-ipcountry')) ??
|
|
34
|
+
normalizeHeader(headers.get('x-vercel-ip-country')) ??
|
|
35
|
+
normalizeHeader(headers.get('x-amz-cf-ipcountry')) ??
|
|
36
|
+
normalizeHeader(headers.get('x-country-code'));
|
|
37
|
+
|
|
38
|
+
const regionCode =
|
|
39
|
+
normalizeHeader(headers.get('x-c15t-region')) ??
|
|
40
|
+
normalizeHeader(headers.get('x-vercel-ip-country-region')) ??
|
|
41
|
+
normalizeHeader(headers.get('x-region-code'));
|
|
42
|
+
|
|
43
|
+
// Get preferred language from Accept-Language header
|
|
44
|
+
const acceptLanguage = normalizeHeader(headers.get('accept-language'));
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
countryCode,
|
|
48
|
+
regionCode,
|
|
49
|
+
acceptLanguage,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function buildResponse({
|
|
54
|
+
shouldShowBanner,
|
|
55
|
+
jurisdiction,
|
|
56
|
+
location,
|
|
57
|
+
acceptLanguage,
|
|
58
|
+
customTranslations,
|
|
59
|
+
branding = 'c15t',
|
|
60
|
+
}: {
|
|
61
|
+
shouldShowBanner: boolean;
|
|
62
|
+
|
|
63
|
+
jurisdiction: {
|
|
64
|
+
code: JurisdictionCode;
|
|
65
|
+
message: string;
|
|
66
|
+
};
|
|
67
|
+
location: { countryCode: string | null; regionCode: string | null };
|
|
68
|
+
acceptLanguage: string | null;
|
|
69
|
+
customTranslations: Record<string, Partial<Translations>> | undefined;
|
|
70
|
+
branding?: Branding;
|
|
71
|
+
}) {
|
|
72
|
+
return {
|
|
73
|
+
showConsentBanner: shouldShowBanner,
|
|
74
|
+
jurisdiction,
|
|
75
|
+
location,
|
|
76
|
+
translations: getTranslations(acceptLanguage, customTranslations),
|
|
77
|
+
branding: branding,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Handler for the show consent banner endpoint
|
|
83
|
+
* Determines if a user should see a consent banner based on their location
|
|
84
|
+
*/
|
|
85
|
+
export const showConsentBanner = os.consent.showBanner.handler(
|
|
86
|
+
({ context }) => {
|
|
87
|
+
const typedContext = context as C15TContext;
|
|
88
|
+
const { customTranslations, disableGeoLocation, branding } =
|
|
89
|
+
typedContext.advanced ?? {};
|
|
90
|
+
const { countryCode, regionCode, acceptLanguage } = getHeaders(
|
|
91
|
+
typedContext.headers
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
if (disableGeoLocation) {
|
|
95
|
+
return buildResponse({
|
|
96
|
+
shouldShowBanner: true,
|
|
97
|
+
jurisdiction: {
|
|
98
|
+
code: 'NONE',
|
|
99
|
+
message: JurisdictionMessages.NONE,
|
|
100
|
+
},
|
|
101
|
+
location: { countryCode: null, regionCode: null },
|
|
102
|
+
acceptLanguage,
|
|
103
|
+
customTranslations,
|
|
104
|
+
branding,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const { showConsentBanner, jurisdictionCode, message } =
|
|
109
|
+
checkJurisdiction(countryCode);
|
|
110
|
+
|
|
111
|
+
return buildResponse({
|
|
112
|
+
shouldShowBanner: showConsentBanner,
|
|
113
|
+
jurisdiction: {
|
|
114
|
+
code: jurisdictionCode,
|
|
115
|
+
message,
|
|
116
|
+
},
|
|
117
|
+
location: { countryCode, regionCode },
|
|
118
|
+
acceptLanguage,
|
|
119
|
+
customTranslations,
|
|
120
|
+
branding,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
);
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { baseTranslations } from '@c15t/translations';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import { getTranslations } from './translations';
|
|
4
|
+
|
|
5
|
+
describe('showBanner > getTranslations', () => {
|
|
6
|
+
it("should return 'en' translations when Accept-Language is null", () => {
|
|
7
|
+
const { translations, language } = getTranslations(null);
|
|
8
|
+
expect(language).toBe('en');
|
|
9
|
+
expect(translations.cookieBanner.title).toBe(
|
|
10
|
+
baseTranslations.en.cookieBanner.title
|
|
11
|
+
);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("should return 'en' translations for unsupported language", () => {
|
|
15
|
+
const { translations, language } = getTranslations('xx-XX,en;q=0.9');
|
|
16
|
+
expect(language).toBe('en');
|
|
17
|
+
expect(translations.cookieBanner.title).toBe(
|
|
18
|
+
baseTranslations.en.cookieBanner.title
|
|
19
|
+
);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("should return 'de' translations for 'de-DE'", () => {
|
|
23
|
+
const { translations, language } = getTranslations(
|
|
24
|
+
'de-DE,de;q=0.9,en;q=0.8'
|
|
25
|
+
);
|
|
26
|
+
expect(language).toBe('de');
|
|
27
|
+
expect(translations.cookieBanner.title).toBe(
|
|
28
|
+
baseTranslations.de.cookieBanner.title
|
|
29
|
+
);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should merge custom translations for the preferred language', () => {
|
|
33
|
+
const customTranslations = {
|
|
34
|
+
en: {
|
|
35
|
+
cookieBanner: {
|
|
36
|
+
title: 'My Custom Cookie Title',
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
const { translations, language } = getTranslations(
|
|
41
|
+
'en-US,en;q=0.9',
|
|
42
|
+
customTranslations
|
|
43
|
+
);
|
|
44
|
+
expect(language).toBe('en');
|
|
45
|
+
expect(translations.cookieBanner.title).toBe('My Custom Cookie Title');
|
|
46
|
+
// Check if other properties from base translations are still there
|
|
47
|
+
expect(translations.cookieBanner.description).toBeDefined();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should not merge custom translations for a different language', () => {
|
|
51
|
+
const customTranslations = {
|
|
52
|
+
de: {
|
|
53
|
+
cookieBanner: {
|
|
54
|
+
title: 'Meine benutzerdefinierte Cookie-Überschrift',
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
const { translations, language } = getTranslations(
|
|
59
|
+
'en-US,en;q=0.9',
|
|
60
|
+
customTranslations
|
|
61
|
+
);
|
|
62
|
+
expect(language).toBe('en');
|
|
63
|
+
expect(translations.cookieBanner.title).toBe(
|
|
64
|
+
baseTranslations.en.cookieBanner.title
|
|
65
|
+
);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should handle partially provided custom translations', () => {
|
|
69
|
+
const customTranslations = {
|
|
70
|
+
en: {
|
|
71
|
+
cookieBanner: {
|
|
72
|
+
// Title is NOT provided, description is.
|
|
73
|
+
description: 'My custom description.',
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
const { translations, language } = getTranslations(
|
|
78
|
+
'en-US,en;q=0.9',
|
|
79
|
+
customTranslations
|
|
80
|
+
);
|
|
81
|
+
expect(language).toBe('en');
|
|
82
|
+
// Title should come from base
|
|
83
|
+
expect(translations.cookieBanner.title).toBe(
|
|
84
|
+
baseTranslations.en.cookieBanner.title
|
|
85
|
+
);
|
|
86
|
+
// Description should be from custom
|
|
87
|
+
expect(translations.cookieBanner.description).toBe(
|
|
88
|
+
'My custom description.'
|
|
89
|
+
);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('should return base translations if custom translations are empty for the language', () => {
|
|
93
|
+
const customTranslations = {
|
|
94
|
+
de: {},
|
|
95
|
+
};
|
|
96
|
+
const { translations, language } = getTranslations(
|
|
97
|
+
'de-DE,de;q=0.9',
|
|
98
|
+
customTranslations
|
|
99
|
+
);
|
|
100
|
+
expect(language).toBe('de');
|
|
101
|
+
expect(translations.cookieBanner.title).toBe(
|
|
102
|
+
baseTranslations.de.cookieBanner.title
|
|
103
|
+
);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should return custom translations for unsupported base language', () => {
|
|
107
|
+
const { translations, language } = getTranslations('xx-XX,en;q=0.9', {
|
|
108
|
+
xx: {
|
|
109
|
+
cookieBanner: {
|
|
110
|
+
title: 'XX Title',
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
expect(language).toBe('xx');
|
|
116
|
+
expect(translations.cookieBanner.title).toBe('XX Title');
|
|
117
|
+
expect(translations.cookieBanner.description).toBe(
|
|
118
|
+
baseTranslations.en.cookieBanner.description
|
|
119
|
+
);
|
|
120
|
+
});
|
|
121
|
+
});
|