@formo/analytics 1.17.2 → 1.17.4

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 (73) hide show
  1. package/package.json +4 -1
  2. package/.env.example +0 -1
  3. package/.github/workflows/ci.yml +0 -50
  4. package/.github/workflows/release.yml +0 -54
  5. package/.husky/post-commit +0 -7
  6. package/.husky/pre-commit +0 -11
  7. package/.releaserc.js +0 -34
  8. package/CONTRIBUTING.md +0 -83
  9. package/scripts/generate-sri.sh +0 -52
  10. package/src/FormoAnalytics.ts +0 -1031
  11. package/src/FormoAnalyticsProvider.tsx +0 -84
  12. package/src/constants/base.ts +0 -6
  13. package/src/constants/config.ts +0 -660
  14. package/src/constants/events.ts +0 -21
  15. package/src/constants/index.ts +0 -3
  16. package/src/global.d.ts +0 -12
  17. package/src/index.ts +0 -3
  18. package/src/lib/event/EventFactory.ts +0 -519
  19. package/src/lib/event/EventManager.ts +0 -39
  20. package/src/lib/event/constants.ts +0 -4
  21. package/src/lib/event/index.ts +0 -3
  22. package/src/lib/event/type.ts +0 -9
  23. package/src/lib/event/utils.ts +0 -33
  24. package/src/lib/fetch.ts +0 -3
  25. package/src/lib/index.ts +0 -5
  26. package/src/lib/logger/Logger.ts +0 -115
  27. package/src/lib/logger/index.ts +0 -2
  28. package/src/lib/logger/type.ts +0 -14
  29. package/src/lib/queue/EventQueue.ts +0 -306
  30. package/src/lib/queue/index.ts +0 -2
  31. package/src/lib/queue/type.ts +0 -6
  32. package/src/lib/ramda/internal/_curry1.ts +0 -19
  33. package/src/lib/ramda/internal/_curry2.ts +0 -37
  34. package/src/lib/ramda/internal/_curry3.ts +0 -68
  35. package/src/lib/ramda/internal/_has.ts +0 -3
  36. package/src/lib/ramda/internal/_isObject.ts +0 -3
  37. package/src/lib/ramda/internal/_isPlaceholder.ts +0 -5
  38. package/src/lib/ramda/mergeDeepRight.ts +0 -13
  39. package/src/lib/ramda/mergeDeepWithKey.ts +0 -22
  40. package/src/lib/ramda/mergeWithKey.ts +0 -28
  41. package/src/lib/storage/StorageManager.ts +0 -51
  42. package/src/lib/storage/built-in/blueprint.ts +0 -17
  43. package/src/lib/storage/built-in/cookie.ts +0 -60
  44. package/src/lib/storage/built-in/memory.ts +0 -23
  45. package/src/lib/storage/built-in/web.ts +0 -57
  46. package/src/lib/storage/constant.ts +0 -2
  47. package/src/lib/storage/index.ts +0 -25
  48. package/src/lib/storage/type.ts +0 -21
  49. package/src/lib/version.ts +0 -2
  50. package/src/types/base.ts +0 -120
  51. package/src/types/events.ts +0 -126
  52. package/src/types/index.ts +0 -3
  53. package/src/types/provider.ts +0 -17
  54. package/src/utils/address.ts +0 -43
  55. package/src/utils/base.ts +0 -3
  56. package/src/utils/converter.ts +0 -44
  57. package/src/utils/generate.ts +0 -16
  58. package/src/utils/index.ts +0 -4
  59. package/src/utils/timestamp.ts +0 -9
  60. package/src/validators/address.ts +0 -69
  61. package/src/validators/agent.ts +0 -4
  62. package/src/validators/checks.ts +0 -160
  63. package/src/validators/index.ts +0 -7
  64. package/src/validators/network.ts +0 -34
  65. package/src/validators/object.ts +0 -4
  66. package/src/validators/string.ts +0 -4
  67. package/src/validators/uint8array.ts +0 -17
  68. package/test/lib/events.spec.ts +0 -12
  69. package/test/utils/address.spec.ts +0 -14
  70. package/test/utils/converter.spec.ts +0 -31
  71. package/test/validators/address.spec.ts +0 -15
  72. package/tsconfig.json +0 -28
  73. package/webpack.config.ts +0 -23
@@ -1,115 +0,0 @@
1
- import { ILogger, LogLevel } from "./type";
2
-
3
- export class Logger implements ILogger {
4
- private static instance: ILogger;
5
- private enabledLevels: Set<LogLevel>;
6
- private enabled: boolean;
7
-
8
- private constructor(
9
- enabled: boolean = false,
10
- enabledLevels: LogLevel[] = []
11
- ) {
12
- this.enabled = enabled;
13
- this.enabledLevels = new Set(enabledLevels);
14
- }
15
-
16
- public static init(config: {
17
- enabled?: boolean;
18
- enabledLevels?: LogLevel[];
19
- }): void {
20
- // Get or create instance
21
- const instance = Logger.getInstance();
22
-
23
- // Update configuration
24
- if (config.enabled !== undefined) {
25
- instance.setEnabled(config.enabled);
26
- }
27
- if (config.enabledLevels !== undefined) {
28
- instance.setEnabledLevels(config.enabledLevels);
29
- }
30
- }
31
-
32
- public static getInstance(config?: {
33
- enabled?: boolean;
34
- enabledLevels?: LogLevel[];
35
- }): ILogger {
36
- if (!Logger.instance) {
37
- Logger.instance = new Logger(
38
- config?.enabled ?? false,
39
- config?.enabledLevels ?? []
40
- );
41
- }
42
- return Logger.instance;
43
- }
44
-
45
- public setEnabled(enabled: boolean): void {
46
- this.enabled = enabled;
47
- }
48
-
49
- public isLoggingEnabled(): boolean {
50
- return this.enabled;
51
- }
52
-
53
- public setEnabledLevels(levels: LogLevel[]): void {
54
- this.enabledLevels = new Set(levels);
55
- }
56
-
57
- public getEnabledLevels(): LogLevel[] {
58
- return Array.from(this.enabledLevels);
59
- }
60
-
61
- private shouldLog(level: LogLevel): boolean {
62
- if (!this.enabled) return false;
63
- return this.enabledLevels.has(level);
64
- }
65
-
66
- private formatMessage(message: string): string {
67
- const timestamp = new Date().toLocaleString("en-US", {
68
- year: "numeric",
69
- month: "2-digit",
70
- day: "2-digit",
71
- hour: "2-digit",
72
- minute: "2-digit",
73
- second: "2-digit",
74
- hour12: false,
75
- });
76
- return `[Formo Analytics][${timestamp}] ${message}`;
77
- }
78
-
79
- public debug(message: string, ...args: any[]): void {
80
- if (this.shouldLog("debug")) {
81
- console.debug(this.formatMessage(message), ...args);
82
- }
83
- }
84
-
85
- public info(message: string, ...args: any[]): void {
86
- if (this.shouldLog("info")) {
87
- console.info(this.formatMessage(message), ...args);
88
- }
89
- }
90
-
91
- public warn(message: string, ...args: any[]): void {
92
- if (this.shouldLog("warn")) {
93
- console.warn(this.formatMessage(message), ...args);
94
- }
95
- }
96
-
97
- public error(message: string, ...args: any[]): void {
98
- if (this.shouldLog("error")) {
99
- console.error(this.formatMessage(message), ...args);
100
- }
101
- }
102
-
103
- public trace(message: string, ...args: any[]): void {
104
- if (this.shouldLog("trace")) {
105
- console.trace(this.formatMessage(message), ...args);
106
- }
107
- }
108
-
109
- public log(message: string, ...args: any[]): void {
110
- this.info(message, ...args);
111
- }
112
- }
113
-
114
- // Export a default instance for easy use
115
- export const logger = Logger.getInstance();
@@ -1,2 +0,0 @@
1
- export * from "./Logger";
2
- export * from "./type";
@@ -1,14 +0,0 @@
1
- export interface ILogger {
2
- setEnabledLevels(levels: LogLevel[]): void;
3
- getEnabledLevels(): LogLevel[];
4
- setEnabled(enabled: boolean): void;
5
- isLoggingEnabled(): boolean;
6
- log(...data: any[]): void;
7
- info(...data: any[]): void;
8
- debug(...data: any[]): void;
9
- warn(...data: any[]): void;
10
- error(...data: any[]): void;
11
- trace(...data: any[]): void;
12
- }
13
-
14
- export type LogLevel = "debug" | "info" | "warn" | "error" | "trace";
@@ -1,306 +0,0 @@
1
- import { isNetworkError } from "../../validators";
2
- import { IFormoEvent, IFormoEventPayload } from "../../types";
3
- import {
4
- clampNumber,
5
- getActionDescriptor,
6
- hash,
7
- millisecondsToSecond,
8
- toDateHourMinute,
9
- } from "../../utils";
10
- import { logger } from "../logger";
11
- import { EVENTS_API_REQUEST_HEADER } from "../../constants";
12
- import fetch from "../fetch";
13
- import { IEventQueue } from "./type";
14
- const noop = () => {};
15
-
16
- type QueueItem = {
17
- message: IFormoEventPayload;
18
- callback: (...args: any) => any;
19
- };
20
-
21
- type IFormoEventFlushPayload = IFormoEventPayload & {
22
- sent_at: string;
23
- };
24
-
25
- type Options = {
26
- url: string;
27
- flushAt?: number;
28
- flushInterval?: number;
29
- host?: string;
30
- retryCount?: number;
31
- errorHandler?: any;
32
- maxQueueSize?: number;
33
- };
34
-
35
- const DEFAULT_RETRY = 3;
36
- const MAX_RETRY = 5;
37
- const MIN_RETRY = 1;
38
-
39
- const DEFAULT_FLUSH_AT = 20;
40
- const MAX_FLUSH_AT = 20;
41
- const MIN_FLUSH_AT = 1;
42
-
43
- const DEFAULT_QUEUE_SIZE = 1_024 * 500; // 500kB
44
- const MAX_QUEUE_SIZE = 1_024 * 500; // 500kB
45
- const MIN_QUEUE_SIZE = 200; // 200 bytes
46
-
47
- const DEFAULT_FLUSH_INTERVAL = 1_000 * 30; // 1 MINUTE
48
- const MAX_FLUSH_INTERVAL = 1_000 * 300; // 5 MINUTES
49
- const MIN_FLUSH_INTERVAL = 1_000 * 10; // 10 SECONDS
50
-
51
- export class EventQueue implements IEventQueue {
52
- private writeKey: string;
53
- private url: string;
54
- private queue: QueueItem[] = [];
55
- private timer: null | NodeJS.Timeout;
56
- private flushAt: number;
57
- private flushIntervalMs: number;
58
- private flushed: boolean;
59
- private maxQueueSize: number; // min 200 bytes, max 500kB
60
- private errorHandler: any;
61
- private retryCount: number;
62
- private pendingFlush: Promise<any> | null;
63
- private payloadHashes: Set<string> = new Set();
64
-
65
- constructor(writeKey: string, options: Options) {
66
- options = options || {};
67
-
68
- this.queue = [];
69
- this.writeKey = writeKey;
70
- this.url = options.url;
71
- this.retryCount = clampNumber(
72
- options.retryCount || DEFAULT_RETRY,
73
- MAX_RETRY,
74
- MIN_RETRY
75
- );
76
- this.flushAt = clampNumber(
77
- options.flushAt || DEFAULT_FLUSH_AT,
78
- MAX_FLUSH_AT,
79
- MIN_FLUSH_AT
80
- );
81
- this.maxQueueSize = clampNumber(
82
- options.maxQueueSize || DEFAULT_QUEUE_SIZE,
83
- MAX_QUEUE_SIZE,
84
- MIN_QUEUE_SIZE
85
- );
86
- this.flushIntervalMs = clampNumber(
87
- options.flushInterval || DEFAULT_FLUSH_INTERVAL,
88
- MAX_FLUSH_INTERVAL,
89
- MIN_FLUSH_INTERVAL
90
- );
91
- this.flushed = true;
92
- this.errorHandler = options.errorHandler;
93
- this.pendingFlush = null;
94
- this.timer = null;
95
-
96
- this.onPageLeave(async (isAccessible: boolean) => {
97
- if (isAccessible === false) {
98
- await this.flush();
99
- }
100
- });
101
- }
102
-
103
- //#region Public functions
104
- private async generateMessageId(event: IFormoEvent): Promise<string> {
105
- const formattedTimestamp = toDateHourMinute(new Date(event.original_timestamp));
106
- const eventForHashing = { ...event, original_timestamp: formattedTimestamp };
107
- const eventString = JSON.stringify(eventForHashing);
108
- return hash(eventString);
109
- }
110
-
111
- async enqueue(event: IFormoEvent, callback?: (...args: any) => void) {
112
- callback = callback || noop;
113
-
114
- const message_id = await this.generateMessageId(event);
115
- // check if the message already exists
116
- if (await this.isDuplicate(message_id)) {
117
- logger.warn(
118
- `Event already enqueued, try again after ${millisecondsToSecond(
119
- this.flushIntervalMs
120
- )} seconds.`
121
- );
122
- return;
123
- }
124
-
125
- this.queue.push({
126
- message: { ...event, message_id },
127
- callback,
128
- });
129
-
130
- logger.log(
131
- `Event enqueued: ${getActionDescriptor(event.type, event.properties)}`
132
- );
133
-
134
- if (!this.flushed) {
135
- this.flushed = true;
136
- this.flush();
137
- return;
138
- }
139
-
140
- const hasReachedFlushAt = this.queue.length >= this.flushAt;
141
- const hasReachedQueueSize =
142
- this.queue.reduce((acc, item) => acc + JSON.stringify(item).length, 0) >=
143
- this.maxQueueSize;
144
-
145
- if (hasReachedFlushAt || hasReachedQueueSize) {
146
- this.flush();
147
- return;
148
- }
149
-
150
- if (this.flushIntervalMs && !this.timer) {
151
- this.timer = setTimeout(this.flush.bind(this), this.flushIntervalMs);
152
- }
153
- }
154
-
155
- async flush(callback?: (...args: any) => void) {
156
- callback = callback || noop;
157
-
158
- if (this.timer) {
159
- clearTimeout(this.timer);
160
- this.timer = null;
161
- }
162
-
163
- if (!this.queue.length) {
164
- callback();
165
- return Promise.resolve();
166
- }
167
-
168
- try {
169
- if (this.pendingFlush) {
170
- await this.pendingFlush;
171
- }
172
- } catch (err) {
173
- this.pendingFlush = null;
174
- throw err;
175
- }
176
-
177
- const items = this.queue.splice(0, this.flushAt);
178
- this.payloadHashes.clear();
179
-
180
- // Generate sent_at once for the entire batch
181
- const sentAt = new Date().toISOString();
182
- const data: IFormoEventFlushPayload[] = items.map((item) => ({
183
- ...item.message,
184
- sent_at: sentAt
185
- }));
186
-
187
- const done = (err?: Error) => {
188
- items.forEach(({ message, callback }) => callback(err, message, data));
189
- callback(err, data);
190
- };
191
-
192
- return (this.pendingFlush = fetch(`${this.url}`, {
193
- headers: EVENTS_API_REQUEST_HEADER(this.writeKey),
194
- method: "POST",
195
- body: JSON.stringify(data),
196
- keepalive: true,
197
- retries: this.retryCount,
198
- retryDelay: (attempt) => Math.pow(2, attempt) * 1_000, // exponential backoff
199
- retryOn: (_, error) => this.isErrorRetryable(error),
200
- })
201
- .then(() => {
202
- done();
203
- return Promise.resolve(data);
204
- })
205
- .catch((err) => {
206
- if (typeof this.errorHandler === "function") {
207
- done(err);
208
- return this.errorHandler(err);
209
- }
210
-
211
- if (err.response) {
212
- const error = new Error(err.response.statusText);
213
- done(error);
214
- throw error;
215
- }
216
-
217
- done(err);
218
- throw err;
219
- }));
220
- }
221
-
222
- //#region Utility functions
223
- private isErrorRetryable(error: any) {
224
- // Retry Network Errors.
225
- if (isNetworkError(error)) return true;
226
-
227
- // Cannot determine if the request can be retried
228
- if (!error?.response) return false;
229
-
230
- // Retry Server Errors (5xx).
231
- if (error?.response?.status >= 500 && error?.response?.status <= 599)
232
- return true;
233
-
234
- // Retry if rate limited.
235
- if (error?.response?.status === 429) return true;
236
-
237
- return false;
238
- }
239
-
240
- private async isDuplicate(eventId: string) {
241
- // check if exists a message with identical payload within 1 minute
242
- if (this.payloadHashes.has(eventId)) return true;
243
-
244
- this.payloadHashes.add(eventId);
245
- return false;
246
- }
247
-
248
- private onPageLeave = (callback: (isAccessible: boolean) => void) => {
249
- // To ensure the callback is only called once even if more than one events
250
- // are fired at once.
251
- let pageLeft = false;
252
- let isAccessible = false;
253
-
254
- function handleOnLeave() {
255
- if (pageLeft) {
256
- return;
257
- }
258
-
259
- pageLeft = true;
260
-
261
- callback(isAccessible);
262
-
263
- // Reset pageLeft on the next tick
264
- // to ensure callback executes for other listeners
265
- // when closing an inactive browser tab.
266
- setTimeout(() => {
267
- pageLeft = false;
268
- }, 0);
269
- }
270
-
271
- // Catches the unloading of the page (e.g., closing the tab or navigating away).
272
- // Includes user actions like clicking a link, entering a new URL,
273
- // refreshing the page, or closing the browser tab
274
- // Note that 'pagehide' is not supported in IE.
275
- // So, this is a fallback.
276
- (globalThis as typeof window).addEventListener("beforeunload", () => {
277
- isAccessible = false;
278
- handleOnLeave();
279
- });
280
-
281
- (globalThis as typeof window).addEventListener("blur", () => {
282
- isAccessible = true;
283
- handleOnLeave();
284
- });
285
-
286
- (globalThis as typeof window).addEventListener("focus", () => {
287
- pageLeft = false;
288
- });
289
-
290
- // Catches the page being hidden, including scenarios like closing the tab.
291
- document.addEventListener("pagehide", () => {
292
- isAccessible = document.visibilityState === "hidden";
293
- handleOnLeave();
294
- });
295
-
296
- // Catches visibility changes, such as switching tabs or minimizing the browser.
297
- document.addEventListener("visibilitychange", () => {
298
- isAccessible = true;
299
- if (document.visibilityState === "hidden") {
300
- handleOnLeave();
301
- } else {
302
- pageLeft = false;
303
- }
304
- });
305
- };
306
- }
@@ -1,2 +0,0 @@
1
- export * from "./EventQueue";
2
- export * from "./type";
@@ -1,6 +0,0 @@
1
- import { IFormoEvent } from "../../types";
2
-
3
- export interface IEventQueue {
4
- enqueue(event: IFormoEvent, callback?: (...args: any) => void): Promise<void>;
5
- flush(callback?: (...args: any) => void): Promise<any>;
6
- }
@@ -1,19 +0,0 @@
1
- import _isPlaceholder from "./_isPlaceholder";
2
-
3
- /**
4
- * Optimized internal one-arity curry function.
5
- *
6
- * @private
7
- * @category Function
8
- * @param {Function} fn The function to curry.
9
- * @return {Function} The curried function.
10
- */
11
- export default function _curry1(fn: Function) {
12
- return function f1(a: any) {
13
- if (arguments.length === 0 || _isPlaceholder(a)) {
14
- return f1;
15
- } else {
16
- return fn.apply(this, arguments);
17
- }
18
- };
19
- }
@@ -1,37 +0,0 @@
1
- import _curry1 from "./_curry1";
2
- import _isPlaceholder from "./_isPlaceholder";
3
-
4
- /**
5
- * Optimized internal two-arity curry function.
6
- *
7
- * @private
8
- * @category Function
9
- * @param {Function} fn The function to curry.
10
- * @return {Function} The curried function.
11
- */
12
- export default function _curry2(fn: Function) {
13
- return function f2(a: any, b: any) {
14
- switch (arguments.length) {
15
- case 0:
16
- return f2;
17
- case 1:
18
- return _isPlaceholder(a)
19
- ? f2
20
- : _curry1(function (_b: any) {
21
- return fn(a, _b);
22
- });
23
- default:
24
- return _isPlaceholder(a) && _isPlaceholder(b)
25
- ? f2
26
- : _isPlaceholder(a)
27
- ? _curry1(function (_a: any) {
28
- return fn(_a, b);
29
- })
30
- : _isPlaceholder(b)
31
- ? _curry1(function (_b: any) {
32
- return fn(a, _b);
33
- })
34
- : fn(a, b);
35
- }
36
- };
37
- }
@@ -1,68 +0,0 @@
1
- import _curry1 from "./_curry1";
2
- import _curry2 from "./_curry2";
3
- import _isPlaceholder from "./_isPlaceholder";
4
-
5
- /**
6
- * Optimized internal three-arity curry function.
7
- *
8
- * @private
9
- * @category Function
10
- * @param {Function} fn The function to curry.
11
- * @return {Function} The curried function.
12
- */
13
- export default function _curry3(fn: Function) {
14
- return function f3(a: any, b: any, c: any) {
15
- switch (arguments.length) {
16
- case 0:
17
- return f3;
18
- case 1:
19
- return _isPlaceholder(a)
20
- ? f3
21
- : _curry2(function (_b: any, _c: any) {
22
- return fn(a, _b, _c);
23
- });
24
- case 2:
25
- return _isPlaceholder(a) && _isPlaceholder(b)
26
- ? f3
27
- : _isPlaceholder(a)
28
- ? _curry2(function (_a: any, _c: any) {
29
- return fn(_a, b, _c);
30
- })
31
- : _isPlaceholder(b)
32
- ? _curry2(function (_b: any, _c: any) {
33
- return fn(a, _b, _c);
34
- })
35
- : _curry1(function (_c: any) {
36
- return fn(a, b, _c);
37
- });
38
- default:
39
- return _isPlaceholder(a) && _isPlaceholder(b) && _isPlaceholder(c)
40
- ? f3
41
- : _isPlaceholder(a) && _isPlaceholder(b)
42
- ? _curry2(function (_a: any, _b: any) {
43
- return fn(_a, _b, c);
44
- })
45
- : _isPlaceholder(a) && _isPlaceholder(c)
46
- ? _curry2(function (_a: any, _c: any) {
47
- return fn(_a, b, _c);
48
- })
49
- : _isPlaceholder(b) && _isPlaceholder(c)
50
- ? _curry2(function (_b: any, _c: any) {
51
- return fn(a, _b, _c);
52
- })
53
- : _isPlaceholder(a)
54
- ? _curry1(function (_a: any) {
55
- return fn(_a, b, c);
56
- })
57
- : _isPlaceholder(b)
58
- ? _curry1(function (_b: any) {
59
- return fn(a, _b, c);
60
- })
61
- : _isPlaceholder(c)
62
- ? _curry1(function (_c: any) {
63
- return fn(a, b, _c);
64
- })
65
- : fn(a, b, c);
66
- }
67
- };
68
- }
@@ -1,3 +0,0 @@
1
- export default function _has(prop: any, obj: any) {
2
- return Object.prototype.hasOwnProperty.call(obj, prop);
3
- }
@@ -1,3 +0,0 @@
1
- export default function _isObject(x: any) {
2
- return Object.prototype.toString.call(x) === "[object Object]";
3
- }
@@ -1,5 +0,0 @@
1
- export default function _isPlaceholder(a: any) {
2
- return (
3
- a != null && typeof a === "object" && a["@@functional/placeholder"] === true
4
- );
5
- }
@@ -1,13 +0,0 @@
1
- import _curry2 from "./internal/_curry2";
2
- import mergeDeepWithKey from "./mergeDeepWithKey";
3
-
4
- const mergeDeepRight = _curry2(function mergeDeepRight(lObj: any, rObj: any) {
5
- return mergeDeepWithKey(
6
- function (_: Function, __: any, rVal: any) {
7
- return rVal;
8
- },
9
- lObj,
10
- rObj
11
- );
12
- });
13
- export default mergeDeepRight;
@@ -1,22 +0,0 @@
1
- import _curry3 from "./internal/_curry3";
2
- import _isObject from "./internal/_isObject";
3
- import mergeWithKey from "./mergeWithKey";
4
-
5
- const mergeDeepWithKey = _curry3(function mergeDeepWithKey(
6
- fn: Function,
7
- lObj: any,
8
- rObj: any
9
- ) {
10
- return mergeWithKey(
11
- function (k: Function, lVal: any, rVal: any) {
12
- if (_isObject(lVal) && _isObject(rVal)) {
13
- return mergeDeepWithKey(fn, lVal, rVal);
14
- } else {
15
- return fn(k, lVal, rVal);
16
- }
17
- },
18
- lObj,
19
- rObj
20
- );
21
- });
22
- export default mergeDeepWithKey;
@@ -1,28 +0,0 @@
1
- import _curry3 from "./internal/_curry3";
2
- import _has from "./internal/_has";
3
-
4
- const mergeWithKey = _curry3(function mergeWithKey(
5
- fn: Function,
6
- l: any,
7
- r: any
8
- ) {
9
- const result: any = {};
10
- let k;
11
- l = l || {};
12
- r = r || {};
13
-
14
- for (k in l) {
15
- if (_has(k, l)) {
16
- result[k] = _has(k, r) ? fn(k, l[k], r[k]) : l[k];
17
- }
18
- }
19
-
20
- for (k in r) {
21
- if (_has(k, r) && !_has(k, result)) {
22
- result[k] = r[k];
23
- }
24
- }
25
-
26
- return result;
27
- });
28
- export default mergeWithKey;