import { QuickDiffProvider , Uri , CancellationToken , WorkspaceFolder , workspace } from "vscode" ; import { utils } from "pddl-workspace" ; import * as path from 'path' ; import { compareMaps , throwForUndefined , assertDefined } from "../utils" ; import { SessionConfiguration , SessionMode } from "./SessionConfiguration" ; import { getText , postJson , getJson } from "../httpUtils" ; import { checkResponseForError } from "../catalog/PlanningDomains" ; /** Represents one Planning.Domains session and meta-data. */ export class SessionContent implements SessionConfiguration { constructor ( public readonly hash , public readonly writeHash , public readonly versionDate , public readonly files , public readonly plugins ) { } static from ( configuration , versionDate ) { if ( configuration . files === undefined ) { throw new Error ( "Failed assertion: SessionConfiguration.files undefined" ) ; } return new SessionContent ( configuration . hash , configuration . writeHash , versionDate , configuration . files , new Map ( ) ) ; } canCommit ( ) { return this . writeHash !== null && this . writeHash !== undefined ; } getHash ( ) { return this . writeHash ?? this . hash ?? throwForUndefined ( "One of read/write hash codes must be set." ) ; } } export function areIdentical ( first , second ) { return compareMaps ( first , second ) ; } export const SESSION_SCHEME = 'planning.domains.session' ; /** This binds the local and remote repository. */ export class SessionRepository implements QuickDiffProvider { private sessionHash : string ; constructor ( private readonly workspaceFolder , session ) { this . sessionHash = session . getHash ( ) ; } provideOriginalResource ( uri , _ ) { const workspaceFolder = workspace . getWorkspaceFolder ( uri ) ; if ( workspaceFolder ) { const fileName = workspace . asRelativePath ( uri , false ) ; return SessionRepository . createDocumentUri ( workspaceFolder . uri , fileName ) ; } else { return undefined ; } } static createDocumentUri ( workspaceFolder , fileName ) { return workspaceFolder . with ( { scheme : SESSION_SCHEME , query : fileName } ) ; } param fileName returns deprecated createLocalResourcePath ( fileName ) { return path . join ( this . workspaceFolder . uri . fsPath , fileName ) ; } param fileName returns createLocalResourceUri ( fileName ) { return Uri . joinPath ( this . workspaceFolder . uri , fileName ) ; } /**
     * Returns write or read session hash
     */ getSessionHash ( ) { return this . sessionHash ; } } const SESSION_URL = "http://editor.planning.domains/session/" ; const SESSION_PLUGINS_PATTERN = /define\s*\(\s*function\s*\(\s*\)\s*\{\s*return\s*{\s*meta:\s*true\s*,\s*plugins\s*:\s*({[\S\s]*})\s*}\s*}\s*\);/ms ; const SESSION_DETAILS_PATTERN = /window\.session_details\s*=\s*{\s*(?:readwrite_hash:\s*"(\w+)"\s*,\s*)?read_hash:\s*"(\w+)"\s*,\s*last_change:\s*"([\w: \(\)\+]+)",?\s*};/ ; param sessionId throws export async function checkSession ( sessionId ) { const url = `${ SESSION_URL }check/${ sessionId }` ; const response = await getJson ( url ) ; checkResponseForError ( response ) ; let sessionMode ; switch ( ( response [ "type" ] as string ) . toLowerCase ( ) ) { case "read" : sessionMode = SessionMode . READ_ONLY ; break ; case "readwrite" : sessionMode = SessionMode . READ_WRITE ; break ; default : throw new Error ( "Unexpected session type: " + response [ "type" ] ) ; } const sessionVersionDate = Math . floor ( Date . parse ( response [ "last_change" ] ) / 1000 ) * 1000 ; return [ sessionMode , sessionVersionDate ] ; } export async function getSession ( sessionConfiguration ) { const rawSession = await getRawSession ( sessionConfiguration ) ; const savedTabsJson = JSON . parse ( rawSession . domainFilesAsString ) ; const fileNames = Object . keys ( savedTabsJson ) ; const sessionFiles = new Map < string , string > ( ) ; fileNames . forEach ( fileName => sessionFiles . set ( fileName , savedTabsJson [ fileName ] ) ) ; return new SessionContent ( rawSession . readOnlyHash , rawSession . readWriteHash , rawSession . sessionDate , sessionFiles , rawSession . plugins ) ; } function createSessionContent ( pluginDefinitions ) { const sessionDefinitionAsString = JSON . stringify ( pluginDefinitions , null , 4 ) ; return `// Put this file online somewhere and import it as a plugin

	define(function () {
    return {
        meta: true,
		plugins:
${ sessionDefinitionAsString }
	}
});
` ; } export async function uploadSession ( session ) { if ( ! session . writeHash ) { throw new Error ( "Check if the session is writable first." ) ; } const rawLatestSession = await getRawSession ( session ) ; const newPluginList = [ ... rawLatestSession . plugins . keys ( ) ] . map ( oldPluginName => { const oldPlugin = rawLatestSession . plugins . get ( oldPluginName ) ! ; let newPlugin ; if ( oldPlugin . name === SAVE_TABS_PLUGIN_NAME ) { newPlugin = { name : oldPlugin . name , url : oldPlugin . url , settings : utils . serializationUtils . strMapToObj ( session . files ) , settingsAsString : undefined } ; } else { newPlugin = oldPlugin ; } return newPlugin ; } ) ; const newPlugins = Object . create ( null ) ; newPluginList . forEach ( plugin => { const newPlugin = Object . create ( null ) ; newPlugin [ "url" ] = plugin . url ; newPlugin [ "settings" ] = plugin . settings ; newPlugins [ plugin . name ] = newPlugin ; } ) ; const newContent = createSessionContent ( newPlugins ) ; const postBody = Object . create ( null ) ; postBody [ "content" ] = newContent ; postBody [ "read_hash" ] = session . hash ; postBody [ "readwrite_hash" ] = session . writeHash ; const url = `${ SESSION_URL }${ session . writeHash }` ; const postResult = await postJson ( url , postBody ) ; if ( postResult [ "error" ] ) { throw new Error ( postResult [ "message" ] ) ; } return getSession ( session ) ; } export async function duplicateSession ( session ) { const rawLatestOrigSession = await getRawSession ( session ) ; const newFilesAsString = JSON . stringify ( utils . serializationUtils . strMapToObj ( session . files ) , null , 4 ) ; const newContent = rawLatestOrigSession . sessionContent . replace ( rawLatestOrigSession . sessionDetails , '' ) . replace ( rawLatestOrigSession . domainFilesAsString , newFilesAsString ) ; const postBody = Object . create ( null ) ; postBody [ "content" ] = newContent ; const postResult = await postJson ( SESSION_URL , postBody ) ; if ( postResult [ "error" ] ) { throw new Error ( postResult [ "message" ] ) ; } return postResult [ "result" ] [ "readwrite_hash" ] ; } const SAVE_TABS_PLUGIN_NAME = "save-tabs" ; const SOLVER_PLUGIN_NAME = "solver" ; param sessionConfiguration async function getRawSession ( sessionConfiguration ) { const url = sessionConfiguration . writeHash ? `${ SESSION_URL }edit/${ sessionConfiguration . writeHash }` : `${ SESSION_URL }${ sessionConfiguration . hash }` ; const sessionContent = await getText ( url ) ; if ( sessionContent . match ( /not found/i ) ) { throw new Error ( `Session ${ sessionConfiguration . writeHash ?? sessionConfiguration . hash } not found.` ) ; } let sessionDetails ; let sessionDate ; let readWriteHash ; let readOnlyHash ; SESSION_DETAILS_PATTERN . lastIndex = 0 ; const matchDetails = SESSION_DETAILS_PATTERN . exec ( sessionContent ) ; if ( matchDetails ) { sessionDetails = matchDetails [ 0 ] ; readWriteHash = matchDetails [ 1 ] ; readOnlyHash = matchDetails [ 2 ] ; sessionDate = Date . parse ( matchDetails [ 3 ] ) ; } else { console . log ( "Malformed saved session. Could not extract session date. Session content:" + sessionContent ) ; throw new Error ( "Malformed saved session. Could not extract session date." ) ; } const plugins = new Map < string , RawSessionPlugin > ( ) ; SESSION_PLUGINS_PATTERN . lastIndex = 0 ; let pluginsMatch = SESSION_PLUGINS_PATTERN . exec ( sessionContent ) ; if ( pluginsMatch = SESSION_PLUGINS_PATTERN . exec ( sessionContent ) ) { const rawPlugins = JSON . parse ( pluginsMatch [ 1 ] ) ; [ SAVE_TABS_PLUGIN_NAME , SOLVER_PLUGIN_NAME ] . forEach ( pluginName => { if ( rawPlugins . hasOwnProperty ( pluginName ) ) { plugins . set ( pluginName , toRawSessionPlugin ( pluginName , rawPlugins [ pluginName ] ) ) ; } } ) ; } else { console . log ( "Malformed saved session plugins. Could not extract session plugins. Session content:" + sessionContent ) ; throw new Error ( "Malformed saved session. Could not extract session plugins." ) ; } if ( ! plugins . has ( SAVE_TABS_PLUGIN_NAME ) ) { throw new Error ( "Saved session contains no saved tabs." ) ; } const domainFilesString = plugins . get ( SAVE_TABS_PLUGIN_NAME ) ! . settingsAsString ; return { sessionDetails : sessionDetails , sessionContent : sessionContent , sessionDate : sessionDate , readOnlyHash : readOnlyHash , readWriteHash : readWriteHash , domainFilesAsString : assertDefined ( domainFilesString , "save-tabs plugin settings" ) , plugins : plugins , } ; } /** Session in its raw - freshly downloaded form. */ interface RawSession { readonly sessionDetails : string ; readonly sessionContent : string ; readonly sessionDate : number ; readonly readWriteHash : string ; readonly readOnlyHash : string ; readonly domainFilesAsString : string ; readonly plugins : Map < string , RawSessionPlugin > ; } function toRawSessionPlugin ( name , json ) { return { name : name , url : json [ "url" ] , settings : json [ "settings" ] , settingsAsString : JSON . stringify ( json [ "settings" ] , null , 4 ) } ; } interface RawSessionPlugin { readonly name : string ; readonly url : string ; readonly settingsAsString : string | undefined ; readonly settings : any ; }