@@ -44,6 +44,10 @@ using v8::Value;
4444namespace node {
4545namespace sea {
4646
47+ // The reserved asset key under which the ZIP archive named by "vfsArchive"
48+ // is stored. Must match the name used by lib/internal/vfs/sea.js.
49+ constexpr std::string_view kVfsArchiveAssetName = " node:sea:vfs.zip" ;
50+
4751namespace {
4852
4953SeaFlags operator |(SeaFlags x, SeaFlags y) {
@@ -246,7 +250,7 @@ bool SeaResource::use_code_cache() const {
246250 return static_cast <bool >(flags & SeaFlags::kUseCodeCache );
247251}
248252
249- SeaResource FindSingleExecutableResource () {
253+ const SeaResource& FindSingleExecutableResource () {
250254 static const SeaResource sea_resource = []() -> SeaResource {
251255 std::string_view blob = FindSingleExecutableBlob ();
252256 per_process::Debug (DebugCategory::SEA ,
@@ -266,12 +270,21 @@ void IsSea(const FunctionCallbackInfo<Value>& args) {
266270void IsVfsEnabled (const FunctionCallbackInfo<Value>& args) {
267271 bool enabled = false ;
268272 if (IsSingleExecutable ()) {
269- SeaResource sea_resource = FindSingleExecutableResource ();
273+ const SeaResource& sea_resource = FindSingleExecutableResource ();
270274 enabled = static_cast <bool >(sea_resource.flags & SeaFlags::kEnableVfs );
271275 }
272276 args.GetReturnValue ().Set (enabled);
273277}
274278
279+ void IsVfsArchiveEnabled (const FunctionCallbackInfo<Value>& args) {
280+ bool enabled = false ;
281+ if (IsSingleExecutable ()) {
282+ const SeaResource& sea_resource = FindSingleExecutableResource ();
283+ enabled = static_cast <bool >(sea_resource.flags & SeaFlags::kVfsArchive );
284+ }
285+ args.GetReturnValue ().Set (enabled);
286+ }
287+
275288void IsExperimentalSeaWarningNeeded (const FunctionCallbackInfo<Value>& args) {
276289 bool is_building_sea =
277290 !per_process::cli_options->experimental_sea_config .empty ();
@@ -285,7 +298,7 @@ void IsExperimentalSeaWarningNeeded(const FunctionCallbackInfo<Value>& args) {
285298 return ;
286299 }
287300
288- SeaResource sea_resource = FindSingleExecutableResource ();
301+ const SeaResource& sea_resource = FindSingleExecutableResource ();
289302 args.GetReturnValue ().Set (!static_cast <bool >(
290303 sea_resource.flags & SeaFlags::kDisableExperimentalSeaWarning ));
291304}
@@ -300,7 +313,7 @@ std::tuple<int, char**> FixupArgsForSEA(int argc,
300313 static std::vector<std::string> exec_argv_storage;
301314 static std::vector<std::string> cli_extension_args;
302315
303- SeaResource sea_resource = FindSingleExecutableResource ();
316+ const SeaResource& sea_resource = FindSingleExecutableResource ();
304317
305318 new_argv.clear ();
306319 exec_argv_storage.clear ();
@@ -466,6 +479,16 @@ std::optional<SeaConfig> ParseSingleExecutableConfig(
466479 if (use_vfs) {
467480 result.flags |= SeaFlags::kEnableVfs ;
468481 }
482+ } else if (key == " vfsArchive" ) {
483+ std::string_view archive_path;
484+ if (field.value ().get_string ().get (archive_path)) {
485+ FPrintF (stderr,
486+ " \" vfsArchive\" field of %s is not a string\n " ,
487+ config_path);
488+ return std::nullopt ;
489+ }
490+ result.vfs_archive_path = archive_path;
491+ result.flags |= SeaFlags::kVfsArchive ;
469492 } else if (key == " assets" ) {
470493 simdjson::ondemand::object assets_object;
471494 if (field.value ().get_object ().get (assets_object)) {
@@ -597,6 +620,26 @@ std::optional<SeaConfig> ParseSingleExecutableConfig(
597620 }
598621 }
599622
623+ if (static_cast <bool >(result.flags & SeaFlags::kVfsArchive )) {
624+ if (!static_cast <bool >(result.flags & SeaFlags::kEnableVfs )) {
625+ FPrintF (stderr, " \" vfsArchive\" requires \" useVfs\" to be true\n " );
626+ return std::nullopt ;
627+ }
628+ if (!result.assets .empty ()) {
629+ FPrintF (stderr,
630+ " \" vfsArchive\" cannot be used together with \" assets\"\n " );
631+ return std::nullopt ;
632+ }
633+ if (result.vfs_archive_path .empty ()) {
634+ FPrintF (stderr,
635+ " \" vfsArchive\" field of %s is not a non-empty string\n " ,
636+ config_path);
637+ return std::nullopt ;
638+ }
639+ // The archive is embedded as a single reserved asset.
640+ result.flags |= SeaFlags::kIncludeAssets ;
641+ }
642+
600643 if (result.main_path .empty ()) {
601644 FPrintF (stderr,
602645 " \" main\" field of %s is not a non-empty string\n " ,
@@ -808,6 +851,27 @@ ExitCode GenerateSingleExecutableBlob(
808851 if (!config.assets .empty () && BuildAssets (config.assets , &assets) != 0 ) {
809852 return ExitCode::kGenericUserError ;
810853 }
854+ if (static_cast <bool >(config.flags & SeaFlags::kVfsArchive )) {
855+ std::string archive;
856+ int r = ReadFileSync (&archive, config.vfs_archive_path .c_str ());
857+ if (r != 0 ) {
858+ const char * err = uv_strerror (r);
859+ FPrintF (stderr,
860+ " Cannot read vfsArchive %s: %s\n " ,
861+ config.vfs_archive_path ,
862+ err);
863+ return ExitCode::kGenericUserError ;
864+ }
865+ // Only a signature sanity check; the archive is parsed by the ZIP
866+ // support in JS when the executable starts.
867+ if (archive.size () < 4 || archive[0 ] != ' P' || archive[1 ] != ' K' ) {
868+ FPrintF (stderr,
869+ " vfsArchive %s is not a ZIP archive\n " ,
870+ config.vfs_archive_path );
871+ return ExitCode::kGenericUserError ;
872+ }
873+ assets.emplace (std::string (kVfsArchiveAssetName ), std::move (archive));
874+ }
811875 std::unordered_map<std::string_view, std::string_view> assets_view;
812876 for (auto const & [key, content] : assets) {
813877 assets_view.emplace (key, content);
@@ -868,7 +932,7 @@ void GetAsset(const FunctionCallbackInfo<Value>& args) {
868932 CHECK_EQ (args.Length (), 1 );
869933 CHECK (args[0 ]->IsString ());
870934 Utf8Value key (args.GetIsolate (), args[0 ]);
871- SeaResource sea_resource = FindSingleExecutableResource ();
935+ const SeaResource& sea_resource = FindSingleExecutableResource ();
872936 if (sea_resource.assets .empty ()) {
873937 return ;
874938 }
@@ -892,7 +956,7 @@ void GetAsset(const FunctionCallbackInfo<Value>& args) {
892956void GetAssetKeys (const FunctionCallbackInfo<Value>& args) {
893957 CHECK_EQ (args.Length (), 0 );
894958 Isolate* isolate = args.GetIsolate ();
895- SeaResource sea_resource = FindSingleExecutableResource ();
959+ const SeaResource& sea_resource = FindSingleExecutableResource ();
896960
897961 Local<Context> context = isolate->GetCurrentContext ();
898962 LocalVector<Value> keys (isolate);
@@ -914,7 +978,7 @@ MaybeLocal<Value> LoadSingleExecutableApplication(
914978 // env->context() is entered.
915979 Environment* env = info.env ();
916980 Local<Context> context = env->context ();
917- SeaResource sea = FindSingleExecutableResource ();
981+ const SeaResource& sea = FindSingleExecutableResource ();
918982
919983 CHECK (!sea.use_snapshot ());
920984 // TODO(joyeecheung): this should be an external string. Refactor UnionBytes
@@ -936,7 +1000,7 @@ bool MaybeLoadSingleExecutableApplication(Environment* env) {
9361000 return false ;
9371001 }
9381002
939- SeaResource sea = FindSingleExecutableResource ();
1003+ const SeaResource& sea = FindSingleExecutableResource ();
9401004
9411005 if (sea.use_snapshot ()) {
9421006 // The SEA preparation blob building process should already enforce this,
@@ -962,7 +1026,7 @@ void Initialize(Local<Object> target,
9621026 Isolate* isolate = env->isolate ();
9631027
9641028 if (IsSingleExecutable ()) {
965- SeaResource sea_resource = FindSingleExecutableResource ();
1029+ const SeaResource& sea_resource = FindSingleExecutableResource ();
9661030 // Expose the main script path recorded in the SEA config so the VFS
9671031 // integration can place the main script at the mount point root.
9681032 if (static_cast <bool >(sea_resource.flags & SeaFlags::kEnableVfs )) {
@@ -983,6 +1047,7 @@ void Initialize(Local<Object> target,
9831047
9841048 SetMethod (context, target, " isSea" , IsSea);
9851049 SetMethod (context, target, " isVfsEnabled" , IsVfsEnabled);
1050+ SetMethod (context, target, " isVfsArchiveEnabled" , IsVfsArchiveEnabled);
9861051 SetMethod (context,
9871052 target,
9881053 " isExperimentalSeaWarningNeeded" ,
@@ -994,6 +1059,7 @@ void Initialize(Local<Object> target,
9941059void RegisterExternalReferences (ExternalReferenceRegistry* registry) {
9951060 registry->Register (IsSea);
9961061 registry->Register (IsVfsEnabled);
1062+ registry->Register (IsVfsArchiveEnabled);
9971063 registry->Register (IsExperimentalSeaWarningNeeded);
9981064 registry->Register (GetAsset);
9991065 registry->Register (GetAssetKeys);
0 commit comments