99#include < mutex>
1010#include < string>
1111#include < vector>
12+ #include " Caches.h"
1213#include " Helpers.h"
1314#include " ModuleInternalCallbacks.h"
1415#include " Runtime.h"
@@ -25,22 +26,6 @@ static inline bool StartsWith(const std::string& s, const char* prefix) {
2526 return s.size () >= n && s.compare (0 , n, prefix) == 0 ;
2627}
2728
28- void MirrorGlobalOnGlobalThis (v8::Isolate* isolate, v8::Local<v8::Context> context,
29- const char * name) {
30- std::string src = " if (typeof globalThis !== 'undefined' && typeof globalThis." +
31- std::string (name) +
32- " === 'undefined') {"
33- " Object.defineProperty(globalThis, '" +
34- std::string (name) + " ', { value: this." + std::string (name) +
35- " , writable: true, configurable: true, enumerable: false });"
36- " }" ;
37-
38- v8::Local<v8::Script> script;
39- if (v8::Script::Compile (context, tns::ToV8String (isolate, src.c_str ())).ToLocal (&script)) {
40- script->Run (context).FromMaybe (v8::Local<v8::Value>());
41- }
42- }
43-
4429static void SetBooleanGlobal (v8::Isolate* isolate, v8::Local<v8::Context> context, const char * key,
4530 bool value) {
4631 context->Global ()
@@ -54,8 +39,8 @@ static void SetBooleanGlobal(v8::Isolate* isolate, v8::Local<v8::Context> contex
5439// Native-side mirror of `__NS_HMR_BOOT_COMPLETE__`. Read by the
5540// runloop pump in `MaybePumpJSThreadDuringBoot` so its gate is a
5641// single relaxed atomic load on the HMR-time hot path. The JS dev
57- // client flips this via the
58- // `__NS_DEV__. setDevBootComplete(bool)` global once the real app root view
42+ // client flips this via ns:runtime
43+ // `setDevBootComplete(bool)` once the real app root view
5944// commits; boot orchestration itself is entirely userland.
6045static std::atomic<bool > g_devSessionBootComplete{false };
6146
@@ -83,7 +68,7 @@ void SetDevBootComplete(v8::Isolate* isolate, v8::Local<v8::Context> context, bo
8368// queries may be normalized, and which paths must keep their query verbatim
8469// because the query IS the identity — is server/framework policy, supplied
8570// by the dev client via
86- // `__NS_DEV__. configureRuntime({ canonicalization: {...} })`.
71+ // ns:runtime ` configureRuntime({ canonicalization: {...} })`.
8772//
8873// Write-before-read contract: the client configures this once, before the
8974// first import wave (session-bootstrap order), so plain statics are
@@ -94,10 +79,6 @@ void SetDevBootComplete(v8::Isolate* isolate, v8::Local<v8::Context> context, bo
9479//
9580// When unconfigured, a built-in vocabulary matching current
9681// `@nativescript/vite` conventions applies.
97- // TODO(feat/hmr-dev-sessions follow-up): delete the built-in vocabulary once
98- // the paired `@nativescript/vite` release that sends `canonicalization` has
99- // been qualified — the runtime should carry zero server/framework URL
100- // strings.
10182struct CanonicalizationConfig {
10283 std::vector<std::string> stripParams; // query param names to drop
10384 std::vector<std::string> devPathPrefixes; // path StartsWith → normalize query
@@ -110,7 +91,7 @@ static void SetCanonicalizationConfig(CanonicalizationConfig config) {
11091 g_canonConfig = std::move (config);
11192 g_canonConfigured = true ;
11293 if (IsScriptLoadingLogEnabled ()) {
113- Log (@" [__NS_DEV__. configureRuntime] canonicalization set (strip=%lu devPrefixes=%lu "
94+ Log (@" [ns:runtime configureRuntime] canonicalization set (strip=%lu devPrefixes=%lu "
11495 @" preserve=%lu )" ,
11596 (unsigned long )g_canonConfig.stripParams .size (),
11697 (unsigned long )g_canonConfig.devPathPrefixes .size (),
@@ -161,7 +142,7 @@ static void ResetCanonicalizationConfig() {
161142 //
162143 // The dev server serves every module under ONE canonical URL — module
163144 // identity IS the URL string. Freshness after an HMR edit is handled by
164- // `__NS_DEV__. invalidateModules` (registry evict) plus the
145+ // ns:runtime ` invalidateModules` (registry evict) plus the
165146 // eviction-driven fetch nonce in `PerformHttpFetchOnceSync`, never by URL
166147 // variation. There is deliberately no path-tag vocabulary to collapse here.
167148 //
@@ -192,7 +173,8 @@ static void ResetCanonicalizationConfig() {
192173 return noHash;
193174 }
194175 } else {
195- // Built-in fallback vocabulary — see the deletion TODO above.
176+ // Unconfigured: built-in vocabulary matching `@nativescript/vite`
177+ // conventions.
196178 if (pathOnly.find (" /@ng/component" ) != std::string::npos) {
197179 return noHash;
198180 }
@@ -300,12 +282,10 @@ static void ClearAllCacheBustMarks() {
300282// which fetches the transitive closure concurrently off the JS
301283// thread before instantiation begins.
302284//
303- // There is deliberately NO body prewarm cache and NO JS-driven prefetch
304- // API here. Measurement (see docs/knowledge/hmr-simplification-pass.md)
305- // showed the async discovery walk beats server-computed
306- // closure/archive seeding on real apps — concurrent fetches overlap
307- // with on-device compile, while seeding serializes a full server-side
308- // transform pass before the entry can start.
285+ // These two are the loader's complete fetch surface: the async graph walk
286+ // owns all body fetching. Concurrent per-module fetches overlap with
287+ // on-device compile, which measured fastest on real apps
288+ // (HMR_API_NECESSITY_REVIEW.md §8.3).
309289
310290// Forward declarations — these helpers are defined below their first use,
311291// matching the existing convention in this file.
@@ -754,7 +734,7 @@ void FetchModuleBodyAsync(const std::string& url,
754734// background threads, so they never pump someone else's runloop.
755735// - `IsDevSessionBootComplete()` short-circuits once the dev client
756736// has committed its first stable view (it calls
757- // `__NS_DEV__. setDevBootComplete(true)`) — no placeholder to repaint, and
737+ // ns:runtime ` setDevBootComplete(true)`) — no placeholder to repaint, and
758738// HMR-time fetches must not pay the pump cost.
759739// - The runloop identity check survives any future change that
760740// decouples the runtime's captured runloop from the current thread.
@@ -814,18 +794,20 @@ void CleanupHMRGlobals() {
814794}
815795
816796// ─────────────────────────────────────────────────────────────
817- // Dev-loader JS-callable globals
797+ // The `ns:runtime` dev surface
818798//
819799// The runtime's dev surface is deliberately small: it exposes
820800// *mechanism* only (resolution config, registry eviction, registry
821801// introspection, boot-complete signal). All HMR *policy* — boot
822802// orchestration, `import.meta.hot`, full reload, CSS apply, WebSocket
823803// protocol — lives in the JS dev client (`@nativescript/vite`).
804+ // The surface is reachable exclusively through the `ns:runtime` builtin
805+ // module (require / static import / import()); there is no global.
824806
825807namespace {
826808
827809// Sets the function name on the v8 Function for nicer stack traces and
828- // attaches it as a method of the `__NS_DEV__` namespace object.
810+ // attaches it as a member of the `ns:runtime` binding object.
829811void InstallDevFunction (v8::Isolate* isolate, v8::Local<v8::Context> context,
830812 v8::Local<v8::Object> target, const char * name,
831813 v8::FunctionCallback callback) {
@@ -843,7 +825,7 @@ void ConfigureDevRuntimeCallback(const v8::FunctionCallbackInfo<v8::Value>& info
843825
844826 if (info.Length () < 1 || !info[0 ]->IsObject ()) {
845827 if (logScriptLoading) {
846- Log (@" [__NS_DEV__. configureRuntime] expected config object argument" );
828+ Log (@" [ns:runtime configureRuntime] expected config object argument" );
847829 }
848830 return ;
849831 }
@@ -877,7 +859,7 @@ void ConfigureDevRuntimeCallback(const v8::FunctionCallbackInfo<v8::Value>& info
877859 if (!jsonStr.empty ()) {
878860 SetImportMap (jsonStr);
879861 if (logScriptLoading) {
880- Log (@" [__NS_DEV__. configureRuntime] import map set (%zu bytes)" , jsonStr.size ());
862+ Log (@" [ns:runtime configureRuntime] import map set (%zu bytes)" , jsonStr.size ());
881863 }
882864 }
883865 }
@@ -907,7 +889,7 @@ void ConfigureDevRuntimeCallback(const v8::FunctionCallbackInfo<v8::Value>& info
907889 if (readStringArray (config, " volatilePatterns" , patterns) && !patterns.empty ()) {
908890 SetVolatilePatterns (patterns);
909891 if (logScriptLoading) {
910- Log (@" [__NS_DEV__. configureRuntime] %zu volatile patterns set" , patterns.size ());
892+ Log (@" [ns:runtime configureRuntime] %zu volatile patterns set" , patterns.size ());
911893 }
912894 }
913895 }
@@ -936,7 +918,7 @@ void InvalidateModulesCallback(const v8::FunctionCallbackInfo<v8::Value>& info)
936918 v8::Local<v8::Context> ctx = isolate->GetCurrentContext ();
937919
938920 if (info.Length () < 1 || !info[0 ]->IsArray ()) {
939- Log (@" [__NS_DEV__. invalidateModules] expected array of URL strings" );
921+ Log (@" [ns:runtime invalidateModules] expected array of URL strings" );
940922 return ;
941923 }
942924
@@ -992,7 +974,7 @@ void GetLoadedModuleUrlsCallback(const v8::FunctionCallbackInfo<v8::Value>& info
992974 info.GetReturnValue ().Set (result);
993975}
994976
995- // `__NS_DEV__. setDevBootComplete(value?: boolean)` — the JS dev client calls
977+ // ns:runtime ` setDevBootComplete(value?: boolean)` — the JS dev client calls
996978// this (with `true`, or no argument) once the real app root view has
997979// committed. It flips both the JS-visible `__NS_HMR_BOOT_COMPLETE__`
998980// global and the native atomic that disarms the cold-boot runloop pump.
@@ -1013,48 +995,48 @@ void SetDevBootCompleteCallback(const v8::FunctionCallbackInfo<v8::Value>& info)
1013995
1014996} // namespace
1015997
1016- void InitializeHmrDevGlobals (v8::Isolate* isolate, v8::Local<v8::Context> context, bool isWorker) {
1017- // The dev host API lives here: `__NS_DEV__`.
1018- v8::Local<v8::Object> dev = v8::Object::New (isolate);
998+ bool BuildNsRuntimeBinding (v8::Local<v8::Context> context, v8::Local<v8::Object> binding) {
999+ v8::Isolate* isolate = v8::Isolate::GetCurrent ();
10191000
1020- InstallDevFunction (isolate, context, dev , " configureRuntime" , ConfigureDevRuntimeCallback);
1021- InstallDevFunction (isolate, context, dev , " invalidateModules" , InvalidateModulesCallback);
1022- InstallDevFunction (isolate, context, dev , " getLoadedModuleUrls" , GetLoadedModuleUrlsCallback);
1023- InstallDevFunction (isolate, context, dev , " setDevBootComplete" , SetDevBootCompleteCallback);
1001+ InstallDevFunction (isolate, context, binding , " configureRuntime" , ConfigureDevRuntimeCallback);
1002+ InstallDevFunction (isolate, context, binding , " invalidateModules" , InvalidateModulesCallback);
1003+ InstallDevFunction (isolate, context, binding , " getLoadedModuleUrls" , GetLoadedModuleUrlsCallback);
1004+ InstallDevFunction (isolate, context, binding , " setDevBootComplete" , SetDevBootCompleteCallback);
10241005
1025- // Main-isolate only: terminating workers from inside a worker would let
1026- // a stuck worker take down its peers (see Worker.h).
1027- if (!isWorker) {
1028- InstallDevFunction (isolate, context, dev, " terminateAllWorkers" ,
1006+ // Main-realm only: terminating workers from inside a worker would let
1007+ // a stuck worker take down its peers (see Worker.h). A worker realm's
1008+ // `ns:runtime` simply has no such member, so feature checks work.
1009+ if (!Caches::Get (isolate)->isWorker ) {
1010+ InstallDevFunction (isolate, context, binding, " terminateAllWorkers" ,
10291011 Worker::TerminateAllWorkersCallback);
10301012 }
10311013
10321014 if (RuntimeConfig.IsDebug ) {
1033- try {
1034- // Debug-only diagnostic: expose the HTTP canonical-key function to JS so
1035- // the test harness can pin its identity behavior across cache-busters
1036- // and dev-endpoint query normalization.
1037- auto canonicalizeCb = []( const v8::FunctionCallbackInfo<v8::Value>& info) {
1038- v8::Isolate* iso = info. GetIsolate ();
1039- if ( info.Length () < 1 || !info[ 0 ]-> IsString ()) {
1040- info. GetReturnValue (). SetEmptyString () ;
1041- return ;
1042- }
1043- v8::String::Utf8Value u (iso, info[ 0 ] );
1044- std::string key = CanonicalizeHttpUrlKey (*u ? std::string (*u) : std::string ( ));
1045- info. GetReturnValue (). Set ( tns::ToV8String (iso, key. c_str ())) ;
1046- } ;
1047- v8::Local<v8:: Function> fn = v8::Function:: New (context, canonicalizeCb).ToLocalChecked ();
1015+ // Debug-only diagnostic: expose the HTTP canonical-key function to JS so
1016+ // the test harness can pin its identity behavior across cache-busters
1017+ // and dev-endpoint query normalization.
1018+ auto canonicalizeCb = []( const v8::FunctionCallbackInfo<v8::Value>& info) {
1019+ v8::Isolate* iso = info. GetIsolate ();
1020+ if (info. Length () < 1 || ! info[ 0 ]-> IsString ()) {
1021+ info.GetReturnValue (). SetEmptyString ();
1022+ return ;
1023+ }
1024+ v8::String::Utf8Value u (iso, info[ 0 ]);
1025+ std::string key = CanonicalizeHttpUrlKey (*u ? std::string (*u) : std::string () );
1026+ info. GetReturnValue (). Set ( tns::ToV8String (iso, key. c_str () ));
1027+ } ;
1028+ v8::Local<v8::Function> fn ;
1029+ if ( v8::Function:: New (context, canonicalizeCb).ToLocal (&fn)) {
10481030 fn->SetName (tns::ToV8String (isolate, " canonicalizeHttpUrlKey" ));
1049- dev->CreateDataProperty (context, tns::ToV8String (isolate, " canonicalizeHttpUrlKey" ), fn)
1050- .Check ();
1051- } catch (...) {
1052- // Don't crash if debug-diagnostic setup fails
1031+ if (!binding
1032+ ->CreateDataProperty (context, tns::ToV8String (isolate, " canonicalizeHttpUrlKey" ), fn)
1033+ .FromMaybe (false )) {
1034+ return false ;
1035+ }
10531036 }
10541037 }
10551038
1056- context->Global ()->Set (context, tns::ToV8String (isolate, " __NS_DEV__" ), dev).FromMaybe (false );
1057- MirrorGlobalOnGlobalThis (isolate, context, " __NS_DEV__" );
1039+ return true ;
10581040}
10591041
10601042} // namespace tns
0 commit comments