@c15t/backend 2.0.0-rc.4 → 2.0.0-rc.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core.cjs +830 -74
- package/dist/core.js +807 -75
- package/dist/db/schema.cjs +37 -0
- package/dist/db/schema.js +33 -2
- package/dist/edge.cjs +1106 -0
- package/dist/edge.js +1069 -0
- package/dist/router.cjs +613 -64
- package/dist/router.js +613 -64
- package/{dist → dist-types}/cache/adapters/cloudflare-kv.d.ts +0 -1
- package/{dist → dist-types}/cache/adapters/index.d.ts +0 -1
- package/{dist → dist-types}/cache/adapters/memory.d.ts +0 -1
- package/{dist → dist-types}/cache/adapters/upstash-redis.d.ts +0 -1
- package/{dist → dist-types}/cache/gvl-resolver.d.ts +1 -2
- package/{dist → dist-types}/cache/index.d.ts +0 -1
- package/{dist → dist-types}/cache/keys.d.ts +0 -1
- package/{dist → dist-types}/cache/types.d.ts +0 -1
- package/{dist → dist-types}/core.d.ts +8 -1
- package/{dist → dist-types}/db/migrator/index.d.ts +0 -1
- package/{dist → dist-types}/db/registry/consent-policy.d.ts +0 -1
- package/{dist → dist-types}/db/registry/consent-purpose.d.ts +0 -1
- package/{dist → dist-types}/db/registry/domain.d.ts +0 -1
- package/{dist → dist-types}/db/registry/index.d.ts +22 -2
- package/dist-types/db/registry/runtime-policy-decision.d.ts +60 -0
- package/{dist → dist-types}/db/registry/subject.d.ts +0 -1
- package/{dist → dist-types}/db/registry/types.d.ts +1 -2
- package/{dist → dist-types}/db/registry/utils/generate-id.d.ts +0 -1
- package/{dist → dist-types}/db/registry/utils.d.ts +0 -1
- package/{dist → dist-types}/db/schema/1.0.0/audit-log.d.ts +0 -1
- package/{dist → dist-types}/db/schema/1.0.0/consent-policy.d.ts +0 -1
- package/{dist → dist-types}/db/schema/1.0.0/consent-purpose.d.ts +0 -1
- package/{dist → dist-types}/db/schema/1.0.0/consent-record.d.ts +0 -1
- package/{dist → dist-types}/db/schema/1.0.0/consent.d.ts +1 -2
- package/{dist → dist-types}/db/schema/1.0.0/domain.d.ts +0 -1
- package/{dist → dist-types}/db/schema/1.0.0/index.d.ts +0 -1
- package/{dist → dist-types}/db/schema/1.0.0/subject.d.ts +0 -1
- package/{dist → dist-types}/db/schema/2.0.0/audit-log.d.ts +1 -2
- package/{dist → dist-types}/db/schema/2.0.0/consent-policy.d.ts +1 -2
- package/{dist → dist-types}/db/schema/2.0.0/consent-purpose.d.ts +1 -2
- package/{dist → dist-types}/db/schema/2.0.0/consent.d.ts +5 -2
- package/{dist → dist-types}/db/schema/2.0.0/domain.d.ts +1 -2
- package/{dist → dist-types}/db/schema/2.0.0/index.d.ts +432 -17
- package/dist-types/db/schema/2.0.0/runtime-policy-decision.d.ts +23 -0
- package/{dist → dist-types}/db/schema/2.0.0/subject.d.ts +1 -2
- package/{dist → dist-types}/db/schema/index.d.ts +862 -33
- package/{dist → dist-types}/db/tenant-scope.d.ts +0 -1
- package/{dist → 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 +33 -19
- 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/src/utils/metrics.ts
DELETED
|
@@ -1,402 +0,0 @@
|
|
|
1
|
-
import type { Counter, Histogram, Meter } from '@opentelemetry/api';
|
|
2
|
-
import type { C15TOptions } from '../types';
|
|
3
|
-
import { getMeter, isTelemetryEnabled } from './create-telemetry-options';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Geographic attributes for consent metrics
|
|
7
|
-
*/
|
|
8
|
-
export interface GeoAttributes {
|
|
9
|
-
/** Legal framework (GDPR, CCPA, LGPD, etc.) */
|
|
10
|
-
jurisdiction?: string;
|
|
11
|
-
/** ISO country code (US, DE, FR, etc.) */
|
|
12
|
-
country?: string;
|
|
13
|
-
/** State/province code (CA, BY, etc.) */
|
|
14
|
-
region?: string;
|
|
15
|
-
/** Index signature for additional attributes */
|
|
16
|
-
[key: string]: string | number | boolean | undefined;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Consent metric attributes
|
|
21
|
-
*/
|
|
22
|
-
export interface ConsentMetricAttributes extends GeoAttributes {
|
|
23
|
-
/** Consent type (cookie_banner, privacy_policy, etc.) */
|
|
24
|
-
type: string;
|
|
25
|
-
/** Consent status (accepted, rejected, partial) */
|
|
26
|
-
status?: string;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* HTTP metric attributes
|
|
31
|
-
*/
|
|
32
|
-
export interface HttpMetricAttributes {
|
|
33
|
-
/** HTTP method */
|
|
34
|
-
method: string;
|
|
35
|
-
/** Route pattern (e.g., /subjects/:id) */
|
|
36
|
-
route: string;
|
|
37
|
-
/** HTTP status code */
|
|
38
|
-
status: number;
|
|
39
|
-
/** Error type if applicable */
|
|
40
|
-
errorType?: string;
|
|
41
|
-
/** Index signature for additional attributes */
|
|
42
|
-
[key: string]: string | number | boolean | undefined;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Database metric attributes
|
|
47
|
-
*/
|
|
48
|
-
export interface DbMetricAttributes {
|
|
49
|
-
/** Operation type (find, create, update, delete) */
|
|
50
|
-
operation: string;
|
|
51
|
-
/** Entity type (consent, subject, audit_log, etc.) */
|
|
52
|
-
entity: string;
|
|
53
|
-
/** Error type if applicable */
|
|
54
|
-
errorType?: string;
|
|
55
|
-
/** Index signature for additional attributes */
|
|
56
|
-
[key: string]: string | number | boolean | undefined;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Cache metric attributes
|
|
61
|
-
*/
|
|
62
|
-
export interface CacheMetricAttributes {
|
|
63
|
-
/** Cache layer (bundled, memory, external) */
|
|
64
|
-
layer: 'bundled' | 'memory' | 'external';
|
|
65
|
-
/** Operation type (get, set, delete) */
|
|
66
|
-
operation?: string;
|
|
67
|
-
/** Index signature for additional attributes */
|
|
68
|
-
[key: string]: string | number | boolean | undefined;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* GVL fetch metric attributes
|
|
73
|
-
*/
|
|
74
|
-
export interface GvlMetricAttributes {
|
|
75
|
-
/** Language code */
|
|
76
|
-
language: string;
|
|
77
|
-
/** Source of the data (cache, fetch) */
|
|
78
|
-
source?: 'cache' | 'fetch';
|
|
79
|
-
/** HTTP status code if fetched */
|
|
80
|
-
status?: number;
|
|
81
|
-
/** Error type if applicable */
|
|
82
|
-
errorType?: string;
|
|
83
|
-
/** Index signature for additional attributes */
|
|
84
|
-
[key: string]: string | number | boolean | undefined;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* C15T Metrics container
|
|
89
|
-
* Holds all metric instruments for the c15t backend
|
|
90
|
-
*/
|
|
91
|
-
export interface C15TMetrics {
|
|
92
|
-
// Business metrics
|
|
93
|
-
readonly consentCreated: Counter;
|
|
94
|
-
readonly consentAccepted: Counter;
|
|
95
|
-
readonly consentRejected: Counter;
|
|
96
|
-
readonly subjectCreated: Counter;
|
|
97
|
-
readonly subjectLinked: Counter;
|
|
98
|
-
readonly consentCheckCount: Counter;
|
|
99
|
-
readonly initCount: Counter;
|
|
100
|
-
|
|
101
|
-
// HTTP metrics
|
|
102
|
-
readonly httpRequestDuration: Histogram;
|
|
103
|
-
readonly httpRequestCount: Counter;
|
|
104
|
-
readonly httpErrorCount: Counter;
|
|
105
|
-
|
|
106
|
-
// Database metrics
|
|
107
|
-
readonly dbQueryDuration: Histogram;
|
|
108
|
-
readonly dbQueryCount: Counter;
|
|
109
|
-
readonly dbErrorCount: Counter;
|
|
110
|
-
|
|
111
|
-
// Cache metrics
|
|
112
|
-
readonly cacheHit: Counter;
|
|
113
|
-
readonly cacheMiss: Counter;
|
|
114
|
-
readonly cacheLatency: Histogram;
|
|
115
|
-
|
|
116
|
-
// GVL metrics
|
|
117
|
-
readonly gvlFetchDuration: Histogram;
|
|
118
|
-
readonly gvlFetchCount: Counter;
|
|
119
|
-
readonly gvlFetchError: Counter;
|
|
120
|
-
|
|
121
|
-
// Helper methods
|
|
122
|
-
recordConsentCreated(attributes: ConsentMetricAttributes): void;
|
|
123
|
-
recordConsentAccepted(
|
|
124
|
-
attributes: Omit<ConsentMetricAttributes, 'status'>
|
|
125
|
-
): void;
|
|
126
|
-
recordConsentRejected(
|
|
127
|
-
attributes: Omit<ConsentMetricAttributes, 'status'>
|
|
128
|
-
): void;
|
|
129
|
-
recordSubjectCreated(attributes: GeoAttributes & { domain?: string }): void;
|
|
130
|
-
recordSubjectLinked(identityProvider?: string): void;
|
|
131
|
-
recordConsentCheck(type: string, found: boolean): void;
|
|
132
|
-
recordInit(attributes: GeoAttributes): void;
|
|
133
|
-
recordHttpRequest(attributes: HttpMetricAttributes, durationMs: number): void;
|
|
134
|
-
recordDbQuery(attributes: DbMetricAttributes, durationMs: number): void;
|
|
135
|
-
recordDbError(attributes: DbMetricAttributes): void;
|
|
136
|
-
recordCacheHit(layer: CacheMetricAttributes['layer']): void;
|
|
137
|
-
recordCacheMiss(layer: CacheMetricAttributes['layer']): void;
|
|
138
|
-
recordCacheLatency(
|
|
139
|
-
attributes: CacheMetricAttributes,
|
|
140
|
-
durationMs: number
|
|
141
|
-
): void;
|
|
142
|
-
recordGvlFetch(attributes: GvlMetricAttributes, durationMs: number): void;
|
|
143
|
-
recordGvlError(attributes: GvlMetricAttributes): void;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Sanitize attributes - remove undefined values and convert to strings
|
|
148
|
-
*/
|
|
149
|
-
function sanitizeAttributes<
|
|
150
|
-
T extends Record<string, string | number | boolean | undefined>,
|
|
151
|
-
>(attrs: T): Record<string, string | number | boolean> {
|
|
152
|
-
return Object.fromEntries(
|
|
153
|
-
Object.entries(attrs).filter(([_, v]) => v !== undefined && v !== null) as [
|
|
154
|
-
string,
|
|
155
|
-
string | number | boolean,
|
|
156
|
-
][]
|
|
157
|
-
);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Create a C15TMetrics instance from an OpenTelemetry Meter.
|
|
162
|
-
*
|
|
163
|
-
* @param meter - OpenTelemetry Meter to create instruments from
|
|
164
|
-
* @returns C15TMetrics object with all instruments and helper methods
|
|
165
|
-
*/
|
|
166
|
-
function createMetrics(meter: Meter): C15TMetrics {
|
|
167
|
-
// Business metrics - High value for c15t
|
|
168
|
-
const consentCreated = meter.createCounter('c15t.consent.created', {
|
|
169
|
-
description: 'Number of consent submissions',
|
|
170
|
-
unit: '1',
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
const consentAccepted = meter.createCounter('c15t.consent.accepted', {
|
|
174
|
-
description: 'Number of consents accepted',
|
|
175
|
-
unit: '1',
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
const consentRejected = meter.createCounter('c15t.consent.rejected', {
|
|
179
|
-
description: 'Number of consents rejected',
|
|
180
|
-
unit: '1',
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
const subjectCreated = meter.createCounter('c15t.subject.created', {
|
|
184
|
-
description: 'Number of new subjects created',
|
|
185
|
-
unit: '1',
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
const subjectLinked = meter.createCounter('c15t.subject.linked', {
|
|
189
|
-
description: 'Number of subjects linked to external ID',
|
|
190
|
-
unit: '1',
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
const consentCheckCount = meter.createCounter('c15t.consent_check.count', {
|
|
194
|
-
description: 'Number of cross-device consent checks',
|
|
195
|
-
unit: '1',
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
const initCount = meter.createCounter('c15t.init.count', {
|
|
199
|
-
description: 'Number of init endpoint calls',
|
|
200
|
-
unit: '1',
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
// HTTP metrics
|
|
204
|
-
const httpRequestDuration = meter.createHistogram(
|
|
205
|
-
'c15t.http.request.duration',
|
|
206
|
-
{
|
|
207
|
-
description: 'HTTP request latency',
|
|
208
|
-
unit: 'ms',
|
|
209
|
-
}
|
|
210
|
-
);
|
|
211
|
-
|
|
212
|
-
const httpRequestCount = meter.createCounter('c15t.http.request.count', {
|
|
213
|
-
description: 'Number of HTTP requests',
|
|
214
|
-
unit: '1',
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
const httpErrorCount = meter.createCounter('c15t.http.error.count', {
|
|
218
|
-
description: 'Number of HTTP errors',
|
|
219
|
-
unit: '1',
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
// Database metrics
|
|
223
|
-
const dbQueryDuration = meter.createHistogram('c15t.db.query.duration', {
|
|
224
|
-
description: 'Database query latency',
|
|
225
|
-
unit: 'ms',
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
const dbQueryCount = meter.createCounter('c15t.db.query.count', {
|
|
229
|
-
description: 'Number of database queries',
|
|
230
|
-
unit: '1',
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
const dbErrorCount = meter.createCounter('c15t.db.error.count', {
|
|
234
|
-
description: 'Number of database errors',
|
|
235
|
-
unit: '1',
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
// Cache metrics
|
|
239
|
-
const cacheHit = meter.createCounter('c15t.cache.hit', {
|
|
240
|
-
description: 'Number of cache hits',
|
|
241
|
-
unit: '1',
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
const cacheMiss = meter.createCounter('c15t.cache.miss', {
|
|
245
|
-
description: 'Number of cache misses',
|
|
246
|
-
unit: '1',
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
const cacheLatency = meter.createHistogram('c15t.cache.latency', {
|
|
250
|
-
description: 'Cache operation latency',
|
|
251
|
-
unit: 'ms',
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
// GVL metrics
|
|
255
|
-
const gvlFetchDuration = meter.createHistogram('c15t.gvl.fetch.duration', {
|
|
256
|
-
description: 'GVL fetch latency',
|
|
257
|
-
unit: 'ms',
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
const gvlFetchCount = meter.createCounter('c15t.gvl.fetch.count', {
|
|
261
|
-
description: 'Number of GVL fetches',
|
|
262
|
-
unit: '1',
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
const gvlFetchError = meter.createCounter('c15t.gvl.fetch.error', {
|
|
266
|
-
description: 'Number of GVL fetch errors',
|
|
267
|
-
unit: '1',
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
return {
|
|
271
|
-
// Instruments
|
|
272
|
-
consentCreated,
|
|
273
|
-
consentAccepted,
|
|
274
|
-
consentRejected,
|
|
275
|
-
subjectCreated,
|
|
276
|
-
subjectLinked,
|
|
277
|
-
consentCheckCount,
|
|
278
|
-
initCount,
|
|
279
|
-
httpRequestDuration,
|
|
280
|
-
httpRequestCount,
|
|
281
|
-
httpErrorCount,
|
|
282
|
-
dbQueryDuration,
|
|
283
|
-
dbQueryCount,
|
|
284
|
-
dbErrorCount,
|
|
285
|
-
cacheHit,
|
|
286
|
-
cacheMiss,
|
|
287
|
-
cacheLatency,
|
|
288
|
-
gvlFetchDuration,
|
|
289
|
-
gvlFetchCount,
|
|
290
|
-
gvlFetchError,
|
|
291
|
-
|
|
292
|
-
// Helper methods
|
|
293
|
-
recordConsentCreated(attributes: ConsentMetricAttributes): void {
|
|
294
|
-
consentCreated.add(1, sanitizeAttributes(attributes));
|
|
295
|
-
},
|
|
296
|
-
|
|
297
|
-
recordConsentAccepted(
|
|
298
|
-
attributes: Omit<ConsentMetricAttributes, 'status'>
|
|
299
|
-
): void {
|
|
300
|
-
consentAccepted.add(1, sanitizeAttributes(attributes));
|
|
301
|
-
},
|
|
302
|
-
|
|
303
|
-
recordConsentRejected(
|
|
304
|
-
attributes: Omit<ConsentMetricAttributes, 'status'>
|
|
305
|
-
): void {
|
|
306
|
-
consentRejected.add(1, sanitizeAttributes(attributes));
|
|
307
|
-
},
|
|
308
|
-
|
|
309
|
-
recordSubjectCreated(
|
|
310
|
-
attributes: GeoAttributes & { domain?: string }
|
|
311
|
-
): void {
|
|
312
|
-
subjectCreated.add(1, sanitizeAttributes(attributes));
|
|
313
|
-
},
|
|
314
|
-
|
|
315
|
-
recordSubjectLinked(identityProvider?: string): void {
|
|
316
|
-
subjectLinked.add(1, {
|
|
317
|
-
identityProvider: identityProvider || 'unknown',
|
|
318
|
-
});
|
|
319
|
-
},
|
|
320
|
-
|
|
321
|
-
recordConsentCheck(type: string, found: boolean): void {
|
|
322
|
-
consentCheckCount.add(1, { type, found: String(found) });
|
|
323
|
-
},
|
|
324
|
-
|
|
325
|
-
recordInit(attributes: GeoAttributes): void {
|
|
326
|
-
initCount.add(1, sanitizeAttributes(attributes));
|
|
327
|
-
},
|
|
328
|
-
|
|
329
|
-
recordHttpRequest(
|
|
330
|
-
attributes: HttpMetricAttributes,
|
|
331
|
-
durationMs: number
|
|
332
|
-
): void {
|
|
333
|
-
const attrs = sanitizeAttributes(attributes);
|
|
334
|
-
httpRequestCount.add(1, attrs);
|
|
335
|
-
httpRequestDuration.record(durationMs, attrs);
|
|
336
|
-
|
|
337
|
-
if (attributes.status >= 400) {
|
|
338
|
-
httpErrorCount.add(1, attrs);
|
|
339
|
-
}
|
|
340
|
-
},
|
|
341
|
-
|
|
342
|
-
recordDbQuery(attributes: DbMetricAttributes, durationMs: number): void {
|
|
343
|
-
const attrs = sanitizeAttributes(attributes);
|
|
344
|
-
dbQueryCount.add(1, attrs);
|
|
345
|
-
dbQueryDuration.record(durationMs, attrs);
|
|
346
|
-
},
|
|
347
|
-
|
|
348
|
-
recordDbError(attributes: DbMetricAttributes): void {
|
|
349
|
-
dbErrorCount.add(1, sanitizeAttributes(attributes));
|
|
350
|
-
},
|
|
351
|
-
|
|
352
|
-
recordCacheHit(layer: CacheMetricAttributes['layer']): void {
|
|
353
|
-
cacheHit.add(1, { layer });
|
|
354
|
-
},
|
|
355
|
-
|
|
356
|
-
recordCacheMiss(layer: CacheMetricAttributes['layer']): void {
|
|
357
|
-
cacheMiss.add(1, { layer });
|
|
358
|
-
},
|
|
359
|
-
|
|
360
|
-
recordCacheLatency(
|
|
361
|
-
attributes: CacheMetricAttributes,
|
|
362
|
-
durationMs: number
|
|
363
|
-
): void {
|
|
364
|
-
cacheLatency.record(durationMs, sanitizeAttributes(attributes));
|
|
365
|
-
},
|
|
366
|
-
|
|
367
|
-
recordGvlFetch(attributes: GvlMetricAttributes, durationMs: number): void {
|
|
368
|
-
const attrs = sanitizeAttributes(attributes);
|
|
369
|
-
gvlFetchCount.add(1, attrs);
|
|
370
|
-
gvlFetchDuration.record(durationMs, attrs);
|
|
371
|
-
},
|
|
372
|
-
|
|
373
|
-
recordGvlError(attributes: GvlMetricAttributes): void {
|
|
374
|
-
gvlFetchError.add(1, sanitizeAttributes(attributes));
|
|
375
|
-
},
|
|
376
|
-
};
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
// Lazy-initialized metric instances
|
|
380
|
-
let metricsInstance: C15TMetrics | null = null;
|
|
381
|
-
|
|
382
|
-
/**
|
|
383
|
-
* Get or create the metrics instance for the c15t backend.
|
|
384
|
-
* Returns null if telemetry is not enabled.
|
|
385
|
-
*
|
|
386
|
-
* @param options - C15T options for telemetry configuration
|
|
387
|
-
* @returns C15TMetrics instance or null if telemetry is disabled
|
|
388
|
-
*/
|
|
389
|
-
export function getMetrics(options?: C15TOptions): C15TMetrics | null {
|
|
390
|
-
if (metricsInstance) return metricsInstance;
|
|
391
|
-
if (!isTelemetryEnabled(options)) return null;
|
|
392
|
-
|
|
393
|
-
metricsInstance = createMetrics(getMeter(options));
|
|
394
|
-
return metricsInstance;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
/**
|
|
398
|
-
* Reset the metrics instance (useful for testing)
|
|
399
|
-
*/
|
|
400
|
-
export function resetMetrics(): void {
|
|
401
|
-
metricsInstance = null;
|
|
402
|
-
}
|
|
@@ -1,323 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* E2E test: every span attribute key must be in the allowlist,
|
|
3
|
-
* and no attribute value may match PII patterns.
|
|
4
|
-
*
|
|
5
|
-
* Uses a real BasicTracerProvider + InMemorySpanExporter (no mocks).
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
BasicTracerProvider,
|
|
10
|
-
InMemorySpanExporter,
|
|
11
|
-
SimpleSpanProcessor,
|
|
12
|
-
} from '@opentelemetry/sdk-trace-base';
|
|
13
|
-
import { afterEach, beforeAll, describe, expect, it } from 'vitest';
|
|
14
|
-
import type { C15TOptions } from '../types';
|
|
15
|
-
import {
|
|
16
|
-
createRequestSpan,
|
|
17
|
-
createTelemetryOptions,
|
|
18
|
-
resetTelemetryConfig,
|
|
19
|
-
withRequestSpan,
|
|
20
|
-
} from './create-telemetry-options';
|
|
21
|
-
import {
|
|
22
|
-
withCacheSpan,
|
|
23
|
-
withDatabaseSpan,
|
|
24
|
-
withExternalSpan,
|
|
25
|
-
} from './instrumentation';
|
|
26
|
-
|
|
27
|
-
// ── Allowlist ───────────────────────────────────────────────────────────
|
|
28
|
-
const ALLOWED_KEYS = new Set([
|
|
29
|
-
'http.method',
|
|
30
|
-
'http.route',
|
|
31
|
-
'http.host',
|
|
32
|
-
'http.path',
|
|
33
|
-
'http.url',
|
|
34
|
-
'error.type',
|
|
35
|
-
'db.system',
|
|
36
|
-
'db.operation',
|
|
37
|
-
'db.entity',
|
|
38
|
-
'cache.operation',
|
|
39
|
-
'cache.layer',
|
|
40
|
-
'service.name',
|
|
41
|
-
'service.version',
|
|
42
|
-
'tenant.id',
|
|
43
|
-
]);
|
|
44
|
-
|
|
45
|
-
const BLOCKED_KEYS = new Set(['error.stack', 'error.message']);
|
|
46
|
-
|
|
47
|
-
const PII_PATTERNS = [
|
|
48
|
-
/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/, // IPv4
|
|
49
|
-
/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/, // email
|
|
50
|
-
/at .+\(.+:\d+:\d+\)/, // stack trace line
|
|
51
|
-
/\?[^=]+=/, // query string
|
|
52
|
-
];
|
|
53
|
-
|
|
54
|
-
// ── Provider setup ──────────────────────────────────────────────────────
|
|
55
|
-
const exporter = new InMemorySpanExporter();
|
|
56
|
-
const provider = new BasicTracerProvider();
|
|
57
|
-
provider.addSpanProcessor(new SimpleSpanProcessor(exporter));
|
|
58
|
-
provider.register();
|
|
59
|
-
|
|
60
|
-
const tracer = provider.getTracer('pii-test');
|
|
61
|
-
|
|
62
|
-
function telemetryOptions(): C15TOptions {
|
|
63
|
-
return {
|
|
64
|
-
trustedOrigins: [],
|
|
65
|
-
adapter: {} as C15TOptions['adapter'],
|
|
66
|
-
telemetry: {
|
|
67
|
-
enabled: true,
|
|
68
|
-
tracer,
|
|
69
|
-
},
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// ── Helpers ─────────────────────────────────────────────────────────────
|
|
74
|
-
function drainSpans() {
|
|
75
|
-
const spans = exporter.getFinishedSpans();
|
|
76
|
-
const copy = [...spans];
|
|
77
|
-
exporter.reset();
|
|
78
|
-
return copy;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function assertAllowlist(spans: ReturnType<typeof drainSpans>) {
|
|
82
|
-
for (const span of spans) {
|
|
83
|
-
const attrs = span.attributes;
|
|
84
|
-
for (const key of Object.keys(attrs)) {
|
|
85
|
-
expect(ALLOWED_KEYS.has(key)).toBe(true);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function assertNoBlockedKeys(spans: ReturnType<typeof drainSpans>) {
|
|
91
|
-
for (const span of spans) {
|
|
92
|
-
const attrs = span.attributes;
|
|
93
|
-
for (const key of Object.keys(attrs)) {
|
|
94
|
-
expect(BLOCKED_KEYS.has(key)).toBe(false);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function assertNoPiiValues(spans: ReturnType<typeof drainSpans>) {
|
|
100
|
-
for (const span of spans) {
|
|
101
|
-
const attrs = span.attributes;
|
|
102
|
-
for (const [key, value] of Object.entries(attrs)) {
|
|
103
|
-
if (typeof value === 'string') {
|
|
104
|
-
for (const pattern of PII_PATTERNS) {
|
|
105
|
-
expect(
|
|
106
|
-
pattern.test(value),
|
|
107
|
-
`Attribute "${key}" value "${value}" matches PII pattern ${pattern}`
|
|
108
|
-
).toBe(false);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// ── Tests ───────────────────────────────────────────────────────────────
|
|
116
|
-
beforeAll(() => {
|
|
117
|
-
createTelemetryOptions('pii-test', { enabled: true, tracer });
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
afterEach(() => {
|
|
121
|
-
exporter.reset();
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
describe('telemetry PII safeguards', () => {
|
|
125
|
-
// 1. withDatabaseSpan success
|
|
126
|
-
it('withDatabaseSpan attributes pass allowlist', async () => {
|
|
127
|
-
await withDatabaseSpan(
|
|
128
|
-
{ operation: 'find', entity: 'subject' },
|
|
129
|
-
async () => 'ok',
|
|
130
|
-
telemetryOptions()
|
|
131
|
-
);
|
|
132
|
-
|
|
133
|
-
const spans = drainSpans();
|
|
134
|
-
expect(spans.length).toBe(1);
|
|
135
|
-
assertAllowlist(spans);
|
|
136
|
-
assertNoPiiValues(spans);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
// 2. withExternalSpan success — span name = hostname only
|
|
140
|
-
it('withExternalSpan span name is HTTP METHOD hostname only', async () => {
|
|
141
|
-
await withExternalSpan(
|
|
142
|
-
{ url: 'https://api.example.com/v1/charges', method: 'GET' },
|
|
143
|
-
async () => 'ok',
|
|
144
|
-
telemetryOptions()
|
|
145
|
-
);
|
|
146
|
-
|
|
147
|
-
const spans = drainSpans();
|
|
148
|
-
expect(spans.length).toBe(1);
|
|
149
|
-
expect(spans[0].name).toBe('HTTP GET api.example.com');
|
|
150
|
-
assertAllowlist(spans);
|
|
151
|
-
assertNoPiiValues(spans);
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
// 3. withExternalSpan strips query params from http.url
|
|
155
|
-
it('withExternalSpan strips query params from http.url', async () => {
|
|
156
|
-
await withExternalSpan(
|
|
157
|
-
{
|
|
158
|
-
url: 'https://api.example.com/v1/data?key=secret&email=user@ex.com',
|
|
159
|
-
method: 'POST',
|
|
160
|
-
},
|
|
161
|
-
async () => 'ok',
|
|
162
|
-
telemetryOptions()
|
|
163
|
-
);
|
|
164
|
-
|
|
165
|
-
const spans = drainSpans();
|
|
166
|
-
expect(spans.length).toBe(1);
|
|
167
|
-
const url = spans[0].attributes['http.url'] as string;
|
|
168
|
-
expect(url).toBe('https://api.example.com/v1/data');
|
|
169
|
-
expect(url).not.toContain('?');
|
|
170
|
-
assertAllowlist(spans);
|
|
171
|
-
assertNoPiiValues(spans);
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
// 4. withCacheSpan success
|
|
175
|
-
it('withCacheSpan has no unexpected attributes', async () => {
|
|
176
|
-
await withCacheSpan('get', 'memory', async () => 'ok', telemetryOptions());
|
|
177
|
-
|
|
178
|
-
const spans = drainSpans();
|
|
179
|
-
expect(spans.length).toBe(1);
|
|
180
|
-
assertAllowlist(spans);
|
|
181
|
-
assertNoPiiValues(spans);
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
// 5. withRequestSpan success — no http.path
|
|
185
|
-
it('withRequestSpan has no http.path attribute', async () => {
|
|
186
|
-
await withRequestSpan(
|
|
187
|
-
'GET',
|
|
188
|
-
'/subjects/abc123',
|
|
189
|
-
async () => 'ok',
|
|
190
|
-
telemetryOptions()
|
|
191
|
-
);
|
|
192
|
-
|
|
193
|
-
const spans = drainSpans();
|
|
194
|
-
expect(spans.length).toBe(1);
|
|
195
|
-
expect(spans[0].attributes['http.path']).toBeUndefined();
|
|
196
|
-
assertAllowlist(spans);
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
// 6. createRequestSpan direct — no http.path
|
|
200
|
-
it('createRequestSpan has no http.path attribute', () => {
|
|
201
|
-
const span = createRequestSpan(
|
|
202
|
-
'POST',
|
|
203
|
-
'/subjects/xyz789',
|
|
204
|
-
telemetryOptions()
|
|
205
|
-
);
|
|
206
|
-
|
|
207
|
-
expect(span).toBeDefined();
|
|
208
|
-
span!.end();
|
|
209
|
-
|
|
210
|
-
const spans = drainSpans();
|
|
211
|
-
expect(spans.length).toBe(1);
|
|
212
|
-
expect(spans[0].attributes['http.path']).toBeUndefined();
|
|
213
|
-
assertAllowlist(spans);
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
// 7. withDatabaseSpan error — PII in message
|
|
217
|
-
it('withDatabaseSpan error does not leak error.message or error.stack', async () => {
|
|
218
|
-
await expect(
|
|
219
|
-
withDatabaseSpan(
|
|
220
|
-
{ operation: 'find', entity: 'subject' },
|
|
221
|
-
async () => {
|
|
222
|
-
throw new Error('User user@example.com not found at 192.168.1.1');
|
|
223
|
-
},
|
|
224
|
-
telemetryOptions()
|
|
225
|
-
)
|
|
226
|
-
).rejects.toThrow();
|
|
227
|
-
|
|
228
|
-
const spans = drainSpans();
|
|
229
|
-
expect(spans.length).toBe(1);
|
|
230
|
-
assertNoBlockedKeys(spans);
|
|
231
|
-
assertNoPiiValues(spans);
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
// 8. withExternalSpan error — IP in message
|
|
235
|
-
it('withExternalSpan error does not leak error.message or error.stack', async () => {
|
|
236
|
-
await expect(
|
|
237
|
-
withExternalSpan(
|
|
238
|
-
{ url: 'https://api.example.com/v1/data', method: 'GET' },
|
|
239
|
-
async () => {
|
|
240
|
-
throw new Error('Connection refused at 10.0.0.1:5432');
|
|
241
|
-
},
|
|
242
|
-
telemetryOptions()
|
|
243
|
-
)
|
|
244
|
-
).rejects.toThrow();
|
|
245
|
-
|
|
246
|
-
const spans = drainSpans();
|
|
247
|
-
expect(spans.length).toBe(1);
|
|
248
|
-
assertNoBlockedKeys(spans);
|
|
249
|
-
assertNoPiiValues(spans);
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
// 9. withCacheSpan error
|
|
253
|
-
it('withCacheSpan error does not leak error.message or error.stack', async () => {
|
|
254
|
-
await expect(
|
|
255
|
-
withCacheSpan(
|
|
256
|
-
'get',
|
|
257
|
-
'external',
|
|
258
|
-
async () => {
|
|
259
|
-
throw new Error('Redis timeout at 172.16.0.1');
|
|
260
|
-
},
|
|
261
|
-
telemetryOptions()
|
|
262
|
-
)
|
|
263
|
-
).rejects.toThrow();
|
|
264
|
-
|
|
265
|
-
const spans = drainSpans();
|
|
266
|
-
expect(spans.length).toBe(1);
|
|
267
|
-
assertNoBlockedKeys(spans);
|
|
268
|
-
assertNoPiiValues(spans);
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
// 10. withRequestSpan error
|
|
272
|
-
it('withRequestSpan error does not leak error.message or error.stack', async () => {
|
|
273
|
-
await expect(
|
|
274
|
-
withRequestSpan(
|
|
275
|
-
'GET',
|
|
276
|
-
'/subjects/abc123',
|
|
277
|
-
async () => {
|
|
278
|
-
throw new Error('Subject user@corp.io failed auth');
|
|
279
|
-
},
|
|
280
|
-
telemetryOptions()
|
|
281
|
-
)
|
|
282
|
-
).rejects.toThrow();
|
|
283
|
-
|
|
284
|
-
const spans = drainSpans();
|
|
285
|
-
expect(spans.length).toBe(1);
|
|
286
|
-
assertNoBlockedKeys(spans);
|
|
287
|
-
assertNoPiiValues(spans);
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
// 11. All 4 span types combined — every key in allowlist
|
|
291
|
-
it('all span types combined: every attribute key is in allowlist', async () => {
|
|
292
|
-
// database
|
|
293
|
-
await withDatabaseSpan(
|
|
294
|
-
{ operation: 'create', entity: 'consent' },
|
|
295
|
-
async () => 'ok',
|
|
296
|
-
telemetryOptions()
|
|
297
|
-
);
|
|
298
|
-
|
|
299
|
-
// external
|
|
300
|
-
await withExternalSpan(
|
|
301
|
-
{ url: 'https://vendor.example.com/api', method: 'POST' },
|
|
302
|
-
async () => 'ok',
|
|
303
|
-
telemetryOptions()
|
|
304
|
-
);
|
|
305
|
-
|
|
306
|
-
// cache
|
|
307
|
-
await withCacheSpan('set', 'bundled', async () => 'ok', telemetryOptions());
|
|
308
|
-
|
|
309
|
-
// request
|
|
310
|
-
await withRequestSpan(
|
|
311
|
-
'DELETE',
|
|
312
|
-
'/consents/123',
|
|
313
|
-
async () => 'ok',
|
|
314
|
-
telemetryOptions()
|
|
315
|
-
);
|
|
316
|
-
|
|
317
|
-
const spans = drainSpans();
|
|
318
|
-
expect(spans.length).toBe(4);
|
|
319
|
-
assertAllowlist(spans);
|
|
320
|
-
assertNoBlockedKeys(spans);
|
|
321
|
-
assertNoPiiValues(spans);
|
|
322
|
-
});
|
|
323
|
-
});
|