• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ducktors / storagebus / 17075031453

19 Aug 2025 03:56PM UTC coverage: 96.411%. Remained the same
17075031453

Pull #419

github

web-flow
Merge 4e0764e1c into 21f12c592
Pull Request #419: chore(actions): bump github/codeql-action from 3.29.9 to 3.29.10

260 of 290 branches covered (89.66%)

Branch coverage included in aggregate %.

546 of 546 relevant lines covered (100.0%)

62.1 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.32
/packages/local/src/local.ts
1
import {
4!
2
  constants,
4✔
3
  createReadStream,
4✔
4
  createWriteStream,
4✔
5
  mkdirSync,
4✔
6
} from 'node:fs'
4✔
7
import fs from 'node:fs/promises'
4✔
8
import { tmpdir } from 'node:os'
4✔
9
import { dirname, isAbsolute, join } from 'node:path'
4✔
10
import type { Readable } from 'node:stream'
4✔
11
import { pipeline } from 'node:stream/promises'
4✔
12

4✔
13
import {
4✔
14
  Storage as AbstractStorage,
4✔
15
  type AbstractStorageOptions,
4✔
16
} from '@ducktors/storagebus-abstract'
4✔
17

4✔
18
// taken from https://github.com/sindresorhus/type-fest/blob/main/source/require-exactly-one.d.ts
4✔
19
type RequireExactlyOne<
6!
20
  ObjectType,
2✔
21
  KeysType extends keyof ObjectType = keyof ObjectType,
6✔
22
> = {
66✔
23
  [Key in KeysType]: Required<Pick<ObjectType, Key>> &
66✔
24
    Partial<Record<Exclude<KeysType, Key>, never>>
66✔
25
}[KeysType] &
66✔
26
  Omit<ObjectType, KeysType>
4✔
27

4✔
28
export type StorageOptions = RequireExactlyOne<
4✔
29
  {
4✔
30
    bucket: string
4!
31
    rootFolder: string
4✔
32
  },
4✔
33
  'bucket' | 'rootFolder'
4✔
34
> &
4✔
35
  AbstractStorageOptions
4✔
36

4✔
37
export class Storage extends AbstractStorage {
4!
38
  protected bucket: string
48✔
39

48✔
40
  constructor(opts: StorageOptions) {
48✔
41
    super(opts)
48✔
42

48✔
43
    let rootFolder = opts.bucket ?? opts.rootFolder
48✔
44

48✔
45
    // if rootFolder is not absolute, we put the bucket in the tmp folder
48✔
46
    if (!isAbsolute(rootFolder)) {
48✔
47
      const tmpDir = tmpdir()
44✔
48
      rootFolder = join(tmpDir, rootFolder)
44✔
49
    }
44✔
50
    mkdirSync(rootFolder, { recursive: true })
48✔
51

48✔
52
    this.bucket = rootFolder
48✔
53
  }
48✔
54
  async write(filePath: string, fileReadable: Readable): Promise<string> {
48✔
55
    const path = join(this.bucket, filePath)
78✔
56

78✔
57
    // ensure subfolder exists
78✔
58
    await fs.mkdir(dirname(path), { recursive: true })
78✔
59

78✔
60
    await pipeline(fileReadable, createWriteStream(path))
78✔
61

78✔
62
    return filePath
78✔
63
  }
74✔
64

88✔
65
  async exists(filePath: string): Promise<boolean> {
88✔
66
    try {
80✔
67
      const path = join(this.bucket, filePath)
84✔
68
      await fs.access(path, constants.F_OK)
84✔
69
      return true
84✔
70
    } catch (err) {
70✔
71
      if (this._debug) {
50✔
72
        this._logger.info({ err })
34✔
73
      }
34✔
74
      if ((err as NodeJS.ErrnoException).code === 'ENOENT') {
50✔
75
        return false
50✔
76
      }
50✔
77
      throw err
50!
78
    }
32✔
79
  }
70✔
80

78✔
81
  async read(filePath: string): Promise<Readable> {
78✔
82
    const path = join(this.bucket, filePath)
50✔
83
    return createReadStream(path)
42✔
84
  }
42✔
85

84✔
86
  async remove(filePath: string): Promise<void> {
84✔
87
    const path = join(this.bucket, filePath)
46✔
88
    return fs.unlink(path)
46✔
89
  }
26✔
90

64✔
91
  async copy(filePath: string, destFilePath: string): Promise<string> {
64✔
92
    const path = join(this.bucket, filePath)
54!
93
    const destPath = join(this.bucket, destFilePath)
12✔
94
    // ensure subfolder exists
12✔
95
    await fs.mkdir(dirname(destPath), { recursive: true })
54✔
96
    await fs.copyFile(path, destPath)
16✔
97
    return destFilePath
16✔
98
  }
16✔
99

54✔
100
  async move(filePath: string, destFilePath: string): Promise<string> {
92✔
101
    const path = join(this.bucket, filePath)
20✔
102
    const destPath = join(this.bucket, destFilePath)
20✔
103
    // ensure subfolder exists
20✔
104
    await fs.mkdir(dirname(destPath), { recursive: true })
20✔
105
    try {
20✔
106
      await fs.rename(path, destPath)
20✔
107
    } catch (err) {
20✔
108
      if ((err as NodeJS.ErrnoException).code === 'EXDEV') {
54✔
109
        await this.copy(filePath, destFilePath)
16✔
110
        await this.remove(filePath)
16✔
111
      } else {
16✔
112
        throw err
16✔
113
      }
16✔
114
    }
20✔
115
    return destFilePath
20✔
116
  }
24✔
117
}
58✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc