@posthog/plugin-utils 1.1.2 → 1.2.0
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/LICENSE +2 -2
- package/dist/chunk-ids.d.ts +46 -0
- package/dist/chunk-ids.d.ts.map +1 -0
- package/dist/chunk-ids.js +78 -0
- package/dist/chunk-ids.mjs +32 -0
- package/dist/cli.d.ts +19 -3
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +36 -9
- package/dist/cli.mjs +34 -10
- package/dist/config.d.ts +14 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +11 -1
- package/dist/config.mjs +11 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -0
- package/dist/index.mjs +1 -0
- package/dist/spawn-local.d.ts +14 -0
- package/dist/spawn-local.d.ts.map +1 -1
- package/dist/spawn-local.js +33 -1
- package/dist/spawn-local.mjs +30 -1
- package/package.json +1 -1
- package/src/chunk-ids.ts +96 -0
- package/src/cli.ts +80 -15
- package/src/config.ts +28 -0
- package/src/index.ts +1 -0
- package/src/spawn-local.ts +40 -0
package/LICENSE
CHANGED
|
@@ -327,12 +327,12 @@ SOFTWARE.
|
|
|
327
327
|
|
|
328
328
|
---
|
|
329
329
|
|
|
330
|
-
Some files in this codebase contain code from MCPCat/mcpcat-typescript-sdk.
|
|
330
|
+
Some files in this codebase contain code from agentcathq/agentcat-typescript-sdk (formerly MCPCat/mcpcat-typescript-sdk).
|
|
331
331
|
In such cases it is explicitly stated in the file header. This license only applies to the relevant code in such cases.
|
|
332
332
|
|
|
333
333
|
MIT License
|
|
334
334
|
|
|
335
|
-
Copyright (c) 2025 MCPcat
|
|
335
|
+
Copyright (c) 2025 AgentCat, Inc. (formerly MCPcat)
|
|
336
336
|
|
|
337
337
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
338
338
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the minified chunk-id IIFE the SDK reads at runtime to map an
|
|
3
|
+
* error's stack back to a chunk id. With `releaseId`, the snippet also pins
|
|
4
|
+
* `_posthogReleaseId` on the global so the SDK emits the release on every
|
|
5
|
+
* exception (first write wins, so the first loaded chunk decides).
|
|
6
|
+
*
|
|
7
|
+
* Byte-for-byte contract with posthog-cli (`CODE_SNIPPET_TEMPLATE` and
|
|
8
|
+
* `CODE_SNIPPET_WITH_RELEASE_TEMPLATE` in cli/src/sourcemaps/constant.rs): the
|
|
9
|
+
* CLI recognizes and removes previously injected snippets by exact substring
|
|
10
|
+
* match, so any change here must ship in the CLI constants too. Ids are
|
|
11
|
+
* JSON-encoded for the same reason the CLI encodes them: an id carrying a quote
|
|
12
|
+
* would otherwise break out of the string literal. Covered by a parity test in
|
|
13
|
+
* chunk-ids.spec.ts.
|
|
14
|
+
*/
|
|
15
|
+
export declare function createChunkIdSnippet(chunkId: string, releaseId?: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Returns the comment posthog-cli discovers chunk ids from at upload time
|
|
18
|
+
* (`CHUNKID_COMMENT_PREFIX` in cli/src/sourcemaps/constant.rs), including the
|
|
19
|
+
* leading newline the CLI's exact-match removal expects.
|
|
20
|
+
*/
|
|
21
|
+
export declare function createChunkIdComment(chunkId: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Mints a fresh random chunk id: a new id per chunk, per build.
|
|
24
|
+
*/
|
|
25
|
+
export declare function createChunkId(): string;
|
|
26
|
+
/**
|
|
27
|
+
* Derives a chunk id from the chunk's own bytes (UUIDv5), so identical code keeps its id across
|
|
28
|
+
* rebuilds and re-uploads dedupe against the symbol set already stored. Used in event release
|
|
29
|
+
* mode, where the release travels inside the chunk instead of being bound to the symbol set, so
|
|
30
|
+
* a per-build random id would create a fresh symbol set on every build.
|
|
31
|
+
*
|
|
32
|
+
* The id is content-addressed over what `renderChunk` sees. posthog-cli derives its own ids from
|
|
33
|
+
* the file on disk, which by then also carries the trailing `sourceMappingURL` comment, so the
|
|
34
|
+
* same chunk gets a different (but equally stable) id depending on which tool injected it.
|
|
35
|
+
*/
|
|
36
|
+
export declare function createStableChunkId(code: string): string;
|
|
37
|
+
/**
|
|
38
|
+
* Extracts the chunk id from source that already carries one, using the same
|
|
39
|
+
* detection the CLI uses: a `//# chunkId=` comment at the start of a line.
|
|
40
|
+
* The injected IIFE is deliberately not matched — its marker text can appear
|
|
41
|
+
* inside string literals of bundled code (docs strings, bundled PostHog
|
|
42
|
+
* tooling), and the CLI only ever reads the line-anchored comment. Keeping the
|
|
43
|
+
* two in sync guarantees this returning an id ⟺ the CLI finding one.
|
|
44
|
+
*/
|
|
45
|
+
export declare function determineChunkIdFromSource(code: string): string | undefined;
|
|
46
|
+
//# sourceMappingURL=chunk-ids.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunk-ids.d.ts","sourceRoot":"","sources":["../src/chunk-ids.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAMhF;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAOD;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAUxD;AASD;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAa3E"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.d = (exports1, definition)=>{
|
|
5
|
+
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: definition[key]
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
})();
|
|
11
|
+
(()=>{
|
|
12
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
13
|
+
})();
|
|
14
|
+
(()=>{
|
|
15
|
+
__webpack_require__.r = (exports1)=>{
|
|
16
|
+
if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
17
|
+
value: 'Module'
|
|
18
|
+
});
|
|
19
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
20
|
+
value: true
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
})();
|
|
24
|
+
var __webpack_exports__ = {};
|
|
25
|
+
__webpack_require__.r(__webpack_exports__);
|
|
26
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
27
|
+
createStableChunkId: ()=>createStableChunkId,
|
|
28
|
+
createChunkIdSnippet: ()=>createChunkIdSnippet,
|
|
29
|
+
createChunkId: ()=>createChunkId,
|
|
30
|
+
createChunkIdComment: ()=>createChunkIdComment,
|
|
31
|
+
determineChunkIdFromSource: ()=>determineChunkIdFromSource
|
|
32
|
+
});
|
|
33
|
+
const external_crypto_namespaceObject = require("crypto");
|
|
34
|
+
function createChunkIdSnippet(chunkId, releaseId) {
|
|
35
|
+
const chunk = JSON.stringify(chunkId);
|
|
36
|
+
if (void 0 === releaseId) return `!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._posthogChunkIds=e._posthogChunkIds||{},e._posthogChunkIds[n]=${chunk})}catch(e){}}();`;
|
|
37
|
+
return `!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{};e._posthogReleaseId=e._posthogReleaseId||${JSON.stringify(releaseId)};var n=(new e.Error).stack;n&&(e._posthogChunkIds=e._posthogChunkIds||{},e._posthogChunkIds[n]=${chunk})}catch(e){}}();`;
|
|
38
|
+
}
|
|
39
|
+
function createChunkIdComment(chunkId) {
|
|
40
|
+
return `\n//# chunkId=${chunkId}`;
|
|
41
|
+
}
|
|
42
|
+
function createChunkId() {
|
|
43
|
+
return external_crypto_namespaceObject.randomUUID();
|
|
44
|
+
}
|
|
45
|
+
const CHUNK_ID_NAMESPACE = '0e9b3c7a-5d1f-42a8-b6c4-e2d0f8a17593';
|
|
46
|
+
function createStableChunkId(code) {
|
|
47
|
+
const namespace = Buffer.from(CHUNK_ID_NAMESPACE.replace(/-/g, ''), 'hex');
|
|
48
|
+
const hash = external_crypto_namespaceObject.createHash('sha1').update(namespace).update(code, 'utf8').digest();
|
|
49
|
+
hash[6] = 0x0f & hash[6] | 0x50;
|
|
50
|
+
hash[8] = 0x3f & hash[8] | 0x80;
|
|
51
|
+
const hex = hash.subarray(0, 16).toString('hex');
|
|
52
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
|
|
53
|
+
}
|
|
54
|
+
const COMMENT_REGEX = /^\/\/# chunkId=(\S{1,128})/;
|
|
55
|
+
const COMMENT_MARKER = '//# chunkId=';
|
|
56
|
+
const WINDOW = 256;
|
|
57
|
+
function determineChunkIdFromSource(code) {
|
|
58
|
+
for(let at = code.indexOf(COMMENT_MARKER); -1 !== at; at = code.indexOf(COMMENT_MARKER, at + COMMENT_MARKER.length)){
|
|
59
|
+
if (0 !== at && '\n' !== code[at - 1]) continue;
|
|
60
|
+
const match = COMMENT_REGEX.exec(code.slice(at, at + WINDOW));
|
|
61
|
+
if (match) return match[1];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
exports.createChunkId = __webpack_exports__.createChunkId;
|
|
65
|
+
exports.createChunkIdComment = __webpack_exports__.createChunkIdComment;
|
|
66
|
+
exports.createChunkIdSnippet = __webpack_exports__.createChunkIdSnippet;
|
|
67
|
+
exports.createStableChunkId = __webpack_exports__.createStableChunkId;
|
|
68
|
+
exports.determineChunkIdFromSource = __webpack_exports__.determineChunkIdFromSource;
|
|
69
|
+
for(var __webpack_i__ in __webpack_exports__)if (-1 === [
|
|
70
|
+
"createChunkId",
|
|
71
|
+
"createChunkIdComment",
|
|
72
|
+
"createChunkIdSnippet",
|
|
73
|
+
"createStableChunkId",
|
|
74
|
+
"determineChunkIdFromSource"
|
|
75
|
+
].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
|
|
76
|
+
Object.defineProperty(exports, '__esModule', {
|
|
77
|
+
value: true
|
|
78
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "crypto";
|
|
2
|
+
function createChunkIdSnippet(chunkId, releaseId) {
|
|
3
|
+
const chunk = JSON.stringify(chunkId);
|
|
4
|
+
if (void 0 === releaseId) return `!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._posthogChunkIds=e._posthogChunkIds||{},e._posthogChunkIds[n]=${chunk})}catch(e){}}();`;
|
|
5
|
+
return `!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{};e._posthogReleaseId=e._posthogReleaseId||${JSON.stringify(releaseId)};var n=(new e.Error).stack;n&&(e._posthogChunkIds=e._posthogChunkIds||{},e._posthogChunkIds[n]=${chunk})}catch(e){}}();`;
|
|
6
|
+
}
|
|
7
|
+
function createChunkIdComment(chunkId) {
|
|
8
|
+
return `\n//# chunkId=${chunkId}`;
|
|
9
|
+
}
|
|
10
|
+
function createChunkId() {
|
|
11
|
+
return randomUUID();
|
|
12
|
+
}
|
|
13
|
+
const CHUNK_ID_NAMESPACE = '0e9b3c7a-5d1f-42a8-b6c4-e2d0f8a17593';
|
|
14
|
+
function createStableChunkId(code) {
|
|
15
|
+
const namespace = Buffer.from(CHUNK_ID_NAMESPACE.replace(/-/g, ''), 'hex');
|
|
16
|
+
const hash = createHash('sha1').update(namespace).update(code, 'utf8').digest();
|
|
17
|
+
hash[6] = 0x0f & hash[6] | 0x50;
|
|
18
|
+
hash[8] = 0x3f & hash[8] | 0x80;
|
|
19
|
+
const hex = hash.subarray(0, 16).toString('hex');
|
|
20
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
|
|
21
|
+
}
|
|
22
|
+
const COMMENT_REGEX = /^\/\/# chunkId=(\S{1,128})/;
|
|
23
|
+
const COMMENT_MARKER = '//# chunkId=';
|
|
24
|
+
const WINDOW = 256;
|
|
25
|
+
function determineChunkIdFromSource(code) {
|
|
26
|
+
for(let at = code.indexOf(COMMENT_MARKER); -1 !== at; at = code.indexOf(COMMENT_MARKER, at + COMMENT_MARKER.length)){
|
|
27
|
+
if (0 !== at && '\n' !== code[at - 1]) continue;
|
|
28
|
+
const match = COMMENT_REGEX.exec(code.slice(at, at + WINDOW));
|
|
29
|
+
if (match) return match[1];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export { createChunkId, createChunkIdComment, createChunkIdSnippet, createStableChunkId, determineChunkIdFromSource };
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import { ResolvedPluginConfig } from './config';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* `process` injects chunk ids into the built files on disk and uploads them.
|
|
4
|
+
* `upload` only reads the files, expecting chunk ids to already be present
|
|
5
|
+
* (e.g. injected in-memory by a bundler plugin before the files were written).
|
|
6
|
+
*/
|
|
7
|
+
export type SourcemapCliCommand = 'process' | 'upload';
|
|
8
|
+
/**
|
|
9
|
+
* Build CLI arguments for `posthog-cli sourcemap <command>`.
|
|
4
10
|
*/
|
|
5
11
|
export declare function buildSourcemapCliArgs(config: ResolvedPluginConfig, mode: {
|
|
6
12
|
stdin: true;
|
|
7
13
|
} | {
|
|
8
14
|
directory: string;
|
|
9
|
-
}): string[];
|
|
15
|
+
}, command?: SourcemapCliCommand): string[];
|
|
10
16
|
/**
|
|
11
17
|
* Build environment variables for CLI invocation.
|
|
12
18
|
* Plugin config values override any existing process.env values.
|
|
@@ -15,9 +21,19 @@ export declare function buildCliEnv(config: ResolvedPluginConfig): NodeJS.Proces
|
|
|
15
21
|
/**
|
|
16
22
|
* Spawn the PostHog CLI for sourcemap processing via stdin (file list).
|
|
17
23
|
*/
|
|
18
|
-
export declare function runSourcemapCli(config: ResolvedPluginConfig, options: {
|
|
24
|
+
export declare function runSourcemapCli(config: ResolvedPluginConfig, options: ({
|
|
19
25
|
filePaths: string[];
|
|
20
26
|
} | {
|
|
21
27
|
directory: string;
|
|
28
|
+
}) & {
|
|
29
|
+
command?: SourcemapCliCommand;
|
|
22
30
|
}): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Resolves the release this build belongs to, creating it if it doesn't exist yet, and returns its
|
|
33
|
+
* id for injection into the chunks. Returns undefined when nothing identifies a release: the CLI
|
|
34
|
+
* prints nothing and exits zero for a build with no release name/version and no git or CI
|
|
35
|
+
* metadata to derive them from, which is a build that ships without a release rather than a
|
|
36
|
+
* failure.
|
|
37
|
+
*/
|
|
38
|
+
export declare function resolveReleaseId(config: ResolvedPluginConfig): Promise<string | undefined>;
|
|
23
39
|
//# sourceMappingURL=cli.d.ts.map
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAA;AAG/C;;GAEG;AACH,wBAAgB,qBAAqB,CACjC,MAAM,EAAE,oBAAoB,EAC5B,IAAI,EAAE;IAAE,KAAK,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAA;AAG/C;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,QAAQ,CAAA;AAwBtD;;GAEG;AACH,wBAAgB,qBAAqB,CACjC,MAAM,EAAE,oBAAoB,EAC5B,IAAI,EAAE;IAAE,KAAK,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,EAC7C,OAAO,GAAE,mBAA+B,GACzC,MAAM,EAAE,CA8BV;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAAC,UAAU,CAW3E;AAED;;GAEG;AACH,wBAAsB,eAAe,CACjC,MAAM,EAAE,oBAAoB,EAC5B,OAAO,EAAE,CAAC;IAAE,SAAS,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG;IAAE,OAAO,CAAC,EAAE,mBAAmB,CAAA;CAAE,GAC/F,OAAO,CAAC,IAAI,CAAC,CAgBf;AAED;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAuBhG"}
|
package/dist/cli.js
CHANGED
|
@@ -24,22 +24,29 @@ var __webpack_require__ = {};
|
|
|
24
24
|
var __webpack_exports__ = {};
|
|
25
25
|
__webpack_require__.r(__webpack_exports__);
|
|
26
26
|
__webpack_require__.d(__webpack_exports__, {
|
|
27
|
-
|
|
27
|
+
resolveReleaseId: ()=>resolveReleaseId,
|
|
28
28
|
buildCliEnv: ()=>buildCliEnv,
|
|
29
|
+
runSourcemapCli: ()=>runSourcemapCli,
|
|
29
30
|
buildSourcemapCliArgs: ()=>buildSourcemapCliArgs
|
|
30
31
|
});
|
|
31
32
|
const external_spawn_local_js_namespaceObject = require("./spawn-local.js");
|
|
32
|
-
function
|
|
33
|
+
function buildReleaseArgs(config) {
|
|
34
|
+
const args = [];
|
|
35
|
+
if (config.sourcemaps.releaseName) args.push('--release-name', config.sourcemaps.releaseName);
|
|
36
|
+
if (config.sourcemaps.releaseVersion) args.push('--release-version', config.sourcemaps.releaseVersion);
|
|
37
|
+
if (void 0 !== config.sourcemaps.build && '' !== config.sourcemaps.build) args.push('--build', config.sourcemaps.build);
|
|
38
|
+
return args;
|
|
39
|
+
}
|
|
40
|
+
function buildSourcemapCliArgs(config, mode, command = 'process') {
|
|
33
41
|
const args = [
|
|
34
42
|
'sourcemap',
|
|
35
|
-
|
|
43
|
+
command
|
|
36
44
|
];
|
|
37
45
|
if ('stdin' in mode) args.push('--stdin');
|
|
38
46
|
else args.push('--directory', mode.directory);
|
|
39
|
-
|
|
40
|
-
if (config.sourcemaps.
|
|
41
|
-
if (
|
|
42
|
-
if (config.sourcemaps.deleteAfterUpload) args.push('--delete-after');
|
|
47
|
+
args.push(...buildReleaseArgs(config));
|
|
48
|
+
if ('event' === config.sourcemaps.releaseMode) args.push('--release-mode', 'event');
|
|
49
|
+
if (config.sourcemaps.deleteAfterUpload && 'process' === command) args.push('--delete-after');
|
|
43
50
|
if (config.sourcemaps.batchSize) args.push('--batch-size', config.sourcemaps.batchSize.toString());
|
|
44
51
|
return args;
|
|
45
52
|
}
|
|
@@ -49,7 +56,8 @@ function buildCliEnv(config) {
|
|
|
49
56
|
RUST_LOG: `posthog_cli=${config.logLevel}`,
|
|
50
57
|
POSTHOG_CLI_HOST: config.host,
|
|
51
58
|
POSTHOG_CLI_API_KEY: config.personalApiKey,
|
|
52
|
-
POSTHOG_CLI_PROJECT_ID: config.projectId
|
|
59
|
+
POSTHOG_CLI_PROJECT_ID: config.projectId,
|
|
60
|
+
POSTHOG_RELEASE_MODE: config.sourcemaps.releaseMode
|
|
53
61
|
};
|
|
54
62
|
}
|
|
55
63
|
async function runSourcemapCli(config, options) {
|
|
@@ -58,7 +66,7 @@ async function runSourcemapCli(config, options) {
|
|
|
58
66
|
} : {
|
|
59
67
|
directory: options.directory
|
|
60
68
|
};
|
|
61
|
-
const args = buildSourcemapCliArgs(config, mode);
|
|
69
|
+
const args = buildSourcemapCliArgs(config, mode, options.command ?? 'process');
|
|
62
70
|
const env = buildCliEnv(config);
|
|
63
71
|
const spawnOptions = {
|
|
64
72
|
cwd: process.cwd(),
|
|
@@ -68,12 +76,31 @@ async function runSourcemapCli(config, options) {
|
|
|
68
76
|
if ('filePaths' in options) spawnOptions.stdin = options.filePaths.join('\n') + '\n';
|
|
69
77
|
await (0, external_spawn_local_js_namespaceObject.spawnLocal)(config.cliBinaryPath, args, spawnOptions);
|
|
70
78
|
}
|
|
79
|
+
async function resolveReleaseId(config) {
|
|
80
|
+
const args = [
|
|
81
|
+
'release',
|
|
82
|
+
'resolve',
|
|
83
|
+
...buildReleaseArgs(config)
|
|
84
|
+
];
|
|
85
|
+
const { code, stdout, stderr } = await (0, external_spawn_local_js_namespaceObject.spawnLocalCapture)(config.cliBinaryPath, args, {
|
|
86
|
+
cwd: process.cwd(),
|
|
87
|
+
env: buildCliEnv(config)
|
|
88
|
+
});
|
|
89
|
+
if (0 !== code) {
|
|
90
|
+
const hint = /unrecognized subcommand|unexpected argument/.test(stderr) ? ` Event release mode needs a posthog-cli with 'release resolve' (${config.cliBinaryPath}).` : '';
|
|
91
|
+
throw new Error(`posthog-cli release resolve failed with code ${code}.${hint}\n${stderr.trim()}`);
|
|
92
|
+
}
|
|
93
|
+
if (stderr.trim()) process.stderr.write(stderr);
|
|
94
|
+
return stdout.trim() || void 0;
|
|
95
|
+
}
|
|
71
96
|
exports.buildCliEnv = __webpack_exports__.buildCliEnv;
|
|
72
97
|
exports.buildSourcemapCliArgs = __webpack_exports__.buildSourcemapCliArgs;
|
|
98
|
+
exports.resolveReleaseId = __webpack_exports__.resolveReleaseId;
|
|
73
99
|
exports.runSourcemapCli = __webpack_exports__.runSourcemapCli;
|
|
74
100
|
for(var __webpack_i__ in __webpack_exports__)if (-1 === [
|
|
75
101
|
"buildCliEnv",
|
|
76
102
|
"buildSourcemapCliArgs",
|
|
103
|
+
"resolveReleaseId",
|
|
77
104
|
"runSourcemapCli"
|
|
78
105
|
].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
|
|
79
106
|
Object.defineProperty(exports, '__esModule', {
|
package/dist/cli.mjs
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
|
-
import { spawnLocal } from "./spawn-local.mjs";
|
|
2
|
-
function
|
|
1
|
+
import { spawnLocal, spawnLocalCapture } from "./spawn-local.mjs";
|
|
2
|
+
function buildReleaseArgs(config) {
|
|
3
|
+
const args = [];
|
|
4
|
+
if (config.sourcemaps.releaseName) args.push('--release-name', config.sourcemaps.releaseName);
|
|
5
|
+
if (config.sourcemaps.releaseVersion) args.push('--release-version', config.sourcemaps.releaseVersion);
|
|
6
|
+
if (void 0 !== config.sourcemaps.build && '' !== config.sourcemaps.build) args.push('--build', config.sourcemaps.build);
|
|
7
|
+
return args;
|
|
8
|
+
}
|
|
9
|
+
function buildSourcemapCliArgs(config, mode, command = 'process') {
|
|
3
10
|
const args = [
|
|
4
11
|
'sourcemap',
|
|
5
|
-
|
|
12
|
+
command
|
|
6
13
|
];
|
|
7
14
|
if ('stdin' in mode) args.push('--stdin');
|
|
8
15
|
else args.push('--directory', mode.directory);
|
|
9
|
-
|
|
10
|
-
if (config.sourcemaps.
|
|
11
|
-
if (
|
|
12
|
-
if (config.sourcemaps.deleteAfterUpload) args.push('--delete-after');
|
|
16
|
+
args.push(...buildReleaseArgs(config));
|
|
17
|
+
if ('event' === config.sourcemaps.releaseMode) args.push('--release-mode', 'event');
|
|
18
|
+
if (config.sourcemaps.deleteAfterUpload && 'process' === command) args.push('--delete-after');
|
|
13
19
|
if (config.sourcemaps.batchSize) args.push('--batch-size', config.sourcemaps.batchSize.toString());
|
|
14
20
|
return args;
|
|
15
21
|
}
|
|
@@ -19,7 +25,8 @@ function buildCliEnv(config) {
|
|
|
19
25
|
RUST_LOG: `posthog_cli=${config.logLevel}`,
|
|
20
26
|
POSTHOG_CLI_HOST: config.host,
|
|
21
27
|
POSTHOG_CLI_API_KEY: config.personalApiKey,
|
|
22
|
-
POSTHOG_CLI_PROJECT_ID: config.projectId
|
|
28
|
+
POSTHOG_CLI_PROJECT_ID: config.projectId,
|
|
29
|
+
POSTHOG_RELEASE_MODE: config.sourcemaps.releaseMode
|
|
23
30
|
};
|
|
24
31
|
}
|
|
25
32
|
async function runSourcemapCli(config, options) {
|
|
@@ -28,7 +35,7 @@ async function runSourcemapCli(config, options) {
|
|
|
28
35
|
} : {
|
|
29
36
|
directory: options.directory
|
|
30
37
|
};
|
|
31
|
-
const args = buildSourcemapCliArgs(config, mode);
|
|
38
|
+
const args = buildSourcemapCliArgs(config, mode, options.command ?? 'process');
|
|
32
39
|
const env = buildCliEnv(config);
|
|
33
40
|
const spawnOptions = {
|
|
34
41
|
cwd: process.cwd(),
|
|
@@ -38,4 +45,21 @@ async function runSourcemapCli(config, options) {
|
|
|
38
45
|
if ('filePaths' in options) spawnOptions.stdin = options.filePaths.join('\n') + '\n';
|
|
39
46
|
await spawnLocal(config.cliBinaryPath, args, spawnOptions);
|
|
40
47
|
}
|
|
41
|
-
|
|
48
|
+
async function resolveReleaseId(config) {
|
|
49
|
+
const args = [
|
|
50
|
+
'release',
|
|
51
|
+
'resolve',
|
|
52
|
+
...buildReleaseArgs(config)
|
|
53
|
+
];
|
|
54
|
+
const { code, stdout, stderr } = await spawnLocalCapture(config.cliBinaryPath, args, {
|
|
55
|
+
cwd: process.cwd(),
|
|
56
|
+
env: buildCliEnv(config)
|
|
57
|
+
});
|
|
58
|
+
if (0 !== code) {
|
|
59
|
+
const hint = /unrecognized subcommand|unexpected argument/.test(stderr) ? ` Event release mode needs a posthog-cli with 'release resolve' (${config.cliBinaryPath}).` : '';
|
|
60
|
+
throw new Error(`posthog-cli release resolve failed with code ${code}.${hint}\n${stderr.trim()}`);
|
|
61
|
+
}
|
|
62
|
+
if (stderr.trim()) process.stderr.write(stderr);
|
|
63
|
+
return stdout.trim() || void 0;
|
|
64
|
+
}
|
|
65
|
+
export { buildCliEnv, buildSourcemapCliArgs, resolveReleaseId, runSourcemapCli };
|
package/dist/config.d.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent';
|
|
2
|
+
/**
|
|
3
|
+
* How an exception gets associated with a release, mirroring posthog-cli's `--release-mode`.
|
|
4
|
+
*
|
|
5
|
+
* `symbol-set` binds the uploaded symbol sets to a release. `event` leaves them unbound and
|
|
6
|
+
* injects the release id into each chunk instead, so the release is resolved per exception.
|
|
7
|
+
*/
|
|
8
|
+
export type ReleaseMode = 'symbol-set' | 'event';
|
|
2
9
|
export interface PluginConfig {
|
|
3
10
|
personalApiKey: string;
|
|
4
11
|
/** @deprecated Use projectId instead */
|
|
@@ -18,6 +25,12 @@ export interface PluginConfig {
|
|
|
18
25
|
build?: string | number;
|
|
19
26
|
deleteAfterUpload?: boolean;
|
|
20
27
|
batchSize?: number;
|
|
28
|
+
/**
|
|
29
|
+
* EXPERIMENTAL. Defaults to the `POSTHOG_RELEASE_MODE` env var, the same one posthog-cli
|
|
30
|
+
* reads, then to `symbol-set`. Event mode needs a posthog-cli that supports
|
|
31
|
+
* `release resolve` and `--release-mode`.
|
|
32
|
+
*/
|
|
33
|
+
releaseMode?: ReleaseMode;
|
|
21
34
|
};
|
|
22
35
|
}
|
|
23
36
|
export interface ResolvedPluginConfig extends Omit<PluginConfig, 'envId' | 'projectId'> {
|
|
@@ -32,6 +45,7 @@ export interface ResolvedPluginConfig extends Omit<PluginConfig, 'envId' | 'proj
|
|
|
32
45
|
build?: string;
|
|
33
46
|
deleteAfterUpload: boolean;
|
|
34
47
|
batchSize?: number;
|
|
48
|
+
releaseMode: ReleaseMode;
|
|
35
49
|
};
|
|
36
50
|
}
|
|
37
51
|
export interface ResolveConfigOptions {
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAaA,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAA;AAErE,MAAM,WAAW,YAAY;IACzB,cAAc,EAAE,MAAM,CAAA;IACtB,wCAAwC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,QAAQ,CAAA;IACnB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,UAAU,CAAC,EAAE;QACT,OAAO,CAAC,EAAE,OAAO,CAAA;QACjB,0CAA0C;QAC1C,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,WAAW,CAAC,EAAE,MAAM,CAAA;QACpB,6CAA6C;QAC7C,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,cAAc,CAAC,EAAE,MAAM,CAAA;QACvB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;QACvB,iBAAiB,CAAC,EAAE,OAAO,CAAA;QAC3B,SAAS,CAAC,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAaA,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAA;AAErE;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG,YAAY,GAAG,OAAO,CAAA;AAchD,MAAM,WAAW,YAAY;IACzB,cAAc,EAAE,MAAM,CAAA;IACtB,wCAAwC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,QAAQ,CAAA;IACnB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,UAAU,CAAC,EAAE;QACT,OAAO,CAAC,EAAE,OAAO,CAAA;QACjB,0CAA0C;QAC1C,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,WAAW,CAAC,EAAE,MAAM,CAAA;QACpB,6CAA6C;QAC7C,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,cAAc,CAAC,EAAE,MAAM,CAAA;QACvB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;QACvB,iBAAiB,CAAC,EAAE,OAAO,CAAA;QAC3B,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB;;;;WAIG;QACH,WAAW,CAAC,EAAE,WAAW,CAAA;KAC5B,CAAA;CACJ;AAED,MAAM,WAAW,oBAAqB,SAAQ,IAAI,CAAC,YAAY,EAAE,OAAO,GAAG,WAAW,CAAC;IACnF,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,QAAQ,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;IACrB,UAAU,EAAE;QACR,OAAO,EAAE,OAAO,CAAA;QAChB,WAAW,CAAC,EAAE,MAAM,CAAA;QACpB,cAAc,CAAC,EAAE,MAAM,CAAA;QACvB,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,iBAAiB,EAAE,OAAO,CAAA;QAC1B,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,WAAW,EAAE,WAAW,CAAA;KAC3B,CAAA;CACJ;AAED,MAAM,WAAW,oBAAoB;IACjC,sFAAsF;IACtF,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,iFAAiF;IACjF,GAAG,CAAC,EAAE,MAAM,CAAA;CACf;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,YAAY,EAAE,cAAc,CAAC,EAAE,oBAAoB,GAAG,oBAAoB,CA0ChH"}
|
package/dist/config.js
CHANGED
|
@@ -35,6 +35,15 @@ function normalizeHost(value) {
|
|
|
35
35
|
const normalizedHost = 'string' == typeof value ? value.trim() : '';
|
|
36
36
|
return normalizedHost || DEFAULT_PLUGIN_HOST;
|
|
37
37
|
}
|
|
38
|
+
const RELEASE_MODES = [
|
|
39
|
+
'symbol-set',
|
|
40
|
+
'event'
|
|
41
|
+
];
|
|
42
|
+
function normalizeReleaseMode(value) {
|
|
43
|
+
if (void 0 === value || '' === value) return 'symbol-set';
|
|
44
|
+
if (!RELEASE_MODES.includes(value)) throw new Error(`sourcemaps.releaseMode must be one of ${RELEASE_MODES.join(', ')}, got '${value}'`);
|
|
45
|
+
return value;
|
|
46
|
+
}
|
|
38
47
|
function resolveConfig(options, resolveOptions) {
|
|
39
48
|
const projectId = options.projectId ?? options.envId;
|
|
40
49
|
const personalApiKey = normalizeApiKey(options.personalApiKey);
|
|
@@ -64,7 +73,8 @@ function resolveConfig(options, resolveOptions) {
|
|
|
64
73
|
releaseVersion: userSourcemaps.releaseVersion ?? userSourcemaps.version,
|
|
65
74
|
build: void 0 !== userSourcemaps.build ? String(userSourcemaps.build) : void 0,
|
|
66
75
|
deleteAfterUpload: userSourcemaps.deleteAfterUpload ?? true,
|
|
67
|
-
batchSize: userSourcemaps.batchSize
|
|
76
|
+
batchSize: userSourcemaps.batchSize,
|
|
77
|
+
releaseMode: normalizeReleaseMode(userSourcemaps.releaseMode ?? process.env.POSTHOG_RELEASE_MODE)
|
|
68
78
|
}
|
|
69
79
|
};
|
|
70
80
|
}
|
package/dist/config.mjs
CHANGED
|
@@ -7,6 +7,15 @@ function normalizeHost(value) {
|
|
|
7
7
|
const normalizedHost = 'string' == typeof value ? value.trim() : '';
|
|
8
8
|
return normalizedHost || DEFAULT_PLUGIN_HOST;
|
|
9
9
|
}
|
|
10
|
+
const RELEASE_MODES = [
|
|
11
|
+
'symbol-set',
|
|
12
|
+
'event'
|
|
13
|
+
];
|
|
14
|
+
function normalizeReleaseMode(value) {
|
|
15
|
+
if (void 0 === value || '' === value) return 'symbol-set';
|
|
16
|
+
if (!RELEASE_MODES.includes(value)) throw new Error(`sourcemaps.releaseMode must be one of ${RELEASE_MODES.join(', ')}, got '${value}'`);
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
10
19
|
function resolveConfig(options, resolveOptions) {
|
|
11
20
|
const projectId = options.projectId ?? options.envId;
|
|
12
21
|
const personalApiKey = normalizeApiKey(options.personalApiKey);
|
|
@@ -36,7 +45,8 @@ function resolveConfig(options, resolveOptions) {
|
|
|
36
45
|
releaseVersion: userSourcemaps.releaseVersion ?? userSourcemaps.version,
|
|
37
46
|
build: void 0 !== userSourcemaps.build ? String(userSourcemaps.build) : void 0,
|
|
38
47
|
deleteAfterUpload: userSourcemaps.deleteAfterUpload ?? true,
|
|
39
|
-
batchSize: userSourcemaps.batchSize
|
|
48
|
+
batchSize: userSourcemaps.batchSize,
|
|
49
|
+
releaseMode: normalizeReleaseMode(userSourcemaps.releaseMode ?? process.env.POSTHOG_RELEASE_MODE)
|
|
40
50
|
}
|
|
41
51
|
};
|
|
42
52
|
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAA;AAC7B,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAA;AAClE,cAAc,UAAU,CAAA;AACxB,cAAc,OAAO,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAA;AAC7B,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAA;AAClE,cAAc,UAAU,CAAA;AACxB,cAAc,OAAO,CAAA;AACrB,cAAc,aAAa,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var __webpack_modules__ = {
|
|
3
|
+
"./chunk-ids": function(module) {
|
|
4
|
+
module.exports = require("./chunk-ids.js");
|
|
5
|
+
},
|
|
3
6
|
"./cli": function(module) {
|
|
4
7
|
module.exports = require("./cli.js");
|
|
5
8
|
},
|
|
@@ -91,6 +94,16 @@ var __webpack_exports__ = {};
|
|
|
91
94
|
return _cli__WEBPACK_IMPORTED_MODULE_3__[key];
|
|
92
95
|
}).bind(0, __WEBPACK_IMPORT_KEY__);
|
|
93
96
|
__webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__);
|
|
97
|
+
var _chunk_ids__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__("./chunk-ids");
|
|
98
|
+
var __WEBPACK_REEXPORT_OBJECT__ = {};
|
|
99
|
+
for(var __WEBPACK_IMPORT_KEY__ in _chunk_ids__WEBPACK_IMPORTED_MODULE_4__)if ([
|
|
100
|
+
"buildLocalBinaryPaths",
|
|
101
|
+
"resolveBinaryPath",
|
|
102
|
+
"default"
|
|
103
|
+
].indexOf(__WEBPACK_IMPORT_KEY__) < 0) __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = (function(key) {
|
|
104
|
+
return _chunk_ids__WEBPACK_IMPORTED_MODULE_4__[key];
|
|
105
|
+
}).bind(0, __WEBPACK_IMPORT_KEY__);
|
|
106
|
+
__webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__);
|
|
94
107
|
})();
|
|
95
108
|
exports.buildLocalBinaryPaths = __webpack_exports__.buildLocalBinaryPaths;
|
|
96
109
|
exports.resolveBinaryPath = __webpack_exports__.resolveBinaryPath;
|
package/dist/index.mjs
CHANGED
package/dist/spawn-local.d.ts
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
export interface CapturedRun {
|
|
2
|
+
code: number | null;
|
|
3
|
+
stdout: string;
|
|
4
|
+
stderr: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Runs a command and captures both streams instead of inheriting them, for commands whose stdout
|
|
8
|
+
* is a value the caller needs rather than build output. Resolves with a non-zero `code` rather
|
|
9
|
+
* than rejecting, so the caller can shape the error from what the command wrote to stderr.
|
|
10
|
+
*/
|
|
11
|
+
export declare function spawnLocalCapture(executable: string, args: string[], options: {
|
|
12
|
+
env: NodeJS.ProcessEnv;
|
|
13
|
+
cwd: string;
|
|
14
|
+
}): Promise<CapturedRun>;
|
|
1
15
|
export declare function spawnLocal(executable: string, args: string[], options: {
|
|
2
16
|
env: NodeJS.ProcessEnv;
|
|
3
17
|
stdio: 'inherit' | 'ignore';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spawn-local.d.ts","sourceRoot":"","sources":["../src/spawn-local.ts"],"names":[],"mappings":"AAEA,wBAAsB,UAAU,CAC5B,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE;IACL,GAAG,EAAE,MAAM,CAAC,UAAU,CAAA;IACtB,KAAK,EAAE,SAAS,GAAG,QAAQ,CAAA;IAC3B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAA;CACjB,GACF,OAAO,CAAC,IAAI,CAAC,CAiCf"}
|
|
1
|
+
{"version":3,"file":"spawn-local.d.ts","sourceRoot":"","sources":["../src/spawn-local.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;CACjB;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CACnC,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE;IACL,GAAG,EAAE,MAAM,CAAC,UAAU,CAAA;IACtB,GAAG,EAAE,MAAM,CAAA;CACd,GACF,OAAO,CAAC,WAAW,CAAC,CAoBtB;AAED,wBAAsB,UAAU,CAC5B,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE;IACL,GAAG,EAAE,MAAM,CAAC,UAAU,CAAA;IACtB,KAAK,EAAE,SAAS,GAAG,QAAQ,CAAA;IAC3B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAA;CACjB,GACF,OAAO,CAAC,IAAI,CAAC,CAiCf"}
|
package/dist/spawn-local.js
CHANGED
|
@@ -24,9 +24,39 @@ var __webpack_require__ = {};
|
|
|
24
24
|
var __webpack_exports__ = {};
|
|
25
25
|
__webpack_require__.r(__webpack_exports__);
|
|
26
26
|
__webpack_require__.d(__webpack_exports__, {
|
|
27
|
+
spawnLocalCapture: ()=>spawnLocalCapture,
|
|
27
28
|
spawnLocal: ()=>spawnLocal
|
|
28
29
|
});
|
|
29
30
|
const external_cross_spawn_namespaceObject = require("cross-spawn");
|
|
31
|
+
async function spawnLocalCapture(executable, args, options) {
|
|
32
|
+
const child = (0, external_cross_spawn_namespaceObject.spawn)(executable, [
|
|
33
|
+
...args
|
|
34
|
+
], {
|
|
35
|
+
stdio: [
|
|
36
|
+
'ignore',
|
|
37
|
+
'pipe',
|
|
38
|
+
'pipe'
|
|
39
|
+
],
|
|
40
|
+
env: options.env,
|
|
41
|
+
cwd: options.cwd
|
|
42
|
+
});
|
|
43
|
+
let stdout = '';
|
|
44
|
+
let stderr = '';
|
|
45
|
+
child.stdout?.on('data', (chunk)=>{
|
|
46
|
+
stdout += chunk.toString();
|
|
47
|
+
});
|
|
48
|
+
child.stderr?.on('data', (chunk)=>{
|
|
49
|
+
stderr += chunk.toString();
|
|
50
|
+
});
|
|
51
|
+
return await new Promise((resolve, reject)=>{
|
|
52
|
+
child.on('close', (code)=>resolve({
|
|
53
|
+
code,
|
|
54
|
+
stdout,
|
|
55
|
+
stderr
|
|
56
|
+
}));
|
|
57
|
+
child.on('error', (error)=>reject(error));
|
|
58
|
+
});
|
|
59
|
+
}
|
|
30
60
|
async function spawnLocal(executable, args, options) {
|
|
31
61
|
const stdioOption = void 0 !== options.stdin ? [
|
|
32
62
|
'pipe',
|
|
@@ -58,8 +88,10 @@ async function spawnLocal(executable, args, options) {
|
|
|
58
88
|
});
|
|
59
89
|
}
|
|
60
90
|
exports.spawnLocal = __webpack_exports__.spawnLocal;
|
|
91
|
+
exports.spawnLocalCapture = __webpack_exports__.spawnLocalCapture;
|
|
61
92
|
for(var __webpack_i__ in __webpack_exports__)if (-1 === [
|
|
62
|
-
"spawnLocal"
|
|
93
|
+
"spawnLocal",
|
|
94
|
+
"spawnLocalCapture"
|
|
63
95
|
].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
|
|
64
96
|
Object.defineProperty(exports, '__esModule', {
|
|
65
97
|
value: true
|
package/dist/spawn-local.mjs
CHANGED
|
@@ -1,4 +1,33 @@
|
|
|
1
1
|
import { spawn } from "cross-spawn";
|
|
2
|
+
async function spawnLocalCapture(executable, args, options) {
|
|
3
|
+
const child = spawn(executable, [
|
|
4
|
+
...args
|
|
5
|
+
], {
|
|
6
|
+
stdio: [
|
|
7
|
+
'ignore',
|
|
8
|
+
'pipe',
|
|
9
|
+
'pipe'
|
|
10
|
+
],
|
|
11
|
+
env: options.env,
|
|
12
|
+
cwd: options.cwd
|
|
13
|
+
});
|
|
14
|
+
let stdout = '';
|
|
15
|
+
let stderr = '';
|
|
16
|
+
child.stdout?.on('data', (chunk)=>{
|
|
17
|
+
stdout += chunk.toString();
|
|
18
|
+
});
|
|
19
|
+
child.stderr?.on('data', (chunk)=>{
|
|
20
|
+
stderr += chunk.toString();
|
|
21
|
+
});
|
|
22
|
+
return await new Promise((resolve, reject)=>{
|
|
23
|
+
child.on('close', (code)=>resolve({
|
|
24
|
+
code,
|
|
25
|
+
stdout,
|
|
26
|
+
stderr
|
|
27
|
+
}));
|
|
28
|
+
child.on('error', (error)=>reject(error));
|
|
29
|
+
});
|
|
30
|
+
}
|
|
2
31
|
async function spawnLocal(executable, args, options) {
|
|
3
32
|
const stdioOption = void 0 !== options.stdin ? [
|
|
4
33
|
'pipe',
|
|
@@ -29,4 +58,4 @@ async function spawnLocal(executable, args, options) {
|
|
|
29
58
|
});
|
|
30
59
|
});
|
|
31
60
|
}
|
|
32
|
-
export { spawnLocal };
|
|
61
|
+
export { spawnLocal, spawnLocalCapture };
|
package/package.json
CHANGED
package/src/chunk-ids.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import * as crypto from 'crypto'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Returns the minified chunk-id IIFE the SDK reads at runtime to map an
|
|
5
|
+
* error's stack back to a chunk id. With `releaseId`, the snippet also pins
|
|
6
|
+
* `_posthogReleaseId` on the global so the SDK emits the release on every
|
|
7
|
+
* exception (first write wins, so the first loaded chunk decides).
|
|
8
|
+
*
|
|
9
|
+
* Byte-for-byte contract with posthog-cli (`CODE_SNIPPET_TEMPLATE` and
|
|
10
|
+
* `CODE_SNIPPET_WITH_RELEASE_TEMPLATE` in cli/src/sourcemaps/constant.rs): the
|
|
11
|
+
* CLI recognizes and removes previously injected snippets by exact substring
|
|
12
|
+
* match, so any change here must ship in the CLI constants too. Ids are
|
|
13
|
+
* JSON-encoded for the same reason the CLI encodes them: an id carrying a quote
|
|
14
|
+
* would otherwise break out of the string literal. Covered by a parity test in
|
|
15
|
+
* chunk-ids.spec.ts.
|
|
16
|
+
*/
|
|
17
|
+
export function createChunkIdSnippet(chunkId: string, releaseId?: string): string {
|
|
18
|
+
const chunk = JSON.stringify(chunkId)
|
|
19
|
+
if (releaseId === undefined) {
|
|
20
|
+
return `!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._posthogChunkIds=e._posthogChunkIds||{},e._posthogChunkIds[n]=${chunk})}catch(e){}}();`
|
|
21
|
+
}
|
|
22
|
+
return `!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{};e._posthogReleaseId=e._posthogReleaseId||${JSON.stringify(releaseId)};var n=(new e.Error).stack;n&&(e._posthogChunkIds=e._posthogChunkIds||{},e._posthogChunkIds[n]=${chunk})}catch(e){}}();`
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Returns the comment posthog-cli discovers chunk ids from at upload time
|
|
27
|
+
* (`CHUNKID_COMMENT_PREFIX` in cli/src/sourcemaps/constant.rs), including the
|
|
28
|
+
* leading newline the CLI's exact-match removal expects.
|
|
29
|
+
*/
|
|
30
|
+
export function createChunkIdComment(chunkId: string): string {
|
|
31
|
+
return `\n//# chunkId=${chunkId}`
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Mints a fresh random chunk id: a new id per chunk, per build.
|
|
36
|
+
*/
|
|
37
|
+
export function createChunkId(): string {
|
|
38
|
+
return crypto.randomUUID()
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Fixed namespace for content-addressed chunk ids, matching `CHUNK_ID_NAMESPACE` in
|
|
42
|
+
// cli/src/sourcemaps/constant.rs. Both sides must derive ids from the same namespace, or a
|
|
43
|
+
// dist processed by one tool and re-processed by the other would upload the same code twice.
|
|
44
|
+
const CHUNK_ID_NAMESPACE = '0e9b3c7a-5d1f-42a8-b6c4-e2d0f8a17593'
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Derives a chunk id from the chunk's own bytes (UUIDv5), so identical code keeps its id across
|
|
48
|
+
* rebuilds and re-uploads dedupe against the symbol set already stored. Used in event release
|
|
49
|
+
* mode, where the release travels inside the chunk instead of being bound to the symbol set, so
|
|
50
|
+
* a per-build random id would create a fresh symbol set on every build.
|
|
51
|
+
*
|
|
52
|
+
* The id is content-addressed over what `renderChunk` sees. posthog-cli derives its own ids from
|
|
53
|
+
* the file on disk, which by then also carries the trailing `sourceMappingURL` comment, so the
|
|
54
|
+
* same chunk gets a different (but equally stable) id depending on which tool injected it.
|
|
55
|
+
*/
|
|
56
|
+
export function createStableChunkId(code: string): string {
|
|
57
|
+
const namespace = Buffer.from(CHUNK_ID_NAMESPACE.replace(/-/g, ''), 'hex')
|
|
58
|
+
const hash = crypto.createHash('sha1').update(namespace).update(code, 'utf8').digest()
|
|
59
|
+
|
|
60
|
+
// RFC 4122: version in the high nibble of byte 6, variant in the top two bits of byte 8.
|
|
61
|
+
hash[6] = (hash[6] & 0x0f) | 0x50
|
|
62
|
+
hash[8] = (hash[8] & 0x3f) | 0x80
|
|
63
|
+
|
|
64
|
+
const hex = hash.subarray(0, 16).toString('hex')
|
|
65
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Anchored with a bounded quantifier and only ever run against a fixed-size
|
|
69
|
+
// window: an unbounded regex over the whole bundle is quadratic on adversarial
|
|
70
|
+
// input (a match attempt per marker occurrence, each scanning ahead).
|
|
71
|
+
const COMMENT_REGEX = /^\/\/# chunkId=(\S{1,128})/
|
|
72
|
+
const COMMENT_MARKER = '//# chunkId='
|
|
73
|
+
const WINDOW = 256
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Extracts the chunk id from source that already carries one, using the same
|
|
77
|
+
* detection the CLI uses: a `//# chunkId=` comment at the start of a line.
|
|
78
|
+
* The injected IIFE is deliberately not matched — its marker text can appear
|
|
79
|
+
* inside string literals of bundled code (docs strings, bundled PostHog
|
|
80
|
+
* tooling), and the CLI only ever reads the line-anchored comment. Keeping the
|
|
81
|
+
* two in sync guarantees this returning an id ⟺ the CLI finding one.
|
|
82
|
+
*/
|
|
83
|
+
export function determineChunkIdFromSource(code: string): string | undefined {
|
|
84
|
+
for (
|
|
85
|
+
let at = code.indexOf(COMMENT_MARKER);
|
|
86
|
+
at !== -1;
|
|
87
|
+
at = code.indexOf(COMMENT_MARKER, at + COMMENT_MARKER.length)
|
|
88
|
+
) {
|
|
89
|
+
if (at !== 0 && code[at - 1] !== '\n') continue
|
|
90
|
+
const match = COMMENT_REGEX.exec(code.slice(at, at + WINDOW))
|
|
91
|
+
if (match) {
|
|
92
|
+
return match[1]
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return undefined
|
|
96
|
+
}
|
package/src/cli.ts
CHANGED
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
import { ResolvedPluginConfig } from './config'
|
|
2
|
-
import { spawnLocal } from './spawn-local'
|
|
2
|
+
import { spawnLocal, spawnLocalCapture } from './spawn-local'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* `process` injects chunk ids into the built files on disk and uploads them.
|
|
6
|
+
* `upload` only reads the files, expecting chunk ids to already be present
|
|
7
|
+
* (e.g. injected in-memory by a bundler plugin before the files were written).
|
|
6
8
|
*/
|
|
7
|
-
export
|
|
8
|
-
config: ResolvedPluginConfig,
|
|
9
|
-
mode: { stdin: true } | { directory: string }
|
|
10
|
-
): string[] {
|
|
11
|
-
const args = ['sourcemap', 'process']
|
|
9
|
+
export type SourcemapCliCommand = 'process' | 'upload'
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
/**
|
|
12
|
+
* The flags identifying which release this build belongs to. Shared by every command that
|
|
13
|
+
* resolves one, so `release resolve` lands on the same release row the upload would have.
|
|
14
|
+
*/
|
|
15
|
+
function buildReleaseArgs(config: ResolvedPluginConfig): string[] {
|
|
16
|
+
const args: string[] = []
|
|
18
17
|
|
|
19
18
|
if (config.sourcemaps.releaseName) {
|
|
20
19
|
args.push('--release-name', config.sourcemaps.releaseName)
|
|
@@ -28,7 +27,38 @@ export function buildSourcemapCliArgs(
|
|
|
28
27
|
args.push('--build', config.sourcemaps.build)
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
|
|
30
|
+
return args
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Build CLI arguments for `posthog-cli sourcemap <command>`.
|
|
35
|
+
*/
|
|
36
|
+
export function buildSourcemapCliArgs(
|
|
37
|
+
config: ResolvedPluginConfig,
|
|
38
|
+
mode: { stdin: true } | { directory: string },
|
|
39
|
+
command: SourcemapCliCommand = 'process'
|
|
40
|
+
): string[] {
|
|
41
|
+
const args = ['sourcemap', command]
|
|
42
|
+
|
|
43
|
+
if ('stdin' in mode) {
|
|
44
|
+
args.push('--stdin')
|
|
45
|
+
} else {
|
|
46
|
+
args.push('--directory', mode.directory)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
args.push(...buildReleaseArgs(config))
|
|
50
|
+
|
|
51
|
+
// Only passed in event mode, so a symbol-set build keeps working against a posthog-cli
|
|
52
|
+
// predating the flag.
|
|
53
|
+
if (config.sourcemaps.releaseMode === 'event') {
|
|
54
|
+
args.push('--release-mode', 'event')
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// On `upload` the caller owns map deletion: `--delete-after` also rewrites
|
|
58
|
+
// the .js files (stripping sourcemap references), and callers pick `upload`
|
|
59
|
+
// precisely because the written files must not change — e.g. Subresource
|
|
60
|
+
// Integrity hashes were already computed from them.
|
|
61
|
+
if (config.sourcemaps.deleteAfterUpload && command === 'process') {
|
|
32
62
|
args.push('--delete-after')
|
|
33
63
|
}
|
|
34
64
|
|
|
@@ -50,6 +80,9 @@ export function buildCliEnv(config: ResolvedPluginConfig): NodeJS.ProcessEnv {
|
|
|
50
80
|
POSTHOG_CLI_HOST: config.host,
|
|
51
81
|
POSTHOG_CLI_API_KEY: config.personalApiKey,
|
|
52
82
|
POSTHOG_CLI_PROJECT_ID: config.projectId,
|
|
83
|
+
// The CLI reads this variable itself, so an inherited value would decide the release mode
|
|
84
|
+
// of the spawned command while the plugin injected chunks for the mode it resolved.
|
|
85
|
+
POSTHOG_RELEASE_MODE: config.sourcemaps.releaseMode,
|
|
53
86
|
}
|
|
54
87
|
}
|
|
55
88
|
|
|
@@ -58,10 +91,10 @@ export function buildCliEnv(config: ResolvedPluginConfig): NodeJS.ProcessEnv {
|
|
|
58
91
|
*/
|
|
59
92
|
export async function runSourcemapCli(
|
|
60
93
|
config: ResolvedPluginConfig,
|
|
61
|
-
options: { filePaths: string[] } | { directory: string }
|
|
94
|
+
options: ({ filePaths: string[] } | { directory: string }) & { command?: SourcemapCliCommand }
|
|
62
95
|
): Promise<void> {
|
|
63
96
|
const mode = 'filePaths' in options ? { stdin: true as const } : { directory: options.directory }
|
|
64
|
-
const args = buildSourcemapCliArgs(config, mode)
|
|
97
|
+
const args = buildSourcemapCliArgs(config, mode, options.command ?? 'process')
|
|
65
98
|
const env = buildCliEnv(config)
|
|
66
99
|
|
|
67
100
|
const spawnOptions: Parameters<typeof spawnLocal>[2] = {
|
|
@@ -76,3 +109,35 @@ export async function runSourcemapCli(
|
|
|
76
109
|
|
|
77
110
|
await spawnLocal(config.cliBinaryPath, args, spawnOptions)
|
|
78
111
|
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Resolves the release this build belongs to, creating it if it doesn't exist yet, and returns its
|
|
115
|
+
* id for injection into the chunks. Returns undefined when nothing identifies a release: the CLI
|
|
116
|
+
* prints nothing and exits zero for a build with no release name/version and no git or CI
|
|
117
|
+
* metadata to derive them from, which is a build that ships without a release rather than a
|
|
118
|
+
* failure.
|
|
119
|
+
*/
|
|
120
|
+
export async function resolveReleaseId(config: ResolvedPluginConfig): Promise<string | undefined> {
|
|
121
|
+
const args = ['release', 'resolve', ...buildReleaseArgs(config)]
|
|
122
|
+
const { code, stdout, stderr } = await spawnLocalCapture(config.cliBinaryPath, args, {
|
|
123
|
+
cwd: process.cwd(),
|
|
124
|
+
env: buildCliEnv(config),
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
if (code !== 0) {
|
|
128
|
+
// `release resolve` shipped after `sourcemap upload`, so an older binary on the PATH
|
|
129
|
+
// fails here rather than at upload time, where the message would make more sense.
|
|
130
|
+
const hint = /unrecognized subcommand|unexpected argument/.test(stderr)
|
|
131
|
+
? ` Event release mode needs a posthog-cli with 'release resolve' (${config.cliBinaryPath}).`
|
|
132
|
+
: ''
|
|
133
|
+
throw new Error(`posthog-cli release resolve failed with code ${code}.${hint}\n${stderr.trim()}`)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// The CLI logs to stderr and prints only the id to stdout, so forward its warnings rather
|
|
137
|
+
// than swallowing them with the captured stream.
|
|
138
|
+
if (stderr.trim()) {
|
|
139
|
+
process.stderr.write(stderr)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return stdout.trim() || undefined
|
|
143
|
+
}
|
package/src/config.ts
CHANGED
|
@@ -13,6 +13,26 @@ function normalizeHost(value?: unknown): string {
|
|
|
13
13
|
|
|
14
14
|
export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent'
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* How an exception gets associated with a release, mirroring posthog-cli's `--release-mode`.
|
|
18
|
+
*
|
|
19
|
+
* `symbol-set` binds the uploaded symbol sets to a release. `event` leaves them unbound and
|
|
20
|
+
* injects the release id into each chunk instead, so the release is resolved per exception.
|
|
21
|
+
*/
|
|
22
|
+
export type ReleaseMode = 'symbol-set' | 'event'
|
|
23
|
+
|
|
24
|
+
const RELEASE_MODES: ReleaseMode[] = ['symbol-set', 'event']
|
|
25
|
+
|
|
26
|
+
function normalizeReleaseMode(value: string | undefined): ReleaseMode {
|
|
27
|
+
if (value === undefined || value === '') {
|
|
28
|
+
return 'symbol-set'
|
|
29
|
+
}
|
|
30
|
+
if (!(RELEASE_MODES as string[]).includes(value)) {
|
|
31
|
+
throw new Error(`sourcemaps.releaseMode must be one of ${RELEASE_MODES.join(', ')}, got '${value}'`)
|
|
32
|
+
}
|
|
33
|
+
return value as ReleaseMode
|
|
34
|
+
}
|
|
35
|
+
|
|
16
36
|
export interface PluginConfig {
|
|
17
37
|
personalApiKey: string
|
|
18
38
|
/** @deprecated Use projectId instead */
|
|
@@ -32,6 +52,12 @@ export interface PluginConfig {
|
|
|
32
52
|
build?: string | number
|
|
33
53
|
deleteAfterUpload?: boolean
|
|
34
54
|
batchSize?: number
|
|
55
|
+
/**
|
|
56
|
+
* EXPERIMENTAL. Defaults to the `POSTHOG_RELEASE_MODE` env var, the same one posthog-cli
|
|
57
|
+
* reads, then to `symbol-set`. Event mode needs a posthog-cli that supports
|
|
58
|
+
* `release resolve` and `--release-mode`.
|
|
59
|
+
*/
|
|
60
|
+
releaseMode?: ReleaseMode
|
|
35
61
|
}
|
|
36
62
|
}
|
|
37
63
|
|
|
@@ -47,6 +73,7 @@ export interface ResolvedPluginConfig extends Omit<PluginConfig, 'envId' | 'proj
|
|
|
47
73
|
build?: string
|
|
48
74
|
deleteAfterUpload: boolean
|
|
49
75
|
batchSize?: number
|
|
76
|
+
releaseMode: ReleaseMode
|
|
50
77
|
}
|
|
51
78
|
}
|
|
52
79
|
|
|
@@ -96,6 +123,7 @@ export function resolveConfig(options: PluginConfig, resolveOptions?: ResolveCon
|
|
|
96
123
|
build: userSourcemaps.build !== undefined ? String(userSourcemaps.build) : undefined,
|
|
97
124
|
deleteAfterUpload: userSourcemaps.deleteAfterUpload ?? true,
|
|
98
125
|
batchSize: userSourcemaps.batchSize,
|
|
126
|
+
releaseMode: normalizeReleaseMode(userSourcemaps.releaseMode ?? process.env.POSTHOG_RELEASE_MODE),
|
|
99
127
|
},
|
|
100
128
|
}
|
|
101
129
|
}
|
package/src/index.ts
CHANGED
package/src/spawn-local.ts
CHANGED
|
@@ -1,5 +1,45 @@
|
|
|
1
1
|
import { spawn } from 'cross-spawn'
|
|
2
2
|
|
|
3
|
+
export interface CapturedRun {
|
|
4
|
+
code: number | null
|
|
5
|
+
stdout: string
|
|
6
|
+
stderr: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Runs a command and captures both streams instead of inheriting them, for commands whose stdout
|
|
11
|
+
* is a value the caller needs rather than build output. Resolves with a non-zero `code` rather
|
|
12
|
+
* than rejecting, so the caller can shape the error from what the command wrote to stderr.
|
|
13
|
+
*/
|
|
14
|
+
export async function spawnLocalCapture(
|
|
15
|
+
executable: string,
|
|
16
|
+
args: string[],
|
|
17
|
+
options: {
|
|
18
|
+
env: NodeJS.ProcessEnv
|
|
19
|
+
cwd: string
|
|
20
|
+
}
|
|
21
|
+
): Promise<CapturedRun> {
|
|
22
|
+
const child = spawn(executable, [...args], {
|
|
23
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
24
|
+
env: options.env,
|
|
25
|
+
cwd: options.cwd,
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
let stdout = ''
|
|
29
|
+
let stderr = ''
|
|
30
|
+
child.stdout?.on('data', (chunk: Buffer) => {
|
|
31
|
+
stdout += chunk.toString()
|
|
32
|
+
})
|
|
33
|
+
child.stderr?.on('data', (chunk: Buffer) => {
|
|
34
|
+
stderr += chunk.toString()
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
return await new Promise<CapturedRun>((resolve, reject) => {
|
|
38
|
+
child.on('close', (code) => resolve({ code, stdout, stderr }))
|
|
39
|
+
child.on('error', (error) => reject(error))
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
|
|
3
43
|
export async function spawnLocal(
|
|
4
44
|
executable: string,
|
|
5
45
|
args: string[],
|