Files
2026-06-24 07:04:59 +02:00

436 lines
12 KiB
JavaScript

/**
* Workspace Manager Module
* Handles request collections, workspaces, environments, and persistence
*/
const WorkspaceManager = (() => {
"use strict";
const STORAGE_KEY = 'DoxaApi.workspaces';
const CURRENT_WS_KEY = 'DoxaApi.currentWorkspace';
const ENV_KEY = 'DoxaApi.environments';
/**
* Workspace structure
*/
class Workspace {
constructor(name = 'Default') {
this.id = generateId();
this.name = name;
this.description = '';
this.collections = [];
this.createdAt = Date.now();
this.updatedAt = Date.now();
}
}
/**
* Collection structure (folders for organizing requests)
*/
class Collection {
constructor(name = 'New Collection') {
this.id = generateId();
this.name = name;
this.description = '';
this.requests = [];
this.folders = [];
this.createdAt = Date.now();
}
}
/**
* Request in a collection
*/
class SavedRequest {
constructor(name = 'New Request', method = 'GET', path = '') {
this.id = generateId();
this.name = name;
this.method = method;
this.path = path;
this.description = '';
this.tags = [];
this.params = {
path: {},
query: {},
header: {},
cookie: {}
};
this.body = null;
this.bodyType = 'json';
this.formData = {};
this.auth = null;
this.createdAt = Date.now();
this.updatedAt = Date.now();
}
clone() {
const copy = new SavedRequest(this.name, this.method, this.path);
copy.id = generateId();
copy.description = this.description;
copy.tags = [...this.tags];
copy.params = JSON.parse(JSON.stringify(this.params));
copy.body = this.body;
copy.bodyType = this.bodyType;
copy.formData = { ...this.formData };
copy.auth = this.auth;
return copy;
}
}
/**
* Environment for variable substitution
*/
class Environment {
constructor(name = 'Default') {
this.id = generateId();
this.name = name;
this.description = '';
this.variables = {};
this.isActive = false;
this.createdAt = Date.now();
}
}
/**
* Generate unique ID
*/
function generateId() {
return 'id_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
}
/**
* Get all workspaces
*/
function getWorkspaces() {
try {
const data = localStorage.getItem(STORAGE_KEY);
return data ? JSON.parse(data).map(deserializeWorkspace) : [];
} catch (e) {
console.error('Failed to load workspaces:', e);
return [];
}
}
/**
* Get current active workspace
*/
function getCurrentWorkspace() {
const workspaces = getWorkspaces();
if (workspaces.length === 0) {
const ws = new Workspace('Default');
saveWorkspace(ws);
return ws;
}
return workspaces[0];
}
/**
* Save workspace
*/
function saveWorkspace(workspace) {
workspace.updatedAt = Date.now();
const workspaces = getWorkspaces();
const index = workspaces.findIndex(w => w.id === workspace.id);
if (index >= 0) {
workspaces[index] = workspace;
} else {
workspaces.push(workspace);
}
localStorage.setItem(STORAGE_KEY, JSON.stringify(workspaces));
return workspace;
}
/**
* Delete workspace
*/
function deleteWorkspace(workspaceId) {
let workspaces = getWorkspaces();
workspaces = workspaces.filter(w => w.id !== workspaceId);
localStorage.setItem(STORAGE_KEY, JSON.stringify(workspaces));
}
/**
* Add collection to workspace
*/
function addCollection(workspaceId, collection) {
const workspace = getWorkspaces().find(w => w.id === workspaceId);
if (workspace) {
workspace.collections.push(collection);
saveWorkspace(workspace);
}
return collection;
}
/**
* Save request to collection
*/
function saveRequestToCollection(workspaceId, collectionId, request) {
const workspace = getWorkspaces().find(w => w.id === workspaceId);
if (!workspace) return null;
const collection = findCollectionRecursive(workspace.collections, collectionId);
if (!collection) return null;
request.updatedAt = Date.now();
const index = collection.requests.findIndex(r => r.id === request.id);
if (index >= 0) {
collection.requests[index] = request;
} else {
collection.requests.push(request);
}
saveWorkspace(workspace);
return request;
}
/**
* Find collection recursively in folder structure
*/
function findCollectionRecursive(collections, collectionId) {
for (const col of collections) {
if (col.id === collectionId) return col;
if (col.folders) {
const found = findCollectionRecursive(col.folders, collectionId);
if (found) return found;
}
}
return null;
}
/**
* Get all requests in workspace
*/
function getAllRequests(workspaceId) {
const workspace = getWorkspaces().find(w => w.id === workspaceId);
if (!workspace) return [];
const requests = [];
function traverse(collections) {
collections.forEach(col => {
requests.push(...(col.requests || []));
if (col.folders) {
traverse(col.folders);
}
});
}
traverse(workspace.collections);
return requests;
}
/**
* Search requests in workspace
*/
function searchRequests(workspaceId, query) {
const allRequests = getAllRequests(workspaceId);
const lowerQuery = query.toLowerCase();
return allRequests.filter(req =>
req.name.toLowerCase().includes(lowerQuery) ||
req.path.toLowerCase().includes(lowerQuery) ||
req.description.toLowerCase().includes(lowerQuery) ||
(req.tags && req.tags.some(t => t.toLowerCase().includes(lowerQuery)))
);
}
/**
* Get environments
*/
function getEnvironments() {
try {
const data = localStorage.getItem(ENV_KEY);
return data ? JSON.parse(data).map(deserializeEnvironment) : [];
} catch (e) {
console.error('Failed to load environments:', e);
return [];
}
}
/**
* Save environment
*/
function saveEnvironment(environment) {
const environments = getEnvironments();
const index = environments.findIndex(e => e.id === environment.id);
if (index >= 0) {
environments[index] = environment;
} else {
environments.push(environment);
}
localStorage.setItem(ENV_KEY, JSON.stringify(environments));
return environment;
}
/**
* Delete environment
*/
function deleteEnvironment(environmentId) {
let environments = getEnvironments();
environments = environments.filter(e => e.id !== environmentId);
localStorage.setItem(ENV_KEY, JSON.stringify(environments));
}
/**
* Get active environment
*/
function getActiveEnvironment() {
const environments = getEnvironments();
return environments.find(e => e.isActive) || null;
}
/**
* Set active environment
*/
function setActiveEnvironment(environmentId) {
const environments = getEnvironments();
environments.forEach(e => e.isActive = e.id === environmentId);
localStorage.setItem(ENV_KEY, JSON.stringify(environments));
}
/**
* Substitute variables in string
*/
function substituteVariables(str, environment) {
if (!str || !environment) return str;
let result = str;
Object.entries(environment.variables).forEach(([name, value]) => {
result = result.replace(new RegExp(`\\$\\{${name}\\}|\\$${name}\\b`, 'g'), String(value || ''));
});
return result;
}
/**
* Export workspace as JSON
*/
function exportWorkspace(workspaceId) {
const workspace = getWorkspaces().find(w => w.id === workspaceId);
if (!workspace) return null;
return {
version: '1.0',
exported: new Date().toISOString(),
workspace: workspace,
environments: getEnvironments()
};
}
/**
* Import workspace from JSON
*/
function importWorkspace(json) {
try {
const data = typeof json === 'string' ? JSON.parse(json) : json;
// Validate version
if (!data.version) throw new Error('Invalid format: missing version');
// Create new workspace with imported data
const workspace = data.workspace;
workspace.id = generateId();
workspace.createdAt = Date.now();
workspace.updatedAt = Date.now();
// Import environments
if (data.environments && Array.isArray(data.environments)) {
data.environments.forEach(env => {
env.id = generateId();
saveEnvironment(env);
});
}
// Save workspace
return saveWorkspace(workspace);
} catch (e) {
console.error('Failed to import workspace:', e);
return null;
}
}
/**
* Duplicate request
*/
function duplicateRequest(request) {
const duplicate = request.clone();
duplicate.name = `${request.name} (copy)`;
return duplicate;
}
/**
* Duplicate collection
*/
function duplicateCollection(collection) {
const duplicate = new Collection(collection.name + ' (copy)');
duplicate.description = collection.description;
duplicate.requests = collection.requests.map(r => r.clone ? r.clone() : r);
if (collection.folders) {
duplicate.folders = collection.folders.map(f => duplicateCollection(f));
}
return duplicate;
}
/**
* Get recent requests
*/
function getRecentRequests(workspaceId, limit = 10) {
return getAllRequests(workspaceId)
.sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0))
.slice(0, limit);
}
/**
* Deserialize workspace (helper for JSON parsing)
*/
function deserializeWorkspace(data) {
const ws = Object.assign(new Workspace(data.name), data);
return ws;
}
/**
* Deserialize environment (helper for JSON parsing)
*/
function deserializeEnvironment(data) {
const env = Object.assign(new Environment(data.name), data);
return env;
}
// Public API
return {
Workspace,
Collection,
SavedRequest,
Environment,
generateId,
getWorkspaces,
getCurrentWorkspace,
saveWorkspace,
deleteWorkspace,
addCollection,
saveRequestToCollection,
getAllRequests,
searchRequests,
getEnvironments,
saveEnvironment,
deleteEnvironment,
getActiveEnvironment,
setActiveEnvironment,
substituteVariables,
exportWorkspace,
importWorkspace,
duplicateRequest,
duplicateCollection,
getRecentRequests
};
})();
// Export for use in other modules
if (typeof module !== 'undefined' && module.exports) {
module.exports = WorkspaceManager;
}