mcp-casual-interview
Version:
MCP server for casual interview preparation workflow management
193 lines • 7.69 kB
JavaScript
import { ok, err } from 'neverthrow';
import { getCurrentState, forceSetState, updateNotionPageUrl, getNotionPageUrl, clearState } from '../workflow-state-storage.js';
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// In-Memory Storage Implementation (for development/testing)
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
class InMemoryWorkflowState {
state = 'information_initially_registered';
notionPageUrl = null; // null means unregistered
lastUpdated = new Date();
get() {
try {
return ok(this.state);
}
catch (cause) {
return err({
type: 'StorageUnavailable',
message: 'Failed to retrieve workflow state from memory',
cause
});
}
}
set(newState) {
try {
this.state = newState;
this.lastUpdated = new Date();
return ok(undefined);
}
catch (cause) {
return err({
type: 'StorageUnavailable',
message: 'Failed to store workflow state in memory',
cause
});
}
}
clear() {
try {
this.state = 'information_initially_registered';
this.lastUpdated = new Date();
return ok(undefined);
}
catch (cause) {
return err({
type: 'StorageUnavailable',
message: 'Failed to clear workflow state from memory',
cause
});
}
}
getLastUpdated() {
return this.lastUpdated;
}
getNotionPageUrl() {
try {
// Return "未登録です" if URL is not registered (null)
return ok(this.notionPageUrl ?? "未登録です");
}
catch (cause) {
return err({
type: 'StorageUnavailable',
message: 'Failed to retrieve notion page URL from memory',
cause
});
}
}
setNotionPageUrl(url) {
try {
this.notionPageUrl = url;
this.lastUpdated = new Date();
return ok(undefined);
}
catch (cause) {
return err({
type: 'StorageUnavailable',
message: 'Failed to store notion page URL in memory',
cause
});
}
}
}
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Storage Implementations
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Global instance for in-memory storage
const inMemoryStorage = new InMemoryWorkflowState();
/**
* Database storage implementation (placeholder for future use)
*/
const createDatabaseStorageEffects = (connectionString) => {
// For now, delegate to in-memory storage
// This can be extended to use actual database persistence
return {
get: () => inMemoryStorage.get(),
set: (state) => inMemoryStorage.set(state),
clear: () => inMemoryStorage.clear(),
getNotionPageUrl: () => inMemoryStorage.getNotionPageUrl(),
setNotionPageUrl: (url) => inMemoryStorage.setNotionPageUrl(url)
};
};
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Error Mapping Utilities
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
const mapStorageErrorToWorkflowError = (storageError) => {
switch (storageError.type) {
case 'StorageUnavailable':
return {
type: 'ValidationFailed',
message: `Storage unavailable: ${storageError.message}`
};
case 'SerializationError':
return {
type: 'ValidationFailed',
message: `Serialization error: ${storageError.message}`
};
case 'StateNotFound':
return {
type: 'ValidationFailed',
message: `State not found: ${storageError.message}`
};
default:
const _exhaustive = storageError.type;
return {
type: 'ValidationFailed',
message: `Unknown storage error: ${_exhaustive}`
};
}
};
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Public API
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Default effects using new file-based storage
export const WorkflowStateEffects = {
getWorkflowState: async () => {
const result = await getCurrentState();
return result
.map(data => data.state)
.mapErr(error => ({
type: 'StorageUnavailable',
message: error.message,
cause: error.cause
}));
},
setWorkflowState: async (state) => {
const result = await forceSetState(state);
return result
.map(() => undefined)
.mapErr(error => ({
type: 'StorageUnavailable',
message: error.message,
cause: error.cause
}));
},
clearWorkflowState: async () => {
const result = await clearState();
return result
.map(() => undefined)
.mapErr(error => ({
type: 'StorageUnavailable',
message: error.message,
cause: error.cause
}));
},
getNotionPageUrl: async () => {
const result = await getNotionPageUrl();
return result
.mapErr(error => ({
type: 'StorageUnavailable',
message: error.message,
cause: error.cause
}));
},
setNotionPageUrl: async (url) => {
const result = await updateNotionPageUrl(url);
return result
.map(() => undefined)
.mapErr(error => ({
type: 'StorageUnavailable',
message: error.message,
cause: error.cause
}));
},
getLastUpdated: () => {
// This is synchronous, so we'll return current time as fallback
return new Date();
}
};
// Effect factories for different storage types (legacy compatibility)
export const createWorkflowStateEffectsWithConfig = () => WorkflowStateEffects;
// Utilities
export const ErrorMapping = {
mapStorageErrorToWorkflowError
};
//# sourceMappingURL=index.js.map