@amplitude/analytics-core 2.41.1 → 2.41.2
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/lib/cjs/storage/cookie.d.ts +6 -0
- package/lib/cjs/storage/cookie.d.ts.map +1 -1
- package/lib/cjs/storage/cookie.js +223 -146
- package/lib/cjs/storage/cookie.js.map +1 -1
- package/lib/cjs/types/storage.d.ts +4 -0
- package/lib/cjs/types/storage.d.ts.map +1 -1
- package/lib/cjs/types/storage.js.map +1 -1
- package/lib/esm/storage/cookie.d.ts +6 -0
- package/lib/esm/storage/cookie.d.ts.map +1 -1
- package/lib/esm/storage/cookie.js +223 -146
- package/lib/esm/storage/cookie.js.map +1 -1
- package/lib/esm/types/storage.d.ts +4 -0
- package/lib/esm/types/storage.d.ts.map +1 -1
- package/lib/esm/types/storage.js.map +1 -1
- package/package.json +1 -1
|
@@ -5,10 +5,16 @@ export declare class CookieStorage<T> implements Storage<T> {
|
|
|
5
5
|
constructor(options?: CookieStorageOptions, config?: CookieStorageConfig);
|
|
6
6
|
isEnabled(): Promise<boolean>;
|
|
7
7
|
get(key: string): Promise<T | undefined>;
|
|
8
|
+
private decodeCookieValue;
|
|
9
|
+
private getSync;
|
|
8
10
|
getRaw(key: string): Promise<string | undefined>;
|
|
11
|
+
private getRawSync;
|
|
9
12
|
set(key: string, value: T | null): Promise<void>;
|
|
13
|
+
private setSync;
|
|
10
14
|
remove(key: string): Promise<void>;
|
|
11
15
|
reset(): Promise<void>;
|
|
16
|
+
static isDomainWritable(domain: string): Promise<boolean>;
|
|
17
|
+
private transaction;
|
|
12
18
|
}
|
|
13
19
|
/**
|
|
14
20
|
* Decodes a cookie value that was encoded with btoa(encodeURIComponent(...)).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cookie.d.ts","sourceRoot":"","sources":["../../../src/storage/cookie.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"cookie.d.ts","sourceRoot":"","sources":["../../../src/storage/cookie.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAe,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AA2BnG,qBAAa,aAAa,CAAC,CAAC,CAAE,YAAW,OAAO,CAAC,CAAC,CAAC;IACjD,OAAO,EAAE,oBAAoB,CAAC;IAC9B,MAAM,EAAE,mBAAmB,CAAC;gBAEhB,OAAO,CAAC,EAAE,oBAAoB,EAAE,MAAM,GAAE,mBAAwB;IAKtE,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IAwC7B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAK9C,OAAO,CAAC,iBAAiB;IAkBzB,OAAO,CAAC,OAAO;IAKT,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAiCtD,OAAO,CAAC,UAAU;IAiCZ,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAItD,OAAO,CAAC,OAAO;IAkCT,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;WAIf,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;YAqBjD,WAAW;CAsB1B;AAoBD;;;GAGG;AACH,eAAO,MAAM,iBAAiB,UAAW,MAAM,KAAG,MAAM,GAAG,SAE1D,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,aAAa,YAAa,MAAM,GAAG,SAAS,WAAW,MAAM,GAAG,SAAS,KAAG,OAUxF,CAAC"}
|
|
@@ -3,7 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.isDomainEqual = exports.decodeCookieValue = exports.CookieStorage = void 0;
|
|
4
4
|
var tslib_1 = require("tslib");
|
|
5
5
|
var global_scope_1 = require("../global-scope");
|
|
6
|
-
|
|
6
|
+
/* istanbul ignore next */
|
|
7
|
+
var getLocks = function () {
|
|
8
|
+
var _a;
|
|
9
|
+
var globalScope = (0, global_scope_1.getGlobalScope)();
|
|
10
|
+
return (_a = globalScope === null || globalScope === void 0 ? void 0 : globalScope.navigator) === null || _a === void 0 ? void 0 : _a.locks;
|
|
11
|
+
};
|
|
7
12
|
var CookieStorage = /** @class */ (function () {
|
|
8
13
|
function CookieStorage(options, config) {
|
|
9
14
|
if (config === void 0) { config = {}; }
|
|
@@ -11,211 +16,221 @@ var CookieStorage = /** @class */ (function () {
|
|
|
11
16
|
this.config = config;
|
|
12
17
|
}
|
|
13
18
|
CookieStorage.prototype.isEnabled = function () {
|
|
14
|
-
var _a, _b;
|
|
15
19
|
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
|
16
|
-
var
|
|
17
|
-
|
|
18
|
-
|
|
20
|
+
var testKey, testCookieOptions, testStorage, testValue;
|
|
21
|
+
var _this = this;
|
|
22
|
+
return tslib_1.__generator(this, function (_a) {
|
|
23
|
+
switch (_a.label) {
|
|
19
24
|
case 0:
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if (!globalScope || !globalScope.document) {
|
|
23
|
-
return [2 /*return*/, false];
|
|
24
|
-
}
|
|
25
|
-
testValue = String(Date.now());
|
|
26
|
-
testCookieOptions = tslib_1.__assign(tslib_1.__assign({}, this.options), { expirationDays: 0.003 });
|
|
25
|
+
testKey = 'AMP_TEST';
|
|
26
|
+
testCookieOptions = tslib_1.__assign({}, this.options);
|
|
27
27
|
testStorage = new CookieStorage(testCookieOptions);
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
28
|
+
testValue = String(Date.now());
|
|
29
|
+
return [4 /*yield*/, testStorage.transaction(testKey, function (storage) {
|
|
30
|
+
var _a, _b;
|
|
31
|
+
try {
|
|
32
|
+
storage.set(testValue);
|
|
33
|
+
var value = storage.get();
|
|
34
|
+
var result = value === testValue;
|
|
35
|
+
/* istanbul ignore next */
|
|
36
|
+
if (!result && _this.config.diagnosticsClient) {
|
|
37
|
+
(_a = _this.config.diagnosticsClient) === null || _a === void 0 ? void 0 : _a.recordEvent('cookies.isEnabled.failure', {
|
|
38
|
+
reason: 'Test Value mismatch',
|
|
39
|
+
testKey: testKey,
|
|
40
|
+
testValue: testValue,
|
|
41
|
+
sync: true,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
catch (e) {
|
|
47
|
+
/* istanbul ignore next */
|
|
48
|
+
if (_this.config.diagnosticsClient) {
|
|
49
|
+
var errMessage = e instanceof Error ? e.message : String(e);
|
|
50
|
+
(_b = _this.config.diagnosticsClient) === null || _b === void 0 ? void 0 : _b.recordEvent('cookies.isEnabled.failure', {
|
|
51
|
+
reason: 'Cookie getter/setter failed',
|
|
52
|
+
testKey: testKey,
|
|
53
|
+
testValue: testValue,
|
|
54
|
+
error: errMessage,
|
|
55
|
+
sync: true,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
finally {
|
|
61
|
+
// clean-up the AMP_TEST cookie behind us
|
|
62
|
+
storage.set(null);
|
|
63
|
+
}
|
|
64
|
+
})];
|
|
65
|
+
case 1: return [2 /*return*/, _a.sent()];
|
|
65
66
|
}
|
|
66
67
|
});
|
|
67
68
|
});
|
|
68
69
|
};
|
|
69
70
|
CookieStorage.prototype.get = function (key) {
|
|
70
71
|
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
|
71
|
-
var value
|
|
72
|
+
var value;
|
|
72
73
|
return tslib_1.__generator(this, function (_a) {
|
|
73
74
|
switch (_a.label) {
|
|
74
75
|
case 0: return [4 /*yield*/, this.getRaw(key)];
|
|
75
76
|
case 1:
|
|
76
77
|
value = _a.sent();
|
|
77
|
-
|
|
78
|
-
return [2 /*return*/, undefined];
|
|
79
|
-
}
|
|
80
|
-
try {
|
|
81
|
-
decodedValue = (0, exports.decodeCookieValue)(value);
|
|
82
|
-
if (decodedValue === undefined) {
|
|
83
|
-
console.error("Amplitude Logger [Error]: Failed to decode cookie value for key: ".concat(key, ", value: ").concat(value));
|
|
84
|
-
return [2 /*return*/, undefined];
|
|
85
|
-
}
|
|
86
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
87
|
-
return [2 /*return*/, JSON.parse(decodedValue)];
|
|
88
|
-
}
|
|
89
|
-
catch (_b) {
|
|
90
|
-
console.error("Amplitude Logger [Error]: Failed to parse cookie value for key: ".concat(key, ", value: ").concat(value));
|
|
91
|
-
return [2 /*return*/, undefined];
|
|
92
|
-
}
|
|
93
|
-
return [2 /*return*/];
|
|
78
|
+
return [2 /*return*/, this.decodeCookieValue(key, value)];
|
|
94
79
|
}
|
|
95
80
|
});
|
|
96
81
|
});
|
|
97
82
|
};
|
|
83
|
+
CookieStorage.prototype.decodeCookieValue = function (key, value) {
|
|
84
|
+
if (!value) {
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
var decodedValue = (0, exports.decodeCookieValue)(value);
|
|
89
|
+
if (decodedValue === undefined) {
|
|
90
|
+
console.error("Amplitude Logger [Error]: Failed to decode cookie value for key: ".concat(key, ", value: ").concat(value));
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
94
|
+
return JSON.parse(decodedValue);
|
|
95
|
+
}
|
|
96
|
+
catch (_a) {
|
|
97
|
+
console.error("Amplitude Logger [Error]: Failed to parse cookie value for key: ".concat(key, ", value: ").concat(value));
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
CookieStorage.prototype.getSync = function (key) {
|
|
102
|
+
var value = this.getRawSync(key);
|
|
103
|
+
return this.decodeCookieValue(key, value);
|
|
104
|
+
};
|
|
98
105
|
CookieStorage.prototype.getRaw = function (key) {
|
|
99
|
-
var _a, _b
|
|
106
|
+
var _a, _b;
|
|
100
107
|
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
|
101
|
-
var globalScope, globalScopeWithCookiesStore, cookieStore,
|
|
102
|
-
var
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
switch (_f.label) {
|
|
108
|
+
var globalScope, globalScopeWithCookiesStore, cookieStore, cookies, cookies_1, cookies_1_1, cookie, ignoreError_1;
|
|
109
|
+
var e_1, _c;
|
|
110
|
+
return tslib_1.__generator(this, function (_d) {
|
|
111
|
+
switch (_d.label) {
|
|
106
112
|
case 0:
|
|
107
113
|
globalScope = (0, global_scope_1.getGlobalScope)();
|
|
108
114
|
globalScopeWithCookiesStore = globalScope;
|
|
109
|
-
|
|
115
|
+
_d.label = 1;
|
|
110
116
|
case 1:
|
|
111
|
-
|
|
117
|
+
_d.trys.push([1, 4, , 5]);
|
|
112
118
|
cookieStore = globalScopeWithCookiesStore === null || globalScopeWithCookiesStore === void 0 ? void 0 : globalScopeWithCookiesStore.cookieStore;
|
|
113
119
|
if (!cookieStore) return [3 /*break*/, 3];
|
|
114
120
|
return [4 /*yield*/, cookieStore.getAll(key)];
|
|
115
121
|
case 2:
|
|
116
|
-
|
|
117
|
-
if (
|
|
122
|
+
cookies = _d.sent();
|
|
123
|
+
if (cookies) {
|
|
118
124
|
/* istanbul ignore if */
|
|
119
|
-
if (
|
|
125
|
+
if (cookies.length > 1) {
|
|
120
126
|
(_a = this.config.diagnosticsClient) === null || _a === void 0 ? void 0 : _a.recordEvent('cookies.duplicate', {
|
|
121
|
-
cookies:
|
|
127
|
+
cookies: cookies.map(function (cookie) { return cookie.domain; }),
|
|
122
128
|
});
|
|
123
129
|
(_b = this.config.diagnosticsClient) === null || _b === void 0 ? void 0 : _b.increment('cookies.duplicate.occurrence.cookieStore');
|
|
124
130
|
}
|
|
125
131
|
try {
|
|
126
|
-
for (cookies_1 = tslib_1.__values(
|
|
132
|
+
for (cookies_1 = tslib_1.__values(cookies), cookies_1_1 = cookies_1.next(); !cookies_1_1.done; cookies_1_1 = cookies_1.next()) {
|
|
127
133
|
cookie = cookies_1_1.value;
|
|
128
134
|
if ((0, exports.isDomainEqual)(cookie.domain, this.options.domain)) {
|
|
129
135
|
return [2 /*return*/, cookie.value];
|
|
130
136
|
}
|
|
131
137
|
}
|
|
132
138
|
}
|
|
133
|
-
catch (
|
|
139
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
134
140
|
finally {
|
|
135
141
|
try {
|
|
136
|
-
if (cookies_1_1 && !cookies_1_1.done && (
|
|
142
|
+
if (cookies_1_1 && !cookies_1_1.done && (_c = cookies_1.return)) _c.call(cookies_1);
|
|
137
143
|
}
|
|
138
|
-
finally { if (
|
|
144
|
+
finally { if (e_1) throw e_1.error; }
|
|
139
145
|
}
|
|
140
146
|
}
|
|
141
|
-
|
|
147
|
+
_d.label = 3;
|
|
142
148
|
case 3: return [3 /*break*/, 5];
|
|
143
149
|
case 4:
|
|
144
|
-
ignoreError_1 =
|
|
150
|
+
ignoreError_1 = _d.sent();
|
|
145
151
|
return [3 /*break*/, 5];
|
|
146
|
-
case 5:
|
|
147
|
-
cookies = ((_d = (_c = globalScope === null || globalScope === void 0 ? void 0 : globalScope.document) === null || _c === void 0 ? void 0 : _c.cookie.split('; ')) !== null && _d !== void 0 ? _d : []).filter(function (c) { return c.indexOf(key + '=') === 0; });
|
|
148
|
-
match = undefined;
|
|
149
|
-
duplicateResolverFn = this.config.duplicateResolverFn;
|
|
150
|
-
if (typeof duplicateResolverFn === 'function' && cookies.length > 1) {
|
|
151
|
-
match = cookies.find(function (c) {
|
|
152
|
-
var _a;
|
|
153
|
-
try {
|
|
154
|
-
var res = duplicateResolverFn(c.substring(key.length + 1));
|
|
155
|
-
if (!res) {
|
|
156
|
-
(_a = _this.config.diagnosticsClient) === null || _a === void 0 ? void 0 : _a.increment('cookies.duplicate.occurrence.document.cookie');
|
|
157
|
-
}
|
|
158
|
-
return res;
|
|
159
|
-
}
|
|
160
|
-
catch (ignoreError) {
|
|
161
|
-
/* istanbul ignore next */
|
|
162
|
-
return false;
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
// if match was not found, just get the first one that matches the key
|
|
167
|
-
if (!match) {
|
|
168
|
-
match = cookies[0];
|
|
169
|
-
}
|
|
170
|
-
if (!match) {
|
|
171
|
-
return [2 /*return*/, undefined];
|
|
172
|
-
}
|
|
173
|
-
return [2 /*return*/, match.substring(key.length + 1)];
|
|
152
|
+
case 5: return [2 /*return*/, this.getRawSync(key)];
|
|
174
153
|
}
|
|
175
154
|
});
|
|
176
155
|
});
|
|
177
156
|
};
|
|
178
|
-
CookieStorage.prototype.
|
|
179
|
-
var
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
157
|
+
CookieStorage.prototype.getRawSync = function (key) {
|
|
158
|
+
var _this = this;
|
|
159
|
+
var _a, _b;
|
|
160
|
+
var globalScope = (0, global_scope_1.getGlobalScope)();
|
|
161
|
+
var cookies = ((_b = (_a = globalScope === null || globalScope === void 0 ? void 0 : globalScope.document) === null || _a === void 0 ? void 0 : _a.cookie.split('; ')) !== null && _b !== void 0 ? _b : []).filter(function (c) { return c.indexOf(key + '=') === 0; });
|
|
162
|
+
var match = undefined;
|
|
163
|
+
// if matcher function is provided, use it to de-duplicate when there's more than one cookie
|
|
164
|
+
/* istanbul ignore if */
|
|
165
|
+
var duplicateResolverFn = this.config.duplicateResolverFn;
|
|
166
|
+
if (typeof duplicateResolverFn === 'function' && cookies.length > 1) {
|
|
167
|
+
match = cookies.find(function (c) {
|
|
168
|
+
var _a;
|
|
183
169
|
try {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
if (expires) {
|
|
188
|
-
date = new Date();
|
|
189
|
-
date.setTime(date.getTime() + expires * 24 * 60 * 60 * 1000);
|
|
190
|
-
expireDate = date;
|
|
191
|
-
}
|
|
192
|
-
str = "".concat(key, "=").concat(btoa(encodeURIComponent(JSON.stringify(value))));
|
|
193
|
-
if (expireDate) {
|
|
194
|
-
str += "; expires=".concat(expireDate.toUTCString());
|
|
195
|
-
}
|
|
196
|
-
str += '; path=/';
|
|
197
|
-
if (this.options.domain) {
|
|
198
|
-
str += "; domain=".concat(this.options.domain);
|
|
199
|
-
}
|
|
200
|
-
if (this.options.secure) {
|
|
201
|
-
str += '; Secure';
|
|
202
|
-
}
|
|
203
|
-
if (this.options.sameSite) {
|
|
204
|
-
str += "; SameSite=".concat(this.options.sameSite);
|
|
205
|
-
}
|
|
206
|
-
globalScope = (0, global_scope_1.getGlobalScope)();
|
|
207
|
-
if (globalScope) {
|
|
208
|
-
globalScope.document.cookie = str;
|
|
170
|
+
var res = duplicateResolverFn(c.substring(key.length + 1));
|
|
171
|
+
if (!res) {
|
|
172
|
+
(_a = _this.config.diagnosticsClient) === null || _a === void 0 ? void 0 : _a.increment('cookies.duplicate.occurrence.document.cookie');
|
|
209
173
|
}
|
|
174
|
+
return res;
|
|
210
175
|
}
|
|
211
|
-
catch (
|
|
212
|
-
|
|
213
|
-
|
|
176
|
+
catch (ignoreError) {
|
|
177
|
+
/* istanbul ignore next */
|
|
178
|
+
return false;
|
|
214
179
|
}
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
// if match was not found, just get the first one that matches the key
|
|
183
|
+
if (!match) {
|
|
184
|
+
match = cookies[0];
|
|
185
|
+
}
|
|
186
|
+
if (!match) {
|
|
187
|
+
return undefined;
|
|
188
|
+
}
|
|
189
|
+
return match.substring(key.length + 1);
|
|
190
|
+
};
|
|
191
|
+
CookieStorage.prototype.set = function (key, value) {
|
|
192
|
+
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
|
193
|
+
return tslib_1.__generator(this, function (_a) {
|
|
194
|
+
this.setSync(key, value);
|
|
215
195
|
return [2 /*return*/];
|
|
216
196
|
});
|
|
217
197
|
});
|
|
218
198
|
};
|
|
199
|
+
CookieStorage.prototype.setSync = function (key, value) {
|
|
200
|
+
var _a;
|
|
201
|
+
try {
|
|
202
|
+
var expirationDays = (_a = this.options.expirationDays) !== null && _a !== void 0 ? _a : 0;
|
|
203
|
+
var expires = value !== null ? expirationDays : -1;
|
|
204
|
+
var expireDate = undefined;
|
|
205
|
+
if (expires) {
|
|
206
|
+
var date = new Date();
|
|
207
|
+
date.setTime(date.getTime() + expires * 24 * 60 * 60 * 1000);
|
|
208
|
+
expireDate = date;
|
|
209
|
+
}
|
|
210
|
+
var str = "".concat(key, "=").concat(btoa(encodeURIComponent(JSON.stringify(value))));
|
|
211
|
+
if (expireDate) {
|
|
212
|
+
str += "; expires=".concat(expireDate.toUTCString());
|
|
213
|
+
}
|
|
214
|
+
str += '; path=/';
|
|
215
|
+
if (this.options.domain) {
|
|
216
|
+
str += "; domain=".concat(this.options.domain);
|
|
217
|
+
}
|
|
218
|
+
if (this.options.secure) {
|
|
219
|
+
str += '; Secure';
|
|
220
|
+
}
|
|
221
|
+
if (this.options.sameSite) {
|
|
222
|
+
str += "; SameSite=".concat(this.options.sameSite);
|
|
223
|
+
}
|
|
224
|
+
var globalScope = (0, global_scope_1.getGlobalScope)();
|
|
225
|
+
if (globalScope) {
|
|
226
|
+
globalScope.document.cookie = str;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
var errorMessage = error instanceof Error ? error.message : String(error);
|
|
231
|
+
console.error("Amplitude Logger [Error]: Failed to set cookie for key: ".concat(key, ". Error: ").concat(errorMessage));
|
|
232
|
+
}
|
|
233
|
+
};
|
|
219
234
|
CookieStorage.prototype.remove = function (key) {
|
|
220
235
|
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
|
221
236
|
return tslib_1.__generator(this, function (_a) {
|
|
@@ -235,6 +250,68 @@ var CookieStorage = /** @class */ (function () {
|
|
|
235
250
|
});
|
|
236
251
|
});
|
|
237
252
|
};
|
|
253
|
+
CookieStorage.isDomainWritable = function (domain) {
|
|
254
|
+
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
|
255
|
+
var options, storageKey, storage, res, error_1;
|
|
256
|
+
return tslib_1.__generator(this, function (_a) {
|
|
257
|
+
switch (_a.label) {
|
|
258
|
+
case 0:
|
|
259
|
+
options = {
|
|
260
|
+
domain: '.' + domain,
|
|
261
|
+
};
|
|
262
|
+
storageKey = 'AMP_TLDTEST';
|
|
263
|
+
storage = new CookieStorage(options);
|
|
264
|
+
_a.label = 1;
|
|
265
|
+
case 1:
|
|
266
|
+
_a.trys.push([1, 3, , 4]);
|
|
267
|
+
return [4 /*yield*/, storage.transaction(storageKey, function (storageSync) {
|
|
268
|
+
try {
|
|
269
|
+
storageSync.set(1);
|
|
270
|
+
return storageSync.get();
|
|
271
|
+
}
|
|
272
|
+
finally {
|
|
273
|
+
storageSync.set(null);
|
|
274
|
+
}
|
|
275
|
+
})];
|
|
276
|
+
case 2:
|
|
277
|
+
res = _a.sent();
|
|
278
|
+
return [2 /*return*/, !!res];
|
|
279
|
+
case 3:
|
|
280
|
+
error_1 = _a.sent();
|
|
281
|
+
return [2 /*return*/, false];
|
|
282
|
+
case 4: return [2 /*return*/];
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
};
|
|
287
|
+
CookieStorage.prototype.transaction = function (key, callback) {
|
|
288
|
+
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
|
289
|
+
var locks, callbackWrapper;
|
|
290
|
+
var _this = this;
|
|
291
|
+
return tslib_1.__generator(this, function (_a) {
|
|
292
|
+
switch (_a.label) {
|
|
293
|
+
case 0:
|
|
294
|
+
locks = getLocks();
|
|
295
|
+
callbackWrapper = function () {
|
|
296
|
+
// construct a sync storage object that is scoped to
|
|
297
|
+
// Cookie with name <key>
|
|
298
|
+
var storageSync = {
|
|
299
|
+
get: function () { return _this.getSync(key); },
|
|
300
|
+
set: function (value) { return _this.setSync(key, value); },
|
|
301
|
+
};
|
|
302
|
+
return callback(storageSync);
|
|
303
|
+
};
|
|
304
|
+
// if 'locks' is missing, it is a legacy browser, just call the callback directly
|
|
305
|
+
// and settle for a transaction that isn't isolated across tabs
|
|
306
|
+
if (!locks) {
|
|
307
|
+
return [2 /*return*/, callbackWrapper()];
|
|
308
|
+
}
|
|
309
|
+
return [4 /*yield*/, locks.request("com.amplitude:cookie-lock:".concat(key), callbackWrapper)];
|
|
310
|
+
case 1: return [2 /*return*/, (_a.sent())];
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
};
|
|
238
315
|
return CookieStorage;
|
|
239
316
|
}());
|
|
240
317
|
exports.CookieStorage = CookieStorage;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cookie.js","sourceRoot":"","sources":["../../../src/storage/cookie.ts"],"names":[],"mappings":"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","sourcesContent":["import { Storage, CookieStorageOptions, CookieStorageConfig } from '../types/storage';\nimport { getGlobalScope } from '../global-scope';\nimport { UUID } from '../utils/uuid';\n\n// CookieStore is a Web API not included in standard TypeScript lib types\n// https://developer.mozilla.org/en-US/docs/Web/API/CookieStore\ninterface CookieStoreSetOptions {\n name: string;\n value: string;\n expires?: number;\n domain?: string;\n sameSite?: 'strict' | 'lax' | 'none';\n}\n\ninterface CookieStore {\n getAll(key: string): Promise<CookieStoreSetOptions[] | undefined>;\n}\n\ntype GlobalScopeWithCookieStore = {\n cookieStore?: CookieStore;\n} & typeof global;\n\nexport class CookieStorage<T> implements Storage<T> {\n options: CookieStorageOptions;\n config: CookieStorageConfig;\n\n constructor(options?: CookieStorageOptions, config: CookieStorageConfig = {}) {\n this.options = { ...options };\n this.config = config;\n }\n\n async isEnabled(): Promise<boolean> {\n const globalScope = getGlobalScope();\n /* istanbul ignore if */\n if (!globalScope || !globalScope.document) {\n return false;\n }\n\n const testValue = String(Date.now());\n const testCookieOptions = {\n ...this.options,\n expirationDays: 0.003, // expire in ~5 minutes\n };\n const testStorage = new CookieStorage<string>(testCookieOptions);\n const testKey = `AMP_TEST_${UUID().substring(0, 8)}`;\n try {\n await testStorage.set(testKey, testValue);\n const value = await testStorage.get(testKey);\n /* istanbul ignore next */\n if (value !== testValue && this.config.diagnosticsClient) {\n this.config.diagnosticsClient?.recordEvent('cookies.isEnabled.failure', {\n reason: 'Test Value mismatch',\n testKey,\n testValue,\n });\n }\n return value === testValue;\n } catch (e) {\n /* istanbul ignore next */\n if (this.config.diagnosticsClient) {\n const errMessage = e instanceof Error ? e.message : String(e);\n this.config.diagnosticsClient?.recordEvent('cookies.isEnabled.failure', {\n reason: 'Cookie getter/setter failed',\n testKey,\n testValue,\n error: errMessage,\n });\n }\n return false;\n } finally {\n await testStorage.remove(testKey);\n }\n }\n\n async get(key: string): Promise<T | undefined> {\n const value = await this.getRaw(key);\n if (!value) {\n return undefined;\n }\n try {\n const decodedValue = decodeCookieValue(value);\n if (decodedValue === undefined) {\n console.error(`Amplitude Logger [Error]: Failed to decode cookie value for key: ${key}, value: ${value}`);\n return undefined;\n }\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return JSON.parse(decodedValue);\n } catch {\n console.error(`Amplitude Logger [Error]: Failed to parse cookie value for key: ${key}, value: ${value}`);\n return undefined;\n }\n }\n\n async getRaw(key: string): Promise<string | undefined> {\n const globalScope = getGlobalScope();\n\n // use CookieStore if available and enabled\n const globalScopeWithCookiesStore = globalScope as GlobalScopeWithCookieStore;\n try {\n const cookieStore = globalScopeWithCookiesStore?.cookieStore;\n if (cookieStore) {\n const cookies = await cookieStore.getAll(key);\n if (cookies) {\n /* istanbul ignore if */\n if (cookies.length > 1) {\n this.config.diagnosticsClient?.recordEvent('cookies.duplicate', {\n cookies: cookies.map((cookie) => cookie.domain),\n });\n this.config.diagnosticsClient?.increment('cookies.duplicate.occurrence.cookieStore');\n }\n\n for (const cookie of cookies) {\n if (isDomainEqual(cookie.domain, this.options.domain)) {\n return cookie.value;\n }\n }\n }\n }\n } catch (ignoreError) {\n /* istanbul ignore next */\n // if cookieStore had a surprise failure, fallback to document.cookie\n }\n\n const cookies = (globalScope?.document?.cookie.split('; ') ?? []).filter((c) => c.indexOf(key + '=') === 0);\n let match: string | undefined = undefined;\n\n // if matcher function is provided, use it to de-duplicate when there's more than one cookie\n /* istanbul ignore if */\n const duplicateResolverFn = this.config.duplicateResolverFn;\n if (typeof duplicateResolverFn === 'function' && cookies.length > 1) {\n match = cookies.find((c) => {\n try {\n const res = duplicateResolverFn(c.substring(key.length + 1));\n if (!res) {\n this.config.diagnosticsClient?.increment('cookies.duplicate.occurrence.document.cookie');\n }\n return res;\n } catch (ignoreError) {\n /* istanbul ignore next */\n return false;\n }\n });\n }\n\n // if match was not found, just get the first one that matches the key\n if (!match) {\n match = cookies[0];\n }\n if (!match) {\n return undefined;\n }\n return match.substring(key.length + 1);\n }\n\n async set(key: string, value: T | null): Promise<void> {\n try {\n const expirationDays = this.options.expirationDays ?? 0;\n const expires = value !== null ? expirationDays : -1;\n let expireDate: Date | undefined = undefined;\n if (expires) {\n const date = new Date();\n date.setTime(date.getTime() + expires * 24 * 60 * 60 * 1000);\n expireDate = date;\n }\n let str = `${key}=${btoa(encodeURIComponent(JSON.stringify(value)))}`;\n if (expireDate) {\n str += `; expires=${expireDate.toUTCString()}`;\n }\n str += '; path=/';\n if (this.options.domain) {\n str += `; domain=${this.options.domain}`;\n }\n if (this.options.secure) {\n str += '; Secure';\n }\n if (this.options.sameSite) {\n str += `; SameSite=${this.options.sameSite}`;\n }\n const globalScope = getGlobalScope();\n if (globalScope) {\n globalScope.document.cookie = str;\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.error(`Amplitude Logger [Error]: Failed to set cookie for key: ${key}. Error: ${errorMessage}`);\n }\n }\n\n async remove(key: string): Promise<void> {\n await this.set(key, null);\n }\n\n async reset(): Promise<void> {\n return;\n }\n}\n\nconst decodeCookiesAsDefault = (value: string): string | undefined => {\n try {\n return decodeURIComponent(atob(value));\n } catch {\n return undefined;\n }\n};\n\nconst decodeCookiesWithDoubleUrlEncoding = (value: string): string | undefined => {\n // Modern Ruby (v7+) automatically encodes cookies with URL encoding by\n // https://api.rubyonrails.org/classes/ActionDispatch/Cookies.html\n try {\n return decodeURIComponent(atob(decodeURIComponent(value)));\n } catch {\n return undefined;\n }\n};\n\n/**\n * Decodes a cookie value that was encoded with btoa(encodeURIComponent(...)).\n * Handles both standard encoding and double URL encoding (used by Ruby Rails v7+).\n */\nexport const decodeCookieValue = (value: string): string | undefined => {\n return decodeCookiesAsDefault(value) ?? decodeCookiesWithDoubleUrlEncoding(value);\n};\n\n/**\n * Compares two domain strings for equality, ignoring leading dots.\n * This is useful for comparing cookie domains since \".example.com\" and \"example.com\"\n * are effectively equivalent for cookie scoping.\n */\nexport const isDomainEqual = (domain1: string | undefined, domain2: string | undefined): boolean => {\n if (domain1 === '' && domain2 === '') {\n return true;\n }\n if (!domain1 || !domain2) {\n return false;\n }\n const normalized1 = domain1.startsWith('.') ? domain1.substring(1) : domain1;\n const normalized2 = domain2.startsWith('.') ? domain2.substring(1) : domain2;\n return normalized1.toLowerCase() === normalized2.toLowerCase();\n};\n"]}
|
|
1
|
+
{"version":3,"file":"cookie.js","sourceRoot":"","sources":["../../../src/storage/cookie.ts"],"names":[],"mappings":"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","sourcesContent":["import { Storage, StorageSync, CookieStorageOptions, CookieStorageConfig } from '../types/storage';\nimport { getGlobalScope } from '../global-scope';\n\n// CookieStore is a Web API not included in standard TypeScript lib types\n// https://developer.mozilla.org/en-US/docs/Web/API/CookieStore\ninterface CookieStoreSetOptions {\n name: string;\n value: string;\n expires?: number;\n domain?: string;\n sameSite?: 'strict' | 'lax' | 'none';\n}\n\ninterface CookieStore {\n getAll(key: string): Promise<CookieStoreSetOptions[] | undefined>;\n}\n\ntype GlobalScopeWithCookieStore = {\n cookieStore?: CookieStore;\n} & typeof global;\n\n/* istanbul ignore next */\nconst getLocks = (): typeof global.navigator.locks | undefined => {\n const globalScope = getGlobalScope();\n return globalScope?.navigator?.locks;\n};\n\nexport class CookieStorage<T> implements Storage<T> {\n options: CookieStorageOptions;\n config: CookieStorageConfig;\n\n constructor(options?: CookieStorageOptions, config: CookieStorageConfig = {}) {\n this.options = { ...options };\n this.config = config;\n }\n\n async isEnabled(): Promise<boolean> {\n const testKey = 'AMP_TEST';\n const testCookieOptions = { ...this.options };\n const testStorage = new CookieStorage<string>(testCookieOptions);\n const testValue = String(Date.now());\n return await testStorage.transaction<boolean>(testKey, (storage: StorageSync<string>) => {\n try {\n storage.set(testValue);\n const value = storage.get();\n const result = value === testValue;\n /* istanbul ignore next */\n if (!result && this.config.diagnosticsClient) {\n this.config.diagnosticsClient?.recordEvent('cookies.isEnabled.failure', {\n reason: 'Test Value mismatch',\n testKey,\n testValue,\n sync: true,\n });\n }\n return result;\n } catch (e) {\n /* istanbul ignore next */\n if (this.config.diagnosticsClient) {\n const errMessage = e instanceof Error ? e.message : String(e);\n this.config.diagnosticsClient?.recordEvent('cookies.isEnabled.failure', {\n reason: 'Cookie getter/setter failed',\n testKey,\n testValue,\n error: errMessage,\n sync: true,\n });\n }\n return false;\n } finally {\n // clean-up the AMP_TEST cookie behind us\n storage.set(null);\n }\n });\n }\n\n async get(key: string): Promise<T | undefined> {\n const value = await this.getRaw(key);\n return this.decodeCookieValue(key, value);\n }\n\n private decodeCookieValue(key: string, value: string | undefined): T | undefined {\n if (!value) {\n return undefined;\n }\n try {\n const decodedValue = decodeCookieValue(value);\n if (decodedValue === undefined) {\n console.error(`Amplitude Logger [Error]: Failed to decode cookie value for key: ${key}, value: ${value}`);\n return undefined;\n }\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return JSON.parse(decodedValue);\n } catch {\n console.error(`Amplitude Logger [Error]: Failed to parse cookie value for key: ${key}, value: ${value}`);\n return undefined;\n }\n }\n\n private getSync(key: string): T | undefined {\n const value = this.getRawSync(key);\n return this.decodeCookieValue(key, value);\n }\n\n async getRaw(key: string): Promise<string | undefined> {\n const globalScope = getGlobalScope();\n\n // use CookieStore if available and enabled\n const globalScopeWithCookiesStore = globalScope as GlobalScopeWithCookieStore;\n try {\n const cookieStore = globalScopeWithCookiesStore?.cookieStore;\n if (cookieStore) {\n const cookies = await cookieStore.getAll(key);\n if (cookies) {\n /* istanbul ignore if */\n if (cookies.length > 1) {\n this.config.diagnosticsClient?.recordEvent('cookies.duplicate', {\n cookies: cookies.map((cookie) => cookie.domain),\n });\n this.config.diagnosticsClient?.increment('cookies.duplicate.occurrence.cookieStore');\n }\n\n for (const cookie of cookies) {\n if (isDomainEqual(cookie.domain, this.options.domain)) {\n return cookie.value;\n }\n }\n }\n }\n } catch (ignoreError) {\n /* istanbul ignore next */\n // if cookieStore had a surprise failure, fallback to document.cookie\n }\n\n return this.getRawSync(key);\n }\n\n private getRawSync(key: string): string | undefined {\n const globalScope = getGlobalScope();\n const cookies = (globalScope?.document?.cookie.split('; ') ?? []).filter((c) => c.indexOf(key + '=') === 0);\n let match: string | undefined = undefined;\n\n // if matcher function is provided, use it to de-duplicate when there's more than one cookie\n /* istanbul ignore if */\n const duplicateResolverFn = this.config.duplicateResolverFn;\n if (typeof duplicateResolverFn === 'function' && cookies.length > 1) {\n match = cookies.find((c) => {\n try {\n const res = duplicateResolverFn(c.substring(key.length + 1));\n if (!res) {\n this.config.diagnosticsClient?.increment('cookies.duplicate.occurrence.document.cookie');\n }\n return res;\n } catch (ignoreError) {\n /* istanbul ignore next */\n return false;\n }\n });\n }\n\n // if match was not found, just get the first one that matches the key\n if (!match) {\n match = cookies[0];\n }\n if (!match) {\n return undefined;\n }\n return match.substring(key.length + 1);\n }\n\n async set(key: string, value: T | null): Promise<void> {\n this.setSync(key, value);\n }\n\n private setSync(key: string, value: T | null): void {\n try {\n const expirationDays = this.options.expirationDays ?? 0;\n const expires = value !== null ? expirationDays : -1;\n let expireDate: Date | undefined = undefined;\n if (expires) {\n const date = new Date();\n date.setTime(date.getTime() + expires * 24 * 60 * 60 * 1000);\n expireDate = date;\n }\n let str = `${key}=${btoa(encodeURIComponent(JSON.stringify(value)))}`;\n if (expireDate) {\n str += `; expires=${expireDate.toUTCString()}`;\n }\n str += '; path=/';\n if (this.options.domain) {\n str += `; domain=${this.options.domain}`;\n }\n if (this.options.secure) {\n str += '; Secure';\n }\n if (this.options.sameSite) {\n str += `; SameSite=${this.options.sameSite}`;\n }\n const globalScope = getGlobalScope();\n if (globalScope) {\n globalScope.document.cookie = str;\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.error(`Amplitude Logger [Error]: Failed to set cookie for key: ${key}. Error: ${errorMessage}`);\n }\n }\n\n async remove(key: string): Promise<void> {\n await this.set(key, null);\n }\n\n async reset(): Promise<void> {\n return;\n }\n\n static async isDomainWritable(domain: string): Promise<boolean> {\n const options = {\n domain: '.' + domain,\n };\n const storageKey = 'AMP_TLDTEST';\n const storage = new CookieStorage<number>(options);\n try {\n const res = await storage.transaction(storageKey, (storageSync) => {\n try {\n storageSync.set(1);\n return storageSync.get();\n } finally {\n storageSync.set(null);\n }\n });\n return !!res;\n } catch (error) {\n return false;\n }\n }\n\n private async transaction<ReturnType>(\n key: string,\n callback: (storageSync: StorageSync<T>) => ReturnType,\n ): Promise<ReturnType> {\n const locks = getLocks();\n const callbackWrapper = () => {\n // construct a sync storage object that is scoped to\n // Cookie with name <key>\n const storageSync: StorageSync<T> = {\n get: () => this.getSync(key),\n set: (value: T | null) => this.setSync(key, value),\n };\n return callback(storageSync);\n };\n\n // if 'locks' is missing, it is a legacy browser, just call the callback directly\n // and settle for a transaction that isn't isolated across tabs\n if (!locks) {\n return callbackWrapper();\n }\n return (await locks.request(`com.amplitude:cookie-lock:${key}`, callbackWrapper)) as ReturnType;\n }\n}\n\nconst decodeCookiesAsDefault = (value: string): string | undefined => {\n try {\n return decodeURIComponent(atob(value));\n } catch {\n return undefined;\n }\n};\n\nconst decodeCookiesWithDoubleUrlEncoding = (value: string): string | undefined => {\n // Modern Ruby (v7+) automatically encodes cookies with URL encoding by\n // https://api.rubyonrails.org/classes/ActionDispatch/Cookies.html\n try {\n return decodeURIComponent(atob(decodeURIComponent(value)));\n } catch {\n return undefined;\n }\n};\n\n/**\n * Decodes a cookie value that was encoded with btoa(encodeURIComponent(...)).\n * Handles both standard encoding and double URL encoding (used by Ruby Rails v7+).\n */\nexport const decodeCookieValue = (value: string): string | undefined => {\n return decodeCookiesAsDefault(value) ?? decodeCookiesWithDoubleUrlEncoding(value);\n};\n\n/**\n * Compares two domain strings for equality, ignoring leading dots.\n * This is useful for comparing cookie domains since \".example.com\" and \"example.com\"\n * are effectively equivalent for cookie scoping.\n */\nexport const isDomainEqual = (domain1: string | undefined, domain2: string | undefined): boolean => {\n if (domain1 === '' && domain2 === '') {\n return true;\n }\n if (!domain1 || !domain2) {\n return false;\n }\n const normalized1 = domain1.startsWith('.') ? domain1.substring(1) : domain1;\n const normalized2 = domain2.startsWith('.') ? domain2.substring(1) : domain2;\n return normalized1.toLowerCase() === normalized2.toLowerCase();\n};\n"]}
|
|
@@ -7,6 +7,10 @@ export interface Storage<T> {
|
|
|
7
7
|
remove(key: string): Promise<void>;
|
|
8
8
|
reset(): Promise<void>;
|
|
9
9
|
}
|
|
10
|
+
export interface StorageSync<T> {
|
|
11
|
+
get: () => T | undefined;
|
|
12
|
+
set: (value: T | null) => void;
|
|
13
|
+
}
|
|
10
14
|
export interface CookieStorageOptions {
|
|
11
15
|
domain?: string;
|
|
12
16
|
expirationDays?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../src/types/storage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAEvE,MAAM,WAAW,OAAO,CAAC,CAAC;IACxB,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IACzC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACjD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,mBAAmB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IACjD,iBAAiB,CAAC,EAAE,kBAAkB,CAAC;CACxC;AAED,MAAM,MAAM,mBAAmB,GAAG,QAAQ,GAAG,cAAc,GAAG,gBAAgB,GAAG,MAAM,CAAC"}
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../src/types/storage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAEvE,MAAM,WAAW,OAAO,CAAC,CAAC;IACxB,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IACzC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACjD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,GAAG,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IACzB,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,mBAAmB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IACjD,iBAAiB,CAAC,EAAE,kBAAkB,CAAC;CACxC;AAED,MAAM,MAAM,mBAAmB,GAAG,QAAQ,GAAG,cAAc,GAAG,gBAAgB,GAAG,MAAM,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../../../src/types/storage.ts"],"names":[],"mappings":"","sourcesContent":["import { IDiagnosticsClient } from '../diagnostics/diagnostics-client';\n\nexport interface Storage<T> {\n isEnabled(): Promise<boolean>;\n get(key: string): Promise<T | undefined>;\n getRaw(key: string): Promise<string | undefined>;\n set(key: string, value: T): Promise<void>;\n remove(key: string): Promise<void>;\n reset(): Promise<void>;\n}\n\nexport interface CookieStorageOptions {\n domain?: string;\n expirationDays?: number;\n sameSite?: string;\n secure?: boolean;\n}\n\n/**\n * Configuration for CookieStorage behavior.\n * Separated from options to keep storage-specific config distinct from cookie attributes.\n */\nexport interface CookieStorageConfig {\n /**\n * Function to resolve duplicate cookies when multiple cookies with the same key exist.\n * Returns true if the cookie value should be used, false otherwise.\n */\n duplicateResolverFn?: (value: string) => boolean;\n diagnosticsClient?: IDiagnosticsClient;\n}\n\nexport type IdentityStorageType = 'cookie' | 'localStorage' | 'sessionStorage' | 'none';\n"]}
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../../../src/types/storage.ts"],"names":[],"mappings":"","sourcesContent":["import { IDiagnosticsClient } from '../diagnostics/diagnostics-client';\n\nexport interface Storage<T> {\n isEnabled(): Promise<boolean>;\n get(key: string): Promise<T | undefined>;\n getRaw(key: string): Promise<string | undefined>;\n set(key: string, value: T): Promise<void>;\n remove(key: string): Promise<void>;\n reset(): Promise<void>;\n}\n\nexport interface StorageSync<T> {\n get: () => T | undefined;\n set: (value: T | null) => void;\n}\n\nexport interface CookieStorageOptions {\n domain?: string;\n expirationDays?: number;\n sameSite?: string;\n secure?: boolean;\n}\n\n/**\n * Configuration for CookieStorage behavior.\n * Separated from options to keep storage-specific config distinct from cookie attributes.\n */\nexport interface CookieStorageConfig {\n /**\n * Function to resolve duplicate cookies when multiple cookies with the same key exist.\n * Returns true if the cookie value should be used, false otherwise.\n */\n duplicateResolverFn?: (value: string) => boolean;\n diagnosticsClient?: IDiagnosticsClient;\n}\n\nexport type IdentityStorageType = 'cookie' | 'localStorage' | 'sessionStorage' | 'none';\n"]}
|
|
@@ -5,10 +5,16 @@ export declare class CookieStorage<T> implements Storage<T> {
|
|
|
5
5
|
constructor(options?: CookieStorageOptions, config?: CookieStorageConfig);
|
|
6
6
|
isEnabled(): Promise<boolean>;
|
|
7
7
|
get(key: string): Promise<T | undefined>;
|
|
8
|
+
private decodeCookieValue;
|
|
9
|
+
private getSync;
|
|
8
10
|
getRaw(key: string): Promise<string | undefined>;
|
|
11
|
+
private getRawSync;
|
|
9
12
|
set(key: string, value: T | null): Promise<void>;
|
|
13
|
+
private setSync;
|
|
10
14
|
remove(key: string): Promise<void>;
|
|
11
15
|
reset(): Promise<void>;
|
|
16
|
+
static isDomainWritable(domain: string): Promise<boolean>;
|
|
17
|
+
private transaction;
|
|
12
18
|
}
|
|
13
19
|
/**
|
|
14
20
|
* Decodes a cookie value that was encoded with btoa(encodeURIComponent(...)).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cookie.d.ts","sourceRoot":"","sources":["../../../src/storage/cookie.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"cookie.d.ts","sourceRoot":"","sources":["../../../src/storage/cookie.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAe,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AA2BnG,qBAAa,aAAa,CAAC,CAAC,CAAE,YAAW,OAAO,CAAC,CAAC,CAAC;IACjD,OAAO,EAAE,oBAAoB,CAAC;IAC9B,MAAM,EAAE,mBAAmB,CAAC;gBAEhB,OAAO,CAAC,EAAE,oBAAoB,EAAE,MAAM,GAAE,mBAAwB;IAKtE,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IAwC7B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAK9C,OAAO,CAAC,iBAAiB;IAkBzB,OAAO,CAAC,OAAO;IAKT,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAiCtD,OAAO,CAAC,UAAU;IAiCZ,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAItD,OAAO,CAAC,OAAO;IAkCT,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;WAIf,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;YAqBjD,WAAW;CAsB1B;AAoBD;;;GAGG;AACH,eAAO,MAAM,iBAAiB,UAAW,MAAM,KAAG,MAAM,GAAG,SAE1D,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,aAAa,YAAa,MAAM,GAAG,SAAS,WAAW,MAAM,GAAG,SAAS,KAAG,OAUxF,CAAC"}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { __assign, __awaiter, __generator, __values } from "tslib";
|
|
2
2
|
import { getGlobalScope } from '../global-scope';
|
|
3
|
-
|
|
3
|
+
/* istanbul ignore next */
|
|
4
|
+
var getLocks = function () {
|
|
5
|
+
var _a;
|
|
6
|
+
var globalScope = getGlobalScope();
|
|
7
|
+
return (_a = globalScope === null || globalScope === void 0 ? void 0 : globalScope.navigator) === null || _a === void 0 ? void 0 : _a.locks;
|
|
8
|
+
};
|
|
4
9
|
var CookieStorage = /** @class */ (function () {
|
|
5
10
|
function CookieStorage(options, config) {
|
|
6
11
|
if (config === void 0) { config = {}; }
|
|
@@ -8,211 +13,221 @@ var CookieStorage = /** @class */ (function () {
|
|
|
8
13
|
this.config = config;
|
|
9
14
|
}
|
|
10
15
|
CookieStorage.prototype.isEnabled = function () {
|
|
11
|
-
var _a, _b;
|
|
12
16
|
return __awaiter(this, void 0, void 0, function () {
|
|
13
|
-
var
|
|
14
|
-
|
|
15
|
-
|
|
17
|
+
var testKey, testCookieOptions, testStorage, testValue;
|
|
18
|
+
var _this = this;
|
|
19
|
+
return __generator(this, function (_a) {
|
|
20
|
+
switch (_a.label) {
|
|
16
21
|
case 0:
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if (!globalScope || !globalScope.document) {
|
|
20
|
-
return [2 /*return*/, false];
|
|
21
|
-
}
|
|
22
|
-
testValue = String(Date.now());
|
|
23
|
-
testCookieOptions = __assign(__assign({}, this.options), { expirationDays: 0.003 });
|
|
22
|
+
testKey = 'AMP_TEST';
|
|
23
|
+
testCookieOptions = __assign({}, this.options);
|
|
24
24
|
testStorage = new CookieStorage(testCookieOptions);
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
25
|
+
testValue = String(Date.now());
|
|
26
|
+
return [4 /*yield*/, testStorage.transaction(testKey, function (storage) {
|
|
27
|
+
var _a, _b;
|
|
28
|
+
try {
|
|
29
|
+
storage.set(testValue);
|
|
30
|
+
var value = storage.get();
|
|
31
|
+
var result = value === testValue;
|
|
32
|
+
/* istanbul ignore next */
|
|
33
|
+
if (!result && _this.config.diagnosticsClient) {
|
|
34
|
+
(_a = _this.config.diagnosticsClient) === null || _a === void 0 ? void 0 : _a.recordEvent('cookies.isEnabled.failure', {
|
|
35
|
+
reason: 'Test Value mismatch',
|
|
36
|
+
testKey: testKey,
|
|
37
|
+
testValue: testValue,
|
|
38
|
+
sync: true,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
catch (e) {
|
|
44
|
+
/* istanbul ignore next */
|
|
45
|
+
if (_this.config.diagnosticsClient) {
|
|
46
|
+
var errMessage = e instanceof Error ? e.message : String(e);
|
|
47
|
+
(_b = _this.config.diagnosticsClient) === null || _b === void 0 ? void 0 : _b.recordEvent('cookies.isEnabled.failure', {
|
|
48
|
+
reason: 'Cookie getter/setter failed',
|
|
49
|
+
testKey: testKey,
|
|
50
|
+
testValue: testValue,
|
|
51
|
+
error: errMessage,
|
|
52
|
+
sync: true,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
// clean-up the AMP_TEST cookie behind us
|
|
59
|
+
storage.set(null);
|
|
60
|
+
}
|
|
61
|
+
})];
|
|
62
|
+
case 1: return [2 /*return*/, _a.sent()];
|
|
62
63
|
}
|
|
63
64
|
});
|
|
64
65
|
});
|
|
65
66
|
};
|
|
66
67
|
CookieStorage.prototype.get = function (key) {
|
|
67
68
|
return __awaiter(this, void 0, void 0, function () {
|
|
68
|
-
var value
|
|
69
|
+
var value;
|
|
69
70
|
return __generator(this, function (_a) {
|
|
70
71
|
switch (_a.label) {
|
|
71
72
|
case 0: return [4 /*yield*/, this.getRaw(key)];
|
|
72
73
|
case 1:
|
|
73
74
|
value = _a.sent();
|
|
74
|
-
|
|
75
|
-
return [2 /*return*/, undefined];
|
|
76
|
-
}
|
|
77
|
-
try {
|
|
78
|
-
decodedValue = decodeCookieValue(value);
|
|
79
|
-
if (decodedValue === undefined) {
|
|
80
|
-
console.error("Amplitude Logger [Error]: Failed to decode cookie value for key: ".concat(key, ", value: ").concat(value));
|
|
81
|
-
return [2 /*return*/, undefined];
|
|
82
|
-
}
|
|
83
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
84
|
-
return [2 /*return*/, JSON.parse(decodedValue)];
|
|
85
|
-
}
|
|
86
|
-
catch (_b) {
|
|
87
|
-
console.error("Amplitude Logger [Error]: Failed to parse cookie value for key: ".concat(key, ", value: ").concat(value));
|
|
88
|
-
return [2 /*return*/, undefined];
|
|
89
|
-
}
|
|
90
|
-
return [2 /*return*/];
|
|
75
|
+
return [2 /*return*/, this.decodeCookieValue(key, value)];
|
|
91
76
|
}
|
|
92
77
|
});
|
|
93
78
|
});
|
|
94
79
|
};
|
|
80
|
+
CookieStorage.prototype.decodeCookieValue = function (key, value) {
|
|
81
|
+
if (!value) {
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
var decodedValue = decodeCookieValue(value);
|
|
86
|
+
if (decodedValue === undefined) {
|
|
87
|
+
console.error("Amplitude Logger [Error]: Failed to decode cookie value for key: ".concat(key, ", value: ").concat(value));
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
91
|
+
return JSON.parse(decodedValue);
|
|
92
|
+
}
|
|
93
|
+
catch (_a) {
|
|
94
|
+
console.error("Amplitude Logger [Error]: Failed to parse cookie value for key: ".concat(key, ", value: ").concat(value));
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
CookieStorage.prototype.getSync = function (key) {
|
|
99
|
+
var value = this.getRawSync(key);
|
|
100
|
+
return this.decodeCookieValue(key, value);
|
|
101
|
+
};
|
|
95
102
|
CookieStorage.prototype.getRaw = function (key) {
|
|
96
|
-
var _a, _b
|
|
103
|
+
var _a, _b;
|
|
97
104
|
return __awaiter(this, void 0, void 0, function () {
|
|
98
|
-
var globalScope, globalScopeWithCookiesStore, cookieStore,
|
|
99
|
-
var
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
switch (_f.label) {
|
|
105
|
+
var globalScope, globalScopeWithCookiesStore, cookieStore, cookies, cookies_1, cookies_1_1, cookie, ignoreError_1;
|
|
106
|
+
var e_1, _c;
|
|
107
|
+
return __generator(this, function (_d) {
|
|
108
|
+
switch (_d.label) {
|
|
103
109
|
case 0:
|
|
104
110
|
globalScope = getGlobalScope();
|
|
105
111
|
globalScopeWithCookiesStore = globalScope;
|
|
106
|
-
|
|
112
|
+
_d.label = 1;
|
|
107
113
|
case 1:
|
|
108
|
-
|
|
114
|
+
_d.trys.push([1, 4, , 5]);
|
|
109
115
|
cookieStore = globalScopeWithCookiesStore === null || globalScopeWithCookiesStore === void 0 ? void 0 : globalScopeWithCookiesStore.cookieStore;
|
|
110
116
|
if (!cookieStore) return [3 /*break*/, 3];
|
|
111
117
|
return [4 /*yield*/, cookieStore.getAll(key)];
|
|
112
118
|
case 2:
|
|
113
|
-
|
|
114
|
-
if (
|
|
119
|
+
cookies = _d.sent();
|
|
120
|
+
if (cookies) {
|
|
115
121
|
/* istanbul ignore if */
|
|
116
|
-
if (
|
|
122
|
+
if (cookies.length > 1) {
|
|
117
123
|
(_a = this.config.diagnosticsClient) === null || _a === void 0 ? void 0 : _a.recordEvent('cookies.duplicate', {
|
|
118
|
-
cookies:
|
|
124
|
+
cookies: cookies.map(function (cookie) { return cookie.domain; }),
|
|
119
125
|
});
|
|
120
126
|
(_b = this.config.diagnosticsClient) === null || _b === void 0 ? void 0 : _b.increment('cookies.duplicate.occurrence.cookieStore');
|
|
121
127
|
}
|
|
122
128
|
try {
|
|
123
|
-
for (cookies_1 = __values(
|
|
129
|
+
for (cookies_1 = __values(cookies), cookies_1_1 = cookies_1.next(); !cookies_1_1.done; cookies_1_1 = cookies_1.next()) {
|
|
124
130
|
cookie = cookies_1_1.value;
|
|
125
131
|
if (isDomainEqual(cookie.domain, this.options.domain)) {
|
|
126
132
|
return [2 /*return*/, cookie.value];
|
|
127
133
|
}
|
|
128
134
|
}
|
|
129
135
|
}
|
|
130
|
-
catch (
|
|
136
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
131
137
|
finally {
|
|
132
138
|
try {
|
|
133
|
-
if (cookies_1_1 && !cookies_1_1.done && (
|
|
139
|
+
if (cookies_1_1 && !cookies_1_1.done && (_c = cookies_1.return)) _c.call(cookies_1);
|
|
134
140
|
}
|
|
135
|
-
finally { if (
|
|
141
|
+
finally { if (e_1) throw e_1.error; }
|
|
136
142
|
}
|
|
137
143
|
}
|
|
138
|
-
|
|
144
|
+
_d.label = 3;
|
|
139
145
|
case 3: return [3 /*break*/, 5];
|
|
140
146
|
case 4:
|
|
141
|
-
ignoreError_1 =
|
|
147
|
+
ignoreError_1 = _d.sent();
|
|
142
148
|
return [3 /*break*/, 5];
|
|
143
|
-
case 5:
|
|
144
|
-
cookies = ((_d = (_c = globalScope === null || globalScope === void 0 ? void 0 : globalScope.document) === null || _c === void 0 ? void 0 : _c.cookie.split('; ')) !== null && _d !== void 0 ? _d : []).filter(function (c) { return c.indexOf(key + '=') === 0; });
|
|
145
|
-
match = undefined;
|
|
146
|
-
duplicateResolverFn = this.config.duplicateResolverFn;
|
|
147
|
-
if (typeof duplicateResolverFn === 'function' && cookies.length > 1) {
|
|
148
|
-
match = cookies.find(function (c) {
|
|
149
|
-
var _a;
|
|
150
|
-
try {
|
|
151
|
-
var res = duplicateResolverFn(c.substring(key.length + 1));
|
|
152
|
-
if (!res) {
|
|
153
|
-
(_a = _this.config.diagnosticsClient) === null || _a === void 0 ? void 0 : _a.increment('cookies.duplicate.occurrence.document.cookie');
|
|
154
|
-
}
|
|
155
|
-
return res;
|
|
156
|
-
}
|
|
157
|
-
catch (ignoreError) {
|
|
158
|
-
/* istanbul ignore next */
|
|
159
|
-
return false;
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
// if match was not found, just get the first one that matches the key
|
|
164
|
-
if (!match) {
|
|
165
|
-
match = cookies[0];
|
|
166
|
-
}
|
|
167
|
-
if (!match) {
|
|
168
|
-
return [2 /*return*/, undefined];
|
|
169
|
-
}
|
|
170
|
-
return [2 /*return*/, match.substring(key.length + 1)];
|
|
149
|
+
case 5: return [2 /*return*/, this.getRawSync(key)];
|
|
171
150
|
}
|
|
172
151
|
});
|
|
173
152
|
});
|
|
174
153
|
};
|
|
175
|
-
CookieStorage.prototype.
|
|
176
|
-
var
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
154
|
+
CookieStorage.prototype.getRawSync = function (key) {
|
|
155
|
+
var _this = this;
|
|
156
|
+
var _a, _b;
|
|
157
|
+
var globalScope = getGlobalScope();
|
|
158
|
+
var cookies = ((_b = (_a = globalScope === null || globalScope === void 0 ? void 0 : globalScope.document) === null || _a === void 0 ? void 0 : _a.cookie.split('; ')) !== null && _b !== void 0 ? _b : []).filter(function (c) { return c.indexOf(key + '=') === 0; });
|
|
159
|
+
var match = undefined;
|
|
160
|
+
// if matcher function is provided, use it to de-duplicate when there's more than one cookie
|
|
161
|
+
/* istanbul ignore if */
|
|
162
|
+
var duplicateResolverFn = this.config.duplicateResolverFn;
|
|
163
|
+
if (typeof duplicateResolverFn === 'function' && cookies.length > 1) {
|
|
164
|
+
match = cookies.find(function (c) {
|
|
165
|
+
var _a;
|
|
180
166
|
try {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
if (expires) {
|
|
185
|
-
date = new Date();
|
|
186
|
-
date.setTime(date.getTime() + expires * 24 * 60 * 60 * 1000);
|
|
187
|
-
expireDate = date;
|
|
188
|
-
}
|
|
189
|
-
str = "".concat(key, "=").concat(btoa(encodeURIComponent(JSON.stringify(value))));
|
|
190
|
-
if (expireDate) {
|
|
191
|
-
str += "; expires=".concat(expireDate.toUTCString());
|
|
192
|
-
}
|
|
193
|
-
str += '; path=/';
|
|
194
|
-
if (this.options.domain) {
|
|
195
|
-
str += "; domain=".concat(this.options.domain);
|
|
196
|
-
}
|
|
197
|
-
if (this.options.secure) {
|
|
198
|
-
str += '; Secure';
|
|
199
|
-
}
|
|
200
|
-
if (this.options.sameSite) {
|
|
201
|
-
str += "; SameSite=".concat(this.options.sameSite);
|
|
202
|
-
}
|
|
203
|
-
globalScope = getGlobalScope();
|
|
204
|
-
if (globalScope) {
|
|
205
|
-
globalScope.document.cookie = str;
|
|
167
|
+
var res = duplicateResolverFn(c.substring(key.length + 1));
|
|
168
|
+
if (!res) {
|
|
169
|
+
(_a = _this.config.diagnosticsClient) === null || _a === void 0 ? void 0 : _a.increment('cookies.duplicate.occurrence.document.cookie');
|
|
206
170
|
}
|
|
171
|
+
return res;
|
|
207
172
|
}
|
|
208
|
-
catch (
|
|
209
|
-
|
|
210
|
-
|
|
173
|
+
catch (ignoreError) {
|
|
174
|
+
/* istanbul ignore next */
|
|
175
|
+
return false;
|
|
211
176
|
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
// if match was not found, just get the first one that matches the key
|
|
180
|
+
if (!match) {
|
|
181
|
+
match = cookies[0];
|
|
182
|
+
}
|
|
183
|
+
if (!match) {
|
|
184
|
+
return undefined;
|
|
185
|
+
}
|
|
186
|
+
return match.substring(key.length + 1);
|
|
187
|
+
};
|
|
188
|
+
CookieStorage.prototype.set = function (key, value) {
|
|
189
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
190
|
+
return __generator(this, function (_a) {
|
|
191
|
+
this.setSync(key, value);
|
|
212
192
|
return [2 /*return*/];
|
|
213
193
|
});
|
|
214
194
|
});
|
|
215
195
|
};
|
|
196
|
+
CookieStorage.prototype.setSync = function (key, value) {
|
|
197
|
+
var _a;
|
|
198
|
+
try {
|
|
199
|
+
var expirationDays = (_a = this.options.expirationDays) !== null && _a !== void 0 ? _a : 0;
|
|
200
|
+
var expires = value !== null ? expirationDays : -1;
|
|
201
|
+
var expireDate = undefined;
|
|
202
|
+
if (expires) {
|
|
203
|
+
var date = new Date();
|
|
204
|
+
date.setTime(date.getTime() + expires * 24 * 60 * 60 * 1000);
|
|
205
|
+
expireDate = date;
|
|
206
|
+
}
|
|
207
|
+
var str = "".concat(key, "=").concat(btoa(encodeURIComponent(JSON.stringify(value))));
|
|
208
|
+
if (expireDate) {
|
|
209
|
+
str += "; expires=".concat(expireDate.toUTCString());
|
|
210
|
+
}
|
|
211
|
+
str += '; path=/';
|
|
212
|
+
if (this.options.domain) {
|
|
213
|
+
str += "; domain=".concat(this.options.domain);
|
|
214
|
+
}
|
|
215
|
+
if (this.options.secure) {
|
|
216
|
+
str += '; Secure';
|
|
217
|
+
}
|
|
218
|
+
if (this.options.sameSite) {
|
|
219
|
+
str += "; SameSite=".concat(this.options.sameSite);
|
|
220
|
+
}
|
|
221
|
+
var globalScope = getGlobalScope();
|
|
222
|
+
if (globalScope) {
|
|
223
|
+
globalScope.document.cookie = str;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
catch (error) {
|
|
227
|
+
var errorMessage = error instanceof Error ? error.message : String(error);
|
|
228
|
+
console.error("Amplitude Logger [Error]: Failed to set cookie for key: ".concat(key, ". Error: ").concat(errorMessage));
|
|
229
|
+
}
|
|
230
|
+
};
|
|
216
231
|
CookieStorage.prototype.remove = function (key) {
|
|
217
232
|
return __awaiter(this, void 0, void 0, function () {
|
|
218
233
|
return __generator(this, function (_a) {
|
|
@@ -232,6 +247,68 @@ var CookieStorage = /** @class */ (function () {
|
|
|
232
247
|
});
|
|
233
248
|
});
|
|
234
249
|
};
|
|
250
|
+
CookieStorage.isDomainWritable = function (domain) {
|
|
251
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
252
|
+
var options, storageKey, storage, res, error_1;
|
|
253
|
+
return __generator(this, function (_a) {
|
|
254
|
+
switch (_a.label) {
|
|
255
|
+
case 0:
|
|
256
|
+
options = {
|
|
257
|
+
domain: '.' + domain,
|
|
258
|
+
};
|
|
259
|
+
storageKey = 'AMP_TLDTEST';
|
|
260
|
+
storage = new CookieStorage(options);
|
|
261
|
+
_a.label = 1;
|
|
262
|
+
case 1:
|
|
263
|
+
_a.trys.push([1, 3, , 4]);
|
|
264
|
+
return [4 /*yield*/, storage.transaction(storageKey, function (storageSync) {
|
|
265
|
+
try {
|
|
266
|
+
storageSync.set(1);
|
|
267
|
+
return storageSync.get();
|
|
268
|
+
}
|
|
269
|
+
finally {
|
|
270
|
+
storageSync.set(null);
|
|
271
|
+
}
|
|
272
|
+
})];
|
|
273
|
+
case 2:
|
|
274
|
+
res = _a.sent();
|
|
275
|
+
return [2 /*return*/, !!res];
|
|
276
|
+
case 3:
|
|
277
|
+
error_1 = _a.sent();
|
|
278
|
+
return [2 /*return*/, false];
|
|
279
|
+
case 4: return [2 /*return*/];
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
};
|
|
284
|
+
CookieStorage.prototype.transaction = function (key, callback) {
|
|
285
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
286
|
+
var locks, callbackWrapper;
|
|
287
|
+
var _this = this;
|
|
288
|
+
return __generator(this, function (_a) {
|
|
289
|
+
switch (_a.label) {
|
|
290
|
+
case 0:
|
|
291
|
+
locks = getLocks();
|
|
292
|
+
callbackWrapper = function () {
|
|
293
|
+
// construct a sync storage object that is scoped to
|
|
294
|
+
// Cookie with name <key>
|
|
295
|
+
var storageSync = {
|
|
296
|
+
get: function () { return _this.getSync(key); },
|
|
297
|
+
set: function (value) { return _this.setSync(key, value); },
|
|
298
|
+
};
|
|
299
|
+
return callback(storageSync);
|
|
300
|
+
};
|
|
301
|
+
// if 'locks' is missing, it is a legacy browser, just call the callback directly
|
|
302
|
+
// and settle for a transaction that isn't isolated across tabs
|
|
303
|
+
if (!locks) {
|
|
304
|
+
return [2 /*return*/, callbackWrapper()];
|
|
305
|
+
}
|
|
306
|
+
return [4 /*yield*/, locks.request("com.amplitude:cookie-lock:".concat(key), callbackWrapper)];
|
|
307
|
+
case 1: return [2 /*return*/, (_a.sent())];
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
};
|
|
235
312
|
return CookieStorage;
|
|
236
313
|
}());
|
|
237
314
|
export { CookieStorage };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cookie.js","sourceRoot":"","sources":["../../../src/storage/cookie.ts"],"names":[],"mappings":"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","sourcesContent":["import { Storage, CookieStorageOptions, CookieStorageConfig } from '../types/storage';\nimport { getGlobalScope } from '../global-scope';\nimport { UUID } from '../utils/uuid';\n\n// CookieStore is a Web API not included in standard TypeScript lib types\n// https://developer.mozilla.org/en-US/docs/Web/API/CookieStore\ninterface CookieStoreSetOptions {\n name: string;\n value: string;\n expires?: number;\n domain?: string;\n sameSite?: 'strict' | 'lax' | 'none';\n}\n\ninterface CookieStore {\n getAll(key: string): Promise<CookieStoreSetOptions[] | undefined>;\n}\n\ntype GlobalScopeWithCookieStore = {\n cookieStore?: CookieStore;\n} & typeof global;\n\nexport class CookieStorage<T> implements Storage<T> {\n options: CookieStorageOptions;\n config: CookieStorageConfig;\n\n constructor(options?: CookieStorageOptions, config: CookieStorageConfig = {}) {\n this.options = { ...options };\n this.config = config;\n }\n\n async isEnabled(): Promise<boolean> {\n const globalScope = getGlobalScope();\n /* istanbul ignore if */\n if (!globalScope || !globalScope.document) {\n return false;\n }\n\n const testValue = String(Date.now());\n const testCookieOptions = {\n ...this.options,\n expirationDays: 0.003, // expire in ~5 minutes\n };\n const testStorage = new CookieStorage<string>(testCookieOptions);\n const testKey = `AMP_TEST_${UUID().substring(0, 8)}`;\n try {\n await testStorage.set(testKey, testValue);\n const value = await testStorage.get(testKey);\n /* istanbul ignore next */\n if (value !== testValue && this.config.diagnosticsClient) {\n this.config.diagnosticsClient?.recordEvent('cookies.isEnabled.failure', {\n reason: 'Test Value mismatch',\n testKey,\n testValue,\n });\n }\n return value === testValue;\n } catch (e) {\n /* istanbul ignore next */\n if (this.config.diagnosticsClient) {\n const errMessage = e instanceof Error ? e.message : String(e);\n this.config.diagnosticsClient?.recordEvent('cookies.isEnabled.failure', {\n reason: 'Cookie getter/setter failed',\n testKey,\n testValue,\n error: errMessage,\n });\n }\n return false;\n } finally {\n await testStorage.remove(testKey);\n }\n }\n\n async get(key: string): Promise<T | undefined> {\n const value = await this.getRaw(key);\n if (!value) {\n return undefined;\n }\n try {\n const decodedValue = decodeCookieValue(value);\n if (decodedValue === undefined) {\n console.error(`Amplitude Logger [Error]: Failed to decode cookie value for key: ${key}, value: ${value}`);\n return undefined;\n }\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return JSON.parse(decodedValue);\n } catch {\n console.error(`Amplitude Logger [Error]: Failed to parse cookie value for key: ${key}, value: ${value}`);\n return undefined;\n }\n }\n\n async getRaw(key: string): Promise<string | undefined> {\n const globalScope = getGlobalScope();\n\n // use CookieStore if available and enabled\n const globalScopeWithCookiesStore = globalScope as GlobalScopeWithCookieStore;\n try {\n const cookieStore = globalScopeWithCookiesStore?.cookieStore;\n if (cookieStore) {\n const cookies = await cookieStore.getAll(key);\n if (cookies) {\n /* istanbul ignore if */\n if (cookies.length > 1) {\n this.config.diagnosticsClient?.recordEvent('cookies.duplicate', {\n cookies: cookies.map((cookie) => cookie.domain),\n });\n this.config.diagnosticsClient?.increment('cookies.duplicate.occurrence.cookieStore');\n }\n\n for (const cookie of cookies) {\n if (isDomainEqual(cookie.domain, this.options.domain)) {\n return cookie.value;\n }\n }\n }\n }\n } catch (ignoreError) {\n /* istanbul ignore next */\n // if cookieStore had a surprise failure, fallback to document.cookie\n }\n\n const cookies = (globalScope?.document?.cookie.split('; ') ?? []).filter((c) => c.indexOf(key + '=') === 0);\n let match: string | undefined = undefined;\n\n // if matcher function is provided, use it to de-duplicate when there's more than one cookie\n /* istanbul ignore if */\n const duplicateResolverFn = this.config.duplicateResolverFn;\n if (typeof duplicateResolverFn === 'function' && cookies.length > 1) {\n match = cookies.find((c) => {\n try {\n const res = duplicateResolverFn(c.substring(key.length + 1));\n if (!res) {\n this.config.diagnosticsClient?.increment('cookies.duplicate.occurrence.document.cookie');\n }\n return res;\n } catch (ignoreError) {\n /* istanbul ignore next */\n return false;\n }\n });\n }\n\n // if match was not found, just get the first one that matches the key\n if (!match) {\n match = cookies[0];\n }\n if (!match) {\n return undefined;\n }\n return match.substring(key.length + 1);\n }\n\n async set(key: string, value: T | null): Promise<void> {\n try {\n const expirationDays = this.options.expirationDays ?? 0;\n const expires = value !== null ? expirationDays : -1;\n let expireDate: Date | undefined = undefined;\n if (expires) {\n const date = new Date();\n date.setTime(date.getTime() + expires * 24 * 60 * 60 * 1000);\n expireDate = date;\n }\n let str = `${key}=${btoa(encodeURIComponent(JSON.stringify(value)))}`;\n if (expireDate) {\n str += `; expires=${expireDate.toUTCString()}`;\n }\n str += '; path=/';\n if (this.options.domain) {\n str += `; domain=${this.options.domain}`;\n }\n if (this.options.secure) {\n str += '; Secure';\n }\n if (this.options.sameSite) {\n str += `; SameSite=${this.options.sameSite}`;\n }\n const globalScope = getGlobalScope();\n if (globalScope) {\n globalScope.document.cookie = str;\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.error(`Amplitude Logger [Error]: Failed to set cookie for key: ${key}. Error: ${errorMessage}`);\n }\n }\n\n async remove(key: string): Promise<void> {\n await this.set(key, null);\n }\n\n async reset(): Promise<void> {\n return;\n }\n}\n\nconst decodeCookiesAsDefault = (value: string): string | undefined => {\n try {\n return decodeURIComponent(atob(value));\n } catch {\n return undefined;\n }\n};\n\nconst decodeCookiesWithDoubleUrlEncoding = (value: string): string | undefined => {\n // Modern Ruby (v7+) automatically encodes cookies with URL encoding by\n // https://api.rubyonrails.org/classes/ActionDispatch/Cookies.html\n try {\n return decodeURIComponent(atob(decodeURIComponent(value)));\n } catch {\n return undefined;\n }\n};\n\n/**\n * Decodes a cookie value that was encoded with btoa(encodeURIComponent(...)).\n * Handles both standard encoding and double URL encoding (used by Ruby Rails v7+).\n */\nexport const decodeCookieValue = (value: string): string | undefined => {\n return decodeCookiesAsDefault(value) ?? decodeCookiesWithDoubleUrlEncoding(value);\n};\n\n/**\n * Compares two domain strings for equality, ignoring leading dots.\n * This is useful for comparing cookie domains since \".example.com\" and \"example.com\"\n * are effectively equivalent for cookie scoping.\n */\nexport const isDomainEqual = (domain1: string | undefined, domain2: string | undefined): boolean => {\n if (domain1 === '' && domain2 === '') {\n return true;\n }\n if (!domain1 || !domain2) {\n return false;\n }\n const normalized1 = domain1.startsWith('.') ? domain1.substring(1) : domain1;\n const normalized2 = domain2.startsWith('.') ? domain2.substring(1) : domain2;\n return normalized1.toLowerCase() === normalized2.toLowerCase();\n};\n"]}
|
|
1
|
+
{"version":3,"file":"cookie.js","sourceRoot":"","sources":["../../../src/storage/cookie.ts"],"names":[],"mappings":"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","sourcesContent":["import { Storage, StorageSync, CookieStorageOptions, CookieStorageConfig } from '../types/storage';\nimport { getGlobalScope } from '../global-scope';\n\n// CookieStore is a Web API not included in standard TypeScript lib types\n// https://developer.mozilla.org/en-US/docs/Web/API/CookieStore\ninterface CookieStoreSetOptions {\n name: string;\n value: string;\n expires?: number;\n domain?: string;\n sameSite?: 'strict' | 'lax' | 'none';\n}\n\ninterface CookieStore {\n getAll(key: string): Promise<CookieStoreSetOptions[] | undefined>;\n}\n\ntype GlobalScopeWithCookieStore = {\n cookieStore?: CookieStore;\n} & typeof global;\n\n/* istanbul ignore next */\nconst getLocks = (): typeof global.navigator.locks | undefined => {\n const globalScope = getGlobalScope();\n return globalScope?.navigator?.locks;\n};\n\nexport class CookieStorage<T> implements Storage<T> {\n options: CookieStorageOptions;\n config: CookieStorageConfig;\n\n constructor(options?: CookieStorageOptions, config: CookieStorageConfig = {}) {\n this.options = { ...options };\n this.config = config;\n }\n\n async isEnabled(): Promise<boolean> {\n const testKey = 'AMP_TEST';\n const testCookieOptions = { ...this.options };\n const testStorage = new CookieStorage<string>(testCookieOptions);\n const testValue = String(Date.now());\n return await testStorage.transaction<boolean>(testKey, (storage: StorageSync<string>) => {\n try {\n storage.set(testValue);\n const value = storage.get();\n const result = value === testValue;\n /* istanbul ignore next */\n if (!result && this.config.diagnosticsClient) {\n this.config.diagnosticsClient?.recordEvent('cookies.isEnabled.failure', {\n reason: 'Test Value mismatch',\n testKey,\n testValue,\n sync: true,\n });\n }\n return result;\n } catch (e) {\n /* istanbul ignore next */\n if (this.config.diagnosticsClient) {\n const errMessage = e instanceof Error ? e.message : String(e);\n this.config.diagnosticsClient?.recordEvent('cookies.isEnabled.failure', {\n reason: 'Cookie getter/setter failed',\n testKey,\n testValue,\n error: errMessage,\n sync: true,\n });\n }\n return false;\n } finally {\n // clean-up the AMP_TEST cookie behind us\n storage.set(null);\n }\n });\n }\n\n async get(key: string): Promise<T | undefined> {\n const value = await this.getRaw(key);\n return this.decodeCookieValue(key, value);\n }\n\n private decodeCookieValue(key: string, value: string | undefined): T | undefined {\n if (!value) {\n return undefined;\n }\n try {\n const decodedValue = decodeCookieValue(value);\n if (decodedValue === undefined) {\n console.error(`Amplitude Logger [Error]: Failed to decode cookie value for key: ${key}, value: ${value}`);\n return undefined;\n }\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return JSON.parse(decodedValue);\n } catch {\n console.error(`Amplitude Logger [Error]: Failed to parse cookie value for key: ${key}, value: ${value}`);\n return undefined;\n }\n }\n\n private getSync(key: string): T | undefined {\n const value = this.getRawSync(key);\n return this.decodeCookieValue(key, value);\n }\n\n async getRaw(key: string): Promise<string | undefined> {\n const globalScope = getGlobalScope();\n\n // use CookieStore if available and enabled\n const globalScopeWithCookiesStore = globalScope as GlobalScopeWithCookieStore;\n try {\n const cookieStore = globalScopeWithCookiesStore?.cookieStore;\n if (cookieStore) {\n const cookies = await cookieStore.getAll(key);\n if (cookies) {\n /* istanbul ignore if */\n if (cookies.length > 1) {\n this.config.diagnosticsClient?.recordEvent('cookies.duplicate', {\n cookies: cookies.map((cookie) => cookie.domain),\n });\n this.config.diagnosticsClient?.increment('cookies.duplicate.occurrence.cookieStore');\n }\n\n for (const cookie of cookies) {\n if (isDomainEqual(cookie.domain, this.options.domain)) {\n return cookie.value;\n }\n }\n }\n }\n } catch (ignoreError) {\n /* istanbul ignore next */\n // if cookieStore had a surprise failure, fallback to document.cookie\n }\n\n return this.getRawSync(key);\n }\n\n private getRawSync(key: string): string | undefined {\n const globalScope = getGlobalScope();\n const cookies = (globalScope?.document?.cookie.split('; ') ?? []).filter((c) => c.indexOf(key + '=') === 0);\n let match: string | undefined = undefined;\n\n // if matcher function is provided, use it to de-duplicate when there's more than one cookie\n /* istanbul ignore if */\n const duplicateResolverFn = this.config.duplicateResolverFn;\n if (typeof duplicateResolverFn === 'function' && cookies.length > 1) {\n match = cookies.find((c) => {\n try {\n const res = duplicateResolverFn(c.substring(key.length + 1));\n if (!res) {\n this.config.diagnosticsClient?.increment('cookies.duplicate.occurrence.document.cookie');\n }\n return res;\n } catch (ignoreError) {\n /* istanbul ignore next */\n return false;\n }\n });\n }\n\n // if match was not found, just get the first one that matches the key\n if (!match) {\n match = cookies[0];\n }\n if (!match) {\n return undefined;\n }\n return match.substring(key.length + 1);\n }\n\n async set(key: string, value: T | null): Promise<void> {\n this.setSync(key, value);\n }\n\n private setSync(key: string, value: T | null): void {\n try {\n const expirationDays = this.options.expirationDays ?? 0;\n const expires = value !== null ? expirationDays : -1;\n let expireDate: Date | undefined = undefined;\n if (expires) {\n const date = new Date();\n date.setTime(date.getTime() + expires * 24 * 60 * 60 * 1000);\n expireDate = date;\n }\n let str = `${key}=${btoa(encodeURIComponent(JSON.stringify(value)))}`;\n if (expireDate) {\n str += `; expires=${expireDate.toUTCString()}`;\n }\n str += '; path=/';\n if (this.options.domain) {\n str += `; domain=${this.options.domain}`;\n }\n if (this.options.secure) {\n str += '; Secure';\n }\n if (this.options.sameSite) {\n str += `; SameSite=${this.options.sameSite}`;\n }\n const globalScope = getGlobalScope();\n if (globalScope) {\n globalScope.document.cookie = str;\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.error(`Amplitude Logger [Error]: Failed to set cookie for key: ${key}. Error: ${errorMessage}`);\n }\n }\n\n async remove(key: string): Promise<void> {\n await this.set(key, null);\n }\n\n async reset(): Promise<void> {\n return;\n }\n\n static async isDomainWritable(domain: string): Promise<boolean> {\n const options = {\n domain: '.' + domain,\n };\n const storageKey = 'AMP_TLDTEST';\n const storage = new CookieStorage<number>(options);\n try {\n const res = await storage.transaction(storageKey, (storageSync) => {\n try {\n storageSync.set(1);\n return storageSync.get();\n } finally {\n storageSync.set(null);\n }\n });\n return !!res;\n } catch (error) {\n return false;\n }\n }\n\n private async transaction<ReturnType>(\n key: string,\n callback: (storageSync: StorageSync<T>) => ReturnType,\n ): Promise<ReturnType> {\n const locks = getLocks();\n const callbackWrapper = () => {\n // construct a sync storage object that is scoped to\n // Cookie with name <key>\n const storageSync: StorageSync<T> = {\n get: () => this.getSync(key),\n set: (value: T | null) => this.setSync(key, value),\n };\n return callback(storageSync);\n };\n\n // if 'locks' is missing, it is a legacy browser, just call the callback directly\n // and settle for a transaction that isn't isolated across tabs\n if (!locks) {\n return callbackWrapper();\n }\n return (await locks.request(`com.amplitude:cookie-lock:${key}`, callbackWrapper)) as ReturnType;\n }\n}\n\nconst decodeCookiesAsDefault = (value: string): string | undefined => {\n try {\n return decodeURIComponent(atob(value));\n } catch {\n return undefined;\n }\n};\n\nconst decodeCookiesWithDoubleUrlEncoding = (value: string): string | undefined => {\n // Modern Ruby (v7+) automatically encodes cookies with URL encoding by\n // https://api.rubyonrails.org/classes/ActionDispatch/Cookies.html\n try {\n return decodeURIComponent(atob(decodeURIComponent(value)));\n } catch {\n return undefined;\n }\n};\n\n/**\n * Decodes a cookie value that was encoded with btoa(encodeURIComponent(...)).\n * Handles both standard encoding and double URL encoding (used by Ruby Rails v7+).\n */\nexport const decodeCookieValue = (value: string): string | undefined => {\n return decodeCookiesAsDefault(value) ?? decodeCookiesWithDoubleUrlEncoding(value);\n};\n\n/**\n * Compares two domain strings for equality, ignoring leading dots.\n * This is useful for comparing cookie domains since \".example.com\" and \"example.com\"\n * are effectively equivalent for cookie scoping.\n */\nexport const isDomainEqual = (domain1: string | undefined, domain2: string | undefined): boolean => {\n if (domain1 === '' && domain2 === '') {\n return true;\n }\n if (!domain1 || !domain2) {\n return false;\n }\n const normalized1 = domain1.startsWith('.') ? domain1.substring(1) : domain1;\n const normalized2 = domain2.startsWith('.') ? domain2.substring(1) : domain2;\n return normalized1.toLowerCase() === normalized2.toLowerCase();\n};\n"]}
|
|
@@ -7,6 +7,10 @@ export interface Storage<T> {
|
|
|
7
7
|
remove(key: string): Promise<void>;
|
|
8
8
|
reset(): Promise<void>;
|
|
9
9
|
}
|
|
10
|
+
export interface StorageSync<T> {
|
|
11
|
+
get: () => T | undefined;
|
|
12
|
+
set: (value: T | null) => void;
|
|
13
|
+
}
|
|
10
14
|
export interface CookieStorageOptions {
|
|
11
15
|
domain?: string;
|
|
12
16
|
expirationDays?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../src/types/storage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAEvE,MAAM,WAAW,OAAO,CAAC,CAAC;IACxB,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IACzC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACjD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,mBAAmB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IACjD,iBAAiB,CAAC,EAAE,kBAAkB,CAAC;CACxC;AAED,MAAM,MAAM,mBAAmB,GAAG,QAAQ,GAAG,cAAc,GAAG,gBAAgB,GAAG,MAAM,CAAC"}
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../src/types/storage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAEvE,MAAM,WAAW,OAAO,CAAC,CAAC;IACxB,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IACzC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACjD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,GAAG,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IACzB,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,mBAAmB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IACjD,iBAAiB,CAAC,EAAE,kBAAkB,CAAC;CACxC;AAED,MAAM,MAAM,mBAAmB,GAAG,QAAQ,GAAG,cAAc,GAAG,gBAAgB,GAAG,MAAM,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../../../src/types/storage.ts"],"names":[],"mappings":"","sourcesContent":["import { IDiagnosticsClient } from '../diagnostics/diagnostics-client';\n\nexport interface Storage<T> {\n isEnabled(): Promise<boolean>;\n get(key: string): Promise<T | undefined>;\n getRaw(key: string): Promise<string | undefined>;\n set(key: string, value: T): Promise<void>;\n remove(key: string): Promise<void>;\n reset(): Promise<void>;\n}\n\nexport interface CookieStorageOptions {\n domain?: string;\n expirationDays?: number;\n sameSite?: string;\n secure?: boolean;\n}\n\n/**\n * Configuration for CookieStorage behavior.\n * Separated from options to keep storage-specific config distinct from cookie attributes.\n */\nexport interface CookieStorageConfig {\n /**\n * Function to resolve duplicate cookies when multiple cookies with the same key exist.\n * Returns true if the cookie value should be used, false otherwise.\n */\n duplicateResolverFn?: (value: string) => boolean;\n diagnosticsClient?: IDiagnosticsClient;\n}\n\nexport type IdentityStorageType = 'cookie' | 'localStorage' | 'sessionStorage' | 'none';\n"]}
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../../../src/types/storage.ts"],"names":[],"mappings":"","sourcesContent":["import { IDiagnosticsClient } from '../diagnostics/diagnostics-client';\n\nexport interface Storage<T> {\n isEnabled(): Promise<boolean>;\n get(key: string): Promise<T | undefined>;\n getRaw(key: string): Promise<string | undefined>;\n set(key: string, value: T): Promise<void>;\n remove(key: string): Promise<void>;\n reset(): Promise<void>;\n}\n\nexport interface StorageSync<T> {\n get: () => T | undefined;\n set: (value: T | null) => void;\n}\n\nexport interface CookieStorageOptions {\n domain?: string;\n expirationDays?: number;\n sameSite?: string;\n secure?: boolean;\n}\n\n/**\n * Configuration for CookieStorage behavior.\n * Separated from options to keep storage-specific config distinct from cookie attributes.\n */\nexport interface CookieStorageConfig {\n /**\n * Function to resolve duplicate cookies when multiple cookies with the same key exist.\n * Returns true if the cookie value should be used, false otherwise.\n */\n duplicateResolverFn?: (value: string) => boolean;\n diagnosticsClient?: IDiagnosticsClient;\n}\n\nexport type IdentityStorageType = 'cookie' | 'localStorage' | 'sessionStorage' | 'none';\n"]}
|