@posthog/core 1.2.2 → 1.2.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.
- package/dist/error-tracking/parsers/base.d.ts +0 -5
- package/dist/error-tracking/parsers/base.d.ts.map +1 -1
- package/dist/error-tracking/parsers/base.js +1 -21
- package/dist/error-tracking/parsers/base.mjs +1 -6
- package/dist/error-tracking/types.d.ts +1 -1
- package/dist/error-tracking/types.d.ts.map +1 -1
- package/dist/logger.d.ts +11 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +74 -0
- package/dist/logger.mjs +37 -0
- package/dist/posthog-core-stateless.d.ts +2 -1
- package/dist/posthog-core-stateless.d.ts.map +1 -1
- package/dist/posthog-core-stateless.js +31 -29
- package/dist/posthog-core-stateless.mjs +31 -29
- package/dist/posthog-core.d.ts.map +1 -1
- package/dist/posthog-core.js +9 -9
- package/dist/posthog-core.mjs +9 -9
- package/dist/process/index.d.ts +2 -0
- package/dist/process/index.d.ts.map +1 -0
- package/dist/process/index.js +60 -0
- package/dist/process/index.mjs +1 -0
- package/dist/process/spawn-local.d.ts +8 -0
- package/dist/process/spawn-local.d.ts.map +1 -0
- package/dist/process/spawn-local.js +64 -0
- package/dist/process/spawn-local.mjs +30 -0
- package/dist/process/utils.d.ts +3 -0
- package/dist/process/utils.d.ts.map +1 -0
- package/dist/process/utils.js +81 -0
- package/dist/process/utils.mjs +33 -0
- package/dist/testing/test-utils.d.ts.map +1 -1
- package/dist/testing/test-utils.js +4 -6
- package/dist/testing/test-utils.mjs +4 -6
- package/dist/types.d.ts +0 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +6 -1
- package/src/error-tracking/parsers/base.ts +0 -5
- package/src/error-tracking/types.ts +1 -1
- package/src/logger.ts +58 -0
- package/src/posthog-core-stateless.ts +35 -35
- package/src/posthog-core.ts +9 -17
- package/src/process/index.ts +1 -0
- package/src/process/spawn-local.ts +47 -0
- package/src/process/utils.spec.ts +11 -0
- package/src/process/utils.ts +41 -0
- package/src/testing/test-utils.ts +4 -6
- package/src/types.ts +0 -2
package/src/logger.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Logger } from './types'
|
|
2
|
+
|
|
3
|
+
// We want to make sure to get the original console methods as soon as possible
|
|
4
|
+
type ConsoleLike = {
|
|
5
|
+
log: (...args: any[]) => void
|
|
6
|
+
warn: (...args: any[]) => void
|
|
7
|
+
error: (...args: any[]) => void
|
|
8
|
+
debug: (...args: any[]) => void
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function createConsole(consoleLike: ConsoleLike = console): ConsoleLike {
|
|
12
|
+
const lockedMethods = {
|
|
13
|
+
log: consoleLike.log.bind(consoleLike),
|
|
14
|
+
warn: consoleLike.warn.bind(consoleLike),
|
|
15
|
+
error: consoleLike.error.bind(consoleLike),
|
|
16
|
+
debug: consoleLike.debug.bind(consoleLike),
|
|
17
|
+
}
|
|
18
|
+
return lockedMethods
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const _createLogger = (
|
|
22
|
+
prefix: string,
|
|
23
|
+
maybeCall: (fn: () => void) => void,
|
|
24
|
+
consoleLike: ConsoleLike
|
|
25
|
+
): Logger => {
|
|
26
|
+
function _log(level: 'log' | 'warn' | 'error', ...args: any[]) {
|
|
27
|
+
maybeCall(() => {
|
|
28
|
+
const consoleMethod = consoleLike[level]
|
|
29
|
+
consoleMethod(prefix, ...args)
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const logger: Logger = {
|
|
34
|
+
info: (...args: any[]) => {
|
|
35
|
+
_log('log', ...args)
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
warn: (...args: any[]) => {
|
|
39
|
+
_log('warn', ...args)
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
error: (...args: any[]) => {
|
|
43
|
+
_log('error', ...args)
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
critical: (...args: any[]) => {
|
|
47
|
+
// Critical errors are always logged to the console
|
|
48
|
+
consoleLike['error'](prefix, ...args)
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
createLogger: (additionalPrefix: string) => _createLogger(`${prefix} ${additionalPrefix}`, maybeCall, consoleLike),
|
|
52
|
+
}
|
|
53
|
+
return logger
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function createLogger(prefix: string, maybeCall: (fn: () => void) => void) {
|
|
57
|
+
return _createLogger(prefix, maybeCall, createConsole())
|
|
58
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { SimpleEventEmitter } from './eventemitter'
|
|
2
2
|
import { getFeatureFlagValue, normalizeFlagsResponse } from './featureFlagUtils'
|
|
3
3
|
import { gzipCompress, isGzipSupported } from './gzip'
|
|
4
|
+
import { createLogger } from './logger'
|
|
4
5
|
import {
|
|
5
6
|
PostHogFlagsResponse,
|
|
6
7
|
PostHogCoreOptions,
|
|
@@ -18,6 +19,7 @@ import {
|
|
|
18
19
|
PostHogFetchOptions,
|
|
19
20
|
PostHogPersistedProperty,
|
|
20
21
|
PostHogQueueItem,
|
|
22
|
+
Logger,
|
|
21
23
|
} from './types'
|
|
22
24
|
import {
|
|
23
25
|
allSettled,
|
|
@@ -127,6 +129,7 @@ export abstract class PostHogCoreStateless {
|
|
|
127
129
|
protected _initPromise: Promise<void>
|
|
128
130
|
protected _isInitialized: boolean = false
|
|
129
131
|
protected _remoteConfigResponsePromise?: Promise<PostHogRemoteConfig | undefined>
|
|
132
|
+
protected _logger: Logger
|
|
130
133
|
|
|
131
134
|
// Abstract methods to be overridden by implementations
|
|
132
135
|
abstract fetch(url: string, options: PostHogFetchOptions): Promise<PostHogFetchResponse>
|
|
@@ -138,34 +141,35 @@ export abstract class PostHogCoreStateless {
|
|
|
138
141
|
abstract getPersistedProperty<T>(key: PostHogPersistedProperty): T | undefined
|
|
139
142
|
abstract setPersistedProperty<T>(key: PostHogPersistedProperty, value: T | null): void
|
|
140
143
|
|
|
141
|
-
constructor(apiKey: string, options
|
|
144
|
+
constructor(apiKey: string, options: PostHogCoreOptions = {}) {
|
|
142
145
|
assert(apiKey, "You must pass your PostHog project's api key.")
|
|
143
146
|
|
|
144
147
|
this.apiKey = apiKey
|
|
145
|
-
this.host = removeTrailingSlash(options
|
|
146
|
-
this.flushAt = options
|
|
147
|
-
this.maxBatchSize = Math.max(this.flushAt, options
|
|
148
|
-
this.maxQueueSize = Math.max(this.flushAt, options
|
|
149
|
-
this.flushInterval = options
|
|
150
|
-
this.preloadFeatureFlags = options
|
|
148
|
+
this.host = removeTrailingSlash(options.host || 'https://us.i.posthog.com')
|
|
149
|
+
this.flushAt = options.flushAt ? Math.max(options.flushAt, 1) : 20
|
|
150
|
+
this.maxBatchSize = Math.max(this.flushAt, options.maxBatchSize ?? 100)
|
|
151
|
+
this.maxQueueSize = Math.max(this.flushAt, options.maxQueueSize ?? 1000)
|
|
152
|
+
this.flushInterval = options.flushInterval ?? 10000
|
|
153
|
+
this.preloadFeatureFlags = options.preloadFeatureFlags ?? true
|
|
151
154
|
// If enable is explicitly set to false we override the optout
|
|
152
|
-
this.defaultOptIn = options
|
|
153
|
-
this.disableSurveys = options
|
|
155
|
+
this.defaultOptIn = options.defaultOptIn ?? true
|
|
156
|
+
this.disableSurveys = options.disableSurveys ?? false
|
|
154
157
|
|
|
155
158
|
this._retryOptions = {
|
|
156
|
-
retryCount: options
|
|
157
|
-
retryDelay: options
|
|
159
|
+
retryCount: options.fetchRetryCount ?? 3,
|
|
160
|
+
retryDelay: options.fetchRetryDelay ?? 3000, // 3 seconds
|
|
158
161
|
retryCheck: isPostHogFetchError,
|
|
159
162
|
}
|
|
160
|
-
this.requestTimeout = options
|
|
161
|
-
this.featureFlagsRequestTimeoutMs = options
|
|
162
|
-
this.remoteConfigRequestTimeoutMs = options
|
|
163
|
-
this.disableGeoip = options
|
|
164
|
-
this.disabled = options
|
|
163
|
+
this.requestTimeout = options.requestTimeout ?? 10000 // 10 seconds
|
|
164
|
+
this.featureFlagsRequestTimeoutMs = options.featureFlagsRequestTimeoutMs ?? 3000 // 3 seconds
|
|
165
|
+
this.remoteConfigRequestTimeoutMs = options.remoteConfigRequestTimeoutMs ?? 3000 // 3 seconds
|
|
166
|
+
this.disableGeoip = options.disableGeoip ?? true
|
|
167
|
+
this.disabled = options.disabled ?? false
|
|
165
168
|
this.historicalMigration = options?.historicalMigration ?? false
|
|
166
169
|
// Init promise allows the derived class to block calls until it is ready
|
|
167
170
|
this._initPromise = Promise.resolve()
|
|
168
171
|
this._isInitialized = true
|
|
172
|
+
this._logger = createLogger('[PostHog]', this.logMsgIfDebug.bind(this))
|
|
169
173
|
this.disableCompression = !isGzipSupported() || (options?.disableCompression ?? false)
|
|
170
174
|
}
|
|
171
175
|
|
|
@@ -177,7 +181,7 @@ export abstract class PostHogCoreStateless {
|
|
|
177
181
|
|
|
178
182
|
protected wrap(fn: () => void): void {
|
|
179
183
|
if (this.disabled) {
|
|
180
|
-
this.
|
|
184
|
+
this._logger.warn('The client is disabled')
|
|
181
185
|
return
|
|
182
186
|
}
|
|
183
187
|
|
|
@@ -245,7 +249,7 @@ export abstract class PostHogCoreStateless {
|
|
|
245
249
|
this.removeDebugCallback?.()
|
|
246
250
|
|
|
247
251
|
if (enabled) {
|
|
248
|
-
const removeDebugCallback = this.on('*', (event, payload) =>
|
|
252
|
+
const removeDebugCallback = this.on('*', (event, payload) => this._logger.info(event, payload))
|
|
249
253
|
this.removeDebugCallback = () => {
|
|
250
254
|
removeDebugCallback()
|
|
251
255
|
this.removeDebugCallback = undefined
|
|
@@ -429,7 +433,7 @@ export abstract class PostHogCoreStateless {
|
|
|
429
433
|
return this.fetchWithRetry(url, fetchOptions, { retryCount: 0 }, this.remoteConfigRequestTimeoutMs)
|
|
430
434
|
.then((response) => response.json() as Promise<PostHogRemoteConfig>)
|
|
431
435
|
.catch((error) => {
|
|
432
|
-
this.
|
|
436
|
+
this._logger.error('Remote config could not be loaded', error)
|
|
433
437
|
this._events.emit('error', error)
|
|
434
438
|
return undefined
|
|
435
439
|
})
|
|
@@ -462,7 +466,7 @@ export abstract class PostHogCoreStateless {
|
|
|
462
466
|
}),
|
|
463
467
|
}
|
|
464
468
|
|
|
465
|
-
this.
|
|
469
|
+
this._logger.info('Flags URL', url)
|
|
466
470
|
|
|
467
471
|
// Don't retry /flags API calls
|
|
468
472
|
return this.fetchWithRetry(url, fetchOptions, { retryCount: 0 }, this.featureFlagsRequestTimeoutMs)
|
|
@@ -731,7 +735,7 @@ export abstract class PostHogCoreStateless {
|
|
|
731
735
|
await this._initPromise
|
|
732
736
|
|
|
733
737
|
if (this.disableSurveys === true) {
|
|
734
|
-
this.
|
|
738
|
+
this._logger.info('Loading surveys is disabled.')
|
|
735
739
|
return []
|
|
736
740
|
}
|
|
737
741
|
|
|
@@ -746,7 +750,7 @@ export abstract class PostHogCoreStateless {
|
|
|
746
750
|
if (response.status !== 200 || !response.json) {
|
|
747
751
|
const msg = `Surveys API could not be loaded: ${response.status}`
|
|
748
752
|
const error = new Error(msg)
|
|
749
|
-
this.
|
|
753
|
+
this._logger.error(error)
|
|
750
754
|
|
|
751
755
|
this._events.emit('error', new Error(msg))
|
|
752
756
|
return undefined
|
|
@@ -755,7 +759,7 @@ export abstract class PostHogCoreStateless {
|
|
|
755
759
|
return response.json() as Promise<SurveyResponse>
|
|
756
760
|
})
|
|
757
761
|
.catch((error) => {
|
|
758
|
-
this.
|
|
762
|
+
this._logger.error('Surveys API could not be loaded', error)
|
|
759
763
|
|
|
760
764
|
this._events.emit('error', error)
|
|
761
765
|
return undefined
|
|
@@ -764,7 +768,7 @@ export abstract class PostHogCoreStateless {
|
|
|
764
768
|
const newSurveys = response?.surveys
|
|
765
769
|
|
|
766
770
|
if (newSurveys) {
|
|
767
|
-
this.
|
|
771
|
+
this._logger.info('Surveys fetched from API: ', JSON.stringify(newSurveys))
|
|
768
772
|
}
|
|
769
773
|
|
|
770
774
|
return newSurveys ?? []
|
|
@@ -819,7 +823,7 @@ export abstract class PostHogCoreStateless {
|
|
|
819
823
|
|
|
820
824
|
if (queue.length >= this.maxQueueSize) {
|
|
821
825
|
queue.shift()
|
|
822
|
-
this.
|
|
826
|
+
this._logger.info('Queue is full, the oldest event is dropped.')
|
|
823
827
|
}
|
|
824
828
|
|
|
825
829
|
queue.push({ message })
|
|
@@ -840,7 +844,7 @@ export abstract class PostHogCoreStateless {
|
|
|
840
844
|
|
|
841
845
|
protected async sendImmediate(type: string, _message: any, options?: PostHogCaptureOptions): Promise<void> {
|
|
842
846
|
if (this.disabled) {
|
|
843
|
-
this.
|
|
847
|
+
this._logger.warn('The client is disabled')
|
|
844
848
|
return
|
|
845
849
|
}
|
|
846
850
|
|
|
@@ -1057,10 +1061,8 @@ export abstract class PostHogCoreStateless {
|
|
|
1057
1061
|
if (isPostHogFetchContentTooLargeError(err) && batchMessages.length > 1) {
|
|
1058
1062
|
// if we get a 413 error, we want to reduce the batch size and try again
|
|
1059
1063
|
this.maxBatchSize = Math.max(1, Math.floor(batchMessages.length / 2))
|
|
1060
|
-
this.
|
|
1061
|
-
|
|
1062
|
-
`Received 413 when sending batch of size ${batchMessages.length}, reducing batch size to ${this.maxBatchSize}`
|
|
1063
|
-
)
|
|
1064
|
+
this._logger.warn(
|
|
1065
|
+
`Received 413 when sending batch of size ${batchMessages.length}, reducing batch size to ${this.maxBatchSize}`
|
|
1064
1066
|
)
|
|
1065
1067
|
// do not persist the queue change, we want to retry the same batch
|
|
1066
1068
|
continue
|
|
@@ -1177,7 +1179,7 @@ export abstract class PostHogCoreStateless {
|
|
|
1177
1179
|
return Promise.race([
|
|
1178
1180
|
new Promise<void>((_, reject) => {
|
|
1179
1181
|
safeSetTimeout(() => {
|
|
1180
|
-
this.
|
|
1182
|
+
this._logger.error('Timed out while shutting down PostHog')
|
|
1181
1183
|
hasTimedOut = true
|
|
1182
1184
|
reject('Timeout while shutting down PostHog. Some events may not have been sent.')
|
|
1183
1185
|
}, shutdownTimeoutMs)
|
|
@@ -1211,10 +1213,8 @@ export abstract class PostHogCoreStateless {
|
|
|
1211
1213
|
*/
|
|
1212
1214
|
async shutdown(shutdownTimeoutMs: number = 30000): Promise<void> {
|
|
1213
1215
|
if (this.shutdownPromise) {
|
|
1214
|
-
this.
|
|
1215
|
-
|
|
1216
|
-
'shutdown() called while already shutting down. shutdown() is meant to be called once before process exit - use flush() for per-request cleanup'
|
|
1217
|
-
)
|
|
1216
|
+
this._logger.warn(
|
|
1217
|
+
'shutdown() called while already shutting down. shutdown() is meant to be called once before process exit - use flush() for per-request cleanup'
|
|
1218
1218
|
)
|
|
1219
1219
|
} else {
|
|
1220
1220
|
this.shutdownPromise = this._shutdown(shutdownTimeoutMs).finally(() => {
|
package/src/posthog-core.ts
CHANGED
|
@@ -462,13 +462,11 @@ export abstract class PostHogCore extends PostHogCoreStateless {
|
|
|
462
462
|
const sessionReplay = response?.sessionRecording
|
|
463
463
|
if (sessionReplay) {
|
|
464
464
|
this.setPersistedProperty(PostHogPersistedProperty.SessionReplay, sessionReplay)
|
|
465
|
-
this.
|
|
466
|
-
console.log('PostHog Debug', `Session replay config from ${source}: `, JSON.stringify(sessionReplay))
|
|
467
|
-
)
|
|
465
|
+
this._logger.info(`Session replay config from ${source}: `, JSON.stringify(sessionReplay))
|
|
468
466
|
} else if (typeof sessionReplay === 'boolean' && sessionReplay === false) {
|
|
469
467
|
// if session replay is disabled, we don't need to cache it
|
|
470
468
|
// we need to check for this because the response might be undefined (/flags does not return sessionRecording yet)
|
|
471
|
-
this.
|
|
469
|
+
this._logger.info(`Session replay config from ${source} disabled.`)
|
|
472
470
|
this.setPersistedProperty(PostHogPersistedProperty.SessionReplay, null)
|
|
473
471
|
}
|
|
474
472
|
}
|
|
@@ -480,16 +478,14 @@ export abstract class PostHogCore extends PostHogCoreStateless {
|
|
|
480
478
|
PostHogPersistedProperty.RemoteConfig
|
|
481
479
|
)
|
|
482
480
|
|
|
483
|
-
this.
|
|
481
|
+
this._logger.info('Cached remote config: ', JSON.stringify(remoteConfig))
|
|
484
482
|
|
|
485
483
|
return super.getRemoteConfig().then((response) => {
|
|
486
484
|
if (response) {
|
|
487
485
|
const remoteConfigWithoutSurveys = { ...response }
|
|
488
486
|
delete remoteConfigWithoutSurveys.surveys
|
|
489
487
|
|
|
490
|
-
this.
|
|
491
|
-
console.log('PostHog Debug', 'Fetched remote config: ', JSON.stringify(remoteConfigWithoutSurveys))
|
|
492
|
-
)
|
|
488
|
+
this._logger.info('Fetched remote config: ', JSON.stringify(remoteConfigWithoutSurveys))
|
|
493
489
|
|
|
494
490
|
if (this.disableSurveys === false) {
|
|
495
491
|
const surveys = response.surveys
|
|
@@ -498,12 +494,10 @@ export abstract class PostHogCore extends PostHogCoreStateless {
|
|
|
498
494
|
|
|
499
495
|
if (!Array.isArray(surveys)) {
|
|
500
496
|
// If surveys is not an array, it means there are no surveys (its a boolean instead)
|
|
501
|
-
this.
|
|
497
|
+
this._logger.info('There are no surveys.')
|
|
502
498
|
hasSurveys = false
|
|
503
499
|
} else {
|
|
504
|
-
this.
|
|
505
|
-
console.log('PostHog Debug', 'Surveys fetched from remote config: ', JSON.stringify(surveys))
|
|
506
|
-
)
|
|
500
|
+
this._logger.info('Surveys fetched from remote config: ', JSON.stringify(surveys))
|
|
507
501
|
}
|
|
508
502
|
|
|
509
503
|
if (hasSurveys) {
|
|
@@ -530,7 +524,7 @@ export abstract class PostHogCore extends PostHogCoreStateless {
|
|
|
530
524
|
// resetting flags to empty object
|
|
531
525
|
this.setKnownFeatureFlagDetails({ flags: {} })
|
|
532
526
|
|
|
533
|
-
this.
|
|
527
|
+
this._logger.warn('Remote config has no feature flags, will not load feature flags.')
|
|
534
528
|
} else if (this.preloadFeatureFlags !== false) {
|
|
535
529
|
this.reloadFeatureFlags()
|
|
536
530
|
}
|
|
@@ -592,9 +586,7 @@ export abstract class PostHogCore extends PostHogCoreStateless {
|
|
|
592
586
|
if (res.errorsWhileComputingFlags) {
|
|
593
587
|
// if not all flags were computed, we upsert flags instead of replacing them
|
|
594
588
|
const currentFlagDetails = this.getKnownFeatureFlagDetails()
|
|
595
|
-
this.
|
|
596
|
-
console.log('PostHog Debug', 'Cached feature flags: ', JSON.stringify(currentFlagDetails))
|
|
597
|
-
)
|
|
589
|
+
this._logger.info('Cached feature flags: ', JSON.stringify(currentFlagDetails))
|
|
598
590
|
|
|
599
591
|
newFeatureFlagDetails = {
|
|
600
592
|
...res,
|
|
@@ -823,7 +815,7 @@ export abstract class PostHogCore extends PostHogCoreStateless {
|
|
|
823
815
|
.catch((e) => {
|
|
824
816
|
options?.cb?.(e, undefined)
|
|
825
817
|
if (!options?.cb) {
|
|
826
|
-
this.
|
|
818
|
+
this._logger.info('Error reloading feature flags', e)
|
|
827
819
|
}
|
|
828
820
|
})
|
|
829
821
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './spawn-local'
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process'
|
|
2
|
+
import { resolveBinaryPath } from './utils'
|
|
3
|
+
|
|
4
|
+
export async function spawnLocal(
|
|
5
|
+
binaryName: string,
|
|
6
|
+
args: string[],
|
|
7
|
+
options: {
|
|
8
|
+
env: NodeJS.ProcessEnv
|
|
9
|
+
stdio: 'inherit' | 'ignore'
|
|
10
|
+
// We start traversing the file system tree from this directory and we go up until we find the binary
|
|
11
|
+
resolveFrom: string
|
|
12
|
+
cwd: string
|
|
13
|
+
onBinaryFound: (binaryLocation: string) => void
|
|
14
|
+
}
|
|
15
|
+
): Promise<void> {
|
|
16
|
+
let binaryLocation
|
|
17
|
+
try {
|
|
18
|
+
binaryLocation = resolveBinaryPath(options.env.PATH ?? '', options.resolveFrom, binaryName)
|
|
19
|
+
options.onBinaryFound(binaryLocation)
|
|
20
|
+
} catch (e) {
|
|
21
|
+
console.error(e)
|
|
22
|
+
throw new Error(
|
|
23
|
+
`Binary ${binaryName} not found. Make sure postinstall script was allowed if it installs the binary`
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const child = spawn(binaryLocation, [...args], {
|
|
28
|
+
shell: true,
|
|
29
|
+
stdio: options?.stdio ?? 'inherit',
|
|
30
|
+
env: options.env,
|
|
31
|
+
cwd: options.cwd,
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
await new Promise<void>((resolve, reject) => {
|
|
35
|
+
child.on('close', (code) => {
|
|
36
|
+
if (code === 0) {
|
|
37
|
+
resolve()
|
|
38
|
+
} else {
|
|
39
|
+
reject(new Error(`Command failed with code ${code}`))
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
child.on('error', (error) => {
|
|
44
|
+
reject(error)
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { buildLocalBinaryPaths } from './utils'
|
|
2
|
+
|
|
3
|
+
describe('buildLocalBinaryPaths', () => {
|
|
4
|
+
it('generates possible binary locations', () => {
|
|
5
|
+
const cwd = '/home/user'
|
|
6
|
+
const result = buildLocalBinaryPaths(cwd)
|
|
7
|
+
expect(result.includes('/home/user/node_modules/.bin')).toBe(true)
|
|
8
|
+
expect(result.includes('/home/node_modules/.bin')).toBe(true)
|
|
9
|
+
expect(result.includes('/node_modules/.bin')).toBe(true)
|
|
10
|
+
})
|
|
11
|
+
})
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
import fs from 'node:fs'
|
|
3
|
+
|
|
4
|
+
const getLocalPaths = (startPath: string): string[] => {
|
|
5
|
+
const paths: string[] = []
|
|
6
|
+
let currentPath = startPath
|
|
7
|
+
|
|
8
|
+
while (true) {
|
|
9
|
+
paths.push(currentPath)
|
|
10
|
+
const parentPath = path.resolve(currentPath, '..')
|
|
11
|
+
|
|
12
|
+
// If we've reached the root directory, stop
|
|
13
|
+
if (parentPath === currentPath) {
|
|
14
|
+
break
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
currentPath = parentPath
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return paths
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const buildLocalBinaryPaths = (cwd: string): string[] => {
|
|
24
|
+
const localPaths = getLocalPaths(path.resolve(cwd)).map((localPath: string) =>
|
|
25
|
+
path.join(localPath, 'node_modules/.bin')
|
|
26
|
+
)
|
|
27
|
+
return localPaths
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function resolveBinaryPath(envPath: string, cwd: string, binName: string): string {
|
|
31
|
+
const envLocations = envPath.split(path.delimiter)
|
|
32
|
+
const localLocations = buildLocalBinaryPaths(cwd)
|
|
33
|
+
const directories = [...new Set([...localLocations, ...envLocations])]
|
|
34
|
+
for (const directory of directories) {
|
|
35
|
+
const binaryPath = path.join(directory, binName)
|
|
36
|
+
if (fs.existsSync(binaryPath)) {
|
|
37
|
+
return binaryPath
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
throw new Error(`Binary ${binName} not found`)
|
|
41
|
+
}
|
|
@@ -36,12 +36,10 @@ export const delay = (ms: number): Promise<void> => {
|
|
|
36
36
|
|
|
37
37
|
export const createMockLogger = (): Logger => {
|
|
38
38
|
return {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
critical: jest.fn(),
|
|
44
|
-
uninitializedWarning: jest.fn(),
|
|
39
|
+
info: jest.fn((...args) => console.log(...args)),
|
|
40
|
+
warn: jest.fn((...args) => console.warn(...args)),
|
|
41
|
+
error: jest.fn((...args) => console.error(...args)),
|
|
42
|
+
critical: jest.fn((...args) => console.error(...args)),
|
|
45
43
|
createLogger: createMockLogger,
|
|
46
44
|
}
|
|
47
45
|
}
|
package/src/types.ts
CHANGED
|
@@ -509,12 +509,10 @@ export type ActionStepType = {
|
|
|
509
509
|
}
|
|
510
510
|
|
|
511
511
|
export type Logger = {
|
|
512
|
-
_log: (level: 'log' | 'warn' | 'error', ...args: any[]) => void
|
|
513
512
|
info: (...args: any[]) => void
|
|
514
513
|
warn: (...args: any[]) => void
|
|
515
514
|
error: (...args: any[]) => void
|
|
516
515
|
critical: (...args: any[]) => void
|
|
517
|
-
uninitializedWarning: (methodName: string) => void
|
|
518
516
|
createLogger: (prefix: string) => Logger
|
|
519
517
|
}
|
|
520
518
|
|