Repository: nodejs/node
Pull request: util: expose CallSite.scriptId

The `scriptId` is essential to construct chrome devtools protocol
structs like `Network.Initiator`, allowing inspectors to associate
a `CallSite` with a unique script.

Changed files:
filename: doc/api/util.md
patch: @@ -371,6 +371,9 @@ util.formatWithOptions({ colors: true }, 'See object %O', { foo: 42 });
 <!-- YAML
 added: v22.9.0
 changes:
+  - version: REPLACEME
+    pr-url: https://github.com/nodejs/node/pull/56551
+    description: Property `CallSite.scriptId` is exposed.
   - version:
     - v23.3.0
     - v22.12.0
@@ -387,6 +390,7 @@ changes:
   * `functionName` {string} Returns the name of the function associated with this call site.
   * `scriptName` {string} Returns the name of the resource that contains the script for the
     function for this call site.
+  * `scriptId` {string} Returns the unique id of the script, as in Chrome DevTools protocol [`Runtime.ScriptId`][].
   * `lineNumber` {number} Returns the number, 1-based, of the line for the associate function call.
   * `column` {number} Returns the 1-based column offset on the line for the associated function call.
 
@@ -3190,6 +3194,7 @@ util.isArray({});
 [`Object.freeze()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze
 [`Promise`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
 [`Proxy`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
+[`Runtime.ScriptId`]: https://chromedevtools.github.io/devtools-protocol/1-3/Runtime/#type-ScriptId
 [`Set`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
 [`SharedArrayBuffer`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer
 [`TypedArray`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray

filename: src/env_properties.h
patch: @@ -320,6 +320,7 @@
   V(salt_length_string, "saltLength")                                          \
   V(scheme_string, "scheme")                                                   \
   V(scopeid_string, "scopeid")                                                 \
+  V(script_id_string, "scriptId")                                              \
   V(script_name_string, "scriptName")                                          \
   V(serial_number_string, "serialNumber")                                      \
   V(serial_string, "serial")                                                   \

filename: src/node_util.cc
patch: @@ -275,14 +275,18 @@ static void GetCallSites(const FunctionCallbackInfo<Value>& args) {
       script_name = v8::String::Empty(isolate);
     }
 
+    std::string script_id = std::to_string(stack_frame->GetScriptId());
+
     Local<Name> names[] = {
         env->function_name_string(),
+        env->script_id_string(),
         env->script_name_string(),
         env->line_number_string(),
         env->column_string(),
     };
     Local<Value> values[] = {
         function_name,
+        OneByteString(isolate, script_id),
         script_name,
         Integer::NewFromUnsigned(isolate, stack_frame->GetLineNumber()),
         Integer::NewFromUnsigned(isolate, stack_frame->GetColumn()),

filename: test/parallel/test-util-getcallsites.js
patch: @@ -79,6 +79,13 @@ const assert = require('node:assert');
   );
 }
 
+// ScriptId is a string.
+{
+  const callSites = getCallSites(1);
+  assert.strictEqual(callSites.length, 1);
+  assert.strictEqual(typeof callSites[0].scriptId, 'string');
+}
+
 // Guarantee [eval] will appear on stacktraces when using -e
 {
   const { status, stderr, stdout } = spawnSync(