This commit is contained in:
Garrett Serack 2018-10-03 10:20:46 -07:00
Родитель 3e62a675df
Коммит f2dd7560d6
13 изменённых файлов: 961 добавлений и 55 удалений

Просмотреть файл

@ -1,11 +1,19 @@
dependencies:
'@rush-temp/async-io': 'file:projects/async-io.tgz'
'@rush-temp/tasks': 'file:projects/tasks.tgz'
'@types/mocha': 5.2.5
'@types/node': 10.11.3
'@types/pify': 0.0.28
file-url: 2.0.2
get-uri: 2.0.2
mocha: 5.2.0
mocha-typescript: 1.1.17
pify: 2.3.0
proper-lockfile: 2.0.1
source-map-support: 0.5.9
strip-bom: 3.0.0
typescript: 3.1.1
urijs: 1.18.12
packages:
/@types/mocha/5.2.5:
dev: false
@ -15,6 +23,10 @@ packages:
dev: false
resolution:
integrity: sha512-3AvcEJAh9EMatxs+OxAlvAEs7OTy6AG94mcH1iqyVDwVVndekLxzwkWQ/Z4SDbY6GO2oyUXyWW8tQ4rENSSQVQ==
/@types/pify/0.0.28:
dev: false
resolution:
integrity: sha1-aCtsM6HQV0i4bwKb2rb2Kqc/9QY=
/ansi-regex/2.1.1:
dev: false
engines:
@ -103,6 +115,10 @@ packages:
dev: false
resolution:
integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
/core-util-is/1.0.2:
dev: false
resolution:
integrity: sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
/cross-spawn/5.1.0:
dependencies:
lru-cache: 4.1.3
@ -123,6 +139,16 @@ packages:
node: '>=4.8'
resolution:
integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
/data-uri-to-buffer/1.2.0:
dev: false
resolution:
integrity: sha512-vKQ9DTQPN1FLYiiEEOQ6IBGFqvjCa5rSK3cWMy/Nespm5d/x3dGFT9UBZnkLxCwua/IXBi2TYnwTEpsOvhC4UQ==
/debug/2.6.9:
dependencies:
ms: 2.0.0
dev: false
resolution:
integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
/debug/3.1.0:
dependencies:
ms: 2.0.0
@ -141,10 +167,6 @@ packages:
node: '>=0.3.1'
resolution:
integrity: sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==
/es6-object-assign/1.1.0:
dev: false
resolution:
integrity: sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=
/escape-string-regexp/1.0.5:
dev: false
engines:
@ -165,6 +187,20 @@ packages:
node: '>=4'
resolution:
integrity: sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=
/extend/3.0.2:
dev: false
resolution:
integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
/file-uri-to-path/1.0.0:
dev: false
resolution:
integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==
/file-url/2.0.2:
dev: false
engines:
node: '>=4'
resolution:
integrity: sha1-6VF4TXkJUSfTcTApqwY/QIGMoq4=
/find-up/2.1.0:
dependencies:
locate-path: 2.0.0
@ -177,6 +213,15 @@ packages:
dev: false
resolution:
integrity: sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
/ftp/0.3.10:
dependencies:
readable-stream: 1.1.14
xregexp: 2.0.0
dev: false
engines:
node: '>=0.8.0'
resolution:
integrity: sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0=
/get-caller-file/1.0.3:
dev: false
resolution:
@ -188,6 +233,17 @@ packages:
resolution:
integrity: sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=
tarball: 'http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz'
/get-uri/2.0.2:
dependencies:
data-uri-to-buffer: 1.2.0
debug: 2.6.9
extend: 3.0.2
file-uri-to-path: 1.0.0
ftp: 0.3.10
readable-stream: 2.3.6
dev: false
resolution:
integrity: sha512-ZD325dMZOgerGqF/rF6vZXyFGTAay62svjQIT+X/oU2PtxYpFxvSkbsdi+oxIrsNxlZVd4y8wUDqkaExWTI/Cw==
/glob/7.1.2:
dependencies:
fs.realpath: 1.0.0
@ -199,17 +255,12 @@ packages:
dev: false
resolution:
integrity: sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==
/glob/7.1.3:
dependencies:
fs.realpath: 1.0.0
inflight: 1.0.6
inherits: 2.0.3
minimatch: 3.0.4
once: 1.4.0
path-is-absolute: 1.0.1
/graceful-fs/4.1.11:
dev: false
engines:
node: '>=0.4.0'
resolution:
integrity: sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==
integrity: sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=
/growl/1.10.5:
dev: false
engines:
@ -238,10 +289,6 @@ packages:
dev: false
resolution:
integrity: sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
/interpret/1.1.0:
dev: false
resolution:
integrity: sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=
/invert-kv/1.0.0:
dev: false
engines:
@ -268,6 +315,14 @@ packages:
node: '>=0.10.0'
resolution:
integrity: sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
/isarray/0.0.1:
dev: false
resolution:
integrity: sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=
/isarray/1.0.0:
dev: false
resolution:
integrity: sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
/isexe/2.0.0:
dev: false
resolution:
@ -321,11 +376,6 @@ packages:
resolution:
integrity: sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
tarball: 'http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz'
/minimist/1.2.0:
dev: false
resolution:
integrity: sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
tarball: 'http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz'
/mkdirp/0.5.1:
dependencies:
minimist: 0.0.8
@ -447,22 +497,52 @@ packages:
node: '>=4'
resolution:
integrity: sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
/path-parse/1.0.6:
/pify/2.3.0:
dev: false
engines:
node: '>=0.10.0'
resolution:
integrity: sha1-7RQaasBDqEnqWISY59yosVMw6Qw=
/process-nextick-args/2.0.0:
dev: false
resolution:
integrity: sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
integrity: sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==
/proper-lockfile/2.0.1:
dependencies:
graceful-fs: 4.1.11
retry: 0.10.1
dev: false
engines:
node: '>=4.0.0'
resolution:
integrity: sha1-FZ+wYZPTIAP0s2kd0uwaY0qoDR0=
/pseudomap/1.0.2:
dev: false
resolution:
integrity: sha1-8FKijacOYYkX7wqKw0wa5aaChrM=
/rechoir/0.6.2:
/readable-stream/1.1.14:
dependencies:
resolve: 1.8.1
core-util-is: 1.0.2
inherits: 2.0.3
isarray: 0.0.1
string_decoder: 0.10.31
dev: false
engines:
node: '>= 0.10'
resolution:
integrity: sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=
integrity: sha1-fPTFTvZI44EwhMY23SB54WbAgdk=
tarball: 'http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz'
/readable-stream/2.3.6:
dependencies:
core-util-is: 1.0.2
inherits: 2.0.3
isarray: 1.0.0
process-nextick-args: 2.0.0
safe-buffer: 5.1.2
string_decoder: 1.1.1
util-deprecate: 1.0.2
dev: false
resolution:
integrity: sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==
tarball: 'http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz'
/require-directory/2.1.1:
dev: false
engines:
@ -473,12 +553,14 @@ packages:
dev: false
resolution:
integrity: sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=
/resolve/1.8.1:
dependencies:
path-parse: 1.0.6
/retry/0.10.1:
dev: false
resolution:
integrity: sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==
integrity: sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=
/safe-buffer/5.1.2:
dev: false
resolution:
integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
/semver/5.5.1:
dev: false
hasBin: true
@ -502,26 +584,6 @@ packages:
node: '>=0.10.0'
resolution:
integrity: sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=
/shelljs/0.7.8:
dependencies:
glob: 7.1.3
interpret: 1.1.0
rechoir: 0.6.2
dev: false
engines:
node: '>=0.11.0'
hasBin: true
resolution:
integrity: sha1-3svPh0sNHl+3LhSxZKloMEjprLM=
/shx/0.2.2:
dependencies:
es6-object-assign: 1.1.0
minimist: 1.2.0
shelljs: 0.7.8
dev: false
hasBin: true
resolution:
integrity: sha1-CjBNAgsO3xMGrYFXDoDwNG31ijk=
/signal-exit/3.0.2:
dev: false
resolution:
@ -558,6 +620,16 @@ packages:
node: '>=4'
resolution:
integrity: sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
/string_decoder/0.10.31:
dev: false
resolution:
integrity: sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=
/string_decoder/1.1.1:
dependencies:
safe-buffer: 5.1.2
dev: false
resolution:
integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
/strip-ansi/3.0.1:
dependencies:
ansi-regex: 2.1.1
@ -575,6 +647,12 @@ packages:
node: '>=4'
resolution:
integrity: sha1-qEeQIusaw2iocTibY1JixQXuNo8=
/strip-bom/3.0.0:
dev: false
engines:
node: '>=4'
resolution:
integrity: sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=
/strip-eof/1.0.0:
dev: false
engines:
@ -604,6 +682,14 @@ packages:
hasBin: true
resolution:
integrity: sha512-Veu0w4dTc/9wlWNf2jeRInNodKlcdLgemvPsrNpfu5Pq39sgfFjvIIgTsvUHCoLBnMhPoUA+tFxsXjU6VexVRQ==
/urijs/1.18.12:
dev: false
resolution:
integrity: sha512-WlvUkocbQ+GYhi8zkcbecbGYq7YLSd2I3InxAfqeh6mWvWalBE7bISDHcAL3J7STrWFfizuJ709srHD+RuABPQ==
/util-deprecate/1.0.2:
dev: false
resolution:
integrity: sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
/which-module/2.0.0:
dev: false
resolution:
@ -629,6 +715,10 @@ packages:
dev: false
resolution:
integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
/xregexp/2.0.0:
dev: false
resolution:
integrity: sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM=
/y18n/3.2.1:
dev: false
resolution:
@ -661,14 +751,31 @@ packages:
resolution:
integrity: sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A==
tarball: 'http://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz'
'file:projects/async-io.tgz':
dependencies:
'@types/node': 10.11.3
'@types/pify': 0.0.28
file-url: 2.0.2
get-uri: 2.0.2
mocha: 5.2.0
mocha-typescript: 1.1.17
pify: 2.3.0
proper-lockfile: 2.0.1
strip-bom: 3.0.0
typescript: 3.1.1
urijs: 1.18.12
dev: false
name: '@rush-temp/async-io'
resolution:
integrity: sha512-i1azoeW0jx/VUDrCeWWBLTUK+NeH4VvW0JHp0OlTY82V84suqO0RV3kH+9s4zQ48R31QgEsu6X0MuBDOnGw6IQ==
tarball: 'file:projects/async-io.tgz'
version: 0.0.0
'file:projects/tasks.tgz':
dependencies:
'@types/mocha': 5.2.5
'@types/node': 10.11.3
mocha: 5.2.0
mocha-typescript: 1.1.17
semver: 5.5.1
shx: 0.2.2
source-map-support: 0.5.9
typescript: 3.1.1
dev: false
@ -681,10 +788,18 @@ registry: 'https://registry.npmjs.org/'
shrinkwrapMinorVersion: 9
shrinkwrapVersion: 3
specifiers:
'@rush-temp/async-io': 'file:./projects/async-io.tgz'
'@rush-temp/tasks': 'file:./projects/tasks.tgz'
'@types/mocha': 5.2.5
'@types/node': 10.11.3
'@types/pify': 0.0.28
file-url: 2.0.2
get-uri: ^2.0.1
mocha: 5.2.0
mocha-typescript: 1.1.17
pify: ^2.3.0
proper-lockfile: ^2.0.1
source-map-support: 0.5.9
strip-bom: 3.0.0
typescript: ^3.1.1
urijs: ~1.18.10

Просмотреть файл

@ -0,0 +1,11 @@
!dist/**/*
test/
dist/test/
package/
.npmignore
tsconfig.json
*.ts
!*.d.ts
*.tgz
.vscode
.scripts

Просмотреть файл

@ -0,0 +1,18 @@
# Project: @microsoft.azure/async-ic
This package contains common code for the developer tools for generating Azure SDKs.
Including:
- AutoRest
### Description
Async IO wrappers
### Contents
----
# Contributing
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.

Просмотреть файл

@ -0,0 +1,185 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as fs from "fs";
import * as path from "path";
import * as promisify from "pify";
import { OutstandingTaskAwaiter, Exception, Delay } from '@microsoft.azure/tasks'
export class PathNotFoundException extends Exception {
constructor(path: string, public exitCode: number = 1) {
super(`File '${path}' not found.`, exitCode);
Object.setPrototypeOf(this, PathNotFoundException.prototype);
}
}
export class PathIsNotFileException extends Exception {
constructor(path: string, public exitCode: number = 1) {
super(`File '${path}' is not a file.`, exitCode);
Object.setPrototypeOf(this, PathIsNotFileException.prototype);
}
}
export class PathIsNotDirectoryException extends Exception {
constructor(path: string, public exitCode: number = 1) {
super(`File '${path}' is not a directory.`, exitCode);
Object.setPrototypeOf(this, PathIsNotFileException.prototype);
}
}
export class UnableToRemoveException extends Exception {
constructor(path: string, public exitCode: number = 1) {
super(`Unable to remove '${path}'.`, exitCode);
Object.setPrototypeOf(this, UnableToRemoveException.prototype);
}
}
export class UnableToMakeDirectoryException extends Exception {
constructor(path: string, public exitCode: number = 1) {
super(`Unable to create directory '${path}'.`, exitCode);
Object.setPrototypeOf(this, UnableToMakeDirectoryException.prototype);
}
}
export const exists: (path: string | Buffer) => Promise<boolean> = path => new Promise<boolean>((r, j) => fs.stat(path, (err: NodeJS.ErrnoException, stats: fs.Stats) => err ? r(false) : r(true)));
export const readdir: (path: string | Buffer) => Promise<Array<string>> = promisify(fs.readdir);
export const close: (fd: number) => Promise<void> = promisify(fs.close);
export const writeFile: (filename: string, content: string) => Promise<void> = (filename, content) => Promise.resolve(fs.writeFileSync(filename, content)); // for some reason writeFile only produced empty files
export const lstat: (path: string | Buffer) => Promise<fs.Stats> = promisify(fs.lstat);
const fs_rmdir: (path: string | Buffer) => Promise<void> = promisify(fs.rmdir);
const unlink: (path: string | Buffer) => Promise<void> = promisify(fs.unlink);
const fs_mkdir: (path: string | Buffer) => Promise<void> = promisify(fs.mkdir);
const fs_open: (path: string | Buffer, flags: string | number) => Promise<number> = promisify(fs.open);
const fs_close: (fs: number) => Promise<void> = promisify(fs.close);
export async function mkdir(dirPath: string) {
if (!await isDirectory(dirPath)) {
const p = path.normalize(dirPath + "/");
const parent = path.dirname(dirPath);
if (! await isDirectory(parent)) {
if (p != parent) {
await mkdir(parent);
}
}
try {
await fs_mkdir(p);
} catch (e) {
if (!await isDirectory(p)) {
throw new UnableToMakeDirectoryException(p);
}
}
}
}
const fs_readFile: (filename: string, encoding: string, ) => Promise<string> = promisify(fs.readFile);
export async function readFile(filename: string): Promise<string> {
return fs_readFile(filename, "utf-8");
}
export async function isDirectory(dirPath: string): Promise<boolean> {
try {
if (await exists(dirPath)) {
return (await lstat(dirPath)).isDirectory();
}
} catch (e) {
// don't throw!
}
return false;
}
export async function isFile(filePath: string): Promise<boolean> {
try {
if (await exists(filePath)) {
return !(await lstat(filePath)).isDirectory();
}
} catch (e) {
// don't throw!
}
return false;
}
export async function rmdir(dirPath: string) {
// if it's not there, do nothing.
if (!await exists(dirPath)) {
return;
}
//if it's not a directory, that's bad.
if (!await isDirectory(dirPath)) {
throw new PathIsNotDirectoryException(dirPath);
}
// make sure this isn't the current directory.
if (process.cwd() === path.normalize(dirPath)) {
process.chdir(`${dirPath}/..`);
}
// make sure the folder is empty first.
const files = await readdir(dirPath);
if (files.length) {
const awaiter = new OutstandingTaskAwaiter();
try {
for (const file of files) {
try {
const p = path.join(dirPath, file);
if (await isDirectory(p)) {
// folders are recursively rmdir'd
awaiter.Await(rmdir(p));
}
else {
// files and symlinks are unlink'd
awaiter.Await(unlink(p).catch(() => { }));
}
} catch (e) {
// uh... can't.. ok.
}
}
} finally {
// after all the entries are done
await awaiter.Wait();
}
}
try {
// if this fails for some reason, check if it's important.
await fs_rmdir(dirPath);
} catch (e) {
// is it gone? that's all we really care about.
if (await isDirectory(dirPath)) {
// directory did not delete
throw new UnableToRemoveException(dirPath);
}
}
}
export async function rmFile(filePath: string) {
// not there? no problem
if (!exists(filePath)) {
return;
}
// not a file? that's not cool.
if (await isDirectory(filePath)) {
throw new PathIsNotFileException(filePath);
}
try {
// files and symlinks are unlink'd
await unlink(filePath);
} catch (e) {
// is it gone? that's all we really care about.
if (await exists(filePath)) {
// directory did not delete
throw new UnableToRemoveException(filePath);
}
}
}

Просмотреть файл

@ -0,0 +1,99 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as fs from "fs";
import * as path from "path";
import * as promisify from "pify";
import { OutstandingTaskAwaiter, Exception, Delay } from '@microsoft.azure/tasks'
import { isFile } from "./file-io"
const { lock, check, } = require("proper-lockfile")
const fs_open: (path: string | Buffer, flags: string | number) => Promise<number> = promisify(fs.open);
export class UnableToReadLockException extends Exception {
constructor(path: string, public exitCode: number = 1) {
super(`Unable to create read lock on '${path}'.`, exitCode);
Object.setPrototypeOf(this, UnableToReadLockException.prototype);
}
}
export interface UnlockOptions {
realpath?: boolean;
delay?: number;
}
export interface CheckOptions extends UnlockOptions {
stale?: number;
}
export interface LockOptions extends CheckOptions {
update?: number;
retries?: number;
}
export type release = () => Promise<void>;
export type _release = () => void;
export class Lock {
private static _exclusive: (path: string, options?: LockOptions) => Promise<_release> = promisify(lock);
public static check: (path: string, options?: CheckOptions) => Promise<boolean> = promisify(check);
public static async exclusive(path: string, options?: LockOptions): Promise<release> {
return promisify(await this._exclusive(path, options));
}
public static async waitForExclusive(path: string, timeout: number = 5 * 60 * 1000): Promise<release | null> {
let result: release | null = null;
const expire = Date.now() + timeout;
do {
try {
result = await this.exclusive(path);
} catch (e) {
// no worries. Just wait a few seconds and see if we can get it.
await Delay(3000);
}
} while (result == null && expire > Date.now())
return result;
}
public static async read(path: string, options?: LockOptions): Promise<release> {
// first try to create the file
// it's ok if it fails
options = options || {};
options.delay = options.delay || 2000;
options.retries = options.retries || 4;
const p = `${path}.lock`;
try {
fs.writeFileSync(p, 'lockfile');
} catch (e) {
// no worries.
}
// try to open the file for read
try {
if (await isFile(p)) {
const fd = await fs_open(p, 'r');
return async () => {
fs.close(fd, (err) => { });
try {
fs.unlinkSync(p);
} catch (E) {
}
};
}
} catch (e) {
}
if (options.retries) {
await Delay(options.delay);
options.retries--;
return await this.read(path, options);
}
throw new UnableToReadLockException(path);
}
}

Просмотреть файл

@ -0,0 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export * from "./file-io"
export * from "./lock"
export * from "./uri"

Просмотреть файл

@ -0,0 +1,51 @@
{
"name": "@microsoft.azure/async-io",
"version": "2.0.7",
"description": "Promisify'd asnyc wrappers (for Azure Open Source Projects)",
"main": "./dist/main.js",
"typings": "./dist/main.d.ts",
"engines": {
"node": ">=7.10.0"
},
"repository": {
"type": "git",
"url": "git+https://github.com/Azure/perks.async-io.git"
},
"scripts": {
"build": "tsc -p .",
"prepare": "npm run build",
"test": "npm run build && mocha dist/test",
"testci": "npm run build && mocha dist/test"
},
"keywords": [
"perks",
"async",
"autorest",
"azure",
"typescript"
],
"author": "Microsoft",
"license": "MIT",
"bugs": {
"url": "https://github.com/Azure/perks.async-io/issues"
},
"homepage": "https://github.com/Azure/perks.async-io#readme",
"readme": "https://github.com/Azure/perks.async-io/readme.md",
"devDependencies": {
"mocha-typescript": "1.1.17",
"@types/mocha": "5.2.5",
"@types/node": "10.11.3",
"mocha": "5.2.0",
"typescript": "^3.1.1",
"@types/pify": "0.0.28"
},
"dependencies": {
"@microsoft.azure/tasks": "2.0.27",
"file-url": "2.0.2",
"get-uri": "^2.0.1",
"pify": "^2.3.0",
"proper-lockfile": "^2.0.1",
"strip-bom": "3.0.0",
"urijs": "~1.18.10"
}
}

Просмотреть файл

@ -0,0 +1,3 @@
import * as Mocha from 'mocha';
declare type MochaDone = Mocha.Done;

Просмотреть файл

@ -0,0 +1,35 @@
import { suite, test, slow, timeout, skip, only } from "mocha-typescript";
import * as tasks from '@microsoft.azure/tasks'
import * as assert from "assert";
import * as asyncio from '../main'
import * as os from 'os'
@suite class AsyncIO {
@test async "Does Pify'd exist work"() {
assert.equal(await asyncio.exists(__filename), true);
}
@test async "mkdir"() {
const tmpFolder = `${os.tmpdir()}/something/deep/deep/inside`;
await asyncio.rmdir(`${os.tmpdir()}/something`);
await asyncio.mkdir(tmpFolder);
assert.equal(await asyncio.isDirectory(tmpFolder), true, "Deep Directory created");
// making it again should not fail
await asyncio.mkdir(tmpFolder);
assert.equal(await asyncio.isDirectory(tmpFolder), true, "still there...");
await asyncio.rmdir(`${os.tmpdir()}/something`);
assert.equal(await asyncio.isDirectory(tmpFolder), false, "removed ");
await asyncio.rmdir(tmpFolder);
assert.equal(await asyncio.isDirectory(tmpFolder), false, "no worries ");
}
}

Просмотреть файл

@ -0,0 +1,66 @@
import { suite, test, slow, timeout, skip, only } from "mocha-typescript";
import * as assert from "assert";
import * as asyncio from '../main'
import * as os from 'os'
// ensure
@suite class AsyncIO {
@test async "Locking simple file"() {
const release = await asyncio.Lock.exclusive(__filename);
assert.equal(await asyncio.Lock.check(__filename), true);
release();
assert.equal(await asyncio.Lock.check(__filename), false);
}
@test async "Locking - does lock deny access"() {
const release = await asyncio.Lock.exclusive(__filename);
let threw = false;
try {
const release2 = await asyncio.Lock.exclusive(__filename);
} catch (e) {
threw = true;
}
assert.equal(threw, true);
release();
assert.equal(await asyncio.Lock.check(__filename), false);
}
@test async "ReadLock simple file"() {
// it should be not locked now
assert.equal(await asyncio.Lock.check(__filename), false);
const release1 = await asyncio.Lock.read(__filename);
const release2 = await asyncio.Lock.read(__filename);
const release3 = await asyncio.Lock.read(__filename);
// it should be locked now
assert.equal(await asyncio.Lock.check(__filename), true);
let threw = false;
try {
// should fail getting write lock here.
await asyncio.Lock.exclusive(__filename);
} catch (e) {
threw = true;
}
assert.equal(threw, true);
release1();
release2();
release3();
const release4 = await asyncio.Lock.exclusive(__filename);
assert.equal(await asyncio.Lock.check(__filename), true);
release4();
assert.equal(await asyncio.Lock.check(__filename), false);
}
}

Просмотреть файл

@ -0,0 +1,31 @@
{
"compilerOptions": {
"outDir": "dist",
"alwaysStrict": true,
"forceConsistentCasingInFileNames": false,
"module": "commonjs",
"noEmitOnError": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"sourceMap": true,
"strict": true,
"declaration": true,
"stripInternal": true,
"noEmitHelpers": false,
"target": "es2017",
"types": [
"node"
],
"lib": [
"es2017"
],
"experimentalDecorators": true,
"newLine": "LF"
},
"exclude": [
"dist",
"node_modules",
"**/*.d.ts"
]
}

280
libraries/async-io/uri.ts Normal file
Просмотреть файл

@ -0,0 +1,280 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export function IsUri(uri: string): boolean {
return /^([a-z0-9+.-]+):(?:\/\/(?:((?:[a-z0-9-._~!$&'()*+,;=:]|%[0-9A-F]{2})*)@)?((?:[a-z0-9-._~!$&'()*+,;=]|%[0-9A-F]{2})*)(?::(\d*))?(\/(?:[a-z0-9-._~!$&'()*+,;=:@/]|%[0-9A-F]{2})*)?|(\/?(?:[a-z0-9-._~!$&'()*+,;=:@]|%[0-9A-F]{2})+(?:[a-z0-9-._~!$&'()*+,;=:@/]|%[0-9A-F]{2})*)?)(?:\?((?:[a-z0-9-._~!$&'()*+,;=:/?@]|%[0-9A-F]{2})*))?(?:#((?:[a-z0-9-._~!$&'()*+,;=:/?@]|%[0-9A-F]{2})*))?$/i.test(uri);
}
/***********************
* Data aquisition
***********************/
import * as promisify from "pify";
import { Readable } from "stream";
import { parse } from "url";
import { sep } from "path";
const stripBom: (text: string) => string = require("strip-bom");
const getUri = require("get-uri");
const getUriAsync: (uri: string) => Promise<Readable> = promisify(getUri);
/**
* Loads a UTF8 string from given URI.
*/
export async function ReadUri(uri: string): Promise<string> {
try {
const readable = await getUriAsync(uri);
const readAll = new Promise<string>(function (resolve, reject) {
let result = "";
readable.on("data", data => result += data.toString());
readable.on("end", () => resolve(result));
readable.on("error", err => reject(err));
});
let result = await readAll;
// fix up UTF16le files
if (result.charCodeAt(0) === 65533 && result.charCodeAt(1) === 65533) {
result = Buffer.from(result.slice(2)).toString("utf16le");
}
return stripBom(result);
} catch (e) {
throw new Error(`Failed to load '${uri}' (${e})`);
}
}
export async function ExistsUri(uri: string): Promise<boolean> {
try {
await ReadUri(uri);
return true;
} catch (e) {
return false;
}
}
/***********************
* URI manipulation
***********************/
import { dirname } from "path";
const URI = require("urijs");
const fileUri: (path: string, options: { resolve: boolean }) => string = require("file-url");
/**
* remake of path.isAbsolute... because it's platform dependent:
* Windows: C:\\... -> true /... -> true
* Linux: C:\\... -> false /... -> true
*/
function isAbsolute(path: string): boolean {
return !!path.match(/^([a-zA-Z]:)?(\/|\\)/);
}
/**
* determines what an absolute URI is for our purposes, consider:
* - we had Ruby try to use "Azure::ARM::SQL" as a file name, so that should not be considered absolute
* - we want simple, easily predictable semantics
*/
function isUriAbsolute(url: string): boolean {
return /^[a-z]+:\/\//.test(url);
}
/**
* Create a 'file:///' URI from given absolute path.
* Examples:
* - "C:\swagger\storage.yaml" -> "file:///C:/swagger/storage.yaml"
* - "/input/swagger.yaml" -> "file:///input/swagger.yaml"
*/
export function CreateFileOrFolderUri(absolutePath: string): string {
if (!isAbsolute(absolutePath)) {
throw new Error(`Can only create file URIs from absolute paths. Got '${absolutePath}'`);
}
let result = fileUri(absolutePath, { resolve: false });
// handle UNCs
if (absolutePath.startsWith("//") || absolutePath.startsWith("\\\\")) {
result = result.replace(/^file:\/\/\/\//, "file://");
}
return result;
}
export function CreateFileUri(absolutePath: string): string {
return EnsureIsFileUri(CreateFileOrFolderUri(absolutePath));
}
export function CreateFolderUri(absolutePath: string): string {
return EnsureIsFolderUri(CreateFileOrFolderUri(absolutePath));
}
export function EnsureIsFolderUri(uri: string): string {
return EnsureIsFileUri(uri) + "/";
}
export function EnsureIsFileUri(uri: string): string {
return uri.replace(/\/$/g, "");
}
export function GetFilename(uri: string): string {
return uri.split("/").reverse()[0].split("\\").reverse()[0];
}
export function GetFilenameWithoutExtension(uri: string): string {
const lastPart = GetFilename(uri);
const ext = lastPart.indexOf(".") === -1 ? "" : lastPart.split(".").reverse()[0];
return lastPart.substr(0, lastPart.length - ext.length - 1);
}
export function ToRawDataUrl(uri: string): string {
// special URI handlers
// - GitHub
if (uri.startsWith("https://github")) {
uri = uri.replace(/^https?:\/\/(github.com)(\/[^\/]+\/[^\/]+\/)(blob|tree)\/(.*)/ig, "https://raw.githubusercontent.com$2$4");
}
return uri;
}
/**
* The singularity of all resolving.
* With URI as our one data type of truth, this method maps an absolute or relative path or URI to a URI using given base URI.
* @param baseUri Absolute base URI
* @param pathOrUri Relative/absolute path/URI
* @returns Absolute URI
*/
export function ResolveUri(baseUri: string, pathOrUri: string): string {
if (isAbsolute(pathOrUri)) {
return CreateFileOrFolderUri(pathOrUri);
}
// known here: `pathOrUri` is eiher URI (relative or absolute) or relative path - which we can normalize to a relative URI
pathOrUri = pathOrUri.replace(/\\/g, "/");
// known here: `pathOrUri` is a URI (relative or absolute)
if (isUriAbsolute(pathOrUri)) {
return pathOrUri;
}
// known here: `pathOrUri` is a relative URI
if (!baseUri) {
throw new Error("'pathOrUri' was detected to be relative so 'baseUri' is required");
}
try {
return new URI(pathOrUri).absoluteTo(baseUri).toString();
} catch (e) {
throw new Error(`Failed resolving '${pathOrUri}' against '${baseUri}'.`);
}
}
export function ParentFolderUri(uri: string): string | null {
// root?
if (uri.endsWith("//")) {
return null;
}
// folder? => cut away last "/"
if (uri.endsWith("/")) {
uri = uri.slice(0, uri.length - 1);
}
// cut away last component
const compLen = uri.split("/").reverse()[0].length;
return uri.slice(0, uri.length - compLen);
}
export function MakeRelativeUri(baseUri: string, absoluteUri: string): string {
return new URI(absoluteUri).relativeTo(baseUri).toString();
}
/***********************
* OS abstraction (writing files, enumerating files)
***********************/
import { readdir, mkdir, exists, writeFile } from "./file-io";
import { lstatSync, unlinkSync, rmdirSync } from "fs";
function isAccessibleFile(localPath: string) {
try {
return lstatSync(localPath).isFile();
} catch (e) {
return false;
}
}
function FileUriToLocalPath(fileUri: string): string {
const uri = parse(fileUri);
if (!fileUri.startsWith("file:///")) {
throw new Error(!fileUri.startsWith("file://")
? `Protocol '${uri.protocol}' not supported for writing.`
: `UNC paths not supported for writing.`);
}
// convert to path
let p = uri.path;
if (p === undefined) {
throw new Error(`Cannot write to '${uri}'. Path not found.`);
}
if (sep === "\\") {
p = p.substr(p.startsWith("/") ? 1 : 0);
p = p.replace(/\//g, "\\");
}
return decodeURI(p);
}
export async function EnumerateFiles(folderUri: string, probeFiles: string[] = []): Promise<string[]> {
const results = new Array<string>();
folderUri = EnsureIsFolderUri(folderUri);
if (folderUri.startsWith("file:")) {
let files: string[] = [];
try {
files = await readdir(FileUriToLocalPath(folderUri));
} catch (e) { }
results.push(...files
.map(f => ResolveUri(folderUri, f))
.filter(f => isAccessibleFile(FileUriToLocalPath(f))));
} else {
for (const candid of probeFiles.map(f => ResolveUri(folderUri, f))) {
if (await ExistsUri(candid)) {
results.push(candid);
}
}
}
return results;
}
async function CreateDirectoryFor(filePath: string): Promise<void> {
var dir: string = dirname(filePath);
if (!await exists(dir)) {
await CreateDirectoryFor(dir);
try {
await mkdir(dir);
} catch (e) {
// mkdir throws if directory already exists - which happens occasionally due to race conditions
}
}
}
async function WriteStringInternal(fileName: string, data: string): Promise<void> {
await CreateDirectoryFor(fileName);
await writeFile(fileName, data);
}
/**
* Writes string to local file system.
* @param fileUri Target file uri.
* @param data String to write (encoding: UTF8).
*/
export function WriteString(fileUri: string, data: string): Promise<void> {
return WriteStringInternal(FileUriToLocalPath(fileUri), data);
}
/**
* Clears a folder on the local file system.
* @param folderUri Folder uri.
*/
export async function ClearFolder(folderUri: string): Promise<void> {
const path = FileUriToLocalPath(folderUri);
const deleteFolderRecursive = async (path: string) => {
if (await exists(path)) {
for (const file of await readdir(path)) {
var curPath = path + "/" + file;
if (lstatSync(curPath).isDirectory()) {
await deleteFolderRecursive(curPath);
} else {
unlinkSync(curPath);
}
}
rmdirSync(path);
}
};
await deleteFolderRecursive(path);
}

Просмотреть файл

@ -257,6 +257,11 @@
"projectFolder": "libraries/tasks",
"reviewCategory": "production"
},
{
"packageName": "@microsoft.azure/async-io",
"projectFolder": "libraries/async-io",
"reviewCategory": "production"
}
//
// {
// "packageName": "my-toolchain",