@adtrackify/at-service-common 4.0.5 → 4.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/dist/cjs/__tests__/identity-cache/identity-cache-dynamodb-db-service-v2-comparison.spec.d.ts +1 -0
  2. package/dist/cjs/__tests__/identity-cache/identity-cache-dynamodb-db-service-v2-comparison.spec.js +646 -0
  3. package/dist/cjs/__tests__/identity-cache/identity-cache-dynamodb-db-service-v2-comparison.spec.js.map +1 -0
  4. package/dist/cjs/__tests__/identity-cache/identity-cache-dynamodb-service-v2-comparison.spec.d.ts +1 -0
  5. package/dist/cjs/__tests__/identity-cache/identity-cache-dynamodb-service-v2-comparison.spec.js +327 -0
  6. package/dist/cjs/__tests__/identity-cache/identity-cache-dynamodb-service-v2-comparison.spec.js.map +1 -0
  7. package/dist/cjs/__tests__/identity-cache/identity-cache-dynamodb-service.spec.js +250 -108
  8. package/dist/cjs/__tests__/identity-cache/identity-cache-dynamodb-service.spec.js.map +1 -1
  9. package/dist/cjs/__tests__/identity-cache/identity-cache-tier-routing.spec.js +37 -36
  10. package/dist/cjs/__tests__/identity-cache/identity-cache-tier-routing.spec.js.map +1 -1
  11. package/dist/cjs/__tests__/identity-cache/identity-gateway-service.spec.d.ts +1 -0
  12. package/dist/cjs/__tests__/identity-cache/identity-gateway-service.spec.js +567 -0
  13. package/dist/cjs/__tests__/identity-cache/identity-gateway-service.spec.js.map +1 -0
  14. package/dist/cjs/services/db/identity-cache-dynamodb-service/constants.d.ts +3 -0
  15. package/dist/cjs/services/db/identity-cache-dynamodb-service/constants.js +7 -0
  16. package/dist/cjs/services/db/identity-cache-dynamodb-service/constants.js.map +1 -0
  17. package/dist/cjs/services/db/identity-cache-dynamodb-service/helpers.d.ts +14 -0
  18. package/dist/cjs/services/db/identity-cache-dynamodb-service/helpers.js +251 -0
  19. package/dist/cjs/services/db/identity-cache-dynamodb-service/helpers.js.map +1 -0
  20. package/dist/cjs/services/db/identity-cache-dynamodb-service/identity-cache-dynamodb-db-service.d.ts +24 -0
  21. package/dist/cjs/services/db/{identity-cache-dynamodb-service.js → identity-cache-dynamodb-service/identity-cache-dynamodb-db-service.js} +221 -343
  22. package/dist/cjs/services/db/identity-cache-dynamodb-service/identity-cache-dynamodb-db-service.js.map +1 -0
  23. package/dist/cjs/services/db/identity-cache-dynamodb-service/identity-gateway-service.d.ts +22 -0
  24. package/dist/cjs/services/db/identity-cache-dynamodb-service/identity-gateway-service.js +375 -0
  25. package/dist/cjs/services/db/identity-cache-dynamodb-service/identity-gateway-service.js.map +1 -0
  26. package/dist/cjs/services/db/identity-cache-dynamodb-service/index.d.ts +4 -0
  27. package/dist/cjs/services/db/identity-cache-dynamodb-service/index.js +21 -0
  28. package/dist/cjs/services/db/identity-cache-dynamodb-service/index.js.map +1 -0
  29. package/dist/cjs/services/db/identity-cache-dynamodb-service/key-builders.d.ts +12 -0
  30. package/dist/cjs/services/db/identity-cache-dynamodb-service/key-builders.js +32 -0
  31. package/dist/cjs/services/db/identity-cache-dynamodb-service/key-builders.js.map +1 -0
  32. package/dist/cjs/services/db/identity-cache-dynamodb-service/types.d.ts +205 -0
  33. package/dist/cjs/services/db/identity-cache-dynamodb-service/types.js +35 -0
  34. package/dist/cjs/services/db/identity-cache-dynamodb-service/types.js.map +1 -0
  35. package/dist/cjs/services/db/index.d.ts +4 -1
  36. package/dist/cjs/services/db/index.js +4 -1
  37. package/dist/cjs/services/db/index.js.map +1 -1
  38. package/dist/esm/__tests__/identity-cache/identity-cache-dynamodb-db-service-v2-comparison.spec.d.ts +1 -0
  39. package/dist/esm/__tests__/identity-cache/identity-cache-dynamodb-db-service-v2-comparison.spec.js +644 -0
  40. package/dist/esm/__tests__/identity-cache/identity-cache-dynamodb-db-service-v2-comparison.spec.js.map +1 -0
  41. package/dist/esm/__tests__/identity-cache/identity-cache-dynamodb-service-v2-comparison.spec.d.ts +1 -0
  42. package/dist/esm/__tests__/identity-cache/identity-cache-dynamodb-service-v2-comparison.spec.js +325 -0
  43. package/dist/esm/__tests__/identity-cache/identity-cache-dynamodb-service-v2-comparison.spec.js.map +1 -0
  44. package/dist/esm/__tests__/identity-cache/identity-cache-dynamodb-service.spec.js +250 -108
  45. package/dist/esm/__tests__/identity-cache/identity-cache-dynamodb-service.spec.js.map +1 -1
  46. package/dist/esm/__tests__/identity-cache/identity-cache-tier-routing.spec.js +37 -36
  47. package/dist/esm/__tests__/identity-cache/identity-cache-tier-routing.spec.js.map +1 -1
  48. package/dist/esm/__tests__/identity-cache/identity-gateway-service.spec.d.ts +1 -0
  49. package/dist/esm/__tests__/identity-cache/identity-gateway-service.spec.js +565 -0
  50. package/dist/esm/__tests__/identity-cache/identity-gateway-service.spec.js.map +1 -0
  51. package/dist/esm/services/db/identity-cache-dynamodb-service/constants.d.ts +3 -0
  52. package/dist/esm/services/db/identity-cache-dynamodb-service/constants.js +4 -0
  53. package/dist/esm/services/db/identity-cache-dynamodb-service/constants.js.map +1 -0
  54. package/dist/esm/services/db/identity-cache-dynamodb-service/helpers.d.ts +14 -0
  55. package/dist/esm/services/db/identity-cache-dynamodb-service/helpers.js +234 -0
  56. package/dist/esm/services/db/identity-cache-dynamodb-service/helpers.js.map +1 -0
  57. package/dist/esm/services/db/identity-cache-dynamodb-service/identity-cache-dynamodb-db-service.d.ts +24 -0
  58. package/dist/esm/services/db/{identity-cache-dynamodb-service.js → identity-cache-dynamodb-service/identity-cache-dynamodb-db-service.js} +199 -321
  59. package/dist/esm/services/db/identity-cache-dynamodb-service/identity-cache-dynamodb-db-service.js.map +1 -0
  60. package/dist/esm/services/db/identity-cache-dynamodb-service/identity-gateway-service.d.ts +22 -0
  61. package/dist/esm/services/db/identity-cache-dynamodb-service/identity-gateway-service.js +368 -0
  62. package/dist/esm/services/db/identity-cache-dynamodb-service/identity-gateway-service.js.map +1 -0
  63. package/dist/esm/services/db/identity-cache-dynamodb-service/index.d.ts +4 -0
  64. package/dist/esm/services/db/identity-cache-dynamodb-service/index.js +5 -0
  65. package/dist/esm/services/db/identity-cache-dynamodb-service/index.js.map +1 -0
  66. package/dist/esm/services/db/identity-cache-dynamodb-service/key-builders.d.ts +12 -0
  67. package/dist/esm/services/db/identity-cache-dynamodb-service/key-builders.js +24 -0
  68. package/dist/esm/services/db/identity-cache-dynamodb-service/key-builders.js.map +1 -0
  69. package/dist/esm/services/db/identity-cache-dynamodb-service/types.d.ts +205 -0
  70. package/dist/esm/services/db/identity-cache-dynamodb-service/types.js +32 -0
  71. package/dist/esm/services/db/identity-cache-dynamodb-service/types.js.map +1 -0
  72. package/dist/esm/services/db/index.d.ts +4 -1
  73. package/dist/esm/services/db/index.js +4 -1
  74. package/dist/esm/services/db/index.js.map +1 -1
  75. package/package.json +2 -1
  76. package/dist/cjs/services/db/identity-cache-dynamodb-service.d.ts +0 -44
  77. package/dist/cjs/services/db/identity-cache-dynamodb-service.js.map +0 -1
  78. package/dist/esm/services/db/identity-cache-dynamodb-service.d.ts +0 -44
  79. package/dist/esm/services/db/identity-cache-dynamodb-service.js.map +0 -1
@@ -1,164 +1,15 @@
1
- import { buildIdentityPk, buildEmailPk, buildIdentityMapPk, buildForcePurgePk, } from '@adtrackify/at-tracking-event-types';
2
1
  import { mergeIdentityTraits } from '@adtrackify/at-shared-utils';
3
2
  import fastDeepEqual from 'fast-deep-equal';
4
3
  import { DateTime } from 'luxon';
5
- import { DynamoDbClient, LambdaInvokeClient } from '../../clients/index.js';
6
- import { Logger } from '../../helpers/index.js';
7
- import { isIdentityCacheStale, IDENTITY_CACHE_STALENESS_TIER, } from '../../helpers/identity-cache-helper.js';
8
- const TABLE_NAME = process.env.IDENTITY_CACHE_TABLE_V2 ?? '';
9
- const GSI1_NAME = process.env.IDENTITY_CACHE_GSI1 ?? 'gsi1pk-index';
10
- const FORCE_PURGE_TTL_SECONDS = 60 * 60 * 24 * 30;
11
- export var CACHE_WRITE_REASON;
12
- (function (CACHE_WRITE_REASON) {
13
- CACHE_WRITE_REASON["CACHE_MISS"] = "cache_miss";
14
- CACHE_WRITE_REASON["STALE_TRAITS"] = "stale_traits";
15
- CACHE_WRITE_REASON["EMAIL_POINTER_DISCOVERY"] = "email_pointer_discovery";
16
- CACHE_WRITE_REASON["ADDRESS_UPDATE"] = "address_update";
17
- })(CACHE_WRITE_REASON || (CACHE_WRITE_REASON = {}));
18
- export class IdentityCacheDynamoDbService {
19
- static lambdaInvokeClient;
20
- static getLambdaInvokeClient() {
21
- if (!this.lambdaInvokeClient) {
22
- this.lambdaInvokeClient = new LambdaInvokeClient();
23
- }
24
- return this.lambdaInvokeClient;
25
- }
26
- static getIdentityWithCaching = async (pixelId, incomingIdentity, identifyPrivateLambdaArn) => {
27
- try {
28
- if (!pixelId || !incomingIdentity) {
29
- Logger.error('IdentityCache - missing required params', { pixelId, incomingIdentity });
30
- return undefined;
31
- }
32
- const cacheResponse = await this.readFromCache(pixelId, incomingIdentity);
33
- let resolvedIdentity = cacheResponse.resolvedIdentity;
34
- const isCacheStale = cacheResponse.isCacheStale;
35
- const cachedIdentity = cacheResponse.cachedIdentity;
36
- const staleTracker = cacheResponse.staleTracker;
37
- const writeReason = cacheResponse.writeReason;
38
- const staleTier = cacheResponse.staleTier;
39
- const skipNeptune = cacheResponse.skipNeptune;
40
- if (resolvedIdentity && !isCacheStale) {
41
- const tierLabel = staleTier === IDENTITY_CACHE_STALENESS_TIER.VOLATILE_ONLY ? 'volatile_traits_only' : 'fresh';
42
- Logger.debug(`IdentityCache - identity cache hit - ${tierLabel}`, {
43
- pixelId,
44
- identityId: resolvedIdentity.identityId,
45
- staleTier,
46
- cachedIdentity: cachedIdentity ?? null,
47
- incomingIdentity,
48
- resolvedIdentity,
49
- incomingClick: incomingIdentity?.traits?.click ?? null,
50
- cachedClick: cachedIdentity?.traits?.click ?? null,
51
- resolvedClick: resolvedIdentity?.traits?.click ?? null,
52
- });
53
- return resolvedIdentity;
54
- }
55
- if (resolvedIdentity && skipNeptune && writeReason === CACHE_WRITE_REASON.ADDRESS_UPDATE) {
56
- const staleTraits = staleTracker ? this.extractStaleTraitNames(staleTracker) : [];
57
- Logger.info('IdentityCache - identity cache write: address update only (skipping Neptune)', {
58
- pixelId,
59
- identityId: resolvedIdentity.identityId,
60
- writeReason,
61
- staleTier,
62
- staleTraits,
63
- cachedIdentity: cachedIdentity ?? null,
64
- incomingIdentity,
65
- resolvedIdentity,
66
- incomingClick: incomingIdentity?.traits?.click ?? null,
67
- cachedClick: cachedIdentity?.traits?.click ?? null,
68
- resolvedClick: resolvedIdentity?.traits?.click ?? null,
69
- traitChanges: cachedIdentity ? this.computeTraitChanges(cachedIdentity, resolvedIdentity) : { reason: 'no cached identity' },
70
- });
71
- await this.safeWriteBackToCache(pixelId, resolvedIdentity, 'address update');
72
- return resolvedIdentity;
73
- }
74
- const identityForNeptune = resolvedIdentity ?? incomingIdentity;
75
- Logger.info('IdentityCache - identity cache stale - fetching from Neptune', {
76
- pixelId,
77
- identityId: identityForNeptune?.identityId,
78
- staleTier,
79
- writeReason,
80
- staleTraits: staleTracker ? this.extractStaleTraitNames(staleTracker) : [],
81
- cachedIdentity: cachedIdentity ?? null,
82
- incomingIdentity,
83
- identityForNeptune,
84
- incomingClick: incomingIdentity?.traits?.click ?? null,
85
- cachedClick: cachedIdentity?.traits?.click ?? null,
86
- hasDiscoveredIdentityId: !!resolvedIdentity?.identityId,
87
- });
88
- const neptuneIdentity = await this.resolveFromNeptune(pixelId, identityForNeptune, identifyPrivateLambdaArn);
89
- if (neptuneIdentity) {
90
- resolvedIdentity = neptuneIdentity;
91
- const cachedForComparison = cachedIdentity ? this.stripAddressTimestamps(cachedIdentity) : undefined;
92
- const neptuneForComparison = this.stripAddressTimestamps(neptuneIdentity);
93
- const shouldWrite = !cachedForComparison || !fastDeepEqual(cachedForComparison, neptuneForComparison);
94
- if (shouldWrite) {
95
- const staleTraits = staleTracker ? this.extractStaleTraitNames(staleTracker) : [];
96
- const isNewIdentity = writeReason === CACHE_WRITE_REASON.CACHE_MISS || writeReason === CACHE_WRITE_REASON.EMAIL_POINTER_DISCOVERY;
97
- const writeType = isNewIdentity ? 'new_identity' : 'stale_traits_update';
98
- Logger.info(`IdentityCache - identity cache write: ${writeType}`, {
99
- pixelId,
100
- identityId: neptuneIdentity.identityId,
101
- writeReason,
102
- staleTier,
103
- staleTraits,
104
- cachedIdentity: cachedIdentity ?? null,
105
- incomingIdentity,
106
- neptuneIdentity,
107
- incomingClick: incomingIdentity?.traits?.click ?? null,
108
- cachedClick: cachedIdentity?.traits?.click ?? null,
109
- neptuneClick: neptuneIdentity?.traits?.click ?? null,
110
- traitChanges: cachedIdentity
111
- ? this.computeTraitChanges(cachedIdentity, neptuneIdentity)
112
- : { reason: 'no cached identity' },
113
- });
114
- await this.safeWriteBackToCache(pixelId, resolvedIdentity, 'neptune write-back');
115
- }
116
- else {
117
- Logger.info('IdentityCache - identity cache skip write - Neptune matches cached', {
118
- pixelId,
119
- identityId: neptuneIdentity.identityId,
120
- staleTier,
121
- cachedIdentity: cachedIdentity ?? null,
122
- incomingIdentity,
123
- neptuneIdentity,
124
- incomingClick: incomingIdentity?.traits?.click ?? null,
125
- cachedClick: cachedIdentity?.traits?.click ?? null,
126
- neptuneClick: neptuneIdentity?.traits?.click ?? null,
127
- });
128
- }
129
- }
130
- else {
131
- Logger.error('IdentityCache - Neptune resolution failed', {
132
- pixelId,
133
- staleTier,
134
- writeReason,
135
- cachedIdentity: cachedIdentity ?? null,
136
- incomingIdentity,
137
- identityForNeptune,
138
- incomingClick: incomingIdentity?.traits?.click ?? null,
139
- });
140
- }
141
- if (!resolvedIdentity) {
142
- Logger.error('IdentityCache - no identity found, using incoming', {
143
- pixelId,
144
- incomingIdentity,
145
- incomingClick: incomingIdentity?.traits?.click ?? null,
146
- });
147
- resolvedIdentity = incomingIdentity;
148
- }
149
- return resolvedIdentity;
150
- }
151
- catch (error) {
152
- Logger.error('IdentityCache - getIdentityWithCaching failed', {
153
- error,
154
- errorMessage: error instanceof Error ? error.message : String(error),
155
- pixelId,
156
- incomingIdentity,
157
- incomingClick: incomingIdentity?.traits?.click ?? null,
158
- });
159
- return undefined;
160
- }
161
- };
4
+ import { DynamoDbClient } from '../../../clients/index.js';
5
+ import { Logger } from '../../../helpers/index.js';
6
+ import { isIdentityCacheStale, IDENTITY_CACHE_STALENESS_TIER, } from '../../../helpers/identity-cache-helper.js';
7
+ import { FORCE_PURGE_TTL_SECONDS, IDENTITY_CACHE_GSI1_NAME, IDENTITY_CACHE_TABLE_NAME } from './constants.js';
8
+ import { buildEmailPk, buildForcePurgePk, buildIdentityMapPk, buildIdentityPk, } from './key-builders.js';
9
+ import { CACHE_WRITE_REASON } from './types.js';
10
+ import { createCacheMissLookupResult, createIdentityCacheLookupResultFromState, createIdentityCacheLookupState, extractStaleTraitNames, stripAddressTimestamps, } from './helpers.js';
11
+ import { IDENTITY_CACHE_LOOKUP_OUTCOME, } from './types.js';
12
+ export class IdentityCacheDynamoDbDbService {
162
13
  static getIdentityFromCache = async (pixelId, incomingIdentity) => {
163
14
  try {
164
15
  if (!pixelId || !incomingIdentity) {
@@ -177,6 +28,15 @@ export class IdentityCacheDynamoDbService {
177
28
  });
178
29
  return { resolvedIdentity: undefined, isCacheStale: true, writeReason: CACHE_WRITE_REASON.CACHE_MISS };
179
30
  }
31
+ if (!cachedIdentityResponse?.response) {
32
+ Logger.debug('IdentityCache - found cached identity but no response', {
33
+ pixelId,
34
+ identityId: incomingIdentity.identityId,
35
+ incomingIdentity,
36
+ cachedIdentityResponse,
37
+ });
38
+ return { resolvedIdentity: undefined, isCacheStale: true, writeReason: CACHE_WRITE_REASON.CACHE_MISS };
39
+ }
180
40
  Logger.debug('IdentityCache - found cached identity', {
181
41
  pixelId,
182
42
  identityId: incomingIdentity.identityId,
@@ -186,8 +46,8 @@ export class IdentityCacheDynamoDbService {
186
46
  const mergedTraits = mergeIdentityTraits(incomingIdentity.traits, cachedIdentityResponse.response.traits);
187
47
  const mergedIdentity = { ...incomingIdentity, traits: mergedTraits };
188
48
  const identityStaleCacheTracker = isIdentityCacheStale(cachedIdentityResponse.response, mergedIdentity);
189
- const cachedForComparison = this.stripAddressTimestamps(cachedIdentityResponse.response);
190
- const mergedForComparison = this.stripAddressTimestamps(mergedIdentity);
49
+ const cachedForComparison = stripAddressTimestamps(cachedIdentityResponse.response);
50
+ const mergedForComparison = stripAddressTimestamps(mergedIdentity);
191
51
  const isDeepEqual = fastDeepEqual(cachedForComparison, mergedForComparison);
192
52
  const staleTier = identityStaleCacheTracker.staleTier;
193
53
  let isCacheStale = identityStaleCacheTracker.isCacheStale;
@@ -281,13 +141,180 @@ export class IdentityCacheDynamoDbService {
281
141
  return { resolvedIdentity: undefined, isCacheStale: true, writeReason: CACHE_WRITE_REASON.CACHE_MISS };
282
142
  }
283
143
  };
144
+ static getIdentityFromCacheV2 = async (pixelId, incomingIdentity) => {
145
+ const state = createIdentityCacheLookupState(pixelId, incomingIdentity);
146
+ try {
147
+ if (!pixelId || !incomingIdentity) {
148
+ state.isCacheStale = true;
149
+ state.writeReason = CACHE_WRITE_REASON.CACHE_MISS;
150
+ return createCacheMissLookupResult(state, IDENTITY_CACHE_LOOKUP_OUTCOME.INVALID_INPUT);
151
+ }
152
+ if (incomingIdentity.identityId) {
153
+ return await this.resolveCacheByIdentityIdV2(state);
154
+ }
155
+ return await this.resolveCacheByEmailPointerV2(state);
156
+ }
157
+ catch (error) {
158
+ Logger.error('IdentityCache - getIdentityFromCache failed', {
159
+ error,
160
+ errorMessage: error instanceof Error ? error.message : String(error),
161
+ pixelId,
162
+ incomingIdentity,
163
+ incomingClick: incomingIdentity?.traits?.click ?? null,
164
+ });
165
+ state.isCacheStale = true;
166
+ state.writeReason = CACHE_WRITE_REASON.CACHE_MISS;
167
+ return createCacheMissLookupResult(state, IDENTITY_CACHE_LOOKUP_OUTCOME.FAILED, {
168
+ errorMessage: error instanceof Error ? error.message : String(error),
169
+ });
170
+ }
171
+ };
172
+ static resolveCacheByIdentityIdV2 = async (state) => {
173
+ const incomingIdentity = state.incomingIdentity;
174
+ const identityId = incomingIdentity.identityId;
175
+ state.didLookupByIdentityId = true;
176
+ const pk = buildIdentityPk(state.pixelId, identityId);
177
+ Logger.debug('IdentityCache - lookup by identityId', { pixelId: state.pixelId, pk, incomingIdentity });
178
+ const cachedIdentityResponse = await this.getItemByPk(pk);
179
+ if (!cachedIdentityResponse) {
180
+ Logger.debug('IdentityCache - identity cache miss - no cached identity found', {
181
+ pixelId: state.pixelId,
182
+ identityId,
183
+ incomingIdentity,
184
+ incomingClick: incomingIdentity?.traits?.click ?? null,
185
+ });
186
+ state.isCacheStale = true;
187
+ state.writeReason = CACHE_WRITE_REASON.CACHE_MISS;
188
+ return createCacheMissLookupResult(state, IDENTITY_CACHE_LOOKUP_OUTCOME.PRIMARY_CACHE_MISS);
189
+ }
190
+ state.cachedIdentityResponse = cachedIdentityResponse;
191
+ if (!cachedIdentityResponse?.response) {
192
+ Logger.debug('IdentityCache - found cached identity but no response', {
193
+ pixelId: state.pixelId,
194
+ identityId,
195
+ incomingIdentity,
196
+ cachedIdentityResponse,
197
+ });
198
+ state.isCacheStale = true;
199
+ state.writeReason = CACHE_WRITE_REASON.CACHE_MISS;
200
+ return createCacheMissLookupResult(state, IDENTITY_CACHE_LOOKUP_OUTCOME.PRIMARY_CACHE_MISS_NO_RESPONSE);
201
+ }
202
+ this.applyPrimaryCacheHitState(state, cachedIdentityResponse);
203
+ return this.createPrimaryCacheLookupResult(state);
204
+ };
205
+ static applyPrimaryCacheHitState(state, cachedIdentityResponse) {
206
+ const incomingIdentity = state.incomingIdentity;
207
+ const identityId = incomingIdentity.identityId;
208
+ Logger.debug('IdentityCache - found cached identity', {
209
+ pixelId: state.pixelId,
210
+ identityId,
211
+ cachedIdentity: cachedIdentityResponse.response,
212
+ cachedClick: cachedIdentityResponse.response?.traits?.click ?? null,
213
+ });
214
+ state.cachedIdentity = cachedIdentityResponse.response;
215
+ const mergedTraits = mergeIdentityTraits(incomingIdentity.traits, cachedIdentityResponse.response.traits);
216
+ state.mergedIdentity = { ...incomingIdentity, traits: mergedTraits };
217
+ state.resolvedIdentity = state.mergedIdentity;
218
+ state.identityStaleCacheTracker = isIdentityCacheStale(cachedIdentityResponse.response, state.mergedIdentity);
219
+ const cachedForComparison = stripAddressTimestamps(cachedIdentityResponse.response);
220
+ const mergedForComparison = stripAddressTimestamps(state.mergedIdentity);
221
+ state.isDeepEqual = fastDeepEqual(cachedForComparison, mergedForComparison);
222
+ state.staleTier = state.identityStaleCacheTracker.staleTier;
223
+ state.isCacheStale = state.identityStaleCacheTracker.isCacheStale;
224
+ if (state.isDeepEqual) {
225
+ Logger.debug('IdentityCache - deep equal (after stripping timestamps)', {
226
+ pixelId: state.pixelId,
227
+ identityId,
228
+ staleTier: state.staleTier,
229
+ });
230
+ state.isCacheStale = false;
231
+ }
232
+ else {
233
+ Logger.debug('IdentityCache - not deep equal', {
234
+ pixelId: state.pixelId,
235
+ identityId,
236
+ staleTier: state.staleTier,
237
+ identityStaleCacheTracker: state.identityStaleCacheTracker,
238
+ incomingClick: incomingIdentity?.traits?.click ?? null,
239
+ cachedClick: cachedIdentityResponse.response?.traits?.click ?? null,
240
+ mergedClick: state.mergedIdentity?.traits?.click ?? null,
241
+ });
242
+ }
243
+ const isVolatileOnly = state.staleTier === IDENTITY_CACHE_STALENESS_TIER.VOLATILE_ONLY;
244
+ const isAddressUpdateOnly = state.staleTier === IDENTITY_CACHE_STALENESS_TIER.ADDRESS_UPDATE;
245
+ const skipNeptune = isVolatileOnly || isAddressUpdateOnly;
246
+ if (isVolatileOnly && !state.isDeepEqual) {
247
+ Logger.debug('IdentityCache - volatile only - treating as cache hit', {
248
+ pixelId: state.pixelId,
249
+ identityId,
250
+ staleTier: state.staleTier,
251
+ });
252
+ state.isCacheStale = false;
253
+ }
254
+ if (state.isCacheStale) {
255
+ state.writeReason = isAddressUpdateOnly ? CACHE_WRITE_REASON.ADDRESS_UPDATE : CACHE_WRITE_REASON.STALE_TRAITS;
256
+ state.skipNeptune = skipNeptune;
257
+ state.staleTraits = extractStaleTraitNames(state.identityStaleCacheTracker);
258
+ Logger.debug('IdentityCache - stale detected', {
259
+ pixelId: state.pixelId,
260
+ identityId,
261
+ isCacheStale: state.isCacheStale,
262
+ isDeepEqual: state.isDeepEqual,
263
+ staleTier: state.staleTier,
264
+ skipNeptune: state.skipNeptune,
265
+ writeReason: state.writeReason,
266
+ incomingClick: incomingIdentity?.traits?.click ?? null,
267
+ cachedClick: cachedIdentityResponse.response?.traits?.click ?? null,
268
+ mergedClick: state.mergedIdentity?.traits?.click ?? null,
269
+ });
270
+ }
271
+ }
272
+ static createPrimaryCacheLookupResult(state) {
273
+ const cacheResult = {
274
+ resolvedIdentity: state.mergedIdentity,
275
+ isCacheStale: state.isCacheStale,
276
+ cachedIdentity: state.cachedIdentity,
277
+ staleTracker: state.isCacheStale ? state.identityStaleCacheTracker : undefined,
278
+ writeReason: state.writeReason,
279
+ staleTier: state.staleTier,
280
+ skipNeptune: state.isCacheStale ? state.skipNeptune : undefined,
281
+ };
282
+ return createIdentityCacheLookupResultFromState(state, cacheResult, {
283
+ outcome: state.isCacheStale ? IDENTITY_CACHE_LOOKUP_OUTCOME.PRIMARY_CACHE_STALE : IDENTITY_CACHE_LOOKUP_OUTCOME.PRIMARY_CACHE_HIT,
284
+ skipNeptune: state.isCacheStale ? state.skipNeptune : undefined,
285
+ });
286
+ }
287
+ static resolveCacheByEmailPointerV2 = async (state) => {
288
+ const incomingIdentity = state.incomingIdentity;
289
+ state.didLookupByEmail = !!incomingIdentity.traits?.emails?.[0];
290
+ state.discoveredIdentityId = await this.getIdentityIdFromEmailLookup(state.pixelId, incomingIdentity);
291
+ if (state.discoveredIdentityId) {
292
+ Logger.debug('IdentityCache - identityId from email pointer', {
293
+ pixelId: state.pixelId,
294
+ discoveredIdentityId: state.discoveredIdentityId,
295
+ incomingIdentity,
296
+ incomingClick: incomingIdentity?.traits?.click ?? null,
297
+ });
298
+ const identityWithDiscoveredId = {
299
+ ...incomingIdentity,
300
+ identityId: state.discoveredIdentityId,
301
+ };
302
+ state.resolvedIdentity = identityWithDiscoveredId;
303
+ state.isCacheStale = true;
304
+ state.writeReason = CACHE_WRITE_REASON.EMAIL_POINTER_DISCOVERY;
305
+ return createIdentityCacheLookupResultFromState(state, { resolvedIdentity: identityWithDiscoveredId, isCacheStale: true, writeReason: CACHE_WRITE_REASON.EMAIL_POINTER_DISCOVERY }, { outcome: IDENTITY_CACHE_LOOKUP_OUTCOME.EMAIL_POINTER_DISCOVERY });
306
+ }
307
+ state.isCacheStale = true;
308
+ state.writeReason = CACHE_WRITE_REASON.CACHE_MISS;
309
+ return createCacheMissLookupResult(state, IDENTITY_CACHE_LOOKUP_OUTCOME.EMAIL_POINTER_MISS);
310
+ };
284
311
  static getIdentityMap = async (pixelId, identityId) => {
285
312
  try {
286
313
  if (!pixelId || !identityId) {
287
314
  return undefined;
288
315
  }
289
316
  const pk = buildIdentityMapPk(pixelId, identityId);
290
- const result = (await DynamoDbClient.safeGet(TABLE_NAME, 'pk', pk));
317
+ const result = (await DynamoDbClient.safeGet(IDENTITY_CACHE_TABLE_NAME, 'pk', pk));
291
318
  return result ?? undefined;
292
319
  }
293
320
  catch (error) {
@@ -306,7 +333,7 @@ export class IdentityCacheDynamoDbService {
306
333
  return false;
307
334
  }
308
335
  const pk = buildForcePurgePk(pixelId, identityId);
309
- const result = await DynamoDbClient.safeGet(TABLE_NAME, 'pk', pk);
336
+ const result = await DynamoDbClient.safeGet(IDENTITY_CACHE_TABLE_NAME, 'pk', pk);
310
337
  return result !== null;
311
338
  }
312
339
  catch (error) {
@@ -347,7 +374,7 @@ export class IdentityCacheDynamoDbService {
347
374
  items.push(pointerRecord);
348
375
  }
349
376
  }
350
- await DynamoDbClient.safeBatchWrite(TABLE_NAME, items);
377
+ await DynamoDbClient.safeBatchWrite(IDENTITY_CACHE_TABLE_NAME, items);
351
378
  Logger.debug('IdentityCache - cache written', { pixelId, identityId: response.identityId, itemCount: items.length });
352
379
  }
353
380
  catch (error) {
@@ -370,7 +397,7 @@ export class IdentityCacheDynamoDbService {
370
397
  linkedIdentities,
371
398
  updatedAt: new Date().toISOString(),
372
399
  };
373
- await DynamoDbClient.safePut(TABLE_NAME, item);
400
+ await DynamoDbClient.safePut(IDENTITY_CACHE_TABLE_NAME, item);
374
401
  Logger.debug('IdentityCache - identity map written', { pixelId, identityId, linkedIdentities });
375
402
  }
376
403
  catch (error) {
@@ -396,7 +423,7 @@ export class IdentityCacheDynamoDbService {
396
423
  createdAt: now,
397
424
  ttl: Math.floor(ttl),
398
425
  };
399
- await DynamoDbClient.safePut(TABLE_NAME, item);
426
+ await DynamoDbClient.safePut(IDENTITY_CACHE_TABLE_NAME, item);
400
427
  Logger.debug('IdentityCache - force purge flag set', { pixelId, identityId, ttlSeconds });
401
428
  }
402
429
  catch (error) {
@@ -459,7 +486,7 @@ export class IdentityCacheDynamoDbService {
459
486
  return;
460
487
  }
461
488
  const pk = buildIdentityMapPk(pixelId, identityId);
462
- await DynamoDbClient.safeDelete(TABLE_NAME, 'pk', pk);
489
+ await DynamoDbClient.safeDelete(IDENTITY_CACHE_TABLE_NAME, 'pk', pk);
463
490
  Logger.debug('IdentityCache - identity map deleted', { pixelId, identityId });
464
491
  }
465
492
  catch (error) {
@@ -477,7 +504,7 @@ export class IdentityCacheDynamoDbService {
477
504
  return;
478
505
  }
479
506
  const pk = buildForcePurgePk(pixelId, identityId);
480
- await DynamoDbClient.safeDelete(TABLE_NAME, 'pk', pk);
507
+ await DynamoDbClient.safeDelete(IDENTITY_CACHE_TABLE_NAME, 'pk', pk);
481
508
  Logger.debug('IdentityCache - force purge flag deleted', { pixelId, identityId });
482
509
  }
483
510
  catch (error) {
@@ -489,50 +516,6 @@ export class IdentityCacheDynamoDbService {
489
516
  });
490
517
  }
491
518
  };
492
- static readFromCache = async (pixelId, incomingIdentity) => {
493
- return await this.getIdentityFromCache(pixelId, incomingIdentity);
494
- };
495
- static resolveFromNeptune = async (pixelId, incomingIdentity, identifyPrivateLambdaArn) => {
496
- try {
497
- const client = this.getLambdaInvokeClient();
498
- const response = await client.invokeFunction(identifyPrivateLambdaArn, {
499
- pixelId,
500
- context: { identity: incomingIdentity },
501
- });
502
- if (response?.statusCode === 200 && response?.body) {
503
- const identifyResponse = JSON.parse(response.body);
504
- return identifyResponse?.identity;
505
- }
506
- return incomingIdentity;
507
- }
508
- catch (error) {
509
- Logger.error('IdentityCache - Neptune resolution failed', {
510
- error,
511
- errorMessage: error instanceof Error ? error.message : String(error),
512
- pixelId,
513
- incomingIdentity,
514
- incomingClick: incomingIdentity?.traits?.click ?? null,
515
- });
516
- return undefined;
517
- }
518
- };
519
- static writeBackToCache = async (pixelId, resolvedIdentity) => {
520
- await this.updateIdentityCache(pixelId, resolvedIdentity);
521
- };
522
- static safeWriteBackToCache = async (pixelId, resolvedIdentity, context) => {
523
- try {
524
- await this.writeBackToCache(pixelId, resolvedIdentity);
525
- }
526
- catch (writeError) {
527
- Logger.error(`IdentityCache - write back failed (${context})`, {
528
- error: writeError,
529
- errorMessage: writeError instanceof Error ? writeError.message : String(writeError),
530
- pixelId,
531
- identityId: resolvedIdentity?.identityId,
532
- resolvedClick: resolvedIdentity?.traits?.click ?? null,
533
- });
534
- }
535
- };
536
519
  static getIdentityIdFromEmailLookup = async (pixelId, incomingIdentity) => {
537
520
  try {
538
521
  if (!pixelId || !incomingIdentity) {
@@ -544,7 +527,7 @@ export class IdentityCacheDynamoDbService {
544
527
  }
545
528
  const pk = buildEmailPk(pixelId, firstEmail);
546
529
  Logger.debug('IdentityCache - email secondary lookup', { pixelId, pk, email: firstEmail });
547
- const result = (await DynamoDbClient.safeGet(TABLE_NAME, 'pk', pk));
530
+ const result = (await DynamoDbClient.safeGet(IDENTITY_CACHE_TABLE_NAME, 'pk', pk));
548
531
  if (!result) {
549
532
  return undefined;
550
533
  }
@@ -563,7 +546,7 @@ export class IdentityCacheDynamoDbService {
563
546
  };
564
547
  static getItemByPk = async (pk) => {
565
548
  try {
566
- const result = (await DynamoDbClient.safeGet(TABLE_NAME, 'pk', pk));
549
+ const result = (await DynamoDbClient.safeGet(IDENTITY_CACHE_TABLE_NAME, 'pk', pk));
567
550
  return result ?? undefined;
568
551
  }
569
552
  catch (error) {
@@ -577,7 +560,7 @@ export class IdentityCacheDynamoDbService {
577
560
  };
578
561
  static queryByGsi1 = async (identityId) => {
579
562
  try {
580
- const results = await DynamoDbClient.safeQueryByGSI(TABLE_NAME, GSI1_NAME, 'gsi1pk', identityId);
563
+ const results = await DynamoDbClient.safeQueryByGSI(IDENTITY_CACHE_TABLE_NAME, IDENTITY_CACHE_GSI1_NAME, 'gsi1pk', identityId);
581
564
  return (results ?? []);
582
565
  }
583
566
  catch (error) {
@@ -600,7 +583,7 @@ export class IdentityCacheDynamoDbService {
600
583
  DeleteRequest: { Key: { pk: key.pk } },
601
584
  }));
602
585
  await DynamoDbClient.batchWrite({
603
- RequestItems: { [TABLE_NAME]: deleteRequests },
586
+ RequestItems: { [IDENTITY_CACHE_TABLE_NAME]: deleteRequests },
604
587
  });
605
588
  }
606
589
  catch (error) {
@@ -619,110 +602,5 @@ export class IdentityCacheDynamoDbService {
619
602
  }
620
603
  return chunks;
621
604
  }
622
- static stripAddressTimestamps(identity) {
623
- if (!identity?.traits?.addresses?.length) {
624
- return identity;
625
- }
626
- const strippedAddresses = identity.traits.addresses.map((addr) => {
627
- if (!addr)
628
- return addr;
629
- const { cra, upa, ...rest } = addr;
630
- return rest;
631
- });
632
- return {
633
- ...identity,
634
- traits: {
635
- ...identity.traits,
636
- addresses: strippedAddresses,
637
- },
638
- };
639
- }
640
- static extractStaleTraitNames(tracker) {
641
- const staleTraits = [];
642
- if (tracker.isIdentityIdStale)
643
- staleTraits.push('identityId');
644
- if (tracker.isUserIdsStale)
645
- staleTraits.push('userIds');
646
- if (tracker.isIdsStale)
647
- staleTraits.push('ids');
648
- if (tracker.isEmailsStale)
649
- staleTraits.push('emails');
650
- if (tracker.isPhonesStale)
651
- staleTraits.push('phones');
652
- if (tracker.isShopifyYIdsStale)
653
- staleTraits.push('shopifyYIds');
654
- if (tracker.isGendersStale)
655
- staleTraits.push('genders');
656
- if (tracker.isDobsStale)
657
- staleTraits.push('dobs');
658
- if (tracker.isAddressesStale)
659
- staleTraits.push('addresses');
660
- if (tracker.isClickInfosStale)
661
- staleTraits.push('clickInfos');
662
- if (tracker.isIpAddressStale)
663
- staleTraits.push('ipAddress');
664
- if (tracker.isUserAgentStale)
665
- staleTraits.push('userAgent');
666
- if (tracker.isThirdPartyContactsStale)
667
- staleTraits.push('thirdPartyContacts');
668
- return staleTraits;
669
- }
670
- static computeTraitChanges(cachedIdentity, resolvedIdentity) {
671
- const changes = {};
672
- const cachedTraits = cachedIdentity.traits ?? {};
673
- const resolvedTraits = resolvedIdentity.traits ?? {};
674
- const arrayTraits = [
675
- 'emails',
676
- 'phones',
677
- 'userIds',
678
- 'shopifyYIds',
679
- 'shopifyCustomerIds',
680
- 'klaviyoIds',
681
- 'genders',
682
- 'dobs',
683
- 'ids',
684
- ];
685
- for (const trait of arrayTraits) {
686
- const cachedValue = cachedTraits[trait];
687
- const resolvedValue = resolvedTraits[trait];
688
- if (!fastDeepEqual(cachedValue, resolvedValue)) {
689
- changes[trait] = {
690
- cached: cachedValue ?? [],
691
- resolved: resolvedValue ?? [],
692
- };
693
- }
694
- }
695
- if (!fastDeepEqual(cachedTraits.addresses, resolvedTraits.addresses)) {
696
- changes.addresses = {
697
- cached: cachedTraits.addresses ?? [],
698
- resolved: resolvedTraits.addresses ?? [],
699
- };
700
- }
701
- if (!fastDeepEqual(cachedTraits.click, resolvedTraits.click)) {
702
- changes.click = {
703
- cached: cachedTraits.click ?? {},
704
- resolved: resolvedTraits.click ?? {},
705
- };
706
- }
707
- if (cachedTraits.ipAddress !== resolvedTraits.ipAddress) {
708
- changes.ipAddress = {
709
- cached: cachedTraits.ipAddress,
710
- resolved: resolvedTraits.ipAddress,
711
- };
712
- }
713
- if (cachedTraits.userAgent !== resolvedTraits.userAgent) {
714
- changes.userAgent = {
715
- cached: cachedTraits.userAgent,
716
- resolved: resolvedTraits.userAgent,
717
- };
718
- }
719
- if (cachedIdentity.identityId !== resolvedIdentity.identityId) {
720
- changes.identityId = {
721
- cached: cachedIdentity.identityId,
722
- resolved: resolvedIdentity.identityId,
723
- };
724
- }
725
- return changes;
726
- }
727
605
  }
728
- //# sourceMappingURL=identity-cache-dynamodb-service.js.map
606
+ //# sourceMappingURL=identity-cache-dynamodb-db-service.js.map