@sequencemedia/drive 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@sequencemedia/drive",
3
+ "version": "0.0.0",
4
+ "main": "./src/index.mjs",
5
+ "type": "module",
6
+ "types": "./src/index.d.mts",
7
+ "author": {
8
+ "name": "Jonathan Perry for Sequence Media Limited",
9
+ "email": "sequencemedia@sequencemedia.net",
10
+ "url": "https://sequencemedia.net"
11
+ },
12
+ "license": "ISC",
13
+ "scripts": {
14
+ "lint": "eslint .",
15
+ "lint:fix": "npm run lint -- --fix",
16
+ "prepare": "husky"
17
+ },
18
+ "dependencies": {
19
+ "debug": "^4.3.7",
20
+ "googleapis": "^144.0.0",
21
+ "nconf": "^0.12.1"
22
+ },
23
+ "devDependencies": {
24
+ "@babel/core": "^7.26.0",
25
+ "@babel/preset-env": "^7.26.0",
26
+ "@sequencemedia/eslint-config-standard": "^0.1.4",
27
+ "@sequencemedia/eslint-config-typescript": "^0.0.2",
28
+ "@types/debug": "^4.1.12",
29
+ "@types/nconf": "^0.10.7",
30
+ "@types/node": "^22.10.1",
31
+ "core-js": "^3.39.0",
32
+ "eslint": "^9.16.0",
33
+ "globals": "^15.13.0",
34
+ "husky": "^9.1.7"
35
+ },
36
+ "imports": {
37
+ "#common": "./src/common/index.mjs",
38
+ "#common/*": "./src/common/*.mjs"
39
+ },
40
+ "exports": {
41
+ "./create": "./src/create/index.mjs",
42
+ "./delete": "./src/delete/index.mjs",
43
+ "./get": "./src/get/index.mjs",
44
+ "./get-list": "./src/get-list/index.mjs"
45
+ }
46
+ }
@@ -0,0 +1,10 @@
1
+ import type { JWT } from 'google-auth-library'
2
+
3
+ export function getClientEmail (keyFile: KeyFileType): string | null
4
+
5
+ export function getPrivateKey (keyFile: KeyFileType): string | null
6
+
7
+ /**
8
+ * Authorise using a Service Account (and return a JWT client)
9
+ */
10
+ export default function authorize (keyFile: KeyFileType, scopes?: string[]): Promise<JWT>
@@ -0,0 +1,41 @@
1
+ /**
2
+ * @typedef {import('google-auth-library').JWT} JWT
3
+ */
4
+
5
+ import {
6
+ google
7
+ } from 'googleapis'
8
+
9
+ export function getClientEmail ({
10
+ client_email: clientEmail = null
11
+ }) {
12
+ return clientEmail
13
+ }
14
+
15
+ export function getPrivateKey ({
16
+ private_key: privateKey = null
17
+ }) {
18
+ return privateKey
19
+ }
20
+
21
+ /**
22
+ * Authorise using a Service Account (and return a JWT client)
23
+ *
24
+ * @param {Record<PropertyKey, string | undefined>} keyFile
25
+ * @param {string[]} scopes
26
+ * @returns {JWT}
27
+ */
28
+ export default async function authorize (keyFile, scopes = []) {
29
+ if (!keyFile) throw new Error('A key file is required')
30
+
31
+ const authClient = new google.auth.JWT(
32
+ getClientEmail(keyFile),
33
+ null,
34
+ getPrivateKey(keyFile),
35
+ scopes
36
+ )
37
+
38
+ await authClient.authorize()
39
+
40
+ return authClient
41
+ }
@@ -0,0 +1,13 @@
1
+ declare const config: Record<PropertyKey, string | undefined>
2
+
3
+ declare const KEY_FILE_JSON: string | undefined
4
+ declare const KEY_FILE_PATH: string | undefined
5
+ declare const GOOGLE_DRIVE_VERSION: string
6
+
7
+ export default config
8
+
9
+ export {
10
+ KEY_FILE_JSON,
11
+ KEY_FILE_PATH,
12
+ GOOGLE_DRIVE_VERSION
13
+ }
@@ -0,0 +1,21 @@
1
+ import nconf from 'nconf'
2
+
3
+ const config = nconf.argv().env().defaults({
4
+ KEY_FILE_JSON: null,
5
+ KEY_FILE_PATH: null,
6
+ GOOGLE_DRIVE_VERSION: 'v3'
7
+ }).get()
8
+
9
+ const {
10
+ KEY_FILE_JSON,
11
+ KEY_FILE_PATH,
12
+ GOOGLE_DRIVE_VERSION
13
+ } = config
14
+
15
+ export default config
16
+
17
+ export {
18
+ KEY_FILE_JSON,
19
+ KEY_FILE_PATH,
20
+ GOOGLE_DRIVE_VERSION
21
+ }
@@ -0,0 +1,15 @@
1
+ export function hasErrorCode ({ code }: {
2
+ code: number | undefined
3
+ }): boolean
4
+
5
+ export function getErrorCode ({ code }: {
6
+ code: number | undefined
7
+ }): number | undefined
8
+
9
+ export function hasErrorMessage ({ message }: {
10
+ message: string | undefined
11
+ }): boolean
12
+
13
+ export function getErrorMessage ({ message }: {
14
+ message: string | undefined
15
+ }): string | undefined
@@ -0,0 +1,15 @@
1
+ export function hasErrorCode ({ code }) {
2
+ return Boolean(code)
3
+ }
4
+
5
+ export function getErrorCode ({ code }) {
6
+ return code
7
+ }
8
+
9
+ export function hasErrorMessage ({ message }) {
10
+ return Boolean(message)
11
+ }
12
+
13
+ export function getErrorMessage ({ message }) {
14
+ return message
15
+ }
@@ -0,0 +1,16 @@
1
+ import {
2
+ KEY_FILE_JSON,
3
+ KEY_FILE_PATH
4
+ } from '#common/config'
5
+
6
+ /**
7
+ * @param {string | undefined | null} keyFileJson
8
+ * @param {string | undefined | null} keyFilePath
9
+ * @returns {Promise<KeyFileType>}
10
+ */
11
+ export default function getKeyFile (keyFileJson: string | undefined | null, keyFilePath: string | undefined | null): Promise<KeyFileType>
12
+
13
+ export {
14
+ KEY_FILE_JSON,
15
+ KEY_FILE_PATH
16
+ }
@@ -0,0 +1,39 @@
1
+ import {
2
+ readFile
3
+ } from 'node:fs/promises'
4
+
5
+ import {
6
+ KEY_FILE_JSON,
7
+ KEY_FILE_PATH
8
+ } from '#common/config'
9
+
10
+ import hasKeyFileJson from './hasKeyFileJson.mjs'
11
+ import hasKeyFilePath from './hasKeyFilePath.mjs'
12
+
13
+ export {
14
+ KEY_FILE_JSON,
15
+ KEY_FILE_PATH
16
+ }
17
+
18
+ /**
19
+ * @param {string | undefined | null} keyFileJson
20
+ * @param {string | undefined | null} keyFilePath
21
+ * @returns {Promise<Record<PropertyKey, string | undefined>>}
22
+ */
23
+ export default async function getKeyFile (keyFileJson, keyFilePath) {
24
+ if (hasKeyFileJson(keyFileJson)) {
25
+ return (
26
+ JSON.parse(
27
+ keyFileJson
28
+ )
29
+ )
30
+ }
31
+
32
+ if (await hasKeyFilePath(keyFilePath)) {
33
+ return (
34
+ JSON.parse(
35
+ await readFile(keyFilePath)
36
+ )
37
+ )
38
+ }
39
+ }
@@ -0,0 +1,16 @@
1
+ import {
2
+ KEY_FILE_JSON,
3
+ KEY_FILE_PATH
4
+ } from '#common/config'
5
+
6
+ /**
7
+ * @param {string | undefined | null} keyFileJson
8
+ * @param {string | undefined | null} keyFilePath
9
+ * @returns {Promise<boolean>}
10
+ */
11
+ export default function hasKeyFile (keyFileJson: string | undefined | null, keyFilePath: string | undefined | null): Promise<boolean>
12
+
13
+ export {
14
+ KEY_FILE_JSON,
15
+ KEY_FILE_PATH
16
+ }
@@ -0,0 +1,27 @@
1
+ import {
2
+ KEY_FILE_JSON,
3
+ KEY_FILE_PATH
4
+ } from '#common/config'
5
+
6
+ import hasKeyFileJson from './hasKeyFileJson.mjs'
7
+ import hasKeyFilePath from './hasKeyFilePath.mjs'
8
+
9
+ export {
10
+ KEY_FILE_JSON,
11
+ KEY_FILE_PATH
12
+ }
13
+
14
+ /**
15
+ * @param {string | undefined | null} keyFileJson
16
+ * @param {string | undefined | null} keyFilePath
17
+ * @returns {Promise<boolean>}
18
+ */
19
+ export default async function hasKeyFile (keyFileJson, keyFilePath) {
20
+ try {
21
+ if (hasKeyFileJson(keyFileJson)) return true
22
+ if (hasKeyFilePath(keyFilePath)) return true
23
+ return false
24
+ } catch {
25
+ return false
26
+ }
27
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * @param {string | undefined | null} keyFileJson
3
+ * @returns {boolean}
4
+ */
5
+ export default function hasKeyFileJson (keyFileJson: string | undefined | null): boolean
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @param {string | undefined | null} keyFileJson
3
+ * @returns {boolean}
4
+ */
5
+ export default function hasKeyFileJson (keyFileJson = null) {
6
+ try {
7
+ if (keyFileJson && JSON.parse(keyFileJson)) return true
8
+ return false
9
+ } catch {
10
+ return false
11
+ }
12
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * @param {string | undefined | null} keyFilePath
3
+ * @returns {Promise<boolean>}
4
+ */
5
+ export default function hasKeyFilePath (keyFilePath: string | undefined | null): Promise<boolean>
@@ -0,0 +1,20 @@
1
+ import {
2
+ constants,
3
+ access
4
+ } from 'node:fs/promises'
5
+
6
+ /**
7
+ * @param {string | undefined | null} keyFilePath
8
+ * @returns {Promise<boolean>}
9
+ */
10
+ export default async function hasKeyFilePath (keyFilePath = null) {
11
+ try {
12
+ if (keyFilePath) {
13
+ await access(keyFilePath, constants.R_OK)
14
+ return true
15
+ }
16
+ return false
17
+ } catch {
18
+ return false
19
+ }
20
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Create a JSON file in Google Drive
3
+ */
4
+ export default function createInDrive (
5
+ driveId: string,
6
+ fileName: string,
7
+ fileData: Buffer,
8
+ keyFile: KeyFileType,
9
+ scopes?: string[]
10
+ ): Promise<FileType>
@@ -0,0 +1,102 @@
1
+ /**
2
+ * @typedef {import('google-auth-library').OAuth2Client} AuthClient
3
+ * @typedef {import('googleapis').drive_v3 } DriveV3
4
+ * @typedef {DriveV3.Schema$File} File
5
+ */
6
+
7
+ import debug from 'debug'
8
+
9
+ import {
10
+ Readable
11
+ } from 'node:stream'
12
+
13
+ import {
14
+ google
15
+ } from 'googleapis'
16
+
17
+ import {
18
+ GOOGLE_DRIVE_VERSION
19
+ } from '#common/config'
20
+
21
+ import {
22
+ hasErrorCode,
23
+ getErrorCode,
24
+ getErrorMessage
25
+ } from '#common/error'
26
+
27
+ import authorize from '#common/authorize'
28
+
29
+ const SCOPES = [
30
+ 'https://www.googleapis.com/auth/drive.file'
31
+ ]
32
+
33
+ const log = debug('@sequencemedia/drive/create')
34
+ const error = debug('@sequencemedia/drive/create:error')
35
+
36
+ log('`@sequencemedia/drive` is awake')
37
+
38
+ /**
39
+ * Create a JSON file in Google Drive
40
+ *
41
+ * @param {string} driveId Drive identifier
42
+ * @param {string} fileName JSON file identifier
43
+ * @param {string} fileData JSON file contents
44
+ * @param {AuthClient} authClient An authorised OAuth2 client
45
+ * @returns {File}
46
+ */
47
+ async function createJsonFile (driveId, fileName, fileData, authClient) {
48
+ try {
49
+ const drive = google.drive({
50
+ version: GOOGLE_DRIVE_VERSION,
51
+ auth: authClient
52
+ })
53
+
54
+ const {
55
+ data
56
+ } = await drive.files.create({
57
+ supportsAllDrives: true,
58
+ // includeItemsFromAllDrives: true,
59
+ requestBody: {
60
+ fields: 'id, name',
61
+ parents: [
62
+ driveId
63
+ ],
64
+ name: fileName
65
+ },
66
+ media: {
67
+ mimeType: 'application/json',
68
+ body: Readable.from(Buffer.from(fileData))
69
+ }
70
+ })
71
+
72
+ return data
73
+ } catch (e) {
74
+ if (hasErrorCode(e)) error(getErrorCode(e))
75
+ error(getErrorMessage(e))
76
+ throw e
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Create a JSON file in Google Drive
82
+ *
83
+ * @param {string} driveId Drive identifier
84
+ * @param {string} fileName JSON file name
85
+ * @param {Buffer} fileData JSON file data
86
+ * @param {Record<PropertyKey, string | undefined>} keyFile Authorisation
87
+ * @param {string[]} scopes Authorisation scopes
88
+ * @returns {File}
89
+ */
90
+ export default async function createInDrive (driveId, fileName, fileData, keyFile, scopes = SCOPES) {
91
+ return (
92
+ await createJsonFile(
93
+ driveId,
94
+ fileName,
95
+ fileData.toString('utf8'),
96
+ await authorize(
97
+ keyFile,
98
+ scopes
99
+ )
100
+ )
101
+ )
102
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Delete a JSON file from Google Drive
3
+ */
4
+ export default function deleteFromDrive (
5
+ fileId: string,
6
+ keyFile: KeyFileType,
7
+ scopes?: string[]
8
+ ): Promise<void>
@@ -0,0 +1,75 @@
1
+ /**
2
+ * @typedef {import('google-auth-library').OAuth2Client} AuthClient
3
+ */
4
+
5
+ import debug from 'debug'
6
+
7
+ import {
8
+ google
9
+ } from 'googleapis'
10
+
11
+ import {
12
+ GOOGLE_DRIVE_VERSION
13
+ } from '#common/config'
14
+
15
+ import {
16
+ hasErrorCode,
17
+ getErrorCode,
18
+ getErrorMessage
19
+ } from '#common/error'
20
+
21
+ import authorize from '#common/authorize'
22
+
23
+ const SCOPES = [
24
+ 'https://www.googleapis.com/auth/drive.file'
25
+ ]
26
+
27
+ const log = debug('@sequencemedia/drive/delete')
28
+ const error = debug('@sequencemedia/drive/delete:error')
29
+
30
+ log('`@sequencemedia/drive` is awake')
31
+
32
+ /**
33
+ * Delete a JSON file from Google Drive
34
+ *
35
+ * @param {string} fileId JSON file identifier
36
+ * @param {AuthClient} authClient An authorised OAuth2 client
37
+ * @returns {Promise<void>}
38
+ */
39
+ async function deleteJsonFile (fileId, authClient) {
40
+ try {
41
+ const drive = google.drive({
42
+ version: GOOGLE_DRIVE_VERSION,
43
+ auth: authClient
44
+ })
45
+
46
+ await drive.files.delete({
47
+ supportsAllDrives: true,
48
+ // includeItemsFromAllDrives: true,
49
+ fileId
50
+ })
51
+ } catch (e) {
52
+ if (hasErrorCode(e)) error(getErrorCode(e))
53
+ error(getErrorMessage(e))
54
+ throw e
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Delete a JSON file from Google Drive
60
+ *
61
+ * @param {string} fileId JSON file identifier
62
+ * @param {Record<PropertyKey, string | undefined>} keyFile An authorised OAuth2 client
63
+ * @returns {Promise<void>}
64
+ */
65
+ export default async function deleteFromDrive (fileId, keyFile, scopes = SCOPES) {
66
+ return (
67
+ await deleteJsonFile(
68
+ fileId,
69
+ await authorize(
70
+ keyFile,
71
+ scopes
72
+ )
73
+ )
74
+ )
75
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Get a JSON file from Google Drive
3
+ */
4
+ export default function getFromDrive (
5
+ fileId: string,
6
+ keyFile: KeyFileType,
7
+ scopes?: string[]
8
+ ): Promise<FileType>
@@ -0,0 +1,81 @@
1
+ /**
2
+ * @typedef {import('google-auth-library').OAuth2Client} AuthClient
3
+ * @typedef {import('googleapis').drive_v3 } DriveV3
4
+ * @typedef {DriveV3.Schema$File} File
5
+ */
6
+
7
+ import debug from 'debug'
8
+
9
+ import {
10
+ google
11
+ } from 'googleapis'
12
+
13
+ import {
14
+ GOOGLE_DRIVE_VERSION
15
+ } from '#common/config'
16
+
17
+ import {
18
+ hasErrorCode,
19
+ getErrorCode,
20
+ getErrorMessage
21
+ } from '#common/error'
22
+
23
+ import authorize from '#common/authorize'
24
+
25
+ const SCOPES = [
26
+ 'https://www.googleapis.com/auth/drive.file'
27
+ ]
28
+
29
+ const log = debug('@sequencemedia/drive/get')
30
+ const error = debug('@sequencemedia/drive/get:error')
31
+
32
+ log('`@sequencemedia/drive` is awake')
33
+
34
+ /**
35
+ * Get a JSON file from Google Drive
36
+ *
37
+ * @param {string} fileId JSON file identifier
38
+ * @param {AuthClient} authClient An authorised OAuth2 client
39
+ * @returns {File}
40
+ */
41
+ async function getJsonFile (fileId, authClient) {
42
+ try {
43
+ const drive = google.drive({
44
+ version: GOOGLE_DRIVE_VERSION,
45
+ auth: authClient
46
+ })
47
+
48
+ const {
49
+ data
50
+ } = await drive.files.get({
51
+ fileId,
52
+ alt: 'media'
53
+ })
54
+
55
+ return data
56
+ } catch (e) {
57
+ if (hasErrorCode(e)) error(getErrorCode(e))
58
+ error(getErrorMessage(e))
59
+ throw e
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Get a JSON file from Google Drive
65
+ *
66
+ * @param {string} fileId JSON file identifier
67
+ * @param {Record<PropertyKey, string | undefined>} keyFile Authorisation
68
+ * @param {string[]} scopes Authorisation scopes
69
+ * @returns {Promise<File>}
70
+ */
71
+ export default async function getFromDrive (fileId, keyFile, scopes = SCOPES) {
72
+ return (
73
+ await getJsonFile(
74
+ fileId,
75
+ await authorize(
76
+ keyFile,
77
+ scopes
78
+ )
79
+ )
80
+ )
81
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Get a list of the JSON files from Google Drive
3
+ */
4
+ export default function getListFromDrive (
5
+ driveId: string,
6
+ keyFile: KeyFileType,
7
+ scopes?: string[]
8
+ ): Promise<FileListType>
@@ -0,0 +1,85 @@
1
+ /**
2
+ * @typedef {import('google-auth-library').OAuth2Client} AuthClient
3
+ */
4
+
5
+ import debug from 'debug'
6
+
7
+ import {
8
+ google
9
+ } from 'googleapis'
10
+
11
+ import {
12
+ GOOGLE_DRIVE_VERSION
13
+ } from '#common/config'
14
+
15
+ import {
16
+ hasErrorCode,
17
+ getErrorCode,
18
+ getErrorMessage
19
+ } from '#common/error'
20
+
21
+ import authorize from '#common/authorize'
22
+
23
+ const SCOPES = [
24
+ 'https://www.googleapis.com/auth/drive'
25
+ ]
26
+
27
+ const log = debug('@sequencemedia/drive/list')
28
+ const error = debug('@sequencemedia/drive/list:error')
29
+
30
+ log('`@sequencemedia/drive` is awake')
31
+
32
+ /**
33
+ * Get a list of the JSON files from Google Drive
34
+ *
35
+ * @param {string} driveId Drive identifier
36
+ * @param {AuthClient} authClient An authorised OAuth2 client
37
+ * @returns {Promise<{id: string, name: string}[]>}
38
+ */
39
+ async function getJsonFileList (driveId, authClient) {
40
+ try {
41
+ const drive = google.drive({
42
+ version: GOOGLE_DRIVE_VERSION,
43
+ auth: authClient
44
+ })
45
+
46
+ const {
47
+ data: {
48
+ files = []
49
+ }
50
+ } = await drive.files.list({
51
+ supportsAllDrives: true,
52
+ corpora: 'drive',
53
+ includeItemsFromAllDrives: true,
54
+ fields: 'nextPageToken, files(id, name)',
55
+ orderBy: 'createdTime desc',
56
+ driveId
57
+ })
58
+
59
+ return files
60
+ } catch (e) {
61
+ if (hasErrorCode(e)) error(getErrorCode(e))
62
+ error(getErrorMessage(e))
63
+ throw e
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Get a list of the JSON files from Google Drive
69
+ *
70
+ * @param {string} driveId Drive identifier
71
+ * @param {Record<PropertyKey, string | undefined>} keyFile Authorisation
72
+ * @param {string[]} scopes Authorisation scopes
73
+ * @returns {Promise<Array<{id: string, name: string}>>}
74
+ */
75
+ export default async function getListFromDrive (driveId, keyFile, scopes = SCOPES) {
76
+ return (
77
+ await getJsonFileList(
78
+ driveId,
79
+ await authorize(
80
+ keyFile,
81
+ scopes
82
+ )
83
+ )
84
+ )
85
+ }
@@ -0,0 +1,9 @@
1
+ import type { drive_v3 as DriveV3 } from 'googleapis'
2
+
3
+ declare global {
4
+ type KeyFileType = Record<PropertyKey, string | undefined>
5
+
6
+ type FileType = DriveV3.Schema$File
7
+
8
+ type FileListType = Array<{ id: string, name: string }>
9
+ }
package/src/index.mjs ADDED
@@ -0,0 +1,4 @@
1
+ export { default as createInDrive } from './create/index.mjs'
2
+ export { default as deleteFromDrive } from './delete/index.mjs'
3
+ export { default as listInDrive } from './list/index.mjs'
4
+ export { default as getFromDrive } from './get/index.mjs'