@c15t/backend 2.1.0 → 2.2.0-canary-20260727202135
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/AGENTS.md +20 -0
- package/README.md +25 -12
- package/dist/{583.js → 261.js} +1 -1
- package/dist/{915.js → 49.js} +202 -123
- package/dist/{302.js → 744.js} +4 -3
- package/dist/cache.cjs +17 -13
- package/dist/cache.js +2 -2
- package/dist/core.cjs +223 -138
- package/dist/core.js +9 -8
- package/dist/db/adapters/drizzle.cjs +9 -5
- package/dist/db/adapters/kysely.cjs +9 -5
- package/dist/db/adapters/mongo.cjs +9 -5
- package/dist/db/adapters/prisma.cjs +9 -5
- package/dist/db/adapters/typeorm.cjs +9 -5
- package/dist/db/adapters.js +1 -0
- package/dist/db/migrator.cjs +12 -8
- package/dist/db/schema.cjs +24 -17
- package/dist/db/schema.js +2 -2
- package/dist/define-config.cjs +12 -8
- package/dist/edge.cjs +59 -18
- package/dist/edge.js +49 -12
- package/dist/router.cjs +229 -124
- package/dist/router.js +1 -1
- package/dist/types/index.cjs +9 -5
- package/dist-types/db/registry/index.d.ts +35 -35
- package/dist-types/db/registry/utils/generate-id.d.ts +32 -0
- package/dist-types/db/schema/1.0.0/index.d.ts +36 -1096
- package/dist-types/db/schema/2.0.0/consent-policy.d.ts +2 -2
- package/dist-types/db/schema/2.0.0/index.d.ts +16 -599
- package/dist-types/db/schema/index.d.ts +253 -2479
- package/dist-types/handlers/init/index.d.ts +1 -1
- package/dist-types/handlers/subject/consent-idempotency.d.ts +87 -0
- package/dist-types/handlers/subject/consent-time.d.ts +12 -0
- package/dist-types/handlers/subject/get.handler.d.ts +1 -3
- package/dist-types/handlers/subject/list.handler.d.ts +1 -3
- package/dist-types/handlers/subject/post.handler.d.ts +5 -1
- package/dist-types/middleware/cors/is-origin-trusted.d.ts +0 -11
- package/dist-types/middleware/openapi/config.d.ts +6 -6
- package/dist-types/version.d.ts +1 -1
- package/docs/README.md +15 -44
- package/docs/self-host/api/configuration.md +98 -0
- package/docs/{api → self-host/api}/endpoints.md +3 -2
- package/docs/{guides → self-host/guides}/caching.md +3 -1
- package/docs/{guides → self-host/guides}/database-setup.md +2 -6
- package/docs/{guides → self-host/guides}/edge-deployment.md +3 -2
- package/docs/{guides → self-host/guides}/framework-integration.md +1 -0
- package/docs/{guides → self-host/guides}/iab-tcf.md +6 -4
- package/docs/{guides → self-host/guides}/observability.md +1 -0
- package/docs/{guides → self-host/guides}/policy-packs.md +3 -1
- package/docs/{quickstart.md → self-host/quickstart.md} +10 -21
- package/package.json +24 -19
- package/readme.json +13 -7
- package/docs/api/configuration.md +0 -208
- /package/dist/{942.js → 292.js} +0 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# @c15t/backend
|
|
2
|
+
|
|
3
|
+
> Self-hosted c15t backend docs for configuration, APIs, storage, policy packs, and operations.
|
|
4
|
+
|
|
5
|
+
These docs ship inside the package so coding agents can read them offline. Open the topic file you need from the list below — paths are relative to this file.
|
|
6
|
+
|
|
7
|
+
## Self Host
|
|
8
|
+
|
|
9
|
+
Run the c15t backend, configure storage, and operate consent infrastructure.
|
|
10
|
+
|
|
11
|
+
- [Configuration Reference](./docs/self-host/api/configuration.md): Complete reference for all c15t backend configuration options.
|
|
12
|
+
- [API Endpoints](./docs/self-host/api/endpoints.md): Full reference for every c15t consent backend endpoint.
|
|
13
|
+
- [Caching](./docs/self-host/guides/caching.md): Add a caching layer to your self-hosted c15t backend for production performance.
|
|
14
|
+
- [Database Setup](./docs/self-host/guides/database-setup.md): Configure a database adapter for your self-hosted c15t backend.
|
|
15
|
+
- [Edge Deployment](./docs/self-host/guides/edge-deployment.md): Run consent policy resolution at the edge for faster initial banner loads.
|
|
16
|
+
- [Framework Integration](./docs/self-host/guides/framework-integration.md): Mount the c15t consent backend in any JavaScript framework or runtime.
|
|
17
|
+
- [IAB TCF Support](./docs/self-host/guides/iab-tcf.md): Enable IAB Transparency and Consent Framework (TCF) support in your self-hosted backend.
|
|
18
|
+
- [Observability](./docs/self-host/guides/observability.md): Add logging, metrics, and tracing to your self-hosted c15t backend.
|
|
19
|
+
- [Policy Packs](./docs/self-host/guides/policy-packs.md): Configure regional policy packs on the backend and return deterministic runtime policy decisions from /init.
|
|
20
|
+
- [Quickstart](./docs/self-host/quickstart.md): Self-host the c15t consent management backend in your own infrastructure.
|
package/README.md
CHANGED
|
@@ -9,25 +9,27 @@
|
|
|
9
9
|
|
|
10
10
|
# @c15t/backend: Consent Management Backend
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
<p>
|
|
13
|
+
<a href="https://www.npmjs.com/package/@c15t/backend"><picture><source media="(prefers-color-scheme: dark)" srcset="https://shieldcn.dev/npm/%40c15t%2Fbackend.svg?variant=outline&mode=dark"><img src="https://shieldcn.dev/npm/%40c15t%2Fbackend.svg?variant=outline&mode=light" alt="Latest NPM Version"></picture></a>
|
|
14
|
+
<a href="https://github.com/c15t/c15t"><picture><source media="(prefers-color-scheme: dark)" srcset="https://shieldcn.dev/github/c15t/c15t/stars.svg?variant=outline&mode=dark"><img src="https://shieldcn.dev/github/c15t/c15t/stars.svg?variant=outline&mode=light" alt="Stars"></picture></a>
|
|
15
|
+
<a href="https://github.com/c15t/c15t/blob/main/LICENSE.md"><picture><source media="(prefers-color-scheme: dark)" srcset="https://shieldcn.dev/github/c15t/c15t/license.svg?variant=outline&mode=dark"><img src="https://shieldcn.dev/github/c15t/c15t/license.svg?variant=outline&mode=light" alt="License"></picture></a>
|
|
16
|
+
<a href="https://c15t.link/discord"><picture><source media="(prefers-color-scheme: dark)" srcset="https://shieldcn.dev/discord/1312171102268690493.svg?variant=outline&mode=dark"><img src="https://shieldcn.dev/discord/1312171102268690493.svg?variant=outline&mode=light" alt="Discord"></picture></a>
|
|
17
|
+
<a href="https://skills.sh/c15t/skills/c15t"><picture><source media="(prefers-color-scheme: dark)" srcset="https://shieldcn.dev/skills/c15t/skills/c15t.svg?variant=outline&mode=dark"><img src="https://shieldcn.dev/skills/c15t/skills/c15t.svg?variant=outline&mode=light" alt="Skills"></picture></a>
|
|
18
|
+
<a href="https://inth.com?utm_source=npm&utm_medium=readme&utm_campaign=oss_readme&utm_content=%40c15t%2Fbackend"><picture><source media="(prefers-color-scheme: dark)" srcset="https://shieldcn.dev/badge/Made%20By-Inth-ffc803.svg?logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCAzOTMgNDAwIj48cGF0aCBmaWxsPSIjMDAwIiBkPSJNMTgyLjY2MiAwdjM2Ljg5NWgtNTkuMDMxdjgyLjczM2g1OS4wMzF2MzYuODkzSDI3LjQ4MnYtMzYuODkzaDU5LjAzVjM2Ljg5NWgtNTkuMDNWMHpNMzIxLjk0MSA4OS44NVYwaDM1LjM1NXYxNTYuNTIxaC0yNS43MTNsLTg2LjEzNy05MC4zNjR2OTAuMzY0aC0zNS4zNTVWMGgyNi4zNTV6Ii8%2BPHBhdGggZmlsbD0iIzAwMCIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMzE4LjU3MSAxODUuNzE0aDc0LjI4NlY0MDBIMFYxODUuNzE0aDI3Mi44NTd2LTQ3LjE0M3ptLTI5MS4wOSAyOC45Njl2MzcuMTE4aDU4LjEzN3YxMTkuNjI4aDM2Ljg5NVYyNTEuODAxaDU4LjU4NHYtMzcuMTE4em0xODIuNjEuMjI0djE1Ni41MjJoMzYuODk0VjMxMy41OWg3My4zNDF2NTcuODM5aDM3LjExOFYyMTQuOTA3aC0zNy4xMTh2NjEuNzg4aC03My4zNDF2LTYxLjc4OHoiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg%3D%3D&color=ffc803&labelTextColor=000000&valueColor=000000&mode=dark"><img src="https://shieldcn.dev/badge/Made%20By-Inth-ffc803.svg?logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCAzOTMgNDAwIj48cGF0aCBmaWxsPSIjMDAwIiBkPSJNMTgyLjY2MiAwdjM2Ljg5NWgtNTkuMDMxdjgyLjczM2g1OS4wMzF2MzYuODkzSDI3LjQ4MnYtMzYuODkzaDU5LjAzVjM2Ljg5NWgtNTkuMDNWMHpNMzIxLjk0MSA4OS44NVYwaDM1LjM1NXYxNTYuNTIxaC0yNS43MTNsLTg2LjEzNy05MC4zNjR2OTAuMzY0aC0zNS4zNTVWMGgyNi4zNTV6Ii8%2BPHBhdGggZmlsbD0iIzAwMCIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMzE4LjU3MSAxODUuNzE0aDc0LjI4NlY0MDBIMFYxODUuNzE0aDI3Mi44NTd2LTQ3LjE0M3ptLTI5MS4wOSAyOC45Njl2MzcuMTE4aDU4LjEzN3YxMTkuNjI4aDM2Ljg5NVYyNTEuODAxaDU4LjU4NHYtMzcuMTE4em0xODIuNjEuMjI0djE1Ni41MjJoMzYuODk0VjMxMy41OWg3My4zNDF2NTcuODM5aDM3LjExOFYyMTQuOTA3aC0zNy4xMTh2NjEuNzg4aC03My4zNDF2LTYxLjc4OHoiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg%3D%3D&color=ffc803&labelTextColor=000000&valueColor=000000&mode=light" alt="Made by Inth"></picture></a>
|
|
19
|
+
</p>
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
Self-hostable consent management backend for c15t. Powers cookie banners, consent managers, preference centers, policy resolution, audit logs, and consent records.
|
|
22
22
|
|
|
23
23
|
## Key Features
|
|
24
24
|
|
|
25
|
-
-
|
|
26
|
-
-
|
|
25
|
+
- Self-hosted CMP backend: Run c15t on your own infrastructure with your own database
|
|
26
|
+
- Consent Management: Track and manage user consent preferences and consent records
|
|
27
|
+
- Geo-Location: Identify a user's location to show relevant consent preferences
|
|
27
28
|
- Server-Side Translation: Reduce banner bundle size by translating consent preferences on the server
|
|
28
29
|
- Audit Logging: Comprehensive logging of all consent-related actions
|
|
29
30
|
- Domain Management: Handle multiple domains and subdomains
|
|
30
31
|
- Policy Management: Version and manage consent policies
|
|
32
|
+
- IAB TCF 2.3 support: Configure GVL, CMP registration, and custom vendors
|
|
31
33
|
|
|
32
34
|
## Prerequisites
|
|
33
35
|
|
|
@@ -48,7 +50,18 @@ Configure your database adapter (supports Drizzle, Kysely, Prisma, TypeORM, and
|
|
|
48
50
|
|
|
49
51
|
## Documentation
|
|
50
52
|
|
|
51
|
-
For further information, guides, and examples visit the [reference documentation](https://c15t.com/docs/self-host/
|
|
53
|
+
For further information, guides, and examples visit the [reference documentation](https://c15t.com/docs/self-host/quickstart).
|
|
54
|
+
|
|
55
|
+
## Hosted or Self-Hosted
|
|
56
|
+
|
|
57
|
+
Use [inth.com](https://inth.com) when you want a hosted c15t backend with no infrastructure to maintain. Use @c15t/backend when you need full control over hosting, database, CORS origins, policy configuration, audit logs, and deployment topology.
|
|
58
|
+
|
|
59
|
+
## Related Packages
|
|
60
|
+
|
|
61
|
+
- [c15t](https://www.npmjs.com/package/c15t): Headless JavaScript consent engine
|
|
62
|
+
- [@c15t/react](https://www.npmjs.com/package/@c15t/react): React consent UI and headless hooks
|
|
63
|
+
- [@c15t/nextjs](https://www.npmjs.com/package/@c15t/nextjs): Next.js App Router and Pages Router integration
|
|
64
|
+
- [@c15t/node-sdk](https://www.npmjs.com/package/@c15t/node-sdk): Type-safe Node.js API client
|
|
52
65
|
|
|
53
66
|
## Support
|
|
54
67
|
|
package/dist/{583.js → 261.js}
RENAMED
|
@@ -2,7 +2,7 @@ import { inspectPolicies, resolvePolicyDecision, resolvePolicySync, validatePoli
|
|
|
2
2
|
import { deepMergeTranslations, selectLanguage } from "@c15t/translations";
|
|
3
3
|
import { baseTranslations } from "@c15t/translations/all";
|
|
4
4
|
import { SignJWT, errors, jwtVerify } from "jose";
|
|
5
|
-
import { createGVLResolver, getMetrics } from "./
|
|
5
|
+
import { createGVLResolver, getMetrics } from "./744.js";
|
|
6
6
|
const DEFAULT_PROFILE = 'default';
|
|
7
7
|
const warnedKeys = new Set();
|
|
8
8
|
function isSupportedBaseLanguage(lang) {
|
package/dist/{915.js → 49.js}
RENAMED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { hashSha256Hex } from "@c15t/schema/types";
|
|
2
2
|
import base_x from "base-x";
|
|
3
|
-
import { checkConsentOutputSchema, checkConsentQuerySchema,
|
|
3
|
+
import { checkConsentOutputSchema, checkConsentQuerySchema, getSubjectOutputSchema, getSubjectParamsSchema, getSubjectQuerySchema, initOutputSchema, isLegalDocumentType, legalDocumentCurrentInputSchema, legalDocumentCurrentOutputSchema, legalDocumentCurrentParamsSchema, listSubjectsOutputSchema, listSubjectsQuerySchema, patchSubjectInputSchema, patchSubjectOutputSchema, patchSubjectParamsSchema, postSubjectInputSchema, postSubjectOutputSchema, statusOutputSchema } from "@c15t/schema";
|
|
4
4
|
import { Hono } from "hono";
|
|
5
5
|
import { describeRoute, resolver, validator } from "hono-openapi";
|
|
6
6
|
import { HTTPException } from "hono/http-exception";
|
|
7
7
|
import { errors, jwtVerify } from "jose";
|
|
8
|
-
import {
|
|
9
|
-
import { getLocation, resolveInitPayload, policy_resolvePolicyDecision, verifyPolicySnapshotToken, getJurisdiction } from "./
|
|
10
|
-
import * as __rspack_external_valibot from "valibot";
|
|
8
|
+
import { version, getMetrics, withDatabaseSpan, extractErrorMessage } from "./744.js";
|
|
9
|
+
import { getLocation, resolveInitPayload, policy_resolvePolicyDecision, verifyPolicySnapshotToken, getJurisdiction } from "./261.js";
|
|
11
10
|
const prefixes = {
|
|
12
11
|
auditLog: 'log',
|
|
13
12
|
consent: 'cns',
|
|
@@ -18,11 +17,12 @@ const prefixes = {
|
|
|
18
17
|
subject: 'sub'
|
|
19
18
|
};
|
|
20
19
|
const b58 = base_x('123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz');
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
20
|
+
const generate_id_EPOCH_TIMESTAMP = 1700000000000;
|
|
21
|
+
const ID_BYTE_LENGTH = 20;
|
|
22
|
+
const TIMESTAMP_BYTE_LENGTH = 8;
|
|
23
|
+
function writeTimestamp(buf, timestamp) {
|
|
24
|
+
const offset = timestamp - generate_id_EPOCH_TIMESTAMP;
|
|
25
|
+
const t = Number.isFinite(offset) ? Math.max(0, offset) : 0;
|
|
26
26
|
const high = Math.floor(t / 0x100000000);
|
|
27
27
|
const low = t >>> 0;
|
|
28
28
|
buf[0] = high >>> 24 & 255;
|
|
@@ -33,7 +33,21 @@ function generateId(model) {
|
|
|
33
33
|
buf[5] = low >>> 16 & 255;
|
|
34
34
|
buf[6] = low >>> 8 & 255;
|
|
35
35
|
buf[7] = 255 & low;
|
|
36
|
-
|
|
36
|
+
}
|
|
37
|
+
function generateId(model) {
|
|
38
|
+
const buf = crypto.getRandomValues(new Uint8Array(ID_BYTE_LENGTH));
|
|
39
|
+
writeTimestamp(buf, Date.now());
|
|
40
|
+
return `${prefixes[model]}_${b58.encode(buf)}`;
|
|
41
|
+
}
|
|
42
|
+
async function generateDeterministicId(model, timestamp, identity) {
|
|
43
|
+
const digest = await hashSha256Hex(JSON.stringify(identity));
|
|
44
|
+
const buf = new Uint8Array(ID_BYTE_LENGTH);
|
|
45
|
+
writeTimestamp(buf, timestamp);
|
|
46
|
+
for(let i = TIMESTAMP_BYTE_LENGTH; i < ID_BYTE_LENGTH; i++){
|
|
47
|
+
const offset = (i - TIMESTAMP_BYTE_LENGTH) * 2;
|
|
48
|
+
buf[i] = Number.parseInt(digest.slice(offset, offset + 2), 16);
|
|
49
|
+
}
|
|
50
|
+
return `${prefixes[model]}_${b58.encode(buf)}`;
|
|
37
51
|
}
|
|
38
52
|
async function generateUniqueId(db, model, ctx, options = {}) {
|
|
39
53
|
const { maxRetries = 10, attempt = 0, baseDelay = 5 } = options;
|
|
@@ -709,7 +723,7 @@ const statusHandler = async (c)=>{
|
|
|
709
723
|
try {
|
|
710
724
|
await ctx.db.findFirst('subject', {});
|
|
711
725
|
return c.json({
|
|
712
|
-
version:
|
|
726
|
+
version: version,
|
|
713
727
|
timestamp: new Date(),
|
|
714
728
|
client: clientInfo
|
|
715
729
|
});
|
|
@@ -1041,9 +1055,6 @@ const patchSubjectHandler = async (c)=>{
|
|
|
1041
1055
|
};
|
|
1042
1056
|
const DEFAULT_ISSUER = 'c15t';
|
|
1043
1057
|
const DEFAULT_AUDIENCE = 'c15t-legal-document-snapshot';
|
|
1044
|
-
function isLegalDocumentPolicyType(type) {
|
|
1045
|
-
return 'privacy_policy' === type || 'terms_and_conditions' === type || 'dpa' === type;
|
|
1046
|
-
}
|
|
1047
1058
|
function resolveSnapshotIssuer(options) {
|
|
1048
1059
|
return options?.issuer?.trim() || DEFAULT_ISSUER;
|
|
1049
1060
|
}
|
|
@@ -1056,7 +1067,7 @@ function getSigningKey(secret) {
|
|
|
1056
1067
|
return new TextEncoder().encode(secret);
|
|
1057
1068
|
}
|
|
1058
1069
|
function isLegalDocumentSnapshotPayload(payload) {
|
|
1059
|
-
return 'string' == typeof payload.iss && 'string' == typeof payload.aud && 'string' == typeof payload.sub &&
|
|
1070
|
+
return 'string' == typeof payload.iss && 'string' == typeof payload.aud && 'string' == typeof payload.sub && isLegalDocumentType(payload.type) && 'string' == typeof payload.version && 'string' == typeof payload.hash && 'string' == typeof payload.effectiveDate && 'number' == typeof payload.iat && 'number' == typeof payload.exp;
|
|
1060
1071
|
}
|
|
1061
1072
|
async function verifyLegalDocumentSnapshotToken(params) {
|
|
1062
1073
|
const { token, options, tenantId } = params;
|
|
@@ -1112,6 +1123,38 @@ async function verifyLegalDocumentSnapshotToken(params) {
|
|
|
1112
1123
|
};
|
|
1113
1124
|
}
|
|
1114
1125
|
}
|
|
1126
|
+
function buildConsentId(input) {
|
|
1127
|
+
return generateDeterministicId('consent', input.givenAt.getTime(), [
|
|
1128
|
+
input.tenantId ?? null,
|
|
1129
|
+
input.subjectId,
|
|
1130
|
+
input.domainId,
|
|
1131
|
+
input.policyId ?? null,
|
|
1132
|
+
input.givenAt.toISOString()
|
|
1133
|
+
]);
|
|
1134
|
+
}
|
|
1135
|
+
function findConsentById(database, consentId) {
|
|
1136
|
+
return database.findFirst('consent', {
|
|
1137
|
+
where: (builder)=>builder('id', '=', consentId)
|
|
1138
|
+
});
|
|
1139
|
+
}
|
|
1140
|
+
async function findExistingConsentSubmission(database, consentId, identity) {
|
|
1141
|
+
const deterministicConsent = await findConsentById(database, consentId);
|
|
1142
|
+
if (deterministicConsent) return deterministicConsent;
|
|
1143
|
+
return database.findFirst('consent', {
|
|
1144
|
+
where: (builder)=>builder.and(builder('subjectId', '=', identity.subjectId), builder('domainId', '=', identity.domainId), builder('policyId', '=', identity.policyId ?? null), builder('givenAt', '=', identity.givenAt), void 0 === identity.tenantId ? builder.isNull('tenantId') : builder('tenantId', '=', identity.tenantId))
|
|
1145
|
+
});
|
|
1146
|
+
}
|
|
1147
|
+
function isUniqueConstraintViolationError(error) {
|
|
1148
|
+
const code = 'object' == typeof error && null !== error && 'code' in error ? String(error.code) : void 0;
|
|
1149
|
+
if ('23505' === code || 'ER_DUP_ENTRY' === code || '1062' === code || 'P2002' === code || code?.startsWith('SQLITE_CONSTRAINT')) return true;
|
|
1150
|
+
const message = extractErrorMessage(error).toLowerCase();
|
|
1151
|
+
return message.includes('unique constraint') || message.includes('unique violation') || message.includes('duplicate key') || message.includes('duplicate entry') || message.includes('unique conflict');
|
|
1152
|
+
}
|
|
1153
|
+
const MAX_FUTURE_CONSENT_TIME_DRIFT_MS = 300000;
|
|
1154
|
+
function clampConsentGivenAt(givenAt, now = Date.now()) {
|
|
1155
|
+
if (givenAt.getTime() > now + MAX_FUTURE_CONSENT_TIME_DRIFT_MS) return new Date(now);
|
|
1156
|
+
return givenAt;
|
|
1157
|
+
}
|
|
1115
1158
|
function buildRuntimeDecisionDedupeKey(input) {
|
|
1116
1159
|
return [
|
|
1117
1160
|
input.tenantId ?? 'default',
|
|
@@ -1191,9 +1234,6 @@ function parseLanguageFromHeader(header) {
|
|
|
1191
1234
|
if (!firstLanguage) return;
|
|
1192
1235
|
return firstLanguage.split('-')[0]?.toLowerCase();
|
|
1193
1236
|
}
|
|
1194
|
-
function isLegalDocumentType(type) {
|
|
1195
|
-
return 'privacy_policy' === type || 'terms_and_conditions' === type || 'dpa' === type;
|
|
1196
|
-
}
|
|
1197
1237
|
function resolveSnapshotFailureMode(ctx) {
|
|
1198
1238
|
return ctx.policySnapshot?.onValidationFailure ?? 'reject';
|
|
1199
1239
|
}
|
|
@@ -1275,7 +1315,13 @@ const postSubjectHandler = async (c)=>{
|
|
|
1275
1315
|
const input = await c.req.json();
|
|
1276
1316
|
const { type, subjectId, identityProvider, externalSubjectId, domain, metadata, givenAt: givenAtEpoch } = input;
|
|
1277
1317
|
const preferences = 'preferences' in input ? input.preferences : void 0;
|
|
1278
|
-
const
|
|
1318
|
+
const requestedGivenAt = new Date(givenAtEpoch);
|
|
1319
|
+
const givenAt = clampConsentGivenAt(requestedGivenAt);
|
|
1320
|
+
const wasGivenAtClamped = givenAt.getTime() !== requestedGivenAt.getTime();
|
|
1321
|
+
if (wasGivenAtClamped) logger.warn('Consent givenAt was too far in the future and was clamped to server time', {
|
|
1322
|
+
requestedGivenAt: requestedGivenAt.toISOString(),
|
|
1323
|
+
clampedGivenAt: givenAt.toISOString()
|
|
1324
|
+
});
|
|
1279
1325
|
const rawConsentAction = 'consentAction' in input ? input.consentAction : void 0;
|
|
1280
1326
|
let derivedConsentAction;
|
|
1281
1327
|
logger.debug('Request parameters', {
|
|
@@ -1510,6 +1556,9 @@ const postSubjectHandler = async (c)=>{
|
|
|
1510
1556
|
...metadata ?? {},
|
|
1511
1557
|
...shouldStoreLanguage && effectiveLanguage ? {
|
|
1512
1558
|
policyLanguage: effectiveLanguage
|
|
1559
|
+
} : {},
|
|
1560
|
+
...wasGivenAtClamped ? {
|
|
1561
|
+
clientGivenAt: requestedGivenAt.toISOString()
|
|
1513
1562
|
} : {}
|
|
1514
1563
|
};
|
|
1515
1564
|
const effectiveJurisdiction = hasValidSnapshot && snapshotPayload ? snapshotPayload.jurisdiction : resolvedJurisdiction;
|
|
@@ -1528,9 +1577,15 @@ const postSubjectHandler = async (c)=>{
|
|
|
1528
1577
|
language: effectiveLanguage,
|
|
1529
1578
|
proofConfig
|
|
1530
1579
|
});
|
|
1531
|
-
const
|
|
1532
|
-
|
|
1533
|
-
|
|
1580
|
+
const consentIdentity = {
|
|
1581
|
+
tenantId: ctx.tenantId,
|
|
1582
|
+
subjectId: subject.id,
|
|
1583
|
+
domainId: domainRecord.id,
|
|
1584
|
+
policyId,
|
|
1585
|
+
givenAt: requestedGivenAt
|
|
1586
|
+
};
|
|
1587
|
+
const consentId = await buildConsentId(consentIdentity);
|
|
1588
|
+
const existingConsent = await findExistingConsentSubmission(db, consentId, consentIdentity);
|
|
1534
1589
|
if (existingConsent) {
|
|
1535
1590
|
logger.debug('Duplicate consent detected, returning existing record', {
|
|
1536
1591
|
consentId: existingConsent.id
|
|
@@ -1547,103 +1602,132 @@ const postSubjectHandler = async (c)=>{
|
|
|
1547
1602
|
givenAt: existingConsent.givenAt
|
|
1548
1603
|
});
|
|
1549
1604
|
}
|
|
1550
|
-
const
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
where: (b)=>b('dedupeKey', '=', decisionPayload.dedupeKey)
|
|
1559
|
-
}) ?? await tx.create('runtimePolicyDecision', {
|
|
1560
|
-
id: `rpd_${crypto.randomUUID().replaceAll('-', '')}`,
|
|
1561
|
-
tenantId: decisionPayload.tenantId,
|
|
1562
|
-
policyId: decisionPayload.policyId,
|
|
1563
|
-
fingerprint: decisionPayload.fingerprint,
|
|
1564
|
-
matchedBy: decisionPayload.matchedBy,
|
|
1565
|
-
countryCode: decisionPayload.countryCode,
|
|
1566
|
-
regionCode: decisionPayload.regionCode,
|
|
1567
|
-
jurisdiction: decisionPayload.jurisdiction,
|
|
1568
|
-
language: decisionPayload.language,
|
|
1569
|
-
model: decisionPayload.model,
|
|
1570
|
-
policyI18n: decisionPayload.policyI18n ? {
|
|
1571
|
-
json: decisionPayload.policyI18n
|
|
1572
|
-
} : void 0,
|
|
1573
|
-
uiMode: decisionPayload.uiMode,
|
|
1574
|
-
bannerUi: decisionPayload.bannerUi ? {
|
|
1575
|
-
json: decisionPayload.bannerUi
|
|
1576
|
-
} : void 0,
|
|
1577
|
-
dialogUi: decisionPayload.dialogUi ? {
|
|
1578
|
-
json: decisionPayload.dialogUi
|
|
1579
|
-
} : void 0,
|
|
1580
|
-
categories: decisionPayload.categories ? {
|
|
1581
|
-
json: decisionPayload.categories
|
|
1582
|
-
} : void 0,
|
|
1583
|
-
preselectedCategories: decisionPayload.preselectedCategories ? {
|
|
1584
|
-
json: decisionPayload.preselectedCategories
|
|
1585
|
-
} : void 0,
|
|
1586
|
-
proofConfig: decisionPayload.proofConfig ? {
|
|
1587
|
-
json: decisionPayload.proofConfig
|
|
1588
|
-
} : void 0,
|
|
1589
|
-
dedupeKey: decisionPayload.dedupeKey
|
|
1590
|
-
}).catch(async ()=>tx.findFirst('runtimePolicyDecision', {
|
|
1605
|
+
const runConsentTransaction = ()=>db.transaction(async (tx)=>{
|
|
1606
|
+
logger.debug('Creating consent record', {
|
|
1607
|
+
subjectId: subject.id,
|
|
1608
|
+
domainId: domainRecord.id,
|
|
1609
|
+
policyId,
|
|
1610
|
+
purposeIds
|
|
1611
|
+
});
|
|
1612
|
+
const runtimePolicyDecision = decisionPayload ? await tx.findFirst('runtimePolicyDecision', {
|
|
1591
1613
|
where: (b)=>b('dedupeKey', '=', decisionPayload.dedupeKey)
|
|
1592
|
-
})
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1614
|
+
}) ?? await tx.create('runtimePolicyDecision', {
|
|
1615
|
+
id: `rpd_${crypto.randomUUID().replaceAll('-', '')}`,
|
|
1616
|
+
tenantId: decisionPayload.tenantId,
|
|
1617
|
+
policyId: decisionPayload.policyId,
|
|
1618
|
+
fingerprint: decisionPayload.fingerprint,
|
|
1619
|
+
matchedBy: decisionPayload.matchedBy,
|
|
1620
|
+
countryCode: decisionPayload.countryCode,
|
|
1621
|
+
regionCode: decisionPayload.regionCode,
|
|
1622
|
+
jurisdiction: decisionPayload.jurisdiction,
|
|
1623
|
+
language: decisionPayload.language,
|
|
1624
|
+
model: decisionPayload.model,
|
|
1625
|
+
policyI18n: decisionPayload.policyI18n ? {
|
|
1626
|
+
json: decisionPayload.policyI18n
|
|
1627
|
+
} : void 0,
|
|
1628
|
+
uiMode: decisionPayload.uiMode,
|
|
1629
|
+
bannerUi: decisionPayload.bannerUi ? {
|
|
1630
|
+
json: decisionPayload.bannerUi
|
|
1631
|
+
} : void 0,
|
|
1632
|
+
dialogUi: decisionPayload.dialogUi ? {
|
|
1633
|
+
json: decisionPayload.dialogUi
|
|
1634
|
+
} : void 0,
|
|
1635
|
+
categories: decisionPayload.categories ? {
|
|
1636
|
+
json: decisionPayload.categories
|
|
1637
|
+
} : void 0,
|
|
1638
|
+
preselectedCategories: decisionPayload.preselectedCategories ? {
|
|
1639
|
+
json: decisionPayload.preselectedCategories
|
|
1640
|
+
} : void 0,
|
|
1641
|
+
proofConfig: decisionPayload.proofConfig ? {
|
|
1642
|
+
json: decisionPayload.proofConfig
|
|
1643
|
+
} : void 0,
|
|
1644
|
+
dedupeKey: decisionPayload.dedupeKey
|
|
1645
|
+
}) : void 0;
|
|
1646
|
+
const consentRecord = await tx.create('consent', {
|
|
1647
|
+
id: consentId,
|
|
1623
1648
|
subjectId: subject.id,
|
|
1624
|
-
|
|
1625
|
-
|
|
1649
|
+
domainId: domainRecord.id,
|
|
1650
|
+
policyId,
|
|
1651
|
+
purposeIds: {
|
|
1652
|
+
json: purposeIds
|
|
1653
|
+
},
|
|
1654
|
+
metadata: Object.keys(metadataWithPolicy).length > 0 ? {
|
|
1655
|
+
json: metadataWithPolicy
|
|
1656
|
+
} : void 0,
|
|
1657
|
+
ipAddress: shouldStoreIp ? ctx.ipAddress : null,
|
|
1658
|
+
userAgent: shouldStoreUserAgent ? ctx.userAgent : null,
|
|
1659
|
+
jurisdiction: effectiveJurisdiction,
|
|
1660
|
+
jurisdictionModel: effectiveModel,
|
|
1661
|
+
tcString: input.tcString,
|
|
1662
|
+
uiSource: input.uiSource,
|
|
1663
|
+
consentAction: derivedConsentAction,
|
|
1664
|
+
givenAt,
|
|
1665
|
+
validUntil,
|
|
1666
|
+
runtimePolicyDecisionId: runtimePolicyDecision?.id,
|
|
1667
|
+
runtimePolicySource: decisionPayload?.source
|
|
1668
|
+
});
|
|
1669
|
+
if (!consentRecord) throw new HTTPException(500, {
|
|
1670
|
+
message: 'Failed to create consent',
|
|
1671
|
+
cause: {
|
|
1672
|
+
code: 'CONSENT_CREATION_FAILED',
|
|
1673
|
+
subjectId: subject.id,
|
|
1674
|
+
domain
|
|
1675
|
+
}
|
|
1676
|
+
});
|
|
1677
|
+
logger.debug('Created consent', {
|
|
1678
|
+
consentRecord: consentRecord.id
|
|
1679
|
+
});
|
|
1680
|
+
return {
|
|
1681
|
+
consent: consentRecord,
|
|
1682
|
+
created: true
|
|
1683
|
+
};
|
|
1626
1684
|
});
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1685
|
+
const maxTransactionAttempts = 3;
|
|
1686
|
+
let result;
|
|
1687
|
+
for(let attempt = 1; attempt <= maxTransactionAttempts; attempt++)try {
|
|
1688
|
+
result = await runConsentTransaction();
|
|
1689
|
+
break;
|
|
1690
|
+
} catch (error) {
|
|
1691
|
+
if (!isUniqueConstraintViolationError(error)) throw error;
|
|
1692
|
+
const concurrentConsent = await findConsentById(db, consentId);
|
|
1693
|
+
if (concurrentConsent) {
|
|
1694
|
+
logger.debug('Consent insert conflicted, returning existing record', {
|
|
1695
|
+
consentId: concurrentConsent.id
|
|
1696
|
+
});
|
|
1697
|
+
result = {
|
|
1698
|
+
consent: concurrentConsent,
|
|
1699
|
+
created: false
|
|
1700
|
+
};
|
|
1701
|
+
break;
|
|
1702
|
+
}
|
|
1703
|
+
if (attempt === maxTransactionAttempts) throw error;
|
|
1704
|
+
}
|
|
1705
|
+
if (!result) throw new HTTPException(500, {
|
|
1706
|
+
message: 'Failed to create consent',
|
|
1707
|
+
cause: {
|
|
1708
|
+
code: 'CONSENT_CREATION_FAILED',
|
|
1709
|
+
subjectId: subject.id,
|
|
1710
|
+
domain
|
|
1711
|
+
}
|
|
1630
1712
|
});
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1713
|
+
if (result.created) {
|
|
1714
|
+
const metrics = getMetrics();
|
|
1715
|
+
if (metrics) {
|
|
1716
|
+
const jurisdiction = effectiveJurisdiction;
|
|
1717
|
+
metrics.recordConsentCreated({
|
|
1718
|
+
type,
|
|
1719
|
+
jurisdiction
|
|
1720
|
+
});
|
|
1721
|
+
const hasAccepted = preferences && Object.values(preferences).some(Boolean);
|
|
1722
|
+
if (hasAccepted) metrics.recordConsentAccepted({
|
|
1723
|
+
type,
|
|
1724
|
+
jurisdiction
|
|
1725
|
+
});
|
|
1726
|
+
else metrics.recordConsentRejected({
|
|
1727
|
+
type,
|
|
1728
|
+
jurisdiction
|
|
1729
|
+
});
|
|
1730
|
+
}
|
|
1647
1731
|
}
|
|
1648
1732
|
return c.json({
|
|
1649
1733
|
subjectId: subject.id,
|
|
@@ -1698,10 +1782,10 @@ const createSubjectRoutes = ()=>{
|
|
|
1698
1782
|
description: 'Subject not found for the given ID'
|
|
1699
1783
|
}
|
|
1700
1784
|
}
|
|
1701
|
-
}), validator('param',
|
|
1785
|
+
}), validator('param', getSubjectParamsSchema), validator('query', getSubjectQuerySchema), getSubjectHandler);
|
|
1702
1786
|
app.post('/', describeRoute({
|
|
1703
1787
|
summary: 'Record consent for a subject',
|
|
1704
|
-
description: "Creates a new consent record (append-only). Creates the subject if it does not exist.\n\n**Request body by `type`:**\n- `cookie_banner` – Requires `preferences` object\n- `privacy_policy`, `dpa`, `terms_and_conditions` – Prefer a signed `documentSnapshotToken`; otherwise use a release `policyHash`, with `policyId` kept only for compatibility\n- `marketing_communications`, `age_verification`, `other` – Optional `preferences`",
|
|
1788
|
+
description: "Creates a new consent record (append-only). Creates the subject if it does not exist.\n\n**Request body by `type`:**\n- `cookie_banner` – Requires `preferences` object\n- `privacy_policy`, `dpa`, `terms_and_conditions`, and suffixed legal-document variants such as `terms_and_conditions_b2b` – Prefer a signed `documentSnapshotToken`; otherwise use a release `policyHash`, with `policyId` kept only for compatibility\n- `marketing_communications`, `age_verification`, `other` – Optional `preferences`",
|
|
1705
1789
|
tags: [
|
|
1706
1790
|
'Subject',
|
|
1707
1791
|
'Consent'
|
|
@@ -1739,12 +1823,7 @@ const createSubjectRoutes = ()=>{
|
|
|
1739
1823
|
description: 'Subject not found for the given ID'
|
|
1740
1824
|
}
|
|
1741
1825
|
}
|
|
1742
|
-
}), validator('param',
|
|
1743
|
-
id: subjectIdSchema
|
|
1744
|
-
})), validator('json', __rspack_external_valibot.object({
|
|
1745
|
-
externalId: __rspack_external_valibot.string(),
|
|
1746
|
-
identityProvider: __rspack_external_valibot.optional(__rspack_external_valibot.string())
|
|
1747
|
-
})), patchSubjectHandler);
|
|
1826
|
+
}), validator('param', patchSubjectParamsSchema), validator('json', patchSubjectInputSchema), patchSubjectHandler);
|
|
1748
1827
|
app.get('/', describeRoute({
|
|
1749
1828
|
summary: 'List subjects by external ID (API key required)',
|
|
1750
1829
|
description: 'Returns all subjects linked to the given external ID. Requires Bearer token (API key). Use for server-side consent lookups.',
|
package/dist/{302.js → 744.js}
RENAMED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { SpanKind, SpanStatusCode as api_SpanStatusCode, context, metrics, trace } from "@opentelemetry/api";
|
|
2
|
+
const version = '2.2.0-canary-20260727202135';
|
|
2
3
|
function extractErrorMessage(error) {
|
|
3
4
|
if (error instanceof AggregateError && error.errors?.length > 0) {
|
|
4
5
|
const inner = error.errors.map((e)=>e instanceof Error ? e.message : String(e)).join('; ');
|
|
@@ -13,7 +14,7 @@ function createTelemetryOptions(appName = 'c15t', telemetryConfig, tenantId) {
|
|
|
13
14
|
const defaultAttributes = {
|
|
14
15
|
...telemetryConfig?.defaultAttributes || {},
|
|
15
16
|
'service.name': String(appName),
|
|
16
|
-
'service.version':
|
|
17
|
+
'service.version': version
|
|
17
18
|
};
|
|
18
19
|
if (tenantId) defaultAttributes['tenant.id'] = tenantId;
|
|
19
20
|
const config = {
|
|
@@ -315,7 +316,6 @@ function getMetrics(options) {
|
|
|
315
316
|
metricsInstance = createMetrics(getMeter(options));
|
|
316
317
|
return metricsInstance;
|
|
317
318
|
}
|
|
318
|
-
const GVL_TTL_MS = 259200000;
|
|
319
319
|
const memory_memoryCache = new Map();
|
|
320
320
|
function createMemoryCacheAdapter() {
|
|
321
321
|
return {
|
|
@@ -368,6 +368,7 @@ function createCacheKey(appName, namespace, ...parts) {
|
|
|
368
368
|
];
|
|
369
369
|
return allParts.join(':');
|
|
370
370
|
}
|
|
371
|
+
const GVL_TTL_MS = 259200000;
|
|
371
372
|
const GVL_ENDPOINT = 'https://gvl.inth.app';
|
|
372
373
|
const inflightRequests = new Map();
|
|
373
374
|
async function fetchGVLWithLanguage(language, vendorIds, endpoint = GVL_ENDPOINT) {
|
|
@@ -469,4 +470,4 @@ function createGVLResolver(options) {
|
|
|
469
470
|
}
|
|
470
471
|
};
|
|
471
472
|
}
|
|
472
|
-
export { GVL_TTL_MS, clearMemoryCache, createCacheKey, createGVLCacheKey, createGVLResolver, createMemoryCacheAdapter, createRequestSpan, createTelemetryOptions, extractErrorMessage, getMemoryCacheSize, getMetrics, getTraceContext, handleSpanError, isTelemetryEnabled, withDatabaseSpan, withSpanContext };
|
|
473
|
+
export { GVL_TTL_MS, clearMemoryCache, createCacheKey, createGVLCacheKey, createGVLResolver, createMemoryCacheAdapter, createRequestSpan, createTelemetryOptions, extractErrorMessage, getMemoryCacheSize, getMetrics, getTraceContext, handleSpanError, isTelemetryEnabled, version, withDatabaseSpan, withSpanContext };
|
package/dist/cache.cjs
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var __webpack_require__ = {};
|
|
3
3
|
(()=>{
|
|
4
|
-
__webpack_require__.d = (exports1,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
__webpack_require__.d = (exports1, getters, values)=>{
|
|
5
|
+
var define = (defs, kind)=>{
|
|
6
|
+
for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
[kind]: defs[key]
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
define(getters, "get");
|
|
12
|
+
define(values, "value");
|
|
9
13
|
};
|
|
10
14
|
})();
|
|
11
15
|
(()=>{
|
|
@@ -24,17 +28,17 @@ var __webpack_require__ = {};
|
|
|
24
28
|
var __webpack_exports__ = {};
|
|
25
29
|
__webpack_require__.r(__webpack_exports__);
|
|
26
30
|
__webpack_require__.d(__webpack_exports__, {
|
|
27
|
-
createGVLResolver: ()=>createGVLResolver,
|
|
28
|
-
createUpstashRedisAdapterFromClient: ()=>createUpstashRedisAdapterFromClient,
|
|
29
|
-
createCacheKey: ()=>createCacheKey,
|
|
30
|
-
clearMemoryCache: ()=>clearMemoryCache,
|
|
31
|
-
MEMORY_TTL_MS: ()=>300000,
|
|
32
|
-
getMemoryCacheSize: ()=>getMemoryCacheSize,
|
|
33
|
-
createUpstashRedisAdapter: ()=>createUpstashRedisAdapter,
|
|
34
31
|
GVL_TTL_MS: ()=>GVL_TTL_MS,
|
|
32
|
+
MEMORY_TTL_MS: ()=>300000,
|
|
33
|
+
clearMemoryCache: ()=>clearMemoryCache,
|
|
34
|
+
createCacheKey: ()=>createCacheKey,
|
|
35
35
|
createCloudflareKVAdapter: ()=>createCloudflareKVAdapter,
|
|
36
36
|
createGVLCacheKey: ()=>createGVLCacheKey,
|
|
37
|
-
|
|
37
|
+
createGVLResolver: ()=>createGVLResolver,
|
|
38
|
+
createMemoryCacheAdapter: ()=>createMemoryCacheAdapter,
|
|
39
|
+
createUpstashRedisAdapter: ()=>createUpstashRedisAdapter,
|
|
40
|
+
createUpstashRedisAdapterFromClient: ()=>createUpstashRedisAdapterFromClient,
|
|
41
|
+
getMemoryCacheSize: ()=>getMemoryCacheSize
|
|
38
42
|
});
|
|
39
43
|
const GVL_TTL_MS = 259200000;
|
|
40
44
|
function createCloudflareKVAdapter(kv) {
|
package/dist/cache.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Redis } from "@upstash/redis";
|
|
2
|
-
import { GVL_TTL_MS } from "./
|
|
2
|
+
import { GVL_TTL_MS } from "./744.js";
|
|
3
3
|
function createCloudflareKVAdapter(kv) {
|
|
4
4
|
return {
|
|
5
5
|
async get (key) {
|
|
@@ -52,5 +52,5 @@ function createUpstashRedisAdapterFromClient(client) {
|
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
54
|
var cache_MEMORY_TTL_MS = 300000;
|
|
55
|
-
export { GVL_TTL_MS, clearMemoryCache, createCacheKey, createGVLCacheKey, createGVLResolver, createMemoryCacheAdapter, getMemoryCacheSize } from "./
|
|
55
|
+
export { GVL_TTL_MS, clearMemoryCache, createCacheKey, createGVLCacheKey, createGVLResolver, createMemoryCacheAdapter, getMemoryCacheSize } from "./744.js";
|
|
56
56
|
export { cache_MEMORY_TTL_MS as MEMORY_TTL_MS, createCloudflareKVAdapter, createUpstashRedisAdapter, createUpstashRedisAdapterFromClient };
|