UNPKG

@minecraft/creator-tools

Version:

Minecraft Creator Tools command line and libraries.

345 lines (344 loc) 15.9 kB
"use strict"; // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.StorageProxyCommands = void 0; const Log_1 = __importDefault(require("../core/Log")); const Utilities_1 = __importDefault(require("../core/Utilities")); const StorageUtilities_1 = __importDefault(require("./StorageUtilities")); var StorageProxyCommands; (function (StorageProxyCommands) { StorageProxyCommands["fsExists"] = "fsExists"; StorageProxyCommands["fsFolderExists"] = "fsFolderExists"; StorageProxyCommands["fsMkdir"] = "fsMkdir"; StorageProxyCommands["fsReadUtf8File"] = "fsReadUtf8File"; StorageProxyCommands["fsReadFile"] = "fsReadFile"; StorageProxyCommands["fsWriteUtf8File"] = "fsWriteUtf8File"; StorageProxyCommands["fsWriteFile"] = "fsWriteFile"; StorageProxyCommands["fsReaddir"] = "fsReaddir"; StorageProxyCommands["fsStat"] = "fsStat"; StorageProxyCommands["fsDeleteItem"] = "fsDeleteItem"; StorageProxyCommands["getDirname"] = "getDirname"; StorageProxyCommands["notifyFileAdded"] = "notifyFileAdded"; StorageProxyCommands["notifyFileContentsUpdated"] = "notifyFileContentsUpdated"; StorageProxyCommands["notifyFileRemoved"] = "notifyFileRemoved"; })(StorageProxyCommands || (exports.StorageProxyCommands = StorageProxyCommands = {})); class StorageProxy { _storage; _id; isReadOnly = false; encodeBinary = false; _subscribersRetriever = undefined; _sendMessage; _alternateContents = {}; constructor(storage, id, sendMessage, subscribersRetriever) { this._storage = storage; this.onFileAdded = this.onFileAdded.bind(this); this.onFileContentsUpdated = this.onFileContentsUpdated.bind(this); this.onFileRemoved = this.onFileRemoved.bind(this); this._storage.onFileAdded.subscribe(this.onFileAdded); this._storage.onFileContentsUpdated.subscribe(this.onFileContentsUpdated); this._storage.onFileRemoved.subscribe(this.onFileRemoved); this._id = id; this._subscribersRetriever = subscribersRetriever; this._sendMessage = sendMessage; } /** * Converts an absolute path (which may include the storage root URI) to a relative path. * The MessageProxyStorage on the webview side sends full paths like "vscode-test-web://mount/subfolder/" * but getFolderFromRelativePath expects paths like "/subfolder/". */ toRelativePath(absolutePath) { // Get the storage root path (e.g., "vscode-test-web://mount/" or "file:///c:/workspace/") const storageRoot = this._storage.rootFolder.fullPath; // If the absolute path starts with the storage root, strip it if (absolutePath.toLowerCase().startsWith(storageRoot.toLowerCase())) { let relativePath = absolutePath.substring(storageRoot.length); // Ensure it starts with / if (!relativePath.startsWith("/")) { relativePath = "/" + relativePath; } return relativePath; } // If it's already a relative path starting with /, return as-is if (absolutePath.startsWith("/")) { return absolutePath; } // Otherwise, treat as relative and prepend / return "/" + absolutePath; } registerAlternateContents(storageRelativePath, contents) { this._alternateContents[StorageUtilities_1.default.canonicalizePath(storageRelativePath)] = contents; } clearAlternateContents(storageRelativePath) { this._alternateContents[StorageUtilities_1.default.canonicalizePath(storageRelativePath)] = undefined; } onFileAdded(storage, file) { const senders = this._subscribersRetriever.getSubscribers(this._id); if (!senders) { Log_1.default.unexpectedUndefined("OFA"); return; } for (let i = 0; i < senders.length; i++) { this._sendMessage(senders[i], StorageProxyCommands.notifyFileAdded, this._id, file.storageRelativePath); } } onFileRemoved(storage, path) { const senders = this._subscribersRetriever.getSubscribers(this._id); if (!senders) { Log_1.default.unexpectedUndefined("OFR"); return; } for (let i = 0; i < senders.length; i++) { this._sendMessage(senders[i], StorageProxyCommands.notifyFileRemoved, this._id, path); } } onFileContentsUpdated(storage, fileEvent) { const senders = this._subscribersRetriever.getSubscribers(this._id); if (!senders) { Log_1.default.unexpectedUndefined("OFCU"); return; } for (let i = 0; i < senders.length; i++) { this._sendMessage(senders, StorageProxyCommands.notifyFileContentsUpdated, this._id, fileEvent.file.storageRelativePath); } } /** * Normalizes a path/ID by removing trailing slashes for comparison purposes. */ normalizeId(id) { // Remove trailing slashes for consistent comparison while (id.endsWith("/") || id.endsWith("\\")) { id = id.substring(0, id.length - 1); } return id.toLowerCase(); } /** * Checks if an incoming message ID matches this storage proxy. * Returns true if the ID matches exactly or if the ID is a child path of this storage's root. */ matchesId(incomingId) { const normalizedIncoming = this.normalizeId(incomingId); const normalizedSelf = this.normalizeId(this._id); // Exact match if (normalizedIncoming === normalizedSelf) { Log_1.default.verbose(`StorageProxy matchesId: exact match for ${incomingId}`); return true; } // Check if incoming ID is a child path of this storage's root // e.g., "vscode-test-web://mount/project/subfolder" matches storage "vscode-test-web://mount/project" if (normalizedIncoming.startsWith(normalizedSelf + "/") || normalizedIncoming.startsWith(normalizedSelf + "\\")) { Log_1.default.verbose(`StorageProxy matchesId: child path match for ${incomingId} under ${this._id}`); return true; } Log_1.default.verbose(`StorageProxy matchesId: NO match - incoming=${incomingId}, self=${this._id}`); return false; } async processMessage(sender, command, id, data) { let baseCommandName = command; // Check if this message is for this storage proxy (exact match or child path) if (!this.matchesId(id)) { return; } if (baseCommandName.startsWith("async")) { baseCommandName = baseCommandName.substring(5); } const baseCommandSegments = baseCommandName.split("|"); if (baseCommandSegments.length > 1) { baseCommandName = baseCommandSegments[0]; } switch (baseCommandName) { case StorageProxyCommands.fsExists: await this.fileExists(sender, command, id, data); break; case StorageProxyCommands.fsFolderExists: await this.folderExists(sender, command, id, data); break; case StorageProxyCommands.fsMkdir: this.readOnlyCheck(); await this.createDirectory(sender, command, id, data); break; case StorageProxyCommands.fsReadUtf8File: await this.readUtf8File(sender, command, id, data); break; case StorageProxyCommands.fsReadFile: await this.readFile(sender, command, id, data); break; case StorageProxyCommands.fsWriteFile: this.readOnlyCheck(); await this.writeFile(sender, command, id, data); break; case StorageProxyCommands.fsWriteUtf8File: this.readOnlyCheck(); await this.writeUtf8File(sender, command, id, data); break; case StorageProxyCommands.fsReaddir: await this.readDir(sender, command, id, data); break; case StorageProxyCommands.fsStat: await this.stat(sender, command, id, data); break; } } readOnlyCheck() { if (this.isReadOnly) { Log_1.default.fail("Trying to perform a write operation on read-only storage."); throw new Error("Trying to perform a write operation on read-only storage."); } } fileIsContainer() { } async writeUtf8File(sender, command, requestId, data) { Log_1.default.assert(typeof data.path === "string", "StorageProxy writeUtf8File not expected type."); const relativePath = this.toRelativePath(data.path); const ensureFile = await this._storage.rootFolder.ensureFileFromRelativePath(relativePath); if (!ensureFile) { this._sendMessage(sender, command, requestId, undefined); return; } // Log.verbose("Setting UTF8 content for '" + data.path + "' " + data.content); ensureFile.setContent(data.content); await ensureFile.saveContent(); this._sendMessage(sender, command, requestId, undefined); } async writeFile(sender, command, requestId, data) { Log_1.default.assert(typeof data.path === "string", "Data path not expected string type."); const relativePath = this.toRelativePath(data.path); const ensureFile = await this._storage.rootFolder.ensureFileFromRelativePath(relativePath); if (!ensureFile) { this._sendMessage(sender, command, requestId, undefined); return; } if (typeof data.content === "string") { data.content = Utilities_1.default.base64ToUint8Array(data.content); } // Log.verbose("Setting content" + data.content); ensureFile.setContent(data.content); await ensureFile.saveContent(); this._sendMessage(sender, command, requestId, undefined); } async readUtf8File(sender, command, requestId, data) { Log_1.default.assert(typeof data === "string", "SPRUF"); const relativePath = this.toRelativePath(data); const ensureFile = await this._storage.rootFolder.getFileFromRelativePath(relativePath); if (!ensureFile) { this._sendMessage(sender, command, requestId, undefined); return; } const canonPath = StorageUtilities_1.default.canonicalizePath(ensureFile.fullPath); if (!ensureFile.isContentLoaded) { await ensureFile.loadContent(); } if (this._alternateContents[canonPath] !== undefined) { Log_1.default.verbose("Loading alternate text content for '" + canonPath + "'"); this._sendMessage(sender, command, requestId, this._alternateContents[canonPath]); return; } // Log.verbose("Reading UTF8 content for '" + ensureFile.fullPath + "' " + ensureFile.content); this._sendMessage(sender, command, requestId, ensureFile.content); } async readFile(sender, command, requestId, data) { Log_1.default.assert(typeof data === "string", "SPXRF"); const relativePath = this.toRelativePath(data); const ensureFile = await this._storage.rootFolder.getFileFromRelativePath(relativePath); if (!ensureFile) { this._sendMessage(sender, command, requestId, undefined); return; } let content = undefined; if (!ensureFile.isContentLoaded) { await ensureFile.loadContent(); } const canonPath = StorageUtilities_1.default.canonicalizePath(ensureFile.fullPath); if (this._alternateContents[canonPath] !== undefined) { // Log.debug("Loading alternate content for '" + canonPath + "'"); content = this._alternateContents[canonPath]; } else { content = ensureFile.content; } if (content && typeof content !== "string" && this.encodeBinary) { content = Utilities_1.default.arrayBufferToBase64(content); } // Log.verbose("Reading bin content for '" + ensureFile.fullPath + "' " + ensureFile.content); if (content === null) { content = "|null|"; } this._sendMessage(sender, command, requestId, content); } async createDirectory(sender, command, requestId, data) { Log_1.default.assert(typeof data === "string", "SPXCD"); const relativePath = this.toRelativePath(data); const ensureFolder = await this._storage.rootFolder.ensureFolderFromRelativePath(relativePath); if (!ensureFolder) { this._sendMessage(sender, command, requestId, false); return; } const ensureExistsResult = await ensureFolder.ensureExists(); this._sendMessage(sender, command, requestId, ensureExistsResult); } async folderExists(sender, command, requestId, data) { Log_1.default.assert(typeof data === "string", "SPXFOE"); const relativePath = this.toRelativePath(data); const folder = await this._storage.rootFolder.getFolderFromRelativePath(relativePath); if (!folder) { this._sendMessage(sender, command, requestId, false); return; } const resultFolder = await folder.exists(); this._sendMessage(sender, command, requestId, resultFolder); } async readDir(sender, command, requestId, data) { Log_1.default.assert(typeof data === "string", "SPXRD"); const relativePath = this.toRelativePath(data); const folder = await this._storage.rootFolder.getFolderFromRelativePath(relativePath); if (!folder) { this._sendMessage(sender, command, requestId, []); return; } if (!folder.isLoaded) { await folder.load(); } const fileNames = []; for (const fileName in folder.files) { fileNames.push(fileName); } for (const folderName in folder.folders) { fileNames.push(folderName); } this._sendMessage(sender, command, requestId, fileNames); } async stat(sender, command, requestId, data) { Log_1.default.assert(typeof data === "string", "SPXST"); const relativePath = this.toRelativePath(data); const folder = await this._storage.rootFolder.getFolderFromRelativePath(relativePath); if (!folder) { const file = await this._storage.rootFolder.getFileFromRelativePath(relativePath); if (file) { this._sendMessage(sender, command, requestId, { isDirectory: false, isFile: true, mtime: file.modified }); } else { this._sendMessage(sender, command, requestId, undefined); } return; } if (!folder.isLoaded) { await folder.load(); } this._sendMessage(sender, command, requestId, { isDirectory: true, isFile: false }); } async fileExists(sender, command, requestId, data) { Log_1.default.assert(typeof data === "string", "SPXFE"); const relativePath = this.toRelativePath(data); const file = await this._storage.rootFolder.getFileFromRelativePath(relativePath); if (!file) { this._sendMessage(sender, command, requestId, false); return; } const result = await file.exists; this._sendMessage(sender, command, requestId, result); } } exports.default = StorageProxy;