@c15t/backend 2.0.0-rc.0 → 2.0.0-rc.10
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/README.md +3 -3
- package/dist/302.js +473 -0
- package/dist/583.js +540 -0
- package/dist/915.js +1771 -0
- package/dist/cache.cjs +5 -5
- package/dist/cache.js +4 -415
- package/dist/core.cjs +1356 -120
- package/dist/core.js +163 -1981
- package/dist/db/adapters/drizzle.cjs +1 -1
- package/dist/db/adapters/drizzle.js +1 -2
- package/dist/db/adapters/kysely.cjs +1 -1
- package/dist/db/adapters/kysely.js +1 -2
- package/dist/db/adapters/mongo.cjs +1 -1
- package/dist/db/adapters/mongo.js +1 -2
- package/dist/db/adapters/prisma.cjs +1 -1
- package/dist/db/adapters/prisma.js +1 -2
- package/dist/db/adapters/typeorm.cjs +1 -1
- package/dist/db/adapters/typeorm.js +1 -2
- package/dist/db/adapters.cjs +1 -1
- package/dist/db/migrator.cjs +1 -1
- package/dist/db/schema.cjs +43 -3
- package/dist/db/schema.js +35 -4
- package/dist/define-config.cjs +1 -1
- package/dist/edge.cjs +1106 -0
- package/dist/edge.js +190 -0
- package/dist/router.cjs +885 -123
- package/dist/router.js +1 -1507
- package/dist/{types.cjs → types/index.cjs} +1 -1
- package/{dist → dist-types}/cache/adapters/cloudflare-kv.d.ts +0 -1
- package/{dist → dist-types}/cache/adapters/index.d.ts +0 -1
- package/{dist → dist-types}/cache/adapters/memory.d.ts +0 -1
- package/{dist → dist-types}/cache/adapters/upstash-redis.d.ts +0 -1
- package/{dist → dist-types}/cache/gvl-resolver.d.ts +0 -1
- 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-types/db/registry/consent-policy.d.ts +78 -0
- 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-types/db/registry/index.d.ts +118 -0
- package/dist-types/db/registry/runtime-policy-decision.d.ts +60 -0
- package/{dist → dist-types}/db/registry/subject.d.ts +0 -2
- package/{dist → dist-types}/db/registry/types.d.ts +1 -1
- 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 -32
- package/{dist → dist-types}/db/schema/1.0.0/subject.d.ts +0 -2
- 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 +3 -3
- 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 +7 -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 +455 -28
- 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 -3
- package/{dist → dist-types}/db/schema/index.d.ts +908 -86
- package/{dist → dist-types}/db/tenant-scope.d.ts +0 -1
- package/dist-types/define-config.d.ts +17 -0
- package/dist-types/edge/index.d.ts +5 -0
- package/dist-types/edge/init-handler.d.ts +40 -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 +2 -3
- 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/legal-document/current.handler.d.ts +11 -0
- package/dist-types/handlers/legal-document/snapshot.d.ts +39 -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 +3 -2
- 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 +3 -2
- package/{dist → dist-types}/handlers/subject/patch.handler.d.ts +0 -2
- package/{dist → dist-types}/handlers/subject/post.handler.d.ts +12 -1
- package/{dist → dist-types}/handlers/utils/consent-enrichment.d.ts +3 -1
- package/{dist → dist-types}/init.d.ts +4 -7
- package/{dist → dist-types}/middleware/auth/index.d.ts +0 -1
- package/{dist → dist-types}/middleware/auth/validate-api-key.d.ts +0 -1
- package/{dist → dist-types}/middleware/cors/cors.d.ts +0 -1
- package/{src/middleware/cors/index.ts → dist-types/middleware/cors/index.d.ts} +0 -1
- package/{dist → dist-types}/middleware/cors/is-origin-trusted.d.ts +0 -1
- 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/{dist → dist-types}/routes/index.d.ts +1 -1
- package/{dist → dist-types}/routes/init.d.ts +0 -1
- package/dist-types/routes/legal-document.d.ts +7 -0
- package/{dist → dist-types}/routes/status.d.ts +0 -1
- package/{dist → dist-types}/routes/subject.d.ts +0 -1
- package/{dist → dist-types}/types/api.d.ts +0 -1
- package/dist-types/types/index.d.ts +464 -0
- package/dist-types/utils/background.d.ts +6 -0
- package/{dist → dist-types}/utils/create-telemetry-options.d.ts +1 -2
- package/{dist → dist-types}/utils/env.d.ts +0 -1
- package/{dist → dist-types}/utils/extract-error-message.d.ts +0 -1
- package/{dist → dist-types}/utils/instrumentation.d.ts +2 -3
- package/{dist → dist-types}/utils/logger.d.ts +0 -1
- 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 +208 -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 +251 -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 +53 -39
- package/.turbo/turbo-build.log +0 -49
- package/CHANGELOG.md +0 -89
- 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 +0 -23
- 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 +0 -57
- package/dist/db/registry/index.d.ts.map +0 -1
- package/dist/db/registry/subject.d.ts.map +0 -1
- package/dist/db/registry/types.d.ts.map +0 -1
- package/dist/db/registry/utils/generate-id.d.ts.map +0 -1
- package/dist/db/registry/utils.d.ts.map +0 -1
- package/dist/db/schema/1.0.0/audit-log.d.ts.map +0 -1
- package/dist/db/schema/1.0.0/consent-policy.d.ts.map +0 -1
- package/dist/db/schema/1.0.0/consent-purpose.d.ts.map +0 -1
- package/dist/db/schema/1.0.0/consent-record.d.ts.map +0 -1
- package/dist/db/schema/1.0.0/consent.d.ts.map +0 -1
- package/dist/db/schema/1.0.0/domain.d.ts.map +0 -1
- package/dist/db/schema/1.0.0/index.d.ts.map +0 -1
- package/dist/db/schema/1.0.0/subject.d.ts.map +0 -1
- package/dist/db/schema/2.0.0/audit-log.d.ts.map +0 -1
- package/dist/db/schema/2.0.0/consent-policy.d.ts.map +0 -1
- package/dist/db/schema/2.0.0/consent-purpose.d.ts.map +0 -1
- package/dist/db/schema/2.0.0/consent.d.ts.map +0 -1
- package/dist/db/schema/2.0.0/domain.d.ts.map +0 -1
- package/dist/db/schema/2.0.0/index.d.ts.map +0 -1
- package/dist/db/schema/2.0.0/subject.d.ts.map +0 -1
- package/dist/db/schema/index.d.ts.map +0 -1
- package/dist/db/tenant-scope.d.ts.map +0 -1
- package/dist/define-config.d.ts +0 -5
- package/dist/define-config.d.ts.map +0 -1
- package/dist/handlers/consent/check.handler.d.ts.map +0 -1
- package/dist/handlers/consent/index.d.ts +0 -12
- package/dist/handlers/consent/index.d.ts.map +0 -1
- package/dist/handlers/init/geo.d.ts.map +0 -1
- package/dist/handlers/init/index.d.ts.map +0 -1
- package/dist/handlers/init/translations.d.ts +0 -28
- package/dist/handlers/init/translations.d.ts.map +0 -1
- package/dist/handlers/status/index.d.ts +0 -7
- package/dist/handlers/status/index.d.ts.map +0 -1
- package/dist/handlers/status/status.handler.d.ts.map +0 -1
- package/dist/handlers/subject/get.handler.d.ts.map +0 -1
- package/dist/handlers/subject/index.d.ts +0 -10
- package/dist/handlers/subject/index.d.ts.map +0 -1
- package/dist/handlers/subject/list.handler.d.ts.map +0 -1
- package/dist/handlers/subject/patch.handler.d.ts.map +0 -1
- package/dist/handlers/subject/post.handler.d.ts.map +0 -1
- package/dist/handlers/utils/consent-enrichment.d.ts.map +0 -1
- package/dist/init.d.ts.map +0 -1
- package/dist/middleware/auth/index.d.ts.map +0 -1
- package/dist/middleware/auth/validate-api-key.d.ts.map +0 -1
- package/dist/middleware/cors/cors.d.ts.map +0 -1
- package/dist/middleware/cors/index.d.ts +0 -30
- package/dist/middleware/cors/index.d.ts.map +0 -1
- package/dist/middleware/cors/is-origin-trusted.d.ts.map +0 -1
- package/dist/middleware/cors/process-cors.d.ts.map +0 -1
- package/dist/middleware/openapi/config.d.ts.map +0 -1
- package/dist/middleware/openapi/handlers.d.ts.map +0 -1
- package/dist/middleware/openapi/index.d.ts +0 -12
- package/dist/middleware/openapi/index.d.ts.map +0 -1
- package/dist/middleware/process-ip/index.d.ts.map +0 -1
- package/dist/router.d.ts.map +0 -1
- package/dist/routes/consent.d.ts.map +0 -1
- package/dist/routes/index.d.ts.map +0 -1
- package/dist/routes/init.d.ts.map +0 -1
- package/dist/routes/status.d.ts.map +0 -1
- package/dist/routes/subject.d.ts.map +0 -1
- package/dist/types/api.d.ts.map +0 -1
- package/dist/types/index.d.ts +0 -255
- 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 -368
- 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 -388
- package/src/db/registry/subject.ts +0 -129
- 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 -12
- 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 -26
- 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 -14
- package/src/db/schema/index.ts +0 -15
- package/src/db/tenant-scope.test.ts +0 -750
- package/src/db/tenant-scope.ts +0 -103
- package/src/define-config.ts +0 -5
- package/src/handlers/consent/check.handler.ts +0 -126
- package/src/handlers/init/geo.test.ts +0 -317
- package/src/handlers/init/geo.ts +0 -195
- package/src/handlers/init/index.test.ts +0 -205
- package/src/handlers/init/index.ts +0 -114
- package/src/handlers/init/translations.test.ts +0 -121
- package/src/handlers/init/translations.ts +0 -72
- package/src/handlers/status/status.handler.test.ts +0 -155
- package/src/handlers/status/status.handler.ts +0 -51
- package/src/handlers/subject/get.handler.ts +0 -93
- package/src/handlers/subject/list.handler.ts +0 -93
- package/src/handlers/subject/patch.handler.ts +0 -122
- package/src/handlers/subject/post.handler.test.ts +0 -294
- package/src/handlers/subject/post.handler.ts +0 -254
- package/src/handlers/utils/consent-enrichment.test.ts +0 -380
- package/src/handlers/utils/consent-enrichment.ts +0 -218
- package/src/init.test.ts +0 -126
- package/src/init.ts +0 -87
- package/src/middleware/auth/index.ts +0 -11
- package/src/middleware/auth/validate-api-key.test.ts +0 -86
- package/src/middleware/auth/validate-api-key.ts +0 -107
- package/src/middleware/cors/cors.test.ts +0 -135
- package/src/middleware/cors/cors.ts +0 -186
- package/src/middleware/cors/is-origin-trusted.test.ts +0 -164
- package/src/middleware/cors/is-origin-trusted.ts +0 -130
- package/src/middleware/cors/process-cors.ts +0 -91
- package/src/middleware/openapi/config.ts +0 -29
- package/src/middleware/openapi/handlers.ts +0 -34
- package/src/middleware/process-ip/index.test.ts +0 -195
- package/src/middleware/process-ip/index.ts +0 -199
- package/src/router.ts +0 -15
- package/src/routes/consent.ts +0 -52
- package/src/routes/index.ts +0 -10
- package/src/routes/init.ts +0 -102
- 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 -288
- package/src/utils/create-telemetry-options.test.ts +0 -302
- package/src/utils/create-telemetry-options.ts +0 -229
- package/src/utils/env.ts +0 -84
- package/src/utils/extract-error-message.ts +0 -21
- package/src/utils/instrumentation.test.ts +0 -185
- package/src/utils/instrumentation.ts +0 -196
- package/src/utils/logger.ts +0 -41
- package/src/utils/metrics.test.ts +0 -323
- package/src/utils/metrics.ts +0 -402
- package/src/utils/telemetry-pii.test.ts +0 -325
- package/src/version.ts +0 -2
- package/tsconfig.json +0 -11
- package/vitest.config.ts +0 -28
- /package/dist/{types.js → types/index.js} +0 -0
- /package/{src/db/adapters/drizzle.ts → dist-types/db/adapters/drizzle.d.ts} +0 -0
- /package/{src/db/adapters/index.ts → dist-types/db/adapters/index.d.ts} +0 -0
- /package/{src/db/adapters/kysely.ts → dist-types/db/adapters/kysely.d.ts} +0 -0
- /package/{src/db/adapters/mongo.ts → dist-types/db/adapters/mongo.d.ts} +0 -0
- /package/{src/db/adapters/prisma.ts → dist-types/db/adapters/prisma.d.ts} +0 -0
- /package/{src/db/adapters/typeorm.ts → dist-types/db/adapters/typeorm.d.ts} +0 -0
- /package/{src/utils/index.ts → dist-types/utils/index.d.ts} +0 -0
|
@@ -1,388 +0,0 @@
|
|
|
1
|
-
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import type { Subject } from '../schema';
|
|
3
|
-
import { subjectRegistry } from './subject';
|
|
4
|
-
import type { Registry } from './types';
|
|
5
|
-
|
|
6
|
-
// Mock generateUniqueId to return a predictable value for assertions
|
|
7
|
-
vi.mock('./utils/generate-id', () => ({
|
|
8
|
-
generateUniqueId: vi.fn().mockResolvedValue('sub_test_123'),
|
|
9
|
-
}));
|
|
10
|
-
|
|
11
|
-
describe('subjectRegistry', () => {
|
|
12
|
-
const mockLogger = {
|
|
13
|
-
debug: vi.fn(),
|
|
14
|
-
error: vi.fn(),
|
|
15
|
-
info: vi.fn(),
|
|
16
|
-
warn: vi.fn(),
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const createMockSubject = (overrides: Partial<Subject> = {}): Subject => ({
|
|
20
|
-
id: 'sub_test_123',
|
|
21
|
-
isIdentified: false,
|
|
22
|
-
externalId: null,
|
|
23
|
-
identityProvider: 'anonymous',
|
|
24
|
-
createdAt: new Date('2024-01-01T00:00:00.000Z'),
|
|
25
|
-
updatedAt: new Date('2024-01-01T00:00:00.000Z'),
|
|
26
|
-
...overrides,
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
afterEach(() => {
|
|
30
|
-
vi.clearAllMocks();
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
describe('findOrCreateSubject', () => {
|
|
34
|
-
describe('when subjectId is provided', () => {
|
|
35
|
-
it('should return existing subject when found', async () => {
|
|
36
|
-
const mockSubject = createMockSubject({
|
|
37
|
-
id: 'sub_existing',
|
|
38
|
-
externalId: 'ext_123',
|
|
39
|
-
isIdentified: true,
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
const db = {
|
|
43
|
-
findFirst: vi.fn().mockResolvedValue(mockSubject),
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
const registry = subjectRegistry({
|
|
47
|
-
db,
|
|
48
|
-
ctx: { logger: mockLogger },
|
|
49
|
-
} as unknown as Registry);
|
|
50
|
-
|
|
51
|
-
const result = await registry.findOrCreateSubject({
|
|
52
|
-
subjectId: 'sub_existing',
|
|
53
|
-
externalSubjectId: 'ext_123',
|
|
54
|
-
ipAddress: '192.168.1.1',
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
expect(db.findFirst).toHaveBeenCalledWith('subject', {
|
|
58
|
-
where: expect.any(Function),
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
expect(result).toEqual(mockSubject);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it('should create new subject when subjectId not found', async () => {
|
|
65
|
-
const createdSubject = createMockSubject({
|
|
66
|
-
id: 'sub_new',
|
|
67
|
-
externalId: null,
|
|
68
|
-
identityProvider: 'anonymous',
|
|
69
|
-
isIdentified: false,
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
const db = {
|
|
73
|
-
findFirst: vi.fn().mockResolvedValue(null),
|
|
74
|
-
create: vi.fn().mockResolvedValue(createdSubject),
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
const registry = subjectRegistry({
|
|
78
|
-
db,
|
|
79
|
-
ctx: { logger: mockLogger },
|
|
80
|
-
} as unknown as Registry);
|
|
81
|
-
|
|
82
|
-
const result = await registry.findOrCreateSubject({
|
|
83
|
-
subjectId: 'sub_new',
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
expect(db.findFirst).toHaveBeenCalledWith('subject', {
|
|
87
|
-
where: expect.any(Function),
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
expect(db.create).toHaveBeenCalledWith('subject', {
|
|
91
|
-
id: 'sub_new',
|
|
92
|
-
externalId: null,
|
|
93
|
-
identityProvider: 'anonymous',
|
|
94
|
-
isIdentified: false,
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
expect(result).toEqual(createdSubject);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it('should create new subject with externalId when both IDs provided but subject not found', async () => {
|
|
101
|
-
const createdSubject = createMockSubject({
|
|
102
|
-
id: 'sub_new',
|
|
103
|
-
externalId: 'ext_123',
|
|
104
|
-
identityProvider: 'external',
|
|
105
|
-
isIdentified: true,
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
const db = {
|
|
109
|
-
findFirst: vi.fn().mockResolvedValue(null),
|
|
110
|
-
create: vi.fn().mockResolvedValue(createdSubject),
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
const registry = subjectRegistry({
|
|
114
|
-
db,
|
|
115
|
-
ctx: { logger: mockLogger },
|
|
116
|
-
} as unknown as Registry);
|
|
117
|
-
|
|
118
|
-
const result = await registry.findOrCreateSubject({
|
|
119
|
-
subjectId: 'sub_new',
|
|
120
|
-
externalSubjectId: 'ext_123',
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
expect(db.create).toHaveBeenCalledWith('subject', {
|
|
124
|
-
id: 'sub_new',
|
|
125
|
-
externalId: 'ext_123',
|
|
126
|
-
identityProvider: 'external',
|
|
127
|
-
isIdentified: true,
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
expect(result).toEqual(createdSubject);
|
|
131
|
-
});
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
describe('when only externalSubjectId is provided', () => {
|
|
135
|
-
it('should create a new subject with external ID', async () => {
|
|
136
|
-
const mockSubject = createMockSubject({
|
|
137
|
-
id: 'sub_test_123',
|
|
138
|
-
externalId: 'ext_existing',
|
|
139
|
-
identityProvider: 'external',
|
|
140
|
-
isIdentified: true,
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
const db = {
|
|
144
|
-
create: vi.fn().mockResolvedValue(mockSubject),
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
const registry = subjectRegistry({
|
|
148
|
-
db,
|
|
149
|
-
ctx: { logger: mockLogger },
|
|
150
|
-
} as unknown as Registry);
|
|
151
|
-
|
|
152
|
-
const result = await registry.findOrCreateSubject({
|
|
153
|
-
externalSubjectId: 'ext_existing',
|
|
154
|
-
ipAddress: '192.168.1.200',
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
expect(db.create).toHaveBeenCalledWith('subject', {
|
|
158
|
-
id: 'sub_test_123',
|
|
159
|
-
externalId: 'ext_existing',
|
|
160
|
-
identityProvider: 'external',
|
|
161
|
-
isIdentified: true,
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
expect(result).toEqual(mockSubject);
|
|
165
|
-
expect(mockLogger.debug).toHaveBeenCalledWith(
|
|
166
|
-
'Creating subject with external ID (legacy flow)',
|
|
167
|
-
{ externalSubjectId: 'ext_existing' }
|
|
168
|
-
);
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
it('should use custom identity provider when specified', async () => {
|
|
172
|
-
const mockSubject = createMockSubject({
|
|
173
|
-
id: 'sub_test_123',
|
|
174
|
-
externalId: 'ext_new',
|
|
175
|
-
identityProvider: 'google',
|
|
176
|
-
isIdentified: true,
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
const db = {
|
|
180
|
-
create: vi.fn().mockResolvedValue(mockSubject),
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
const registry = subjectRegistry({
|
|
184
|
-
db,
|
|
185
|
-
ctx: { logger: mockLogger },
|
|
186
|
-
} as unknown as Registry);
|
|
187
|
-
|
|
188
|
-
const result = await registry.findOrCreateSubject({
|
|
189
|
-
externalSubjectId: 'ext_new',
|
|
190
|
-
identityProvider: 'google',
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
expect(db.create).toHaveBeenCalledWith('subject', {
|
|
194
|
-
id: 'sub_test_123',
|
|
195
|
-
externalId: 'ext_new',
|
|
196
|
-
identityProvider: 'google',
|
|
197
|
-
isIdentified: true,
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
expect(result).toEqual(mockSubject);
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
it('should default identityProvider to "external"', async () => {
|
|
204
|
-
const mockSubject = createMockSubject({
|
|
205
|
-
externalId: 'ext_test',
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
const db = {
|
|
209
|
-
create: vi.fn().mockResolvedValue(mockSubject),
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
const registry = subjectRegistry({
|
|
213
|
-
db,
|
|
214
|
-
ctx: { logger: mockLogger },
|
|
215
|
-
} as unknown as Registry);
|
|
216
|
-
|
|
217
|
-
await registry.findOrCreateSubject({
|
|
218
|
-
externalSubjectId: 'ext_test',
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
expect(db.create).toHaveBeenCalledWith('subject', {
|
|
222
|
-
id: 'sub_test_123',
|
|
223
|
-
externalId: 'ext_test',
|
|
224
|
-
identityProvider: 'external',
|
|
225
|
-
isIdentified: true,
|
|
226
|
-
});
|
|
227
|
-
});
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
describe('when no identifiers are provided (anonymous subject)', () => {
|
|
231
|
-
it('should create a new anonymous subject', async () => {
|
|
232
|
-
const mockSubject = createMockSubject({
|
|
233
|
-
externalId: null,
|
|
234
|
-
isIdentified: false,
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
const db = {
|
|
238
|
-
create: vi.fn().mockResolvedValue(mockSubject),
|
|
239
|
-
};
|
|
240
|
-
|
|
241
|
-
const registry = subjectRegistry({
|
|
242
|
-
db,
|
|
243
|
-
ctx: { logger: mockLogger },
|
|
244
|
-
} as unknown as Registry);
|
|
245
|
-
|
|
246
|
-
const result = await registry.findOrCreateSubject({
|
|
247
|
-
ipAddress: '10.0.0.1',
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
expect(db.create).toHaveBeenCalledWith('subject', {
|
|
251
|
-
id: 'sub_test_123',
|
|
252
|
-
externalId: null,
|
|
253
|
-
identityProvider: 'anonymous',
|
|
254
|
-
isIdentified: false,
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
expect(result).toEqual(mockSubject);
|
|
258
|
-
expect(mockLogger.debug).toHaveBeenCalledWith(
|
|
259
|
-
'Creating new anonymous subject'
|
|
260
|
-
);
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
it('should create anonymous subject when no arguments provided', async () => {
|
|
264
|
-
const mockSubject = createMockSubject({
|
|
265
|
-
externalId: null,
|
|
266
|
-
isIdentified: false,
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
const db = {
|
|
270
|
-
create: vi.fn().mockResolvedValue(mockSubject),
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
const registry = subjectRegistry({
|
|
274
|
-
db,
|
|
275
|
-
ctx: { logger: mockLogger },
|
|
276
|
-
} as unknown as Registry);
|
|
277
|
-
|
|
278
|
-
const result = await registry.findOrCreateSubject({});
|
|
279
|
-
|
|
280
|
-
expect(db.create).toHaveBeenCalledWith('subject', {
|
|
281
|
-
id: 'sub_test_123',
|
|
282
|
-
externalId: null,
|
|
283
|
-
identityProvider: 'anonymous',
|
|
284
|
-
isIdentified: false,
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
expect(result).toEqual(mockSubject);
|
|
288
|
-
});
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
describe('edge cases and error handling', () => {
|
|
292
|
-
it('should handle empty string externalSubjectId as falsy', async () => {
|
|
293
|
-
const mockSubject = createMockSubject();
|
|
294
|
-
|
|
295
|
-
const db = {
|
|
296
|
-
create: vi.fn().mockResolvedValue(mockSubject),
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
const registry = subjectRegistry({
|
|
300
|
-
db,
|
|
301
|
-
ctx: { logger: mockLogger },
|
|
302
|
-
} as unknown as Registry);
|
|
303
|
-
|
|
304
|
-
await registry.findOrCreateSubject({
|
|
305
|
-
externalSubjectId: '',
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
// Should create anonymous subject since empty string is falsy
|
|
309
|
-
expect(db.create).toHaveBeenCalledWith('subject', {
|
|
310
|
-
id: 'sub_test_123',
|
|
311
|
-
externalId: null,
|
|
312
|
-
identityProvider: 'anonymous',
|
|
313
|
-
isIdentified: false,
|
|
314
|
-
});
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
it('should handle empty string subjectId as falsy', async () => {
|
|
318
|
-
const mockSubject = createMockSubject();
|
|
319
|
-
|
|
320
|
-
const db = {
|
|
321
|
-
create: vi.fn().mockResolvedValue(mockSubject),
|
|
322
|
-
};
|
|
323
|
-
|
|
324
|
-
const registry = subjectRegistry({
|
|
325
|
-
db,
|
|
326
|
-
ctx: { logger: mockLogger },
|
|
327
|
-
} as unknown as Registry);
|
|
328
|
-
|
|
329
|
-
await registry.findOrCreateSubject({
|
|
330
|
-
subjectId: '',
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
// Should create anonymous subject since empty string is falsy
|
|
334
|
-
expect(db.create).toHaveBeenCalledWith('subject', {
|
|
335
|
-
id: 'sub_test_123',
|
|
336
|
-
externalId: null,
|
|
337
|
-
identityProvider: 'anonymous',
|
|
338
|
-
isIdentified: false,
|
|
339
|
-
});
|
|
340
|
-
});
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
describe('database query construction', () => {
|
|
344
|
-
it('should construct correct findFirst query for subjectId lookup', async () => {
|
|
345
|
-
const mockSubject = createMockSubject();
|
|
346
|
-
const db = {
|
|
347
|
-
findFirst: vi.fn().mockResolvedValue(mockSubject),
|
|
348
|
-
};
|
|
349
|
-
|
|
350
|
-
const registry = subjectRegistry({
|
|
351
|
-
db,
|
|
352
|
-
ctx: { logger: mockLogger },
|
|
353
|
-
} as unknown as Registry);
|
|
354
|
-
|
|
355
|
-
await registry.findOrCreateSubject({
|
|
356
|
-
subjectId: 'sub_test',
|
|
357
|
-
});
|
|
358
|
-
|
|
359
|
-
expect(db.findFirst).toHaveBeenCalledWith('subject', {
|
|
360
|
-
where: expect.any(Function),
|
|
361
|
-
});
|
|
362
|
-
});
|
|
363
|
-
|
|
364
|
-
it('should construct correct create call for externalSubjectId', async () => {
|
|
365
|
-
const mockSubject = createMockSubject();
|
|
366
|
-
const db = {
|
|
367
|
-
create: vi.fn().mockResolvedValue(mockSubject),
|
|
368
|
-
};
|
|
369
|
-
|
|
370
|
-
const registry = subjectRegistry({
|
|
371
|
-
db,
|
|
372
|
-
ctx: { logger: mockLogger },
|
|
373
|
-
} as unknown as Registry);
|
|
374
|
-
|
|
375
|
-
await registry.findOrCreateSubject({
|
|
376
|
-
externalSubjectId: 'ext_test',
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
expect(db.create).toHaveBeenCalledWith('subject', {
|
|
380
|
-
id: 'sub_test_123',
|
|
381
|
-
externalId: 'ext_test',
|
|
382
|
-
identityProvider: 'external',
|
|
383
|
-
isIdentified: true,
|
|
384
|
-
});
|
|
385
|
-
});
|
|
386
|
-
});
|
|
387
|
-
});
|
|
388
|
-
});
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import { withDatabaseSpan } from '~/utils/instrumentation';
|
|
2
|
-
import { getMetrics } from '~/utils/metrics';
|
|
3
|
-
import type { Registry } from './types';
|
|
4
|
-
import { generateUniqueId } from './utils/generate-id';
|
|
5
|
-
|
|
6
|
-
export function subjectRegistry({ db, ctx }: Registry) {
|
|
7
|
-
const { logger } = ctx;
|
|
8
|
-
return {
|
|
9
|
-
/**
|
|
10
|
-
* Finds or creates a subject.
|
|
11
|
-
*
|
|
12
|
-
* For the subject-centric API (v2.0):
|
|
13
|
-
* - subjectId is required and client-generated
|
|
14
|
-
* - Creates subject if it doesn't exist with the provided ID
|
|
15
|
-
* - externalSubjectId can be provided at creation time
|
|
16
|
-
* - Multiple subjects can have the same externalId (1:many relationship)
|
|
17
|
-
*
|
|
18
|
-
* @param subjectId - Client-generated subject ID (required for v2.0)
|
|
19
|
-
* @param externalSubjectId - Optional external user ID from auth system
|
|
20
|
-
* @param identityProvider - Optional identity provider name
|
|
21
|
-
* @param ipAddress - Client IP address
|
|
22
|
-
*/
|
|
23
|
-
findOrCreateSubject: async ({
|
|
24
|
-
subjectId,
|
|
25
|
-
externalSubjectId,
|
|
26
|
-
identityProvider,
|
|
27
|
-
ipAddress = null,
|
|
28
|
-
}: {
|
|
29
|
-
subjectId?: string;
|
|
30
|
-
externalSubjectId?: string;
|
|
31
|
-
identityProvider?: string;
|
|
32
|
-
ipAddress?: string | null;
|
|
33
|
-
}) => {
|
|
34
|
-
const start = Date.now();
|
|
35
|
-
try {
|
|
36
|
-
const result = await withDatabaseSpan(
|
|
37
|
-
{ operation: 'findOrCreate', entity: 'subject' },
|
|
38
|
-
async () => {
|
|
39
|
-
// If subjectId is provided (v2.0 flow), find or create with that ID
|
|
40
|
-
if (subjectId) {
|
|
41
|
-
logger.debug(
|
|
42
|
-
'Finding/Creating subject with client-generated ID',
|
|
43
|
-
{
|
|
44
|
-
subjectId,
|
|
45
|
-
}
|
|
46
|
-
);
|
|
47
|
-
|
|
48
|
-
// Try to find existing subject
|
|
49
|
-
const existingSubject = await db.findFirst('subject', {
|
|
50
|
-
where: (b) => b('id', '=', subjectId),
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
if (existingSubject) {
|
|
54
|
-
logger.debug('Found existing subject', {
|
|
55
|
-
subjectId,
|
|
56
|
-
});
|
|
57
|
-
return existingSubject;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Create new subject with client-provided ID
|
|
61
|
-
logger.debug('Creating new subject with client-generated ID', {
|
|
62
|
-
subjectId,
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
const newSubject = await db.create('subject', {
|
|
66
|
-
id: subjectId,
|
|
67
|
-
externalId: externalSubjectId ?? null,
|
|
68
|
-
identityProvider: externalSubjectId
|
|
69
|
-
? (identityProvider ?? 'external')
|
|
70
|
-
: 'anonymous',
|
|
71
|
-
isIdentified: !!externalSubjectId,
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
logger.debug('Created new subject', {
|
|
75
|
-
subject: newSubject,
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
return newSubject;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Legacy flow: If only externalSubjectId provided, find or create
|
|
82
|
-
// Note: This creates a new subject for each call since we don't have a subjectId
|
|
83
|
-
// With 1:many relationship, we should not upsert by externalId anymore
|
|
84
|
-
if (externalSubjectId) {
|
|
85
|
-
logger.debug('Creating subject with external ID (legacy flow)', {
|
|
86
|
-
externalSubjectId,
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
const subject = await db.create('subject', {
|
|
90
|
-
id: await generateUniqueId(db, 'subject', ctx),
|
|
91
|
-
externalId: externalSubjectId,
|
|
92
|
-
identityProvider: identityProvider ?? 'external',
|
|
93
|
-
isIdentified: true,
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
return subject;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// If no identifiers provided, create an anonymous subject
|
|
100
|
-
logger?.debug('Creating new anonymous subject');
|
|
101
|
-
const subject = await db.create('subject', {
|
|
102
|
-
id: await generateUniqueId(db, 'subject', ctx),
|
|
103
|
-
externalId: null,
|
|
104
|
-
identityProvider: 'anonymous',
|
|
105
|
-
isIdentified: false,
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
logger.debug('Created new anonymous subject', {
|
|
109
|
-
subject,
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
return subject;
|
|
113
|
-
}
|
|
114
|
-
);
|
|
115
|
-
getMetrics()?.recordDbQuery(
|
|
116
|
-
{ operation: 'findOrCreate', entity: 'subject' },
|
|
117
|
-
Date.now() - start
|
|
118
|
-
);
|
|
119
|
-
return result;
|
|
120
|
-
} catch (error) {
|
|
121
|
-
getMetrics()?.recordDbError({
|
|
122
|
-
operation: 'findOrCreate',
|
|
123
|
-
entity: 'subject',
|
|
124
|
-
});
|
|
125
|
-
throw error;
|
|
126
|
-
}
|
|
127
|
-
},
|
|
128
|
-
};
|
|
129
|
-
}
|
package/src/db/registry/types.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { createLogger } from '@c15t/logger';
|
|
2
|
-
import type { InferFumaDB } from 'fumadb';
|
|
3
|
-
import type { LatestDB } from '~/db/schema';
|
|
4
|
-
|
|
5
|
-
export interface Registry {
|
|
6
|
-
db: ReturnType<InferFumaDB<typeof LatestDB>['orm']>;
|
|
7
|
-
ctx: {
|
|
8
|
-
logger: ReturnType<typeof createLogger>;
|
|
9
|
-
};
|
|
10
|
-
}
|