{
  "version": 3,
  "sources": ["../index.ts", "../system.ts"],
  "sourcesContent": ["/**\n * @module @xmcl/system\n */\nimport { open, readEntry, readAllEntries } from '@xmcl/unzip'\nimport { access as saccess, stat as sstat, writeFile as swriteFile, readFile as sreadFile, readdir as sreaddir } from 'fs'\nimport { promisify } from 'util'\nimport { join, sep } from 'path'\nimport { FileSystem } from './system'\nimport { ZipFile, Entry } from 'yauzl'\n\nconst access = promisify(saccess)\nconst stat = promisify(sstat)\nconst writeFile = promisify(swriteFile)\nconst readFile = promisify(sreadFile)\nconst readdir = promisify(sreaddir)\n\nexport async function openFileSystem(basePath: string | Uint8Array): Promise<FileSystem> {\n  if (typeof basePath === 'string') {\n    const fstat = await stat(basePath)\n    if (fstat.isDirectory()) {\n      return new NodeFileSystem(basePath)\n    } else {\n      const zip = await open(basePath)\n      Object.assign(zip, {\n        validateFileName: (e: Entry) => {\n          const fileName = e.fileName\n          if (/^[a-zA-Z]:/.test(fileName) || /^\\//.test(fileName)) {\n            // absolute path convert to relative path\n            e.fileName = fileName.replace(/^[a-zA-Z]:/, '').replace(/^\\//, '')\n          }\n          return null;\n        },\n      })\n      const entries = await readAllEntries(zip).then(es =>\n        // ignore entries with '..' in the path\n        es.filter(e => e.fileName.split(\"/\").indexOf(\"..\") === -1)\n      )\n      const entriesRecord: Record<string, Entry> = {}\n      for (const entry of entries) {\n        entriesRecord[entry.fileName] = entry\n      }\n      return new NodeZipFileSystem(basePath, zip, entriesRecord)\n    }\n  } else {\n    const zip = await open(basePath as Buffer)\n    const entries = await readAllEntries(zip)\n    const entriesRecord: Record<string, Entry> = {}\n    for (const entry of entries) {\n      entriesRecord[entry.fileName] = entry\n    }\n    return new NodeZipFileSystem('', zip, entriesRecord)\n  }\n}\nexport function resolveFileSystem(base: string | Uint8Array | FileSystem): Promise<FileSystem> {\n  if (typeof base === 'string' || base instanceof Uint8Array || base instanceof Buffer) {\n    return openFileSystem(base)\n  } else {\n    return Promise.resolve(base)\n  }\n}\n\nclass NodeFileSystem extends FileSystem {\n  sep = sep\n  type = 'path' as const\n  writeable = true\n  join(...paths: string[]): string {\n    return join(...paths)\n  }\n\n  getUrl(name: string) {\n    return `file://${this.join(this.root, name)}`\n  }\n\n  isDirectory(name: string): Promise<boolean> {\n    return stat(join(this.root, name)).then((s) => s.isDirectory())\n  }\n\n  writeFile(name: string, data: Uint8Array): Promise<void> {\n    return writeFile(join(this.root, name), data)\n  }\n\n  existsFile(name: string): Promise<boolean> {\n    return access(join(this.root, name)).then(() => true, () => false)\n  }\n\n  readFile(name: any, encoding?: any) {\n    return readFile(join(this.root, name), { encoding }) as any\n  }\n\n  listFiles(name: string): Promise<string[]> {\n    return readdir(join(this.root, name))\n  }\n\n  cd(name: string): void {\n    this.root = join(this.root, name)\n  }\n\n  constructor(public root: string) { super() }\n}\nclass NodeZipFileSystem extends FileSystem {\n  sep = '/'\n  type = 'zip' as const\n  writeable = false\n\n  private zipRoot = ''\n\n  private fileRoot: string\n\n  constructor(root: string, private zip: ZipFile, private entries: Record<string, Entry>) {\n    super()\n    this.fileRoot = root\n  }\n\n  isClosed(): boolean {\n    return !this.zip.isOpen\n  }\n\n  close(): void {\n    this.zip.close()\n  }\n\n  get root() { return this.fileRoot + (this.zipRoot.length === 0 ? '' : `/${this.zipRoot}`) }\n\n  protected normalizePath(name: string): string {\n    if (name.startsWith('/')) {\n      name = name.substring(1)\n    }\n    if (this.zipRoot !== '') {\n      name = [this.zipRoot, name].join('/')\n    }\n    return name\n  }\n\n  join(...paths: string[]): string {\n    return paths.join('/')\n  }\n\n  isDirectory(name: string): Promise<boolean> {\n    name = this.normalizePath(name)\n    if (name === '') {\n      return Promise.resolve(true)\n    }\n    if (this.entries[name]) {\n      return Promise.resolve(name.endsWith('/'))\n    }\n    if (this.entries[name + '/']) {\n      return Promise.resolve(true)\n    }\n    // the root dir won't have entries\n    // therefore we need to do an extra track here\n    const entries = Object.keys(this.entries)\n    return Promise.resolve(entries.some((e) => e.startsWith(name + '/')))\n  }\n\n  existsFile(name: string): Promise<boolean> {\n    name = this.normalizePath(name)\n    if (this.entries[name] ||\n      this.entries[name + '/']) { return Promise.resolve(true) }\n    // the root dir won't have entries\n    // therefore we need to do an extra track here\n    const entries = Object.keys(this.entries)\n    return Promise.resolve(entries.some((e) => e.startsWith(name + '/')))\n  }\n\n  async readFile(name: string, encoding?: 'utf-8' | 'base64'): Promise<any> {\n    name = this.normalizePath(name)\n    const entry = this.entries[name]\n    if (!entry) { throw new Error(`Not found file named ${name}`) }\n    const buffer = await readEntry(this.zip, entry)\n    if (encoding === 'utf-8') {\n      return buffer.toString('utf-8')\n    }\n    if (encoding === 'base64') {\n      return buffer.toString('base64')\n    }\n    return buffer\n  }\n\n  listFiles(name: string): Promise<string[]> {\n    name = this.normalizePath(name)\n    return Promise.resolve([\n      ...new Set(Object.keys(this.entries)\n        .filter((n) => n.startsWith(name))\n        .map((n) => n.substring(name.length))\n        .map((n) => n.startsWith('/') ? n.substring(1) : n)\n        .map((n) => n.split('/')[0])),\n    ])\n  }\n\n  cd(name: string): void {\n    if (name.startsWith('/')) {\n      this.zipRoot = name.substring(1)\n      return\n    }\n    const paths = name.split('/')\n    for (const path of paths) {\n      if (path === '.') {\n        continue\n      } else if (path === '..') {\n        const sub = this.zipRoot.split('/')\n        if (sub.length > 0) {\n          sub.pop()\n          this.zipRoot = sub.join('/')\n        }\n      } else {\n        if (this.zipRoot === '') {\n          this.zipRoot = path\n        } else {\n          this.zipRoot += `/${path}`\n        }\n      }\n    }\n  }\n\n  async walkFiles(startingDir: string, walker: (path: string) => void | Promise<void>) {\n    startingDir = this.normalizePath(startingDir)\n    const root = startingDir.startsWith('/') ? startingDir.substring(1) : startingDir\n    for (const child of Object.keys(this.entries).filter((e) => e.startsWith(root))) {\n      if (child.endsWith('/')) { continue }\n      const result = walker(child)\n      if (result instanceof Promise) {\n        await result\n      }\n    }\n  }\n}\n\nexport * from './system'\n", "export abstract class FileSystem {\n  abstract readonly root: string\n  abstract readonly sep: string\n  abstract readonly type: 'zip' | 'path'\n  abstract readonly writeable: boolean\n\n  // base methods\n\n  abstract join(...paths: string[]): string\n\n  abstract isDirectory(name: string): Promise<boolean>\n  abstract existsFile(name: string): Promise<boolean>\n  abstract readFile(name: string, encoding: 'utf-8' | 'base64'): Promise<string>\n  abstract readFile(name: string, encoding: undefined): Promise<Uint8Array>\n  abstract readFile(name: string): Promise<Uint8Array>\n  abstract readFile(name: string, encoding?: 'utf-8' | 'base64'): Promise<Uint8Array | string>\n\n  /**\n     * Get the url for a file entry. If the system does not support get url. This should return an empty string.\n     */\n  getUrl(name: string): string { return '' }\n\n  abstract listFiles(name: string): Promise<string[]>\n\n  abstract cd(name: string): void\n\n  isClosed(): boolean { return false }\n  close(): void { }\n\n  // extension methods\n\n  async missingFile(name: string) {\n    return this.existsFile(name).then((v) => !v)\n  }\n\n  async walkFiles(target: string, walker: (path: string) => void | Promise<void>) {\n    if (await this.isDirectory(target)) {\n      const childs = await this.listFiles(target)\n      for (const child of childs) {\n        await this.walkFiles(this.join(target, child), walker)\n      }\n    } else {\n      const result = walker(this.join(target))\n      if (result instanceof Promise) {\n        await result\n      }\n    }\n  }\n}\n"],
  "mappings": "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",
  "names": []
}
