carp-streamer
Advanced tools
Comparing version 0.9.2 to 0.10.0-0
@@ -30,5 +30,4 @@ #!/usr/bin/env node | ||
const app_1 = require("./app"); | ||
// tslint:disable-next-line: no-var-requires | ||
const npmPackage = require('../package.json'); | ||
const debug = util_1.default.debuglog(`${npmPackage.name}:index`); | ||
const packageName = process.env.npm_package_name || 'carp-streamer'; | ||
const debug = util_1.default.debuglog(`${packageName}:index`); | ||
const argsOption = { | ||
@@ -50,3 +49,3 @@ alias: { t: 'token', v: 'version', c: 'concurrency' }, | ||
if (args.version) { | ||
console.log(npmPackage.version); | ||
console.log(process.env.npm_package_version); | ||
process.exit(0); | ||
@@ -57,3 +56,3 @@ } | ||
if (sources.length === 0 || destination === undefined) { | ||
console.error(`usage: ${npmPackage.name} [options] source... destination`); | ||
console.error(`usage: ${packageName} [options] source... destination`); | ||
process.exit(1); | ||
@@ -60,0 +59,0 @@ } |
{ | ||
"name": "carp-streamer", | ||
"version": "0.9.2", | ||
"version": "0.10.0-0", | ||
"description": "carp-streamer is backup tool for files on local computer using Box Platform API.", | ||
@@ -34,2 +34,3 @@ "bin": "dist/index.js", | ||
"@types/progress": "^2.0.3", | ||
"@types/sinon": "^7.0.13", | ||
"chai": "^4.2.0", | ||
@@ -42,2 +43,3 @@ "codacy-coverage": "^3.4.0", | ||
"nyc": "^14.1.1", | ||
"sinon": "^7.3.2", | ||
"source-map-support": "^0.5.12", | ||
@@ -44,0 +46,0 @@ "ts-node": "^8.3.0", |
@@ -13,4 +13,4 @@ declare module 'box-node-sdk' { | ||
type: 'folder' | 'file'; | ||
name: string; | ||
sequence_id: string | null; | ||
name?: string; | ||
sequence_id?: string | null; | ||
etag: string | null; | ||
@@ -40,3 +40,3 @@ } | ||
interface MiniUser extends Object { | ||
export interface MiniUser extends Object { | ||
type: 'user'; | ||
@@ -48,30 +48,30 @@ name: string; | ||
export interface Folder extends MiniFolder { | ||
created_at: DateTime | null; | ||
modified_at: DateTime; | ||
description: string; | ||
size: number; | ||
path_collection: PathCollection; | ||
created_by: MiniUser; | ||
modified_by: MiniUser; | ||
trashed_at: DateTime | null; | ||
purged_at: DateTime | null; | ||
content_created_at: DateTime | null; | ||
content_modified_at: DateTime | null; | ||
created_at?: DateTime | null; | ||
modified_at?: DateTime | null; | ||
description?: string; | ||
size?: number; | ||
path_collection?: PathCollection; | ||
created_by?: MiniUser; | ||
modified_by?: MiniUser; | ||
trashed_at?: DateTime | null; | ||
purged_at?: DateTime | null; | ||
content_created_at?: DateTime | null; | ||
content_modified_at?: DateTime | null; | ||
expires_at?: DateTime; | ||
owned_by: MiniUser; | ||
shared_link: SharedLink | null; | ||
folder_upload_email: { access: AccessLevel; email: string } | null; | ||
parent: MiniFolder; | ||
item_status: ItemStatus; | ||
item_collection: Items<Item>; | ||
sync_state: SyncState; | ||
has_collaborations: boolean; | ||
permissions: Permissions; | ||
tags: string[]; | ||
can_non_owners_invite: boolean; | ||
is_externally_owned: boolean; | ||
is_collaboration_restricted_to_enterprise: boolean; | ||
allowed_shared_link_access_levels: AccessLevel[]; | ||
allowed_invitee_roles: string[]; | ||
watermark_info: WatermarkInfo; | ||
owned_by?: MiniUser; | ||
shared_link?: SharedLink | null; | ||
folder_upload_email?: { access: AccessLevel; email: string } | null; | ||
parent?: MiniFolder | null; | ||
item_status?: ItemStatus; | ||
item_collection?: Items<Item>; | ||
sync_state?: SyncState; | ||
has_collaborations?: boolean; | ||
permissions?: Permissions; | ||
tags?: string[]; | ||
can_non_owners_invite?: boolean; | ||
is_externally_owned?: boolean; | ||
is_collaboration_restricted_to_enterprise?: boolean; | ||
allowed_shared_link_access_levels?: AccessLevel[]; | ||
allowed_invitee_roles?: string[]; | ||
watermark_info?: WatermarkInfo; | ||
} | ||
@@ -123,30 +123,30 @@ | ||
export interface File extends MiniFile { | ||
description: string; | ||
size: number; | ||
path_collection: PathCollection; | ||
created_at: DateTime; | ||
modified_at: DateTime; | ||
trashed_at: DateTime; | ||
purged_at: DateTime; | ||
content_created_at: DateTime; | ||
content_modified_at: DateTime; | ||
expires_at: DateTime; | ||
created_by: MiniUser; | ||
modified_by: MiniUser; | ||
owned_by: MiniUser; | ||
shared_link: SharedLink; | ||
parent: MiniFolder; | ||
item_status: ItemStatus; | ||
version_number: string; | ||
comment_count: number; | ||
permissions: Permissions; | ||
tags: string[]; | ||
lock: Lock | null; | ||
extension: string; | ||
is_package: boolean; | ||
expiring_embed_link: string; | ||
watermark_info: WatermarkInfo; | ||
allowed_invitee_roles: string[]; | ||
is_externally_owned: boolean; | ||
has_collaborations: boolean; | ||
description?: string; | ||
size?: number; | ||
path_collection?: PathCollection; | ||
created_at?: DateTime; | ||
modified_at?: DateTime; | ||
trashed_at?: DateTime | null; | ||
purged_at?: DateTime | null; | ||
content_created_at?: DateTime; | ||
content_modified_at?: DateTime; | ||
expires_at?: DateTime; | ||
created_by?: MiniUser; | ||
modified_by?: MiniUser; | ||
owned_by?: MiniUser; | ||
shared_link?: SharedLink | null; | ||
parent?: MiniFolder; | ||
item_status?: ItemStatus; | ||
version_number?: string; | ||
comment_count?: number; | ||
permissions?: Permissions; | ||
tags?: string[]; | ||
lock?: Lock | null; | ||
extension?: string; | ||
is_package?: boolean; | ||
expiring_embed_link?: string; | ||
watermark_info?: WatermarkInfo; | ||
allowed_invitee_roles?: string[]; | ||
is_externally_owned?: boolean; | ||
has_collaborations?: boolean; | ||
} | ||
@@ -187,2 +187,5 @@ | ||
public get<T>(path: string, params: any, calback?: (err: any, result: T) => void): Promise<T>; | ||
public options<T>(path: string, params: any, calback?: (err: any, result: T) => void): Promise<T>; | ||
public post<T>(path: string, params: any, calback?: (err: any, result: T) => void): Promise<T>; | ||
public upload<T>(path: string, params: any, formData: any, calback?: (err: any, result: T) => void): Promise<T>; | ||
public setCustomHeader(header: string, value: any): void; | ||
@@ -189,0 +192,0 @@ public wrapWithDefaultHandler<U extends any[], T>(method: (...args: U) => T): (...args: U) => T; |
@@ -1,2 +0,2 @@ | ||
import async from 'async'; | ||
import async, { find } from 'async'; | ||
import * as box from 'box-node-sdk'; | ||
@@ -8,8 +8,10 @@ import BoxClient from 'box-node-sdk/lib/box-client'; | ||
import fs from 'fs'; | ||
import os from 'os'; | ||
import path from 'path'; | ||
import util from 'util'; | ||
import { name as packageName } from '../package.json'; | ||
import BoxClientBuilder, { BoxClientConfig } from './box-client-builder'; | ||
import BoxFinder, { CacheConfig } from './box-finder'; | ||
const debug = util.debuglog('carp-streamer:app'); | ||
const debug = util.debuglog(`${packageName}:app`); | ||
@@ -26,8 +28,3 @@ const readdirAsync = util.promisify(fs.readdir); | ||
export class Synchronizer extends EventEmitter { | ||
private client: BoxClient; | ||
private q: async.AsyncQueue<Task>; | ||
private cacheConfig: CacheConfig; | ||
constructor(appConfig?: AppConfig, accessToken?: string, options?: { asUser: string }, concurrency: number = 0, cacheConfig: CacheConfig = {}) { | ||
super(); | ||
public static async create(appConfig?: AppConfig, accessToken?: string, options?: { asUser: string }, destination = '0', cacheConfig: CacheConfig = {}, concurrency: number = 0) { | ||
const configurator = options && options.asUser | ||
@@ -38,12 +35,19 @@ ? ((client: BoxClient) => client.asUser(options.asUser)) : undefined; | ||
: { kind: 'AppAuth', type: 'enterprise', configurator }; | ||
this.client = new BoxClientBuilder(appConfig, clientConfig).build(); | ||
const clietBuilder = new BoxClientBuilder(appConfig, clientConfig); | ||
const finder = await BoxFinder.create(clietBuilder.build(), destination, cacheConfig); | ||
finder.loadCache(path.join(os.tmpdir(), 'box-finder.cache.json')); | ||
return new Synchronizer(finder, concurrency); | ||
} | ||
private q: async.AsyncQueue<Task>; | ||
private constructor(private finder: BoxFinder, concurrency: number = 0) { | ||
super(); | ||
this.q = async.queue<Task, SyncResult, Error>(worker, concurrency); | ||
this.cacheConfig = cacheConfig; | ||
} | ||
public async synchronize(source: string, destination = '0', excludes: string[] = [], pretend = false) { | ||
public async synchronize(source: string, excludes: string[] = [], pretend = false) { | ||
const callback: async.AsyncResultCallback<SyncResult> = (error, result = { status: SyncResultStatus.UNKNOWN }) => { | ||
this.emit(SyncEventType.SYNCHRONIZE, error, result.absolutePath, result.status); | ||
}; | ||
const finder = await BoxFinder.create(this.client, destination, this.cacheConfig); | ||
const stats = await statAsync(source); | ||
@@ -54,5 +58,5 @@ if (!stats.isDirectory()) { | ||
this.emit(SyncEventType.ENTER, source); | ||
this.q.push({ entry, rootPath: dir, finder, pretend, excludes }, callback); | ||
this.q.push({ entry, rootPath: dir, finder: this.finder, pretend, excludes }, callback); | ||
this.emit(SyncEventType.ENTERED, 1); | ||
return this.q.drain(); | ||
return this.q.drain().then(() => this.finder.saveCache(path.join(os.tmpdir(), 'box-finder.cache.json'))); | ||
} | ||
@@ -62,7 +66,7 @@ let count = 0; | ||
this.emit(SyncEventType.ENTER, entry.path); | ||
this.q.push({ entry, rootPath: source, finder, pretend, excludes }, callback); | ||
this.q.push({ entry, rootPath: source, finder: this.finder, pretend, excludes }, callback); | ||
count++; | ||
} | ||
this.emit(SyncEventType.ENTERED, count); | ||
return this.q.drain(); | ||
return this.q.drain().then(() => this.finder.saveCache(path.join(os.tmpdir(), 'box-finder.cache.json'))); | ||
} | ||
@@ -69,0 +73,0 @@ } |
@@ -0,1 +1,2 @@ | ||
import assert from 'assert'; | ||
import * as box from 'box-node-sdk'; | ||
@@ -7,3 +8,3 @@ import BoxClient from 'box-node-sdk/lib/box-client'; | ||
import { ResponseError } from 'box-node-sdk/lib/util/errors'; | ||
import { ReadStream, Stats } from 'fs'; | ||
import fs from 'fs'; | ||
import _ from 'lodash'; | ||
@@ -15,6 +16,7 @@ import LRUCache from 'lru-cache'; | ||
import util from 'util'; | ||
import { name as packageName } from '../package.json'; | ||
import { INIT_RETRY_TIMES } from './config'; | ||
import { sleep } from './util'; | ||
const debug = util.debuglog('carp-streamer:box'); | ||
const debug = util.debuglog(`${packageName}:box-finder`); | ||
@@ -111,3 +113,3 @@ const CHUNKED_UPLOAD_MINIMUM = 20_000_000; | ||
public async uploadFile(name: string, content: string | Buffer | ReadStream, stats?: Stats, folder?: box.MiniFolder) { | ||
public async uploadFile(name: string, content: string | Buffer | fs.ReadStream, stats?: fs.Stats, folder?: box.MiniFolder) { | ||
const folderId = (folder || this.current).id; | ||
@@ -157,3 +159,6 @@ const options = { | ||
public async uploadNewFileVersion(file: box.MiniFile, content: string | Buffer | ReadStream, stats?: Stats) { | ||
public async uploadNewFileVersion(file: box.MiniFile, content: string | Buffer | fs.ReadStream, stats?: fs.Stats) { | ||
if (file.name === undefined) { | ||
return assert.fail('file.name is required.'); | ||
} | ||
const options = { | ||
@@ -187,2 +192,14 @@ content_modified_at: stats && toRFC3339String(stats.mtime), | ||
public async loadCache(file: string | Buffer | url.URL | fs.promises.FileHandle) { | ||
const buffer = await fs.promises.readFile(file); | ||
return this.cache.load(JSON.parse(buffer.toString('UTF-8'))); | ||
} | ||
public async saveCache(file: string | Buffer | url.URL | fs.promises.FileHandle) { | ||
const entries = this.cache.dump(); | ||
debug('cache entries: %o', entries); | ||
const json = JSON.stringify(entries, null, 0); | ||
return fs.promises.writeFile(file, json, { encoding: 'UTF-8' }); | ||
} | ||
private new(folder: box.MiniFolder, cache = this.cache, disableCachedResponsesValidation = this.disableCachedResponsesValidation) { | ||
@@ -192,6 +209,6 @@ return BoxFinder.new(this.client, folder, cache, disableCachedResponsesValidation); | ||
private createFolder(folderName: string, parentFolder = this.current): Promise<box.Folder> { | ||
private createFolder(folderName: string, parentFolder = this.current) { | ||
const parentFolderId = parentFolder.id; | ||
return makeRetriable(this.folders.create, this.folders, retryIfFolderConflictError)(parentFolderId, folderName) | ||
.then(cacheItem(this.cache)); | ||
const create = makeRetriable(this.folders.create, this.folders, retryIfFolderConflictError); | ||
return create(parentFolderId, folderName).then(cacheItem(this.cache)); | ||
} | ||
@@ -208,3 +225,3 @@ | ||
private async findItemByName<T extends box.Item>(itemName: string, isItem: (item: box.Item) => item is T, parentFolder = this.current) { | ||
const filter = (item: T) => item.name.normalize() === itemName.normalize(); | ||
const filter = (item: T) => item.name && item.name.normalize() === itemName.normalize(); | ||
const cachedItem = _.first((this.cache.get(parentFolder.id) || []).filter(isItem).filter(filter)); | ||
@@ -244,3 +261,4 @@ if (cachedItem) { | ||
}; | ||
return this.client.wrapWithDefaultHandler(this.client.get)<U>(basePath, params) | ||
const get = this.client.wrapWithDefaultHandler(this.client.get); | ||
return get<U>(basePath, params) | ||
.then(cacheItem(this.cache)) | ||
@@ -269,2 +287,5 @@ .catch(error => { | ||
debug('new %s: %o', isMiniFile(newItem) ? 'file' : isMiniFolder(newItem) ? 'folder' : 'item', newItem); | ||
if (newItem.parent === undefined || newItem.parent === null) { | ||
return assert.fail('folder or file parent is required.'); | ||
} | ||
const parentFolderId = newItem.parent.id; | ||
@@ -271,0 +292,0 @@ const cachedItems = cache.get(parentFolderId) || []; |
@@ -10,7 +10,6 @@ #!/usr/bin/env node | ||
import util from 'util'; | ||
import { name as packageName, version as packageVersion } from '../package.json'; | ||
import { SyncEventType, Synchronizer, SyncResultStatus } from './app'; | ||
// tslint:disable-next-line: no-var-requires | ||
const npmPackage = require('../package.json'); | ||
const debug = util.debuglog(`${npmPackage.name}:index`); | ||
const debug = util.debuglog(`${packageName}:index`); | ||
@@ -33,3 +32,3 @@ const argsOption = { | ||
if (args.version) { | ||
console.log(npmPackage.version); | ||
console.log(packageVersion); | ||
process.exit(0); | ||
@@ -42,3 +41,3 @@ } | ||
if (sources.length === 0 || destination === undefined) { | ||
console.error(`usage: ${npmPackage.name} [options] source... destination`); | ||
console.error(`usage: ${packageName} [options] source... destination`); | ||
process.exit(1); | ||
@@ -74,3 +73,3 @@ } | ||
}; | ||
const synchronizer = new Synchronizer(appConfig, args.token, args['as-user'], concurrency, cacheConfig); | ||
const synchronizer = await Synchronizer.create(appConfig, args.token, args['as-user'], destination, cacheConfig, concurrency); | ||
synchronizer | ||
@@ -121,3 +120,3 @@ .on(SyncEventType.ENTER, absolutePath => { | ||
const rootPath = path.resolve(process.cwd(), source); | ||
await synchronizer.synchronize(rootPath, destination, excludes, pretend); | ||
await synchronizer.synchronize(rootPath, excludes, pretend); | ||
} | ||
@@ -124,0 +123,0 @@ if (needProgress) { console.error('Successful!'); } |
@@ -0,1 +1,3 @@ | ||
/// <reference types="../src/@types/box-node-sdk" /> | ||
import AnonymousSession from 'box-node-sdk/lib/sessions/anonymous-session'; | ||
@@ -2,0 +4,0 @@ import AppAuthSession from 'box-node-sdk/lib/sessions/app-auth-session'; |
{ | ||
"compilerOptions": { | ||
/* Basic Options */ | ||
"target": "es2016", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ | ||
// "incremental": true, /* Enable incremental compilation */ | ||
"target": "es2016", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ | ||
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ | ||
@@ -11,2 +12,3 @@ // "lib": [], /* Specify library files to be included in the compilation. */ | ||
// "declaration": true, /* Generates corresponding '.d.ts' file. */ | ||
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ | ||
"sourceMap": true, /* Generates corresponding '.map' file. */ | ||
@@ -16,2 +18,4 @@ // "outFile": "./", /* Concatenate and emit output to single file. */ | ||
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ | ||
// "composite": true, /* Enable project compilation */ | ||
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ | ||
// "removeComments": true, /* Do not emit comments to output. */ | ||
@@ -28,2 +32,3 @@ // "noEmit": true, /* Do not emit outputs. */ | ||
// "strictFunctionTypes": true, /* Enable strict checking of function types. */ | ||
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ | ||
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ | ||
@@ -50,8 +55,10 @@ // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ | ||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ | ||
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ | ||
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ | ||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ | ||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ | ||
"resolveJsonModule": true, | ||
/* Source Map Options */ | ||
// "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ | ||
// "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */ | ||
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ | ||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ | ||
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ | ||
@@ -67,2 +74,2 @@ // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ | ||
] | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
257469
46
3901
21
11