"use strict";
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Drive = exports.ContentsManager = exports.Contents = void 0;
const coreutils_1 = require("@jupyterlab/coreutils");
const algorithm_1 = require("@lumino/algorithm");
const signaling_1 = require("@lumino/signaling");
const __1 = require("..");
const validate = __importStar(require("./validate"));
/**
 * The url for the default drive service.
 */
const SERVICE_DRIVE_URL = 'api/contents';
/**
 * The url for the file access.
 */
const FILES_URL = 'files';
/**
 * A namespace for contents interfaces.
 */
var Contents;
(function (Contents) {
    /**
     * Validates an IModel, throwing an error if it does not pass.
     */
    function validateContentsModel(contents) {
        validate.validateContentsModel(contents);
    }
    Contents.validateContentsModel = validateContentsModel;
    /**
     * Validates an ICheckpointModel, throwing an error if it does not pass.
     */
    function validateCheckpointModel(checkpoint) {
        validate.validateCheckpointModel(checkpoint);
    }
    Contents.validateCheckpointModel = validateCheckpointModel;
})(Contents = exports.Contents || (exports.Contents = {}));
/**
 * A contents manager that passes file operations to the server.
 * Multiple servers implementing the `IDrive` interface can be
 * attached to the contents manager, so that the same session can
 * perform file operations on multiple backends.
 *
 * This includes checkpointing with the normal file operations.
 */
class ContentsManager {
    /**
     * Construct a new contents manager object.
     *
     * @param options - The options used to initialize the object.
     */
    constructor(options = {}) {
        var _a, _b;
        this._isDisposed = false;
        this._additionalDrives = new Map();
        this._fileChanged = new signaling_1.Signal(this);
        const serverSettings = (this.serverSettings = (_a = options.serverSettings) !== null && _a !== void 0 ? _a : __1.ServerConnection.makeSettings());
        this._defaultDrive = (_b = options.defaultDrive) !== null && _b !== void 0 ? _b : new Drive({ serverSettings });
        this._defaultDrive.fileChanged.connect(this._onFileChanged, this);
    }
    /**
     * A signal emitted when a file operation takes place.
     */
    get fileChanged() {
        return this._fileChanged;
    }
    /**
     * Test whether the manager has been disposed.
     */
    get isDisposed() {
        return this._isDisposed;
    }
    /**
     * Dispose of the resources held by the manager.
     */
    dispose() {
        if (this.isDisposed) {
            return;
        }
        this._isDisposed = true;
        signaling_1.Signal.clearData(this);
    }
    /**
     * Add an `IDrive` to the manager.
     */
    addDrive(drive) {
        this._additionalDrives.set(drive.name, drive);
        drive.fileChanged.connect(this._onFileChanged, this);
    }
    /**
     * Given a path, get a ModelDB.IFactory from the
     * relevant backend. Returns `undefined` if the backend
     * does not provide one.
     */
    getModelDBFactory(path) {
        var _a;
        const [drive] = this._driveForPath(path);
        return (_a = drive === null || drive === void 0 ? void 0 : drive.modelDBFactory) !== null && _a !== void 0 ? _a : null;
    }
    /**
     * Given a path of the form `drive:local/portion/of/it.txt`
     * get the local part of it.
     *
     * @param path: the path.
     *
     * @returns The local part of the path.
     */
    localPath(path) {
        const parts = path.split('/');
        const firstParts = parts[0].split(':');
        if (firstParts.length === 1 || !this._additionalDrives.has(firstParts[0])) {
            return coreutils_1.PathExt.removeSlash(path);
        }
        return coreutils_1.PathExt.join(firstParts.slice(1).join(':'), ...parts.slice(1));
    }
    /**
     * Normalize a global path. Reduces '..' and '.' parts, and removes
     * leading slashes from the local part of the path, while retaining
     * the drive name if it exists.
     *
     * @param path: the path.
     *
     * @returns The normalized path.
     */
    normalize(path) {
        const parts = path.split(':');
        if (parts.length === 1) {
            return coreutils_1.PathExt.normalize(path);
        }
        return `${parts[0]}:${coreutils_1.PathExt.normalize(parts.slice(1).join(':'))}`;
    }
    /**
     * Resolve a global path, starting from the root path. Behaves like
     * posix-path.resolve, with 3 differences:
     *  - will never prepend cwd
     *  - if root has a drive name, the result is prefixed with "<drive>:"
     *  - before adding drive name, leading slashes are removed
     *
     * @param path: the path.
     *
     * @returns The normalized path.
     */
    resolvePath(root, path) {
        const driveName = this.driveName(root);
        const localPath = this.localPath(root);
        const resolved = coreutils_1.PathExt.resolve('/', localPath, path);
        return driveName ? `${driveName}:${resolved}` : resolved;
    }
    /**
     * Given a path of the form `drive:local/portion/of/it.txt`
     * get the name of the drive. If the path is missing
     * a drive portion, returns an empty string.
     *
     * @param path: the path.
     *
     * @returns The drive name for the path, or the empty string.
     */
    driveName(path) {
        const parts = path.split('/');
        const firstParts = parts[0].split(':');
        if (firstParts.length === 1) {
            return '';
        }
        if (this._additionalDrives.has(firstParts[0])) {
            return firstParts[0];
        }
        return '';
    }
    /**
     * Get a file or directory.
     *
     * @param path: The path to the file.
     *
     * @param options: The options used to fetch the file.
     *
     * @returns A promise which resolves with the file content.
     */
    get(path, options) {
        const [drive, localPath] = this._driveForPath(path);
        return drive.get(localPath, options).then(contentsModel => {
            const listing = [];
            if (contentsModel.type === 'directory' && contentsModel.content) {
                algorithm_1.each(contentsModel.content, (item) => {
                    listing.push(Object.assign(Object.assign({}, item), { path: this._toGlobalPath(drive, item.path) }));
                });
                return Object.assign(Object.assign({}, contentsModel), { path: this._toGlobalPath(drive, localPath), content: listing, serverPath: contentsModel.path });
            }
            else {
                return Object.assign(Object.assign({}, contentsModel), { path: this._toGlobalPath(drive, localPath), serverPath: contentsModel.path });
            }
        });
    }
    /**
     * Get an encoded download url given a file path.
     *
     * @param path - An absolute POSIX file path on the server.
     *
     * #### Notes
     * It is expected that the path contains no relative paths.
     *
     * The returned URL may include a query parameter.
     */
    getDownloadUrl(path) {
        const [drive, localPath] = this._driveForPath(path);
        return drive.getDownloadUrl(localPath);
    }
    /**
     * Create a new untitled file or directory in the specified directory path.
     *
     * @param options: The options used to create the file.
     *
     * @returns A promise which resolves with the created file content when the
     *    file is created.
     */
    newUntitled(options = {}) {
        if (options.path) {
            const globalPath = this.normalize(options.path);
            const [drive, localPath] = this._driveForPath(globalPath);
            return drive
                .newUntitled(Object.assign(Object.assign({}, options), { path: localPath }))
                .then(contentsModel => {
                return Object.assign(Object.assign({}, contentsModel), { path: coreutils_1.PathExt.join(globalPath, contentsModel.name), serverPath: contentsModel.path });
            });
        }
        else {
            return this._defaultDrive.newUntitled(options);
        }
    }
    /**
     * Delete a file.
     *
     * @param path - The path to the file.
     *
     * @returns A promise which resolves when the file is deleted.
     */
    delete(path) {
        const [drive, localPath] = this._driveForPath(path);
        return drive.delete(localPath);
    }
    /**
     * Rename a file or directory.
     *
     * @param path - The original file path.
     *
     * @param newPath - The new file path.
     *
     * @returns A promise which resolves with the new file contents model when
     *   the file is renamed.
     */
    rename(path, newPath) {
        const [drive1, path1] = this._driveForPath(path);
        const [drive2, path2] = this._driveForPath(newPath);
        if (drive1 !== drive2) {
            throw Error('ContentsManager: renaming files must occur within a Drive');
        }
        return drive1.rename(path1, path2).then(contentsModel => {
            return Object.assign(Object.assign({}, contentsModel), { path: this._toGlobalPath(drive1, path2), serverPath: contentsModel.path });
        });
    }
    /**
     * Save a file.
     *
     * @param path - The desired file path.
     *
     * @param options - Optional overrides to the model.
     *
     * @returns A promise which resolves with the file content model when the
     *   file is saved.
     *
     * #### Notes
     * Ensure that `model.content` is populated for the file.
     */
    save(path, options = {}) {
        const globalPath = this.normalize(path);
        const [drive, localPath] = this._driveForPath(path);
        return drive
            .save(localPath, Object.assign(Object.assign({}, options), { path: localPath }))
            .then(contentsModel => {
            return Object.assign(Object.assign({}, contentsModel), { path: globalPath, serverPath: contentsModel.path });
        });
    }
    /**
     * Copy a file into a given directory.
     *
     * @param path - The original file path.
     *
     * @param toDir - The destination directory path.
     *
     * @returns A promise which resolves with the new contents model when the
     *  file is copied.
     *
     * #### Notes
     * The server will select the name of the copied file.
     */
    copy(fromFile, toDir) {
        const [drive1, path1] = this._driveForPath(fromFile);
        const [drive2, path2] = this._driveForPath(toDir);
        if (drive1 === drive2) {
            return drive1.copy(path1, path2).then(contentsModel => {
                return Object.assign(Object.assign({}, contentsModel), { path: this._toGlobalPath(drive1, contentsModel.path), serverPath: contentsModel.path });
            });
        }
        else {
            throw Error('Copying files between drives is not currently implemented');
        }
    }
    /**
     * Create a checkpoint for a file.
     *
     * @param path - The path of the file.
     *
     * @returns A promise which resolves with the new checkpoint model when the
     *   checkpoint is created.
     */
    createCheckpoint(path) {
        const [drive, localPath] = this._driveForPath(path);
        return drive.createCheckpoint(localPath);
    }
    /**
     * List available checkpoints for a file.
     *
     * @param path - The path of the file.
     *
     * @returns A promise which resolves with a list of checkpoint models for
     *    the file.
     */
    listCheckpoints(path) {
        const [drive, localPath] = this._driveForPath(path);
        return drive.listCheckpoints(localPath);
    }
    /**
     * Restore a file to a known checkpoint state.
     *
     * @param path - The path of the file.
     *
     * @param checkpointID - The id of the checkpoint to restore.
     *
     * @returns A promise which resolves when the checkpoint is restored.
     */
    restoreCheckpoint(path, checkpointID) {
        const [drive, localPath] = this._driveForPath(path);
        return drive.restoreCheckpoint(localPath, checkpointID);
    }
    /**
     * Delete a checkpoint for a file.
     *
     * @param path - The path of the file.
     *
     * @param checkpointID - The id of the checkpoint to delete.
     *
     * @returns A promise which resolves when the checkpoint is deleted.
     */
    deleteCheckpoint(path, checkpointID) {
        const [drive, localPath] = this._driveForPath(path);
        return drive.deleteCheckpoint(localPath, checkpointID);
    }
    /**
     * Given a drive and a local path, construct a fully qualified
     * path. The inverse of `_driveForPath`.
     *
     * @param drive: an `IDrive`.
     *
     * @param localPath: the local path on the drive.
     *
     * @returns the fully qualified path.
     */
    _toGlobalPath(drive, localPath) {
        if (drive === this._defaultDrive) {
            return coreutils_1.PathExt.removeSlash(localPath);
        }
        else {
            return `${drive.name}:${coreutils_1.PathExt.removeSlash(localPath)}`;
        }
    }
    /**
     * Given a path, get the `IDrive to which it refers,
     * where the path satisfies the pattern
     * `'driveName:path/to/file'`. If there is no `driveName`
     * prepended to the path, it returns the default drive.
     *
     * @param path: a path to a file.
     *
     * @returns A tuple containing an `IDrive` object for the path,
     * and a local path for that drive.
     */
    _driveForPath(path) {
        const driveName = this.driveName(path);
        const localPath = this.localPath(path);
        if (driveName) {
            return [this._additionalDrives.get(driveName), localPath];
        }
        else {
            return [this._defaultDrive, localPath];
        }
    }
    /**
     * Respond to fileChanged signals from the drives attached to
     * the manager. This prepends the drive name to the path if necessary,
     * and then forwards the signal.
     */
    _onFileChanged(sender, args) {
        var _a, _b;
        if (sender === this._defaultDrive) {
            this._fileChanged.emit(args);
        }
        else {
            let newValue = null;
            let oldValue = null;
            if ((_a = args.newValue) === null || _a === void 0 ? void 0 : _a.path) {
                newValue = Object.assign(Object.assign({}, args.newValue), { path: this._toGlobalPath(sender, args.newValue.path) });
            }
            if ((_b = args.oldValue) === null || _b === void 0 ? void 0 : _b.path) {
                oldValue = Object.assign(Object.assign({}, args.oldValue), { path: this._toGlobalPath(sender, args.oldValue.path) });
            }
            this._fileChanged.emit({
                type: args.type,
                newValue,
                oldValue
            });
        }
    }
}
exports.ContentsManager = ContentsManager;
/**
 * A default implementation for an `IDrive`, talking to the
 * server using the Jupyter REST API.
 */
class Drive {
    /**
     * Construct a new contents manager object.
     *
     * @param options - The options used to initialize the object.
     */
    constructor(options = {}) {
        var _a, _b, _c;
        this._isDisposed = false;
        this._fileChanged = new signaling_1.Signal(this);
        this.name = (_a = options.name) !== null && _a !== void 0 ? _a : 'Default';
        this._apiEndpoint = (_b = options.apiEndpoint) !== null && _b !== void 0 ? _b : SERVICE_DRIVE_URL;
        this.serverSettings = (_c = options.serverSettings) !== null && _c !== void 0 ? _c : __1.ServerConnection.makeSettings();
    }
    /**
     * A signal emitted when a file operation takes place.
     */
    get fileChanged() {
        return this._fileChanged;
    }
    /**
     * Test whether the manager has been disposed.
     */
    get isDisposed() {
        return this._isDisposed;
    }
    /**
     * Dispose of the resources held by the manager.
     */
    dispose() {
        if (this.isDisposed) {
            return;
        }
        this._isDisposed = true;
        signaling_1.Signal.clearData(this);
    }
    /**
     * Get a file or directory.
     *
     * @param localPath: The path to the file.
     *
     * @param options: The options used to fetch the file.
     *
     * @returns A promise which resolves with the file content.
     *
     * Uses the [Jupyter Notebook API](http://petstore.swagger.io/?url=https://raw.githubusercontent.com/jupyter/notebook/master/notebook/services/api/api.yaml#!/contents) and validates the response model.
     */
    async get(localPath, options) {
        let url = this._getUrl(localPath);
        if (options) {
            // The notebook type cannot take an format option.
            if (options.type === 'notebook') {
                delete options['format'];
            }
            const content = options.content ? '1' : '0';
            const params = Object.assign(Object.assign({}, options), { content });
            url += coreutils_1.URLExt.objectToQueryString(params);
        }
        const settings = this.serverSettings;
        const response = await __1.ServerConnection.makeRequest(url, {}, settings);
        if (response.status !== 200) {
            const err = await __1.ServerConnection.ResponseError.create(response);
            throw err;
        }
        const data = await response.json();
        validate.validateContentsModel(data);
        return data;
    }
    /**
     * Get an encoded download url given a file path.
     *
     * @param localPath - An absolute POSIX file path on the server.
     *
     * #### Notes
     * It is expected that the path contains no relative paths.
     *
     * The returned URL may include a query parameter.
     */
    getDownloadUrl(localPath) {
        const baseUrl = this.serverSettings.baseUrl;
        let url = coreutils_1.URLExt.join(baseUrl, FILES_URL, coreutils_1.URLExt.encodeParts(localPath));
        const xsrfTokenMatch = document.cookie.match('\\b_xsrf=([^;]*)\\b');
        if (xsrfTokenMatch) {
            const fullUrl = new URL(url);
            fullUrl.searchParams.append('_xsrf', xsrfTokenMatch[1]);
            url = fullUrl.toString();
        }
        return Promise.resolve(url);
    }
    /**
     * Create a new untitled file or directory in the specified directory path.
     *
     * @param options: The options used to create the file.
     *
     * @returns A promise which resolves with the created file content when the
     *    file is created.
     *
     * #### Notes
     * Uses the [Jupyter Notebook API](http://petstore.swagger.io/?url=https://raw.githubusercontent.com/jupyter/notebook/master/notebook/services/api/api.yaml#!/contents) and validates the response model.
     */
    async newUntitled(options = {}) {
        var _a;
        let body = '{}';
        if (options) {
            if (options.ext) {
                options.ext = Private.normalizeExtension(options.ext);
            }
            body = JSON.stringify(options);
        }
        const settings = this.serverSettings;
        const url = this._getUrl((_a = options.path) !== null && _a !== void 0 ? _a : '');
        const init = {
            method: 'POST',
            body
        };
        const response = await __1.ServerConnection.makeRequest(url, init, settings);
        if (response.status !== 201) {
            const err = await __1.ServerConnection.ResponseError.create(response);
            throw err;
        }
        const data = await response.json();
        validate.validateContentsModel(data);
        this._fileChanged.emit({
            type: 'new',
            oldValue: null,
            newValue: data
        });
        return data;
    }
    /**
     * Delete a file.
     *
     * @param localPath - The path to the file.
     *
     * @returns A promise which resolves when the file is deleted.
     *
     * #### Notes
     * Uses the [Jupyter Notebook API](http://petstore.swagger.io/?url=https://raw.githubusercontent.com/jupyter/notebook/master/notebook/services/api/api.yaml#!/contents).
     */
    async delete(localPath) {
        const url = this._getUrl(localPath);
        const settings = this.serverSettings;
        const init = { method: 'DELETE' };
        const response = await __1.ServerConnection.makeRequest(url, init, settings);
        // TODO: update IPEP27 to specify errors more precisely, so
        // that error types can be detected here with certainty.
        if (response.status !== 204) {
            const err = await __1.ServerConnection.ResponseError.create(response);
            throw err;
        }
        this._fileChanged.emit({
            type: 'delete',
            oldValue: { path: localPath },
            newValue: null
        });
    }
    /**
     * Rename a file or directory.
     *
     * @param oldLocalPath - The original file path.
     *
     * @param newLocalPath - The new file path.
     *
     * @returns A promise which resolves with the new file contents model when
     *   the file is renamed.
     *
     * #### Notes
     * Uses the [Jupyter Notebook API](http://petstore.swagger.io/?url=https://raw.githubusercontent.com/jupyter/notebook/master/notebook/services/api/api.yaml#!/contents) and validates the response model.
     */
    async rename(oldLocalPath, newLocalPath) {
        const settings = this.serverSettings;
        const url = this._getUrl(oldLocalPath);
        const init = {
            method: 'PATCH',
            body: JSON.stringify({ path: newLocalPath })
        };
        const response = await __1.ServerConnection.makeRequest(url, init, settings);
        if (response.status !== 200) {
            const err = await __1.ServerConnection.ResponseError.create(response);
            throw err;
        }
        const data = await response.json();
        validate.validateContentsModel(data);
        this._fileChanged.emit({
            type: 'rename',
            oldValue: { path: oldLocalPath },
            newValue: data
        });
        return data;
    }
    /**
     * Save a file.
     *
     * @param localPath - The desired file path.
     *
     * @param options - Optional overrides to the model.
     *
     * @returns A promise which resolves with the file content model when the
     *   file is saved.
     *
     * #### Notes
     * Ensure that `model.content` is populated for the file.
     *
     * Uses the [Jupyter Notebook API](http://petstore.swagger.io/?url=https://raw.githubusercontent.com/jupyter/notebook/master/notebook/services/api/api.yaml#!/contents) and validates the response model.
     */
    async save(localPath, options = {}) {
        const settings = this.serverSettings;
        const url = this._getUrl(localPath);
        const init = {
            method: 'PUT',
            body: JSON.stringify(options)
        };
        const response = await __1.ServerConnection.makeRequest(url, init, settings);
        // will return 200 for an existing file and 201 for a new file
        if (response.status !== 200 && response.status !== 201) {
            const err = await __1.ServerConnection.ResponseError.create(response);
            throw err;
        }
        const data = await response.json();
        validate.validateContentsModel(data);
        this._fileChanged.emit({
            type: 'save',
            oldValue: null,
            newValue: data
        });
        return data;
    }
    /**
     * Copy a file into a given directory.
     *
     * @param localPath - The original file path.
     *
     * @param toDir - The destination directory path.
     *
     * @returns A promise which resolves with the new contents model when the
     *  file is copied.
     *
     * #### Notes
     * The server will select the name of the copied file.
     *
     * Uses the [Jupyter Notebook API](http://petstore.swagger.io/?url=https://raw.githubusercontent.com/jupyter/notebook/master/notebook/services/api/api.yaml#!/contents) and validates the response model.
     */
    async copy(fromFile, toDir) {
        const settings = this.serverSettings;
        const url = this._getUrl(toDir);
        const init = {
            method: 'POST',
            body: JSON.stringify({ copy_from: fromFile })
        };
        const response = await __1.ServerConnection.makeRequest(url, init, settings);
        if (response.status !== 201) {
            const err = await __1.ServerConnection.ResponseError.create(response);
            throw err;
        }
        const data = await response.json();
        validate.validateContentsModel(data);
        this._fileChanged.emit({
            type: 'new',
            oldValue: null,
            newValue: data
        });
        return data;
    }
    /**
     * Create a checkpoint for a file.
     *
     * @param localPath - The path of the file.
     *
     * @returns A promise which resolves with the new checkpoint model when the
     *   checkpoint is created.
     *
     * #### Notes
     * Uses the [Jupyter Notebook API](http://petstore.swagger.io/?url=https://raw.githubusercontent.com/jupyter/notebook/master/notebook/services/api/api.yaml#!/contents) and validates the response model.
     */
    async createCheckpoint(localPath) {
        const url = this._getUrl(localPath, 'checkpoints');
        const init = { method: 'POST' };
        const response = await __1.ServerConnection.makeRequest(url, init, this.serverSettings);
        if (response.status !== 201) {
            const err = await __1.ServerConnection.ResponseError.create(response);
            throw err;
        }
        const data = await response.json();
        validate.validateCheckpointModel(data);
        return data;
    }
    /**
     * List available checkpoints for a file.
     *
     * @param localPath - The path of the file.
     *
     * @returns A promise which resolves with a list of checkpoint models for
     *    the file.
     *
     * #### Notes
     * Uses the [Jupyter Notebook API](http://petstore.swagger.io/?url=https://raw.githubusercontent.com/jupyter/notebook/master/notebook/services/api/api.yaml#!/contents) and validates the response model.
     */
    async listCheckpoints(localPath) {
        const url = this._getUrl(localPath, 'checkpoints');
        const response = await __1.ServerConnection.makeRequest(url, {}, this.serverSettings);
        if (response.status !== 200) {
            const err = await __1.ServerConnection.ResponseError.create(response);
            throw err;
        }
        const data = await response.json();
        if (!Array.isArray(data)) {
            throw new Error('Invalid Checkpoint list');
        }
        for (let i = 0; i < data.length; i++) {
            validate.validateCheckpointModel(data[i]);
        }
        return data;
    }
    /**
     * Restore a file to a known checkpoint state.
     *
     * @param localPath - The path of the file.
     *
     * @param checkpointID - The id of the checkpoint to restore.
     *
     * @returns A promise which resolves when the checkpoint is restored.
     *
     * #### Notes
     * Uses the [Jupyter Notebook API](http://petstore.swagger.io/?url=https://raw.githubusercontent.com/jupyter/notebook/master/notebook/services/api/api.yaml#!/contents).
     */
    async restoreCheckpoint(localPath, checkpointID) {
        const url = this._getUrl(localPath, 'checkpoints', checkpointID);
        const init = { method: 'POST' };
        const response = await __1.ServerConnection.makeRequest(url, init, this.serverSettings);
        if (response.status !== 204) {
            const err = await __1.ServerConnection.ResponseError.create(response);
            throw err;
        }
    }
    /**
     * Delete a checkpoint for a file.
     *
     * @param localPath - The path of the file.
     *
     * @param checkpointID - The id of the checkpoint to delete.
     *
     * @returns A promise which resolves when the checkpoint is deleted.
     *
     * #### Notes
     * Uses the [Jupyter Notebook API](http://petstore.swagger.io/?url=https://raw.githubusercontent.com/jupyter/notebook/master/notebook/services/api/api.yaml#!/contents).
     */
    async deleteCheckpoint(localPath, checkpointID) {
        const url = this._getUrl(localPath, 'checkpoints', checkpointID);
        const init = { method: 'DELETE' };
        const response = await __1.ServerConnection.makeRequest(url, init, this.serverSettings);
        if (response.status !== 204) {
            const err = await __1.ServerConnection.ResponseError.create(response);
            throw err;
        }
    }
    /**
     * Get a REST url for a file given a path.
     */
    _getUrl(...args) {
        const parts = args.map(path => coreutils_1.URLExt.encodeParts(path));
        const baseUrl = this.serverSettings.baseUrl;
        return coreutils_1.URLExt.join(baseUrl, this._apiEndpoint, ...parts);
    }
}
exports.Drive = Drive;
/**
 * A namespace for module private data.
 */
var Private;
(function (Private) {
    /**
     * Normalize a file extension to be of the type `'.foo'`.
     *
     * Adds a leading dot if not present and converts to lower case.
     */
    function normalizeExtension(extension) {
        if (extension.length > 0 && extension.indexOf('.') !== 0) {
            extension = `.${extension}`;
        }
        return extension;
    }
    Private.normalizeExtension = normalizeExtension;
})(Private || (Private = {}));
//# sourceMappingURL=index.js.map