##// END OF EJS Templates
initial port of implab/web
cin -
r0:7110eac54b19 v1.0.0 default
parent child
Show More
@@ -0,0 +1,27
1 {
2 "env": {
3 "browser": true,
4 "commonjs": true,
5 "amd": true,
6 "node": true
7 },
8 "parserOptions": {
9 "ecmaFeatures": {
10 "jsx": true
11 },
12 "sourceType": "module"
13 },
14 "extends": "eslint:recommended",
15 "rules": {
16 "no-const-assign": "warn",
17 "no-this-before-super": "warn",
18 "no-undef": "error",
19 "no-unreachable": "warn",
20 "no-unused-vars": "warn",
21 "constructor-super": "warn",
22 "valid-typeof": "warn",
23 "semi" : "warn",
24 "no-invalid-this" : "error",
25 "no-console": "off"
26 }
27 } No newline at end of file
@@ -0,0 +1,5
1 syntax: glob
2 .gradle/
3 build/
4 node_modules/
5 src/typings/
@@ -0,0 +1,16
1 {
2 "java.configuration.updateBuildConfiguration": "disabled",
3 "tslint.enable": true,
4 "search.exclude": {
5 "**/node_modules": true,
6 "**/bower_components": true,
7 "/build": true
8 },
9 "files.watcherExclude": {
10 "**/.git/objects/**": true,
11 "**/.git/subtree-cache/**": true,
12 "**/node_modules/**": true,
13 "/build": true
14 },
15 "editor.minimap.enabled": false
16 } No newline at end of file
@@ -0,0 +1,276
1 // если версия явно не заданы вычисляем ее из тэга ревизии v.{num}***
2 // результатом будет версия '{num}.{distance}' где distance - расстояние от
3 // текущей ревизии до ревизии с тэгом
4 def tagDistance = 0;
5 def isRelease = false;
6
7 if (!version) {
8
9 def rev = ["hg", "log", "-r", ".", "--template", "{latesttag('re:^v') % '{tag}-{distance}'}"].execute().text.trim();
10
11 def tagVersion;
12
13 def match = (rev =~ /^v(\d+\.\d+\.\d+).*-(\d+)$/);
14
15 if (match.size()) {
16 tagVersion = match[0][1];
17 tagDistance = match[0][2].toInteger();
18
19 version = tagVersion;
20
21 if (tagDistance > 0)
22 version++;
23 } else {
24 throw new Exception("A version must be specied");
25 }
26 } else {
27 println "explicit version: $version";
28 }
29
30 if (hasProperty('versionSuffix') && versionSuffix) {
31 version += "-$versionSuffix"
32 }
33
34 if(!npmName)
35 npmName = name;
36
37 if (hasProperty('release')) {
38 isRelease = (release != 'false')
39 } else {
40 isRelease = (tagDistance == 0);
41 }
42
43 if(!["amd", "commonjs", "system", "umd", "es6", "esnext"].contains(jsmodule))
44 throw new Exception("Invalid jsmodule specified: $jsmodule");
45 if(!["es3", "es5", "es6", "es2016", "es2017", "esnext"].contains(target))
46 throw new Exception("Invalid target specified: $target")
47
48 def targetLibs = [
49 "es3" : "es5,es2015.promise,es2015.symbol,dom,scripthost",
50 "es5" : "es5,es2015.promise,es2015.symbol,dom,scripthost"
51 ];
52
53 ext.packageName="@$npmScope/$npmName";
54
55 def srcDir = "$projectDir/src"
56 def typingsDir = "$srcDir/typings"
57 def distDir = "$buildDir/dist"
58 def testDir = "$buildDir/test"
59 def lib = targetLibs[target] ?: "${target},dom";
60
61 println "lib: $lib";
62
63 def sourceSets = ["main", "amd", "cjs"];
64 def testSets = ["test", "testAmd", "testCjs"];
65
66 task beforeBuild {
67 }
68
69 def createSoursetTasks = { String name, String outDir ->
70 def setName = name.capitalize();
71
72 def compileDir = "$buildDir/compile/$name"
73 def declDir = "$typingsDir/$name"
74 def setDir = "$projectDir/src/$name"
75 def jsDir = outDir;
76
77 def beforeBuildTask = task "beforeBuild$setName"(dependsOn: beforeBuild) {
78 }
79
80 def copyJsTask = task "copyJs$setName"(dependsOn: beforeBuildTask, type: Copy) {
81 from "$setDir/js"
82 into jsDir
83 }
84
85 def lintJsTask = task "lintJs$setName"(dependsOn: beforeBuildTask, type: Exec) {
86 inputs.dir("$setDir/js/").skipWhenEmpty();
87 commandLine "eslint", '--format', 'stylish', "$setDir/js/"
88 }
89
90 def compileTsTask = task "compileTs$setName"(dependsOn: beforeBuildTask, type: Exec) {
91 inputs.dir("$setDir/ts").skipWhenEmpty()
92 inputs.file("$srcDir/tsconfig.json")
93 inputs.file("$setDir/tsconfig.json")
94 outputs.dir(compileDir)
95 outputs.dir(declDir)
96
97 commandLine 'node_modules/.bin/tsc',
98 '-p', "$setDir/tsconfig.json",
99 '-t', target,
100 '-m', jsmodule,
101 '-d',
102 '--outDir', compileDir,
103 '--declarationDir', declDir
104
105 if (lib)
106 args '--lib', lib
107 }
108
109 def copyTsOutputTask = task "copyTsOutput$setName"(dependsOn: compileTsTask, type: Copy) {
110 from compileDir
111 into jsDir
112 }
113
114 def copyTypingsTask = task "copyTypings$setName"(dependsOn: compileTsTask, type: Copy) {
115 from declDir
116 into jsDir
117 }
118
119 def copyResourcesTask = task "copyResources$setName"(dependsOn: beforeBuildTask, type: Copy) {
120 from "$setDir/resources"
121 into outDir
122 }
123
124 task "build$setName" {
125 dependsOn copyTypingsTask,
126 copyTsOutputTask,
127 copyJsTask,
128 copyResourcesTask,
129 lintJsTask
130 }
131 }
132
133 task printVersion {
134 doLast {
135 println "version: $version";
136 println "isRelease: $isRelease, tagDistance: $tagDistance";
137 println "packageName: $packageName";
138 println "bundle: ${pack.outputs.files.join(',')}";
139 println "target: $target";
140 println "module: $jsmodule";
141 }
142 }
143
144 task clean {
145 doLast {
146 delete buildDir
147 delete typingsDir
148 }
149 }
150
151 task _initBuild {
152 mustRunAfter clean
153
154 def buildInfoFile = "$buildDir/platform";
155 inputs.property('target',target);
156 inputs.property('jsmodule',jsmodule);
157 outputs.file(buildInfoFile);
158
159 doLast {
160 delete buildDir
161 mkdir buildDir
162
163 def f = new File(buildInfoFile);
164 f << "$target-$jsmodule";
165 }
166 }
167
168 task cleanNpm {
169 doLast {
170 delete 'node_modules'
171 }
172 }
173
174 task _npmInstall() {
175 inputs.file("package.json")
176 outputs.dir("node_modules")
177 doLast {
178 exec {
179 commandLine 'npm', 'install'
180 }
181 }
182 }
183
184 beforeBuild {
185 dependsOn _initBuild
186 dependsOn _npmInstall
187 }
188
189 sourceSets.each { createSoursetTasks(it, distDir) }
190
191 testSets.each { createSoursetTasks(it, testDir) }
192
193 compileTsAmd {
194 dependsOn compileTsMain
195 }
196
197 compileTsCjs {
198 dependsOn compileTsMain
199 }
200
201 task build(dependsOn: buildMain) {
202 if (jsmodule == "amd")
203 dependsOn buildAmd
204 if (jsmodule == "commonjs")
205 dependsOn buildCjs
206 }
207
208 compileTsTest {
209 dependsOn build
210 }
211
212 compileTsTestAmd {
213 dependsOn compileTsTest
214 }
215
216 compileTsTestCjs {
217 dependsOn compileTsTest
218 }
219
220 task _installLocalCjsDependency(dependsOn: [buildTestCjs, "_packageMeta"], type: Exec) {
221 inputs.file("$distDir/package.json")
222 outputs.upToDateWhen {
223 new File("$testDir/$packageName").exists()
224 }
225
226 workingDir testDir
227
228 commandLine 'npm', 'install', '--no-save', '--force', distDir
229 }
230
231 task test(dependsOn: [buildTest], type: Exec) {
232 if (jsmodule == "amd")
233 dependsOn buildTestAmd
234 if (jsmodule == "commonjs") {
235 dependsOn buildTestCjs
236 dependsOn _installLocalCjsDependency
237 }
238
239 commandLine 'node', "$testDir/run-tests.js"
240 }
241
242 task _packageMeta(type: Copy) {
243 mustRunAfter build
244
245 inputs.property("version", version)
246 from('.') {
247 include '.npmignore', 'readme.md', 'license', 'history.md'
248 }
249 from("package.${jsmodule}.json") {
250 expand project.properties
251 rename { "package.json" }
252 }
253 into distDir
254 }
255
256 task pack(dependsOn: [build, _packageMeta], type: Exec) {
257 workingDir distDir
258 outputs.file("$npmScope-$npmName-${version}.tgz")
259
260 commandLine 'npm', 'pack'
261 }
262
263 task publish(dependsOn: [build, _packageMeta], type: Exec) {
264 doFirst {
265 if (!isRelease)
266 throw new Exception("Can't publish an unreleased version");
267 }
268 workingDir distDir
269
270 commandLine 'npm', 'publish', '--access', 'public'
271 }
272
273 task markRelease(type: Exec) {
274 onlyIf { tagDistance > 1 }
275 commandLine "hg", "tag", "v$version";
276 } No newline at end of file
@@ -0,0 +1,9
1 version=
2 author=Implab team
3 jsmodule=amd
4 target=es5
5 description=The simple framework for writing a RESTful application
6 license=BSD-2-Clause
7 repository=https://bitbucket.org/implab/implabjs-web
8 npmScope=implab
9 npmName=web No newline at end of file
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
@@ -0,0 +1,5
1 distributionBase=GRADLE_USER_HOME
2 distributionPath=wrapper/dists
3 distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-bin.zip
4 zipStoreBase=GRADLE_USER_HOME
5 zipStorePath=wrapper/dists
@@ -0,0 +1,172
1 #!/usr/bin/env sh
2
3 ##############################################################################
4 ##
5 ## Gradle start up script for UN*X
6 ##
7 ##############################################################################
8
9 # Attempt to set APP_HOME
10 # Resolve links: $0 may be a link
11 PRG="$0"
12 # Need this for relative symlinks.
13 while [ -h "$PRG" ] ; do
14 ls=`ls -ld "$PRG"`
15 link=`expr "$ls" : '.*-> \(.*\)$'`
16 if expr "$link" : '/.*' > /dev/null; then
17 PRG="$link"
18 else
19 PRG=`dirname "$PRG"`"/$link"
20 fi
21 done
22 SAVED="`pwd`"
23 cd "`dirname \"$PRG\"`/" >/dev/null
24 APP_HOME="`pwd -P`"
25 cd "$SAVED" >/dev/null
26
27 APP_NAME="Gradle"
28 APP_BASE_NAME=`basename "$0"`
29
30 # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 DEFAULT_JVM_OPTS='"-Xmx64m"'
32
33 # Use the maximum available, or set MAX_FD != -1 to use that value.
34 MAX_FD="maximum"
35
36 warn () {
37 echo "$*"
38 }
39
40 die () {
41 echo
42 echo "$*"
43 echo
44 exit 1
45 }
46
47 # OS specific support (must be 'true' or 'false').
48 cygwin=false
49 msys=false
50 darwin=false
51 nonstop=false
52 case "`uname`" in
53 CYGWIN* )
54 cygwin=true
55 ;;
56 Darwin* )
57 darwin=true
58 ;;
59 MINGW* )
60 msys=true
61 ;;
62 NONSTOP* )
63 nonstop=true
64 ;;
65 esac
66
67 CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68
69 # Determine the Java command to use to start the JVM.
70 if [ -n "$JAVA_HOME" ] ; then
71 if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 # IBM's JDK on AIX uses strange locations for the executables
73 JAVACMD="$JAVA_HOME/jre/sh/java"
74 else
75 JAVACMD="$JAVA_HOME/bin/java"
76 fi
77 if [ ! -x "$JAVACMD" ] ; then
78 die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79
80 Please set the JAVA_HOME variable in your environment to match the
81 location of your Java installation."
82 fi
83 else
84 JAVACMD="java"
85 which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86
87 Please set the JAVA_HOME variable in your environment to match the
88 location of your Java installation."
89 fi
90
91 # Increase the maximum file descriptors if we can.
92 if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 MAX_FD_LIMIT=`ulimit -H -n`
94 if [ $? -eq 0 ] ; then
95 if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 MAX_FD="$MAX_FD_LIMIT"
97 fi
98 ulimit -n $MAX_FD
99 if [ $? -ne 0 ] ; then
100 warn "Could not set maximum file descriptor limit: $MAX_FD"
101 fi
102 else
103 warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 fi
105 fi
106
107 # For Darwin, add options to specify how the application appears in the dock
108 if $darwin; then
109 GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 fi
111
112 # For Cygwin, switch paths to Windows format before running java
113 if $cygwin ; then
114 APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 JAVACMD=`cygpath --unix "$JAVACMD"`
117
118 # We build the pattern for arguments to be converted via cygpath
119 ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 SEP=""
121 for dir in $ROOTDIRSRAW ; do
122 ROOTDIRS="$ROOTDIRS$SEP$dir"
123 SEP="|"
124 done
125 OURCYGPATTERN="(^($ROOTDIRS))"
126 # Add a user-defined pattern to the cygpath arguments
127 if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 fi
130 # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 i=0
132 for arg in "$@" ; do
133 CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135
136 if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 else
139 eval `echo args$i`="\"$arg\""
140 fi
141 i=$((i+1))
142 done
143 case $i in
144 (0) set -- ;;
145 (1) set -- "$args0" ;;
146 (2) set -- "$args0" "$args1" ;;
147 (3) set -- "$args0" "$args1" "$args2" ;;
148 (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 esac
155 fi
156
157 # Escape application args
158 save () {
159 for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 echo " "
161 }
162 APP_ARGS=$(save "$@")
163
164 # Collect all arguments for the java command, following the shell quoting and substitution rules
165 eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166
167 # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 cd "$(dirname "$0")"
170 fi
171
172 exec "$JAVACMD" "$@"
@@ -0,0 +1,84
1 @if "%DEBUG%" == "" @echo off
2 @rem ##########################################################################
3 @rem
4 @rem Gradle startup script for Windows
5 @rem
6 @rem ##########################################################################
7
8 @rem Set local scope for the variables with windows NT shell
9 if "%OS%"=="Windows_NT" setlocal
10
11 set DIRNAME=%~dp0
12 if "%DIRNAME%" == "" set DIRNAME=.
13 set APP_BASE_NAME=%~n0
14 set APP_HOME=%DIRNAME%
15
16 @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 set DEFAULT_JVM_OPTS="-Xmx64m"
18
19 @rem Find java.exe
20 if defined JAVA_HOME goto findJavaFromJavaHome
21
22 set JAVA_EXE=java.exe
23 %JAVA_EXE% -version >NUL 2>&1
24 if "%ERRORLEVEL%" == "0" goto init
25
26 echo.
27 echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 echo.
29 echo Please set the JAVA_HOME variable in your environment to match the
30 echo location of your Java installation.
31
32 goto fail
33
34 :findJavaFromJavaHome
35 set JAVA_HOME=%JAVA_HOME:"=%
36 set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37
38 if exist "%JAVA_EXE%" goto init
39
40 echo.
41 echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 echo.
43 echo Please set the JAVA_HOME variable in your environment to match the
44 echo location of your Java installation.
45
46 goto fail
47
48 :init
49 @rem Get command-line arguments, handling Windows variants
50
51 if not "%OS%" == "Windows_NT" goto win9xME_args
52
53 :win9xME_args
54 @rem Slurp the command line arguments.
55 set CMD_LINE_ARGS=
56 set _SKIP=2
57
58 :win9xME_args_slurp
59 if "x%~1" == "x" goto execute
60
61 set CMD_LINE_ARGS=%*
62
63 :execute
64 @rem Setup the command line
65
66 set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67
68 @rem Execute Gradle
69 "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70
71 :end
72 @rem End local scope for the variables with windows NT shell
73 if "%ERRORLEVEL%"=="0" goto mainEnd
74
75 :fail
76 rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 rem the _cmd.exe /c_ return code!
78 if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 exit /b 1
80
81 :mainEnd
82 if "%OS%"=="Windows_NT" endlocal
83
84 :omega
@@ -0,0 +1,10
1 HISTORY
2 =======
3
4 1.0.0
5 -----
6
7 First release, the port from legacy project, intorduces the following features
8
9 - `security` - basic security model and concepts
10 - A resource-oriented web application framework No newline at end of file
@@ -0,0 +1,22
1 Copyright 2017-2019 Implab team
2
3 Redistribution and use in source and binary forms, with or without
4 modification, are permitted provided that the following conditions are met:
5
6 1. Redistributions of source code must retain the above copyright notice, this
7 list of conditions and the following disclaimer.
8
9 2. Redistributions in binary form must reproduce the above copyright notice,
10 this list of conditions and the following disclaimer in the documentation
11 and/or other materials provided with the distribution.
12
13 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
17 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
20 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
21 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
22 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. No newline at end of file
@@ -0,0 +1,477
1 {
2 "name": "@implab/web",
3 "version": "0.0.1-dev",
4 "lockfileVersion": 1,
5 "requires": true,
6 "dependencies": {
7 "@implab/core-amd": {
8 "version": "1.2.15",
9 "resolved": "https://registry.npmjs.org/@implab/core-amd/-/core-amd-1.2.15.tgz",
10 "integrity": "sha512-Vc5L9W/jz62R2fW1RnhPd6S503oztJLgddBGgNgJ4JjaBZt9P/Ym+98jAMkFbtCY3dX1RLM0SuI33hDPoG8Wgw==",
11 "dev": true
12 },
13 "@types/node": {
14 "version": "11.9.4",
15 "resolved": "https://registry.npmjs.org/@types/node/-/node-11.9.4.tgz",
16 "integrity": "sha512-Zl8dGvAcEmadgs1tmSPcvwzO1YRsz38bVJQvH1RvRqSR9/5n61Q1ktcDL0ht3FXWR+ZpVmXVwN1LuH4Ax23NsA==",
17 "dev": true
18 },
19 "@types/requirejs": {
20 "version": "2.1.31",
21 "resolved": "https://registry.npmjs.org/@types/requirejs/-/requirejs-2.1.31.tgz",
22 "integrity": "sha512-b2soeyuU76rMbcRJ4e0hEl0tbMhFwZeTC0VZnfuWlfGlk6BwWNsev6kFu/twKABPX29wkX84wU2o+cEJoXsiTw==",
23 "dev": true
24 },
25 "@types/tape": {
26 "version": "4.2.33",
27 "resolved": "https://registry.npmjs.org/@types/tape/-/tape-4.2.33.tgz",
28 "integrity": "sha512-ltfyuY5BIkYlGuQfwqzTDT8f0q8Z5DGppvUnWGs39oqDmMd6/UWhNpX3ZMh/VYvfxs3rFGHMrLC/eGRdLiDGuw==",
29 "dev": true,
30 "requires": {
31 "@types/node": "*"
32 }
33 },
34 "balanced-match": {
35 "version": "1.0.0",
36 "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
37 "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
38 "dev": true
39 },
40 "brace-expansion": {
41 "version": "1.1.11",
42 "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
43 "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
44 "dev": true,
45 "requires": {
46 "balanced-match": "^1.0.0",
47 "concat-map": "0.0.1"
48 }
49 },
50 "concat-map": {
51 "version": "0.0.1",
52 "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
53 "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
54 "dev": true
55 },
56 "core-util-is": {
57 "version": "1.0.2",
58 "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
59 "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
60 "dev": true
61 },
62 "deep-equal": {
63 "version": "0.1.2",
64 "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-0.1.2.tgz",
65 "integrity": "sha1-skbCuApXCkfBG+HZvRBw7IeLh84=",
66 "dev": true
67 },
68 "define-properties": {
69 "version": "1.1.3",
70 "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
71 "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
72 "dev": true,
73 "requires": {
74 "object-keys": "^1.0.12"
75 },
76 "dependencies": {
77 "object-keys": {
78 "version": "1.1.0",
79 "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.0.tgz",
80 "integrity": "sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg==",
81 "dev": true
82 }
83 }
84 },
85 "defined": {
86 "version": "0.0.0",
87 "resolved": "https://registry.npmjs.org/defined/-/defined-0.0.0.tgz",
88 "integrity": "sha1-817qfXBekzuvE7LwOz+D2SFAOz4=",
89 "dev": true
90 },
91 "dojo": {
92 "version": "1.15.0",
93 "resolved": "https://registry.npmjs.org/dojo/-/dojo-1.15.0.tgz",
94 "integrity": "sha512-+1r5Nj1+iaHI8AxUadqsSp8wJMJM6sslr3INgWKhxUA0xHznBNY0htt38XLyheuy1G7oOwsh4X1An+Uzirj7Gw==",
95 "dev": true
96 },
97 "duplexer": {
98 "version": "0.1.1",
99 "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
100 "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=",
101 "dev": true
102 },
103 "es-abstract": {
104 "version": "1.13.0",
105 "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz",
106 "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==",
107 "dev": true,
108 "requires": {
109 "es-to-primitive": "^1.2.0",
110 "function-bind": "^1.1.1",
111 "has": "^1.0.3",
112 "is-callable": "^1.1.4",
113 "is-regex": "^1.0.4",
114 "object-keys": "^1.0.12"
115 },
116 "dependencies": {
117 "object-keys": {
118 "version": "1.1.0",
119 "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.0.tgz",
120 "integrity": "sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg==",
121 "dev": true
122 }
123 }
124 },
125 "es-to-primitive": {
126 "version": "1.2.0",
127 "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz",
128 "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==",
129 "dev": true,
130 "requires": {
131 "is-callable": "^1.1.4",
132 "is-date-object": "^1.0.1",
133 "is-symbol": "^1.0.2"
134 }
135 },
136 "faucet": {
137 "version": "0.0.1",
138 "resolved": "https://registry.npmjs.org/faucet/-/faucet-0.0.1.tgz",
139 "integrity": "sha1-WX3PHSGJosBiMhtZHo8VHtIDnZw=",
140 "dev": true,
141 "requires": {
142 "defined": "0.0.0",
143 "duplexer": "~0.1.1",
144 "minimist": "0.0.5",
145 "sprintf": "~0.1.3",
146 "tap-parser": "~0.4.0",
147 "tape": "~2.3.2",
148 "through2": "~0.2.3"
149 },
150 "dependencies": {
151 "tape": {
152 "version": "2.3.3",
153 "resolved": "https://registry.npmjs.org/tape/-/tape-2.3.3.tgz",
154 "integrity": "sha1-Lnzgox3wn41oUWZKcYQuDKUFevc=",
155 "dev": true,
156 "requires": {
157 "deep-equal": "~0.1.0",
158 "defined": "~0.0.0",
159 "inherits": "~2.0.1",
160 "jsonify": "~0.0.0",
161 "resumer": "~0.0.0",
162 "through": "~2.3.4"
163 }
164 }
165 }
166 },
167 "for-each": {
168 "version": "0.3.3",
169 "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
170 "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
171 "dev": true,
172 "requires": {
173 "is-callable": "^1.1.3"
174 }
175 },
176 "fs.realpath": {
177 "version": "1.0.0",
178 "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
179 "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
180 "dev": true
181 },
182 "function-bind": {
183 "version": "1.1.1",
184 "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
185 "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
186 "dev": true
187 },
188 "glob": {
189 "version": "7.1.3",
190 "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
191 "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
192 "dev": true,
193 "requires": {
194 "fs.realpath": "^1.0.0",
195 "inflight": "^1.0.4",
196 "inherits": "2",
197 "minimatch": "^3.0.4",
198 "once": "^1.3.0",
199 "path-is-absolute": "^1.0.0"
200 }
201 },
202 "has": {
203 "version": "1.0.3",
204 "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
205 "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
206 "dev": true,
207 "requires": {
208 "function-bind": "^1.1.1"
209 }
210 },
211 "has-symbols": {
212 "version": "1.0.0",
213 "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
214 "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=",
215 "dev": true
216 },
217 "inflight": {
218 "version": "1.0.6",
219 "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
220 "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
221 "dev": true,
222 "requires": {
223 "once": "^1.3.0",
224 "wrappy": "1"
225 }
226 },
227 "inherits": {
228 "version": "2.0.3",
229 "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
230 "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
231 "dev": true
232 },
233 "is-callable": {
234 "version": "1.1.4",
235 "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz",
236 "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==",
237 "dev": true
238 },
239 "is-date-object": {
240 "version": "1.0.1",
241 "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
242 "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=",
243 "dev": true
244 },
245 "is-regex": {
246 "version": "1.0.4",
247 "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
248 "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
249 "dev": true,
250 "requires": {
251 "has": "^1.0.1"
252 }
253 },
254 "is-symbol": {
255 "version": "1.0.2",
256 "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz",
257 "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==",
258 "dev": true,
259 "requires": {
260 "has-symbols": "^1.0.0"
261 }
262 },
263 "isarray": {
264 "version": "0.0.1",
265 "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
266 "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
267 "dev": true
268 },
269 "jsonify": {
270 "version": "0.0.0",
271 "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
272 "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=",
273 "dev": true
274 },
275 "minimatch": {
276 "version": "3.0.4",
277 "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
278 "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
279 "dev": true,
280 "requires": {
281 "brace-expansion": "^1.1.7"
282 }
283 },
284 "minimist": {
285 "version": "0.0.5",
286 "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.5.tgz",
287 "integrity": "sha1-16oye87PUY+RBqxrjwA/o7zqhWY=",
288 "dev": true
289 },
290 "object-inspect": {
291 "version": "1.6.0",
292 "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz",
293 "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==",
294 "dev": true
295 },
296 "object-keys": {
297 "version": "0.4.0",
298 "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz",
299 "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=",
300 "dev": true
301 },
302 "once": {
303 "version": "1.4.0",
304 "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
305 "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
306 "dev": true,
307 "requires": {
308 "wrappy": "1"
309 }
310 },
311 "path-is-absolute": {
312 "version": "1.0.1",
313 "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
314 "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
315 "dev": true
316 },
317 "path-parse": {
318 "version": "1.0.6",
319 "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
320 "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
321 "dev": true
322 },
323 "readable-stream": {
324 "version": "1.1.14",
325 "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
326 "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
327 "dev": true,
328 "requires": {
329 "core-util-is": "~1.0.0",
330 "inherits": "~2.0.1",
331 "isarray": "0.0.1",
332 "string_decoder": "~0.10.x"
333 }
334 },
335 "requirejs": {
336 "version": "2.3.6",
337 "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.6.tgz",
338 "integrity": "sha512-ipEzlWQe6RK3jkzikgCupiTbTvm4S0/CAU5GlgptkN5SO6F3u0UD0K18wy6ErDqiCyP4J4YYe1HuAShvsxePLg==",
339 "dev": true
340 },
341 "resolve": {
342 "version": "1.10.0",
343 "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz",
344 "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==",
345 "dev": true,
346 "requires": {
347 "path-parse": "^1.0.6"
348 }
349 },
350 "resumer": {
351 "version": "0.0.0",
352 "resolved": "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz",
353 "integrity": "sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=",
354 "dev": true,
355 "requires": {
356 "through": "~2.3.4"
357 }
358 },
359 "sprintf": {
360 "version": "0.1.5",
361 "resolved": "https://registry.npmjs.org/sprintf/-/sprintf-0.1.5.tgz",
362 "integrity": "sha1-j4PjmpMXwaUCy324BQ5Rxnn27c8=",
363 "dev": true
364 },
365 "string.prototype.trim": {
366 "version": "1.1.2",
367 "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz",
368 "integrity": "sha1-0E3iyJ4Tf019IG8Ia17S+ua+jOo=",
369 "dev": true,
370 "requires": {
371 "define-properties": "^1.1.2",
372 "es-abstract": "^1.5.0",
373 "function-bind": "^1.0.2"
374 }
375 },
376 "string_decoder": {
377 "version": "0.10.31",
378 "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
379 "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
380 "dev": true
381 },
382 "tap-parser": {
383 "version": "0.4.3",
384 "resolved": "https://registry.npmjs.org/tap-parser/-/tap-parser-0.4.3.tgz",
385 "integrity": "sha1-pOrhkMENdsehEZIf84u+TVjwnuo=",
386 "dev": true,
387 "requires": {
388 "inherits": "~2.0.1",
389 "readable-stream": "~1.1.11"
390 }
391 },
392 "tape": {
393 "version": "4.10.1",
394 "resolved": "https://registry.npmjs.org/tape/-/tape-4.10.1.tgz",
395 "integrity": "sha512-G0DywYV1jQeY3axeYnXUOt6ktnxS9OPJh97FGR3nrua8lhWi1zPflLxcAHavZ7Jf3qUfY7cxcVIVFa4mY2IY1w==",
396 "dev": true,
397 "requires": {
398 "deep-equal": "~1.0.1",
399 "defined": "~1.0.0",
400 "for-each": "~0.3.3",
401 "function-bind": "~1.1.1",
402 "glob": "~7.1.3",
403 "has": "~1.0.3",
404 "inherits": "~2.0.3",
405 "minimist": "~1.2.0",
406 "object-inspect": "~1.6.0",
407 "resolve": "~1.10.0",
408 "resumer": "~0.0.0",
409 "string.prototype.trim": "~1.1.2",
410 "through": "~2.3.8"
411 },
412 "dependencies": {
413 "deep-equal": {
414 "version": "1.0.1",
415 "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz",
416 "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=",
417 "dev": true
418 },
419 "defined": {
420 "version": "1.0.0",
421 "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz",
422 "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=",
423 "dev": true
424 },
425 "minimist": {
426 "version": "1.2.0",
427 "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
428 "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
429 "dev": true
430 }
431 }
432 },
433 "through": {
434 "version": "2.3.8",
435 "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
436 "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
437 "dev": true
438 },
439 "through2": {
440 "version": "0.2.3",
441 "resolved": "https://registry.npmjs.org/through2/-/through2-0.2.3.tgz",
442 "integrity": "sha1-6zKE2k6jEbbMis42U3SKUqvyWj8=",
443 "dev": true,
444 "requires": {
445 "readable-stream": "~1.1.9",
446 "xtend": "~2.1.1"
447 }
448 },
449 "tslib": {
450 "version": "1.9.3",
451 "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
452 "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==",
453 "dev": true
454 },
455 "typescript": {
456 "version": "3.3.3",
457 "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.3.3.tgz",
458 "integrity": "sha512-Y21Xqe54TBVp+VDSNbuDYdGw0BpoR/Q6wo/+35M8PAU0vipahnyduJWirxxdxjsAkS7hue53x2zp8gz7F05u0A==",
459 "dev": true
460 },
461 "wrappy": {
462 "version": "1.0.2",
463 "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
464 "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
465 "dev": true
466 },
467 "xtend": {
468 "version": "2.1.2",
469 "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz",
470 "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=",
471 "dev": true,
472 "requires": {
473 "object-keys": "~0.4.0"
474 }
475 }
476 }
477 }
@@ -0,0 +1,25
1 {
2 "name": "${packageName}",
3 "version": "${version}",
4 "description": "${description}",
5 "main": "main.js",
6 "keywords": [
7 "di",
8 "ioc",
9 "logging",
10 "template engine",
11 "dependency injection"
12 ],
13 "author": "${author}",
14 "license": "${license}",
15 "repository": "$repository",
16 "publishConfig": {
17 "access": "public"
18 },
19 "peerDependencies": {
20 "dojo": "^1.10.0",
21 "@implab/core-amd": "^1.2.0"
22 },
23 "module": "${jsmodule}",
24 "target": "${target}"
25 } No newline at end of file
@@ -0,0 +1,37
1 {
2 "name": "@implab/web",
3 "version": "0.0.1-dev",
4 "description": "Simple web framework",
5 "main": "main.js",
6 "keywords": [
7 "di",
8 "ioc",
9 "logging",
10 "template engine",
11 "dependency injection"
12 ],
13 "author": "Implab team",
14 "license": "BSD-2-Clause",
15 "repository": "https://bitbucket.org/implab/implabjs",
16 "publishConfig": {
17 "access": "public"
18 },
19 "peerDependencies": {
20 "dojo": "^1.10.0",
21 "@implab/core-amd": "^1.2.15",
22 "tslib": "latest"
23 },
24 "devDependencies": {
25 "@types/node": "latest",
26 "@types/requirejs": "latest",
27 "@types/tape": "latest",
28 "@implab/core-amd": "^1.2.15",
29 "dojo": "^1.10.0",
30 "faucet": "latest",
31 "requirejs": "latest",
32 "tape": "^4.9.2",
33 "tslib": "latest",
34 "typescript": "latest"
35 },
36 "types": "main.d.ts"
37 }
@@ -0,0 +1,27
1 # Implabjs-core
2
3 Набор стандартных библиотек для создания приложений со сложным функционалом.
4 Данную библиотеку можно использовать как для разработки приложений, которые
5 будут работать в среде браузеров, так и в серверных средах.
6
7 Библиотека написана на TypeScript, некоторая часть на JavaScript, но постепенно
8 планируется перейти полностью на использование TypeScript
9
10 Более подробная документация доступна по ссылке: <https://bitbucket.org/implab/implabjs-core/src/default/docs/ru/>
11
12 ## Основные компоненты
13
14 ### DI
15
16 Контейнер для внедрения зависимостей, позволяет гибко описывать структуру
17 приложения и создавать слабосвязанные компоненты.
18
19 ### LOG
20
21 Средства журналирования похожие на JLog, позволяют эффективно вести журнал
22 выполнения программы.
23
24 ### Cancellations
25
26 Специальные маркеры для отмены асинхронных операций, по аналогии с .NET
27 CancelationToken.
@@ -0,0 +1,15
1 /*
2 * This settings file was generated by the Gradle 'init' task.
3 *
4 * The settings file is used to specify which projects to include in your build.
5 * In a single project build this file can be empty or even removed.
6 *
7 * Detailed information about configuring a multi-project build in Gradle can be found
8 * in the user guide at https://docs.gradle.org/3.5/userguide/multi_project_builds.html
9 */
10
11 // To declare projects as part of a multi-project build use the 'include' method
12
13 //include 'sub-project-name'
14
15 rootProject.name = 'web' No newline at end of file
@@ -0,0 +1,167
1 define([
2 "dojo/_base/declare",
3 "dojo/_base/lang",
4 "dojo/when",
5 "cluster",
6 "os",
7 "implab/safe",
8 "./HttpResponse",
9 "./BaseResponse",
10 "dojo/Deferred",
11 "./HttpException",
12 "@implab/core/log/trace!"],
13 function (declare, lang, when, cluster, os, safe, HttpResponse, BaseResponse, Deferred, HttpException, trace) {
14 return declare(null, {
15 _container: null,
16 _serverPort: null,
17 _bindAddr: null,
18
19 _useCluster: null,
20
21 _requestConfig: null,
22 _restartFailedWorkers: null,
23
24 // модули, обрабатывающие запрос.
25 _modules: null,
26 _handlers: null,
27
28 constructor: function (options) {
29 safe.argumentNotNull(options, "options");
30 safe.argumentNotNull(options.container, "options.container");
31 safe.argumentNotNull(options.serverPort, "options.serverPort");
32 safe.argumentNotNull(options.useCluster, "options.useCluster");
33 safe.argumentNotEmptyString(options.bindAddr, "options.bindAddr");
34 safe.argumentNotEmptyString(options.requestConfig, "options.requestConfig");
35
36 this.options = options || {};
37 this._modules = [];
38
39 this._container = options.container;
40
41 this._serverPort = options.serverPort;
42 this._bindAddr = options.bindAddr;
43 this._useCluster = options.useCluster;
44 this._restartFailedWorkers = options.restartFailedWorkers;
45
46 this._requestConfig = options.requestConfig;
47
48 this._container.getService("httpHandlers").forEach(handler => {
49 this.handle(handler);
50 });
51 },
52
53 start: function () {
54 let me = this;
55 const numCPUs = os.cpus().length;
56 if (cluster.isMaster && me._useCluster) {
57 // Fork workers.
58 for (let i = 0; i < numCPUs; i++) {
59 cluster.fork();
60 }
61
62 cluster.on('exit', function (worker, code, signal) {
63 if(me._restartFailedWorkers){
64 trace.log("worker {0} died. Signal {1}. Restarting ...", worker.process.pid, signal);
65 cluster.fork();
66 } else {
67 trace.log("worker {0} died.", worker.process.pid);
68 }
69 });
70 } else {
71 // Workers can share any TCP connection
72 // In this case it is an HTTP server
73 this.listen(this._serverPort, this._bindAddr);
74 }
75 },
76
77 listen: function (port, host) {
78 this._host = host;
79 this._port = port;
80
81 this._createServer(this.options, lang.hitch(this, "_handler")).listen(port, host, lang.hitch(this, "_listening"));
82 },
83
84 handle: function (module) {
85 if (module instanceof Function)
86 this._modules.push(module);
87 else if (module.invoke instanceof Function)
88 this._modules.push(function (req, next) {
89 return module.invoke(req, next);
90 });
91 else
92 throw "module shoud be a function or should have an invoke method";
93 },
94
95 _createServer: function () {
96 throw "NOT IMPLEMENTED";
97 },
98
99 _createRequest: function (container, req, res) {
100 return container.configure(this._requestConfig)
101 .then(function () {
102 let httpRequest = container.getService("httpRequest");
103 httpRequest.init(req, res);
104 return httpRequest;
105 });
106 },
107
108 //handler function to pass to the approperate server (like node.http)
109 _handler: function (req, res) {
110 let i = 0;
111 let me = this;
112
113 let requestContainer = this._container.createChildContainer();
114
115 when(this._createRequest(requestContainer, req, res), function (httpRequest) {
116 let next = function () {
117 if (i < me._modules.length) {
118 let module = me._modules[i];
119 i++;
120 try {
121 return when(module(httpRequest, next));
122 } catch (err) {
123 let d = new Deferred();
124 d.reject(err);
125 return d;
126 }
127 }
128 };
129
130 when(next(), function (result) {
131 if (!result)
132 throw "no response is provided, this is a serious bug";
133
134 if (result instanceof BaseResponse) {
135 result.send(res);
136 } else {
137 let httpResp = new HttpResponse();
138 httpResp.content = "BUG: " + result.toString();
139 httpResp.send(res);
140 }
141 requestContainer.dispose();
142 }).then(null, function (err) {
143 if (res.finished) {
144 trace.log("caught exception after the response is sent: {0}", err);
145 return;
146 }
147 let httpResp;
148 if (err instanceof HttpException)
149 httpResp = new HttpResponse(err.message, {
150 statusCode: err.code,
151 headers: err.headers
152 });
153 else
154 httpResp = new HttpResponse(err.toString(), {
155 statusCode: 500
156 });
157 httpResp.send(res);
158 requestContainer.dispose();
159 });
160 });
161 },
162 _listening: function () {
163 trace.log("Listening {0}:{1}", this._host, this._port);
164 },
165
166 });
167 }); No newline at end of file
@@ -0,0 +1,7
1 define([ "dojo/_base/declare"], function(declare) {
2 return declare(null, {
3 send : function(/*serverResponse*/) {
4 throw "NOT IMPLEMENTED";
5 }
6 });
7 }); No newline at end of file
@@ -0,0 +1,22
1 define(["dojo/_base/declare", "dojo/when", "./BaseResponse", "./ModelResponse"],function(declare,when, BaseResponse, ModelResponse) {
2 return declare(null,{
3 invoke: function(httpRequest, next) {
4 return when(next(), function(result) {
5 if (!(result instanceof BaseResponse) ){
6 result = new ModelResponse(result);
7 }
8
9 if (!(result instanceof ModelResponse))
10 return result;
11
12 result.presenter = {
13 present: JSON.stringify,
14 contentType: "application/json; charset=utf-8"
15 };
16
17 // TODO select presenter and assing it to result.presenter
18 return result;
19 });
20 }
21 });
22 }); No newline at end of file
@@ -0,0 +1,206
1 define([ "dojo/_base/declare", "dojo/_base/lang" ], function(declare, lang) {
2 let cls = declare(null, {
3 type : null,
4 subtype : null,
5 parameters: null,
6
7 constructor: function(media,parameters) {
8 if (!media)
9 throw "A media must be specified";
10 media = media.toString();
11 let parts = media.match(/^([a-zA-Z-]+)\/([a-zA-Z\-+]+)$/);
12 if(!parts)
13 throw "An invalid media type supplied";
14 this.type = parts[1];
15 this.subtype = parts[2];
16 this.parameters = parameters || {};
17 },
18
19 toString : function() {
20 let res = [this.type,'/',this.subtype];
21 for(var p in this.parameters) {
22 res.push('; ');
23 res.push(p);
24 res.push('=');
25 res.push(this._escapeValue(this.parameters[p]));
26 }
27
28 return res.join('');
29 },
30
31 _escapeValue : function(value) {
32 if (value) {
33 value = value.toString();
34 if(value.match(/[()<>@,:;\\".[\]]/))
35 return '"' + value.replace(/[()<>@,:;\\".[\]]/g, function(x) { return "\\"+ x; } ) + '"';
36 }
37 return value;
38 }
39
40 });
41
42 let parseSpace = function(text,start) {
43 for(var i = start; i < text.length; i++) {
44 if(!text[i].match(/\s/))
45 break;
46 }
47 return {
48 pos: i
49 };
50 };
51
52 let parseToken = function(text,start,required) {
53 let token = [];
54 for(var i=start; i< text.length; i++) {
55 let char = text[i];
56 if (!char.match(/[\w-]/))
57 break;
58 token.push(char);
59 }
60
61 if (required && token.length == 0) {
62 if (i == text.length)
63 throw new Error("Unexpected end of line");
64 else
65 throw lang.replace("Unexpected char '{char}' at '{pos}'", { char: text[i], pos: i});
66 }
67
68 return {
69 value : token.join(''),
70 pos : i
71 };
72 };
73
74
75 let parseMedia = function(text,start) {
76 let type, subtype;
77 let t = parseToken(text,start,true);
78
79 type = t.value;
80 start = t.pos;
81
82 if (text[start] != '/')
83 throw lang.replace("Unexpected char '{char}' at '{pos}'", { char: text[start], pos: start});
84 start++;
85
86 t = parseToken(text,start,true);
87 subtype = t.value;
88 start = t.pos;
89
90 return {
91 value: {
92 type : type,
93 subtype: subtype
94 },
95 pos : start
96 };
97 };
98
99 let parseLiteral = function(text,pos,required) {
100 let data = [];
101 let escape, stop;
102 for(var i=pos; (i < text.length) && !stop; i++) {
103 if (escape) {
104 data.push(text[i]);
105 escape = false;
106 } else {
107 switch(text[i]) {
108
109 case '"':
110 if (pos != i)
111 stop = true;
112 break;
113 case '\\':
114 escape = true;
115 break;
116 default:
117 if (pos == i) {
118 stop = true;
119 i--;
120 } else {
121 data.push(text[i]);
122 }
123 }
124 }
125 }
126
127 if(required && data.length == 0)
128 new lang.replace("Unexpected char '{char}' at '{pos}'", { char: text[i], pos: i});
129
130 return {
131 value : data.join(''),
132 pos : i
133 };
134 };
135
136 let parseParam = function(text,pos) {
137 let t,name,value;
138 t = parseToken(text,pos,true);
139
140 name = t.value;
141 pos = t.pos;
142
143 t = parseSpace(text,pos);
144 pos = t.pos;
145
146 if (text[pos] != '=')
147 throw lang.replace("Unexpected char '{char}' at '{pos}'", { char: text[pos], pos: pos});
148 pos++;
149
150 t = parseSpace(text,pos);
151 pos = t.pos;
152
153 t = parseToken(text,pos,false);
154 if (t.pos != pos) {
155 value = t.value;
156 pos = t.pos;
157 } else {
158 t = parseLiteral(text,pos,false);
159 if (t.pos == pos)
160 throw lang.replace("Unexpected char '{char}' at '{pos}'", { char: text[pos], pos: pos});
161 pos = t.pos;
162 value = t.value;
163 }
164
165 return {
166 value : {
167 name : name,
168 value : value
169 },
170 pos : pos
171 };
172 };
173
174 cls.parse = function(text) {
175 let t,pos = 0, media, params = {};
176 t = parseMedia(text,pos);
177 media = t.value;
178 pos = t.pos;
179
180
181 while(pos < text.length) {
182 t = parseSpace(text,pos);
183 pos = t.pos;
184
185 if (pos < text.length) {
186 if(text[pos] == ';') {
187 pos ++;
188 t = parseSpace(text,pos);
189 pos = t.pos;
190
191 t = parseParam(text,pos);
192
193 params[t.value.name] = t.value.value;
194 pos = t.pos;
195
196 } else {
197 throw lang.replace("Unexpected char '{char}' at '{pos}'", { char: text[pos], pos: pos});
198 }
199 }
200 }
201
202 return new cls( media.type + '/' + media.subtype, params );
203 };
204
205 return cls;
206 }); No newline at end of file
@@ -0,0 +1,238
1 /**
2 *
3 */
4 define(["dojo/_base/declare", "dojo/_base/lang", "implab/safe"], function (declare, lang, safe) {
5
6
7 var formats = {
8 "base64+json": {
9 encode: function (data) {
10 return new Buffer(JSON.stringify(data)).toString('base64');
11 },
12 decode: function (data) {
13 try {
14 return JSON.parse(new Buffer(data, 'base64').toString());
15 } catch (err) {
16 return null;
17 }
18 }
19 },
20 "base64": {
21 encode: function (data) {
22 if (!safe.isPrimitive(data))
23 throw new Error("Can'n serialize a complex data");
24 return new Buffer(data).toString('base64');
25 },
26 decode: function (data) {
27 return new Buffer(data, 'base64').toString();
28 },
29 },
30 "simple": {
31 encode: function (data) {
32 if (!safe.isPrimitive(data))
33 throw new Error("Can'n serialize a complex data");
34 return safe.isNull(data) ? "" : data.toString();
35 },
36 decode: function (data) {
37 return data;
38 }
39 }
40 };
41
42 let args = {
43 secure: false,
44 httpOnly: false,
45 path: null,
46 domain: null,
47 maxAge: null,
48 expires: null,
49 extension: null,
50 format: null
51 };
52
53 let Cookie = declare(null, {
54 secure: false,
55 httpOnly: false,
56 path: null,
57 name: null,
58 domain: null,
59 maxAge: null,
60 expires: null,
61 extension: null,
62 value: null,
63 format: "base64+json",
64 _createTime: null,
65
66 constructor: function (name, value, opts) {
67 safe.argumentNotEmptyString(name, "name");
68
69 if (opts) {
70 for (let i in opts)
71 if (i in args)
72 this[i] = opts[i];
73 }
74
75 this.name = name;
76 this.value = value;
77 this._createTime = new Date();
78 },
79
80 isExpired: function () {
81 let expires;
82 if (this.maxAge) {
83 let dt = this.getNormalMaxAge();
84 expires = new Date(this._createTime.getTime() + dt * 1000);
85 } else if (this.expires) {
86 expires = new Date(this.expires);
87 }
88
89 return (expires && new Date() > expires);
90 },
91
92 getNormalMaxAge: function () {
93 if (safe.isNull(this.maxAge))
94 return null;
95 if (safe.isNumber(this.maxAge))
96 return Math.round(this.maxAge);
97
98 let norm = Number(this.maxAge);
99
100 if (norm == this.maxAge)
101 return Math.round(norm);
102
103 let parts = this.maxAge.toString().match("^(?:(\\d+)d)?(?:(\\d+)h)?(?:(\\d+)m)?(?:(\\d+)s)?$");
104 if (parts) {
105 let factor = [0, 86400, 3600, 60, 1];
106 norm = 0;
107 for (let i = 1; i < parts.length; i++) {
108 norm += factor[i] * (Number(parts[i]) || 0);
109 }
110 } else {
111 norm = null;
112 }
113
114 return norm;
115 },
116
117 toString: function () {
118 let data = [];
119
120 let pair = function (name, value, enc) {
121 if (!safe.isNullOrEmptyString(value))
122 data.push([name, enc ? enc(value) : value].join('='));
123 };
124
125 let flag = function (name, isSet) {
126 if (isSet)
127 data.push(name);
128 };
129
130 pair(this.name, this.value, this.format ? Cookie.formats[this.format].encode : null);
131 pair("Expires", this.expires, function (d) {
132 return new Date(d).toUTCString();
133 });
134 pair("Max-Age", this.getNormalMaxAge());
135 pair("Domain", this.domain);
136 pair("Path", this.path);
137 flag("Secure", this.secure);
138 flag("HttpOnly", this.httpOnly);
139 flag(this.extension, this.extension);
140
141 return data.join('; ');
142 }
143
144 });
145
146 /**
147 * Парсит строку с печеньками из HTTP заголока Cookies
148 *
149 * @return возвращает коллекцию печенек в виде { "cookie-name" : {
150 * value='raw%20data', decode : function(format) {} } }
151 *
152 */
153 Cookie.parseHeaderValue = function (str) {
154 safe.argumentNotEmptyString(str, "str");
155
156 let pairs = str.split(/;\s*/);
157 let cookies = {};
158
159 for (let i in pairs) {
160 let pair = pairs[i];
161 let idx = pair.indexOf('=');
162 if (idx >= 0) {
163 cookies[pair.substring(0, idx)] = {
164 value: pair.substring(idx + 1),
165 decode: function (format) {
166 if (!format)
167 format = "simple";
168
169 if (!formats[format])
170 throw new Error("The specified format '" + format + "' is unsupported");
171 let decode = formats[format].decode;
172
173 return decode(this.value);
174 }
175 };
176 }
177 }
178
179 return cookies;
180
181 };
182
183 Cookie.formats = formats;
184
185 Cookie.parse = function (str, format) {
186 safe.argumentNotEmptyString(str, "str");
187 if (!format)
188 format = "simple";
189
190 if (!formats[format])
191 throw new Error("The specified format '" + format + "' is unsupported");
192
193 let pairs = str.split(/;\s*/);
194 let options = {
195 format: format
196 };
197
198 let cookieName, cookieValue, first = true;
199
200 let map = {
201 "Expires": "expires",
202 "Max-Age": "maxAge",
203 "Domain": "domain",
204 "Path": "path",
205 "Secure": "secure",
206 "HttpOnly": "httpOnly"
207 };
208
209 for (let i in pairs) {
210 let pair = pairs[i];
211 let idx = pair.indexOf('=');
212
213 if (idx >= 0) {
214
215 let name = pair.substring(0, idx);
216 let value = pair.substring(idx + 1);
217
218 if (first) {
219 cookieName = name;
220 cookieValue = value;
221 first = false;
222 } else {
223 if (name in map)
224 options[map[name]] = value;
225 }
226 } else {
227 if (pair in map)
228 options[map[pair]] = true;
229 else
230 options.extension = pair;
231 }
232 }
233
234 return new Cookie(cookieName, cookieValue, options);
235 };
236
237 return Cookie;
238 }); No newline at end of file
@@ -0,0 +1,6
1 define(["dojo/_base/declare", "./HttpException"], function (declare, httpException) {
2 return declare([httpException], {
3 code: 403,
4 message: "Forbidden"
5 });
6 });
@@ -0,0 +1,9
1 define(["dojo/_base/declare","./HttpResponse"],function(declare,HttpResponse) {
2 return declare(null,{
3 invoke: function(/*httpRequest, next*/) {
4 return new HttpResponse("Hello, world!",{
5 contentType : "text/plain; charset=utf-8"
6 });
7 }
8 });
9 }); No newline at end of file
@@ -0,0 +1,11
1 define([
2 "dojo/_base/declare",
3 "./BaseApplication",
4 "dojo/node!http"
5 ], function(declare, base, http) {
6 return declare(base, {
7 _createServer : function(options, handler) {
8 return http.createServer(handler);
9 }
10 });
11 }); No newline at end of file
@@ -0,0 +1,7
1 define(["dojo/_base/declare"], function (declare) {
2 return declare(null, {
3 code: 500,
4 message: "Internal server error",
5 headers: null
6 });
7 });
@@ -0,0 +1,123
1 define([
2 "dojo/_base/declare",
3 "dojo/Deferred",
4 "url",
5 "implab/safe",
6 "./Cookie",
7 "./ContentType",
8 "implab/log/trace!"], function (declare, Deferred, url, safe, Cookie, ContentType, trace) {
9 return declare([], {
10
11 // http.IncommingMessage
12 message: null,
13
14 response: null,
15
16 session: null,
17
18 cookieFormat: "base64+json",
19 _contentType: null,
20 _container: null,
21 _initialised: false,
22
23 constructor: function (options) {
24 safe.argumentNotNull(options, "options");
25 safe.argumentNotNull(options.container, "options.container");
26 this._container = options.container;
27
28 trace.log("Request created");
29 },
30
31 init: function (message, response) {
32 safe.argumentNotNull(message, "message");
33
34 this.message = message;
35 this.method = message.method;
36 this.path = url.parse(message.url, true, true).pathname;
37
38 this.response = response;
39
40 this._initialised = true;
41 },
42
43 _checkInit: function () {
44 if(!this._initialised){
45 throw new Error("Request is not initialised. 'init' function should be called");
46 }
47 },
48
49 getService: function (service) {
50 return this._container.getService(service);
51 },
52
53 getContainer: function () {
54 return this._container;
55 },
56
57 getRemoteAddress: function () {
58 return this.message.socket.remoteAddress;
59 },
60
61 readAllText: function (encoding) {
62 this._checkInit();
63 if (!encoding)
64 encoding = this.getContentType().parameters.charset || 'utf8';
65 this.message.setEncoding(encoding);
66
67 let d = new Deferred();
68
69 let chunks = [];
70
71 this.message.on('data', function (chunk) {
72 chunks.push(chunk);
73 });
74 this.message.on('end', function () {
75 d.resolve(chunks.join(''));
76 });
77 this.message.on('error', function (e) {
78 d.reject(e);
79 });
80 return d;
81 },
82
83 header: function (name) {
84 safe.argumentNotEmptyString(name, "name");
85 this._checkInit();
86
87 return this.message.headers[name.toLowerCase()];
88 },
89
90 getContentType: function () {
91 this._checkInit();
92 if (safe.isNull(this._contentType))
93 this._contentType = ContentType.parse(this.header('Content-Type'));
94 return this._contentType;
95 },
96 /** Возвращает значение печеньки из запроса.
97 * @name Имя печеньки
98 * @format Формат значения, например json+base64
99 * @return Объект со значением печеньки
100 */
101 cookie: function (name, format) {
102 safe.argumentNotEmptyString(name, "name");
103 this._checkInit();
104
105 if (!format)
106 format = this.cookieFormat;
107
108 if (safe.isNull(this._cookies)) {
109 let cookiesHeader = this.header("Cookie");
110 if (!safe.isNullOrEmptyString(cookiesHeader))
111 this._cookies = Cookie.parseHeaderValue(cookiesHeader);
112 else
113 this._cookies = {};
114 }
115
116 let cookie = this._cookies[name];
117 if (safe.isNull(cookie))
118 return null;
119
120 return cookie.decode(format);
121 }
122 });
123 }); No newline at end of file
@@ -0,0 +1,137
1 define([ "dojo/_base/declare", "dojo/_base/lang", "implab/safe" ,"./ContentType", "./BaseResponse", "./Cookie" ], function(declare, lang,safe, ContentType, BaseResponse, Cookie) {
2 return declare(BaseResponse, {
3 statusCode : 0,
4 _headers : null,
5 content : null,
6 _contentType : null,
7 _cookies : null,
8
9 getContentType : function() {
10 return this._contentType;
11 },
12
13 setContentType : function(value) {
14 if (value) {
15 this._contentType = value instanceof ContentType ? value : ContentType.parse(value.toString());
16 this._headers["Content-Type"] = this._contentType.toString();
17 } else {
18 this._contentType = null;
19 delete this._headers["Content-Type"];
20 }
21 this._headers["Content-Type"] = value ? value.toString() : value;
22 },
23
24 /**
25 * Записывает cookies
26 * @cookie {String|Cookie} печенька или имя печеньки или печенька сохраненная в строку.
27 * @value? {*} если {cookie} строка с именем, то в этом параметре передается значение.
28 * @options? {Object} если {cookie} строка с именем, то в этом параметре передаются дополнительные свойства печенек.
29 */
30 setCookie : function(cookie /*, value, options*/) {
31 if (arguments.length == 1) {
32 if (safe.isString(cookie))
33 cookie = Cookie.parse(cookie);
34 safe.argumentOfType(cookie, Cookie, "cookie");
35 this._cookies[cookie.name] = cookie;
36 } else {
37 safe.argumentNotEmptyString(cookie,"cookie");
38 let value = arguments[1];
39 let options = arguments[2];
40
41 let cc = new Cookie(cookie,value,options);
42
43 this._cookies[cc.name] = cc;
44 }
45 },
46
47 _setCookies : function(cookies) {
48 if (!(cookies instanceof Array))
49 cookies = [cookies];
50 let me = this;
51 cookies.forEach(function(cookie){
52 me.setCookie(cookie);
53 });
54 },
55
56 forgetCookie : function(name) {
57 this.setCookie(new Cookie(name,"__erased__", { maxAge : 0 }));
58 },
59
60 setHeader : function(name, value) {
61 if (!name)
62 throw "A name is requried";
63 name = this._normalizeHeaderName(name);
64
65 if (name == "Content-Type")
66 this.setContentType(value);
67 if (name == "Set-Cookie")
68 this._setCookies(value);
69 else
70 this._headers[name] = value;
71 },
72
73 getHeader : function(name) {
74 if (!name)
75 throw "A name is requried";
76 name = this._normalizeHeaderName(name);
77
78 return this._headers[name];
79 },
80
81 _normalizeHeaderName : function(name) {
82 return name.toLowerCase().replace(/\b\w/g, function(s) {
83 return s.toUpperCase();
84 });
85 },
86
87 constructor : function(content, options) {
88 this._headers = {};
89 this._cookies = {};
90
91 this.content = content;
92 this.statusCode = content ? 200 : 203;
93
94 if (options) {
95 if ("statusCode" in options)
96 this.statusCode = options.statusCode;
97 if ("headers" in options) {
98 for ( let header in options.headers)
99 this.setHeader(header, options.headers[header]);
100 }
101 if ("contentType" in options)
102 this.setContentType(options.contentType);
103 }
104 },
105
106 send : function(serverResponse) {
107 if (!serverResponse)
108 throw "ServerResponse is required";
109
110 this.writeHead(serverResponse);
111
112 this.writeEntity(serverResponse);
113
114 serverResponse.end();
115 },
116
117 writeHead : function(serverResponse) {
118 let headers = lang.clone(this._headers);
119 let cookies = [];
120 for (let key in this._cookies)
121 cookies.push(this._cookies[key].toString());
122 headers["Set-Cookie"] = cookies;
123
124 serverResponse.writeHead(this.statusCode, headers);
125 },
126
127 writeEntity : function(serverResponse) {
128 if (this.content instanceof Function) {
129 this.content(serverResponse);
130 } else if (this.content) {
131 serverResponse.write(this.content, this._contentType ? this._contentType.parameters.charset : undefined);
132 }
133 }
134
135
136 });
137 }); No newline at end of file
@@ -0,0 +1,93
1 define([], function() {
2 return {
3 application : {
4 atom: 'application/atom+xml',
5 json : 'application/json',
6 javascript: 'application/javascript',
7 octetStream: 'application/octet-stream',
8 ogg: 'application/ogg',
9 pdf: 'application/pdf',
10 soap: 'application/soap+xml',
11 xhtml: 'application/xhtml+xml',
12 dtd: 'application/xml-dtd',
13 zip: 'application/zip',
14 gzip: 'application/x-gzip',
15 form: 'application/x-www-form-urlencoded',
16 ttf: 'application/x-font-ttf',
17 tar: 'application/x-tar',
18 pkcs12: 'application/x-pkcs12',
19 pfx: 'application/x-pkcs12',
20 spc: 'application/x-pkcs7-certificates',
21 p7b: 'application/x-pkcs7-certificates',
22 p7r: 'application/x-pkcs7-certreqresp',
23 p7c: 'application/x-pkcs7-mime',
24 p7s: 'application/x-pkcs7-signature'
25 },
26 audio : {
27 mulaw: 'audio/basic',
28 pcm24: 'audio/L24',
29 mp4: 'audio/mp4',
30 mp3: 'audio/mpeg',
31 mpeg: 'audio/mpeg',
32 ogg: 'audio/ogg',
33 vorbis: 'audio/vorbis',
34 wma: 'audio/x-ms-wma',
35 wmaRedirect: 'audio/x-ms-wax',
36 realAudio: 'audio/vnd.rn-realaudio',
37 wav: 'audio/vnd.wave',
38 webm: 'audio/webm'
39 },
40 image: {
41 gif: 'image/gif',
42 jpeg: 'image/jpeg',
43 msJpeg: 'image/pjpeg',
44 png: 'image/png',
45 svg: 'image/svg+xml',
46 tiff: 'image/tiff',
47 ico: 'image/vnd.microsoft.icon',
48 wbmp: 'image/vnd.wap.wbmp',
49 bmp: 'image/bmp'
50 },
51 message : {
52 http: 'message/http',
53 imdn: 'message/imdn+xml',
54 emailPartial: 'message/partial',
55 email: 'message/rfc822'
56 },
57 model : {
58 example: 'model/example',
59 iges: 'model/iges',
60 mesh: 'model/mesh',
61 vrml: 'model/vrml',
62 x3db: 'model/x3d+binary',
63 x3dv: 'model/x3d+vrml',
64 x3d: 'model/x3d+xml'
65 },
66 multipart: {
67 mixed: 'multipart/mixed',
68 alternative: 'multipart/alternative',
69 related: 'multipart/related',
70 form: 'multipart/form-data',
71 signed: 'multipart/signed',
72 encrypted: 'multipart/encrypted'
73 },
74 text : {
75 cmd: 'text/cmd',
76 css: 'text/css',
77 csv: 'text/csv',
78 html: 'text/html',
79 plain: 'text/plain',
80 xml: 'text/xml'
81 },
82 video: {
83 mpeg: 'video/mpeg',
84 mp4: 'video/mp4',
85 ogg: 'video/ogg',
86 quicktime: 'video/quicktime',
87 webm: 'video/webm',
88 wmv: 'video/x-ms-wmv',
89 flv: 'video/x-flv'
90 }
91
92 };
93 }); No newline at end of file
@@ -0,0 +1,29
1 define([ "dojo/_base/declare", "./HttpResponse" ], function(declare, HttpResponse) {
2 return declare(HttpResponse, {
3 presenter : null,
4
5 constructor : function(model, options) {
6 if (options && options.presenter)
7 this.presenter = options.presenter;
8 },
9
10 send : function() {
11 console.log("try to present: " + this.content);
12 if (!this.presenter)
13 throw "The presenter isn't specified for the model '" + this.content + "'";
14
15 if (this.statusCode != 203 && this.presenter.contentType)
16 this.setHeader("Content-type", this.presenter.contentType);
17
18 this.inherited(arguments);
19
20 },
21
22 writeEntity : function(serverResponse) {
23 if (this.statusCode == 203)
24 return;
25
26 serverResponse.write(this.presenter.present(this.content), this.getContentType() ? this.getContentType().parameters.charset : undefined);
27 }
28 });
29 }); No newline at end of file
@@ -0,0 +1,6
1 define(["dojo/_base/declare", "./HttpException"], function (declare, httpException) {
2 return declare([httpException], {
3 code: 405,
4 message: "Method Not Allowed"
5 });
6 });
@@ -0,0 +1,6
1 define(["dojo/_base/declare", "./HttpException"], function (declare, httpException) {
2 return declare([httpException], {
3 code: 404,
4 message: "Not found"
5 });
6 });
@@ -0,0 +1,129
1 define([
2 "dojo/_base/declare",
3 "dojo/_base/lang",
4 "dojo/when",
5 "implab/safe",
6 "./NotAllowedException",
7 "./HttpResponse"
8 ], function (
9 declare,
10 lang,
11 when,
12 safe,
13 NotAllowedException,
14 HttpResponse
15 ) {
16 let resource = declare(null, {
17
18 // instance members
19 request: null,
20
21 // родительский ресурс
22 parent: null,
23
24 // имя текущего ресурса, является фрагментом пути к запрашиваемому
25 // ресурсу
26 name: null,
27
28 allowedMethods: {
29 "head": 0,
30 "options": 0,
31 "get": 0,
32 "post": 0,
33 "put": 1, // extended method
34 "delete": 1 // extended method
35 },
36
37 constructor: function (options) {
38 if (options) {
39 declare.safeMixin(this, options);
40 }
41 },
42
43 accessCheck: null,
44
45 getAllowedMethods: function (cors) {
46 let methods = [];
47 for (var m in this.allowedMethods) {
48 if (m in this && (cors && this.allowedMethods[m] || !cors))
49 methods.push(m.toUpperCase());
50 }
51 return methods;
52 },
53
54 options: function () {
55 let resp = new HttpResponse(null, {
56 headers: {
57 "Access-Control-Allow-Methods": this.getAllowedMethods(true),
58 "Allowed-Methods": this.getAllowedMethods()
59 }
60 });
61 return resp;
62 },
63
64 invoke: function () {
65 let method = this.request.method.toLowerCase();
66
67 let me = this;
68
69 if (!(method in this)) {
70 throw new NotAllowedException(); // TODO: Возвратить список возможных методов (verbs)
71 }
72
73 if (!safe.isNull(this.accessCheck)) {
74 return when(this.request.session, function (session) {
75 me.session = session;
76 return when(me.accessCheck(), function () {
77 return me[method]();
78 });
79 }, function (err) {
80 console.log(err);
81 throw err;
82 });
83 } else {
84 return this[method]();
85 }
86 },
87
88 render: function (view, model, mimeType) {
89 return function (resp) {
90 if (mimeType)
91 resp.type(mimeType);
92 resp.render(view, {
93 model: model
94 });
95 };
96
97 },
98
99 getChild: function (name) {
100 if (this.children && name in this.children) {
101 let child = this.children[name];
102
103 if (typeof child == "function") {
104 return child({
105 request: this.request,
106 parent: this,
107 name: name
108 });
109 } else if (child.hasOwnProperty("isInstanceOf") && child.isInstanceOf(resource)) {
110 return lang.mixin(child, {
111 request: this.request,
112 parent: this,
113 name: name
114 });
115 } else {
116 return new resource(lang.mixin(child, {
117 request: this.request,
118 parent: this,
119 name: name
120 }));
121 }
122 } else {
123 return null;
124 }
125 }
126 });
127
128 return resource;
129 }); No newline at end of file
@@ -0,0 +1,50
1 define([
2 "dojo/_base/declare",
3 "dojo/_base/lang",
4 "dojo/when",
5 "./NotFoundException",
6 "./HttpException",
7 "./Resource",
8 "implab/safe"
9 ],
10 function (declare, lang, when, notFoundException, httpException, resource, safe) {
11
12 return declare([], {
13 _resourcesConfig: null,
14
15 constructor: function (options) {
16 safe.argumentNotNull(options, "options");
17 safe.argumentNotNull(options.resourcesConfig, "options.resourcesConfig");
18
19 this._resourcesConfig = options.resourcesConfig;
20 },
21
22 createResource: function (req) {
23 let container = req.getContainer();
24 return container.configure(this._resourcesConfig)
25 .then(function(){
26 let rc = container.getService("resource");
27
28 if(rc.hasOwnProperty("isInstanceOf") && rc.isInstanceOf(resource)) {
29 return rc;
30 } else {
31 return new resource(lang.mixin(rc,{request: req}));
32 }
33 });
34 },
35
36 invoke: function (req/*, next*/) {
37 return when(this.createResource(req), function (rc) {
38 req.path.split(/\/+/).forEach(function (child) {
39 if (child) {
40 rc = rc.getChild(child);
41 if (!rc) {
42 throw new notFoundException();
43 }
44 }
45 });
46 return rc.invoke();
47 });
48 }
49 });
50 });
@@ -0,0 +1,23
1 define([ "dojo/_base/declare", "./BaseResponse" ], function(declare, BaseResponse) {
2 /**
3 * Заглушка вместо ответа сервера, используется при использовании "родных" модулей
4 * node, требующи работы напрямую с ответом, для чего у HttpRequest запрашивается
5 * свйоство response, а вместо ответа возвращается заглушка.
6 */
7 return declare(BaseResponse, {
8 _serverResponse : null,
9
10 constructor : function(serverResponse) {
11 this._serverResponse = serverResponse;
12 },
13
14 setHeader : function() {
15 this._serverResponse.setHeader.apply(this._serverResponse, arguments);
16 },
17
18 send : function(serverResponse) {
19 if (!serverResponse.finished)
20 serverResponse.end();
21 }
22 });
23 }); No newline at end of file
@@ -0,0 +1,18
1 /**
2 * Created by internet on 6/21/16.
3 */
4 'user strict';
5 /*=====
6 return {
7 // summary:
8 // The implab package main module; implab package is somewhat unusual in that the main module currently just provides an empty object.
9 // Apps should require modules from the implab packages directly, rather than loading this module.
10 };
11 =====*/
12
13 /**
14 The entry point
15 @module implab
16 */
17
18 module.exports = {}; No newline at end of file
@@ -0,0 +1,13
1 /**
2 * Created by andrei on 22.08.16.
3 */
4 define(['dojo/_base/declare'], function(declare){
5 let AuthCode = declare(null, {
6 });
7
8 AuthCode.SUCCSESS = 0;
9 AuthCode.INCMPLETE = 1;
10 AuthCode.FAIL = 2;
11
12 return AuthCode;
13 });
@@ -0,0 +1,93
1 /**
2 * Модуль, отвечающий за создание контекста безопасности (сессии).
3 *
4 * Идентификатор сессии сохраняется в печеньках на клиенте.
5 */
6 define([
7 "dojo/_base/declare",
8 "dojo/when",
9 "implab/safe",
10 "../Cookie",
11 "../security/SecData",
12 "../BaseResponse",
13 "implab/log/trace!"
14 ], function (declare, when, safe, Cookie, SecData, BaseResponse, trace) {
15 let COOKIE_NAME = "ssid";
16
17 return declare(null, {
18 _request : null,
19 _provider : null,
20 _cookies : null,
21 _sessionData: null,
22
23 constructor : function(options) {
24 safe.argumentNotNull(options, "options");
25 safe.argumentNotNull(options.securityProvider, "options.securityProvider");
26 safe.argumentNotNull(options.request, "options.request");
27
28 this._request = options.request;
29 this._provider = options.securityProvider;
30 this._cookies = [];
31 },
32
33 /**
34 * Вызывается из {SecurityHandler} по окончании обработки запроса.
35 *
36 * @resp ответ сервера
37 */
38 completeRequest : function(resp) {
39 trace.log("completeRequest");
40 if (resp instanceof BaseResponse)
41 this._cookies.forEach(function(cookie) {
42 resp.setCookie(cookie);
43 });
44 if (!safe.isNull(this._sessionData))
45 return this._sessionData.save().then(function () {
46 return resp;
47 });
48 return resp;
49 },
50
51 handleError : function(err) {
52 throw err;
53 },
54
55 initSession : function(userIdentity) {
56 safe.argumentNotNull(userIdentity, "userIdentity");
57 let me = this;
58
59 return when(me._provider.getSessions().createSession(userIdentity, SecData.newSSID()), function(session) {
60 if (!session) {
61 throw new Error("Can`t init session");
62 }
63
64 me._sessionData = session;
65 trace.log("Created session {0} for user {1}", session.sessionId, userIdentity.getUser().login);
66 me._cookies.push(new Cookie(COOKIE_NAME,session.sessionId));
67 return session;
68 });
69 },
70
71 /* aync */
72 getSession : function() {
73 let cookie;
74 let me = this;
75 try {
76 cookie = this._request.cookie(COOKIE_NAME);
77
78 if (!cookie)
79 return null;
80
81 return when(me._provider.getSessions().getSession(cookie), function(session) {
82 me._sessionData = session;
83 return session;
84 });
85 } catch (e) {
86 trace.log("getSession: {0}", e);
87 return null;
88 }
89 }
90
91 });
92
93 }); No newline at end of file
@@ -0,0 +1,81
1 /**
2 * @class Identity идентификационная информация (удостоверение) пользователя.
3 * Описывает пользователя и способ его аутентификации в системе. Данный
4 * класс является заглушкой, описывающей интерфейс, а также может
5 * использоваться для создания идентификацинной информации вручную.
6 */
7 define([
8 "dojo/_base/declare",
9 "implab/safe",
10 "./SecData"
11 ], function (declare, safe, SecData) {
12 let identity = declare(null, {
13 _user : null,
14
15 _isAnonymous : null,
16
17 _secData : null,
18
19 constructor : function(user, opts) {
20 this._user = user;
21 safe.mixin(this, opts, {
22 isAnonymous : '_isAnonymous',
23 secData : '_secData'
24 });
25 },
26
27 getUser : function() {
28 return this._user;
29 },
30
31 getSecData : function() {
32 return this._secData;
33 },
34
35 /**
36 * Проводит раунд аутентификации, изменяет текущее состояние. Делегирует
37 * выполнение процедуры объекту SecData.
38 *
39 * @param challenge
40 * {*} - данные для аутентификации, зависит от реализации,
41 * например, пароль.
42 * @retuns authResult {Object} - результат аутентификации { challenge :
43 * 'response data', code : AUTH_* }.
44 * @throws {Error}
45 * в случае ошибки или если модуль аутентификации не задан.
46 */
47 doAuth : function(challenge) {
48 if (this._secData)
49 return this._secData.doAuth(challenge);
50
51 throw new Error("Authentication is not available for this object");
52 },
53
54 getAuthType : function() {
55 if (this._secData)
56 return this._secData.getAuthType();
57
58 return null;
59 },
60
61 getIsAuthenticated : function() {
62 if (this._secData) {
63 return this._secData.getAuthSate() == SecData.AUTH_SUCCESS;
64 }
65 return false;
66 },
67
68 getIsAnonymous : function() {
69 return this._isAnonymous;
70 },
71
72 getAuthState : function() {
73 if (this._secData)
74 return this._secData.getAuthState();
75
76 throw new Error("Authentication is not available for this object");
77 }
78 });
79
80 return identity;
81 }); No newline at end of file
@@ -0,0 +1,18
1 define([
2 "dojo/_base/declare",
3 "./SecData"
4 ], function (declare, SecData) {
5 return declare(SecData, {
6 doAuth: function (/*challenge*/) {
7 return {
8 code: SecData.AUTH_SUCCESS
9 };
10 },
11 getAuthState: function () {
12 return SecData.AUTH_SUCCESS;
13 },
14 getAuthType: function () {
15 return "none";
16 }
17 });
18 }); No newline at end of file
@@ -0,0 +1,73
1 define([
2 "dojo/_base/declare", "dojo/node!crypto"], function (declare, crypto) {
3 let SecData = declare(null, {
4 _state: null,
5 _token: null,
6 _authType: null,
7
8 constructor: function (authType) {
9 this._authType = authType;
10 },
11
12 /**
13 * Проводит раунд аутентификации, изменяет текущее состояние.
14 * @param challenge {*} - данные для аутентификации, зависит от реализации, например, пароль.
15 * @retuns authResult {Object} - результат аутентификации { challenge : 'response data', code : AUTH_* }.
16 */
17 doAuth: function (challenge) {
18 let password = challenge;
19
20 if (this.validateHash(password)) {
21 this._state = SecData.AUTH_SUCCESS;
22 } else {
23 this._state = SecData.AUTH_FAIL;
24 }
25
26 return {
27 code: this._state
28 };
29
30 },
31 getAuthState: function () {
32 return this._state;
33 },
34 getAuthType: function () {
35 return this._authType;
36 },
37
38 generateHash: function (password) {
39 return md5hex(password);
40 },
41 parse: function (token) {
42 this._token = token;
43 },
44
45 validateHash: function (password) {
46 return this.generateHash(password) == this._token;
47 }
48 });
49
50
51 SecData.AUTH_SUCCESS = 0;
52 SecData.AUTH_INCOMPLETE = 1;
53 SecData.AUTH_FAIL = 2;
54
55
56 function md5hex() {
57 let md5 = crypto.createHash('md5');
58
59 for (let i = 0; i < arguments.length; i++)
60 md5.update(String(arguments[i]));
61
62 return md5.digest('hex');
63 }
64
65 let i = 0;
66
67 SecData.md5hex = md5hex;
68 SecData.newSSID = function () {
69 return md5hex(new Date().getTime(), Math.random(), i++);
70 };
71
72 return SecData;
73 }); No newline at end of file
@@ -0,0 +1,56
1 /**
2 * Обработчик системы безопасности, встраивается в стек обработки запросов и
3 * контролирует процесс создания контекста безопасности регистрирует в запросе
4 * сервис с именем session.
5 *
6 * Для получения и создания контекста безопасности используется
7 * SecurityAuthority которое отвечает за механизмы проверки подлинности и
8 * доверенность полученной сессии, по сути SecurityAuthority реализует протокол
9 * безопасности.
10 *
11 * При первом доступе к сервисам возможна прозрачная аутентификация, при этом
12 * создается новая сессия, для ее создания используется IdentityProvider.
13 * IdentityProvider - внешний модуль аутентификации, который по требованию
14 * предоставляет идентификатор пользователя, используя собственные механизмы.
15 *
16 * После получения данных аутентификации от IdentityProvider они используются в
17 * SecurityAuthority для создания сессии.
18 *
19 * Многоэтапная аутентификация сессии - случай пока чисто теоретический, оданко,
20 * в случае необходимости SecurityAuthtority вызывает исключение при получении
21 * сесии, если требуется следующий этап аутентификации сессии, данное исключение
22 * прерывает текущую обработку запроса и попадает в обработчик
23 * SecurityAuthority.handleError который в свою очередь формирует ответ сервера
24 * для продолжения аутентификации.
25 */
26 define([ "dojo/_base/declare", "dojo/when"],
27 function(declare, when) {
28
29 return declare(null, {
30 constructor : function(/*options*/) {
31 },
32
33 /**
34 * Точка входа в обработчик, вызывается инфраструктурой при
35 * обработке запроса.
36 *
37 * @req Текущий запрос.
38 * @next Следующий обработчик в цепочке, вызывается когда сессия уже
39 * зарегистрирована в локаторе.
40 * @async
41 */
42 invoke : function(req, next) {
43 let authority = req.getService("securityAuthority");
44 return when(next(), function(resp) {
45 if (authority)
46 return authority.completeRequest(resp);
47 return resp;
48 }, function(err) {
49 if (authority)
50 return authority.handleError(err);
51 else
52 throw err;
53 });
54 }
55 });
56 });
@@ -0,0 +1,31
1 /**
2 * Created by andrei on 21.08.16.
3 */
4 define([
5 "dojo/_base/declare",
6 "implab/safe",
7 "./NoneSecData",
8 "./SecData"
9 ], function(declare, safe, NoneSecData, SecData) {
10 let SecurityServices = declare(null, {
11 });
12
13 SecurityServices.getSecurityDataService = function(tokenType) {
14 switch (tokenType) {
15 case this.PASSWORD_AUTH_TYPE:
16 return new SecData(tokenType);
17 case this.SATISFY_ANY_AUTH_TYPE:
18 return new NoneSecData();
19 case this.REJECT_ALL_AUTH_TYPE:
20 throw Error("NotImplemented");
21 default:
22 throw Error("Unsupported auth type " + tokenType);
23 }
24
25 };
26 SecurityServices.PASSWORD_AUTH_TYPE = "password";
27 SecurityServices.SATISFY_ANY_AUTH_TYPE = "satisfyany";
28 SecurityServices.REJECT_ALL_AUTH_TYPE = "rejectall";
29
30 return SecurityServices;
31 });
@@ -0,0 +1,101
1 /**
2 * Created by andrei on 08.04.16.
3 */
4 define([
5 "dojo/_base/declare",
6 "dojo/_base/lang",
7 "dojo/when",
8 "implab/safe",
9 "../ForbiddenException"],
10 function (declare, lang, when, safe, ForbiddenException) {
11 return declare(null, {
12 _container: null,
13 _sessionData: null,
14 /**
15 * Создавать анонимную сессию. Если прозрачная аутентификация не
16 * проводилась, и текущей сессии нет, то может быть создана сессия
17 * для анонимного пользователя. Это сделано опцией, поскольку не во
18 * всех системах требуется, отслеживать активность анонимных
19 * пользователей (это не бесплатно).
20 */
21 _createAnonymousSession: false,
22
23 /**
24 * Регистрировать пользователей, полученных из внешнего источника
25 * аутентификации.
26 */
27 _autoRegisterUsers: false,
28
29 constructor: function (options) {
30 safe.argumentNotNull(options, "options");
31 safe.argumentNotNull(options.container, "options.container");
32
33 this._container = options.container;
34
35 this._createAnonymousSession = options.createAnonymousSession || this._createAnonymousSession;
36 this._autoRegisterUsers = options.autoRegisterUsers || this._autoRegisterUsers;
37
38 this._sessionData = this._initSession();
39 },
40 getSessionData: function () {
41 return this._sessionData;
42 },
43
44 /**
45 * @remarks Сосздание/восстановление сессии происходит в несколько
46 * этапов 1. Получаем текущую сессию. 2. Если упешно, то
47 * выходим. 3. Получаем идентификатор пользователя,
48 * используя identityProvider 4. Если успешно 5. Создаем
49 * новую сессию для полученного пользователя, выходим 6.
50 * Иначе если установлен параметр _createAnonymousSession 7.
51 * Создаем новую анонимную сессию. 8. Иначе, выходим
52 */
53 _initSession: function () {
54 let me = this;
55
56 let securityAuthority = me._container.getService("securityAuthority");
57 let securityProvider = me._container.getService("securityProvider");
58 let identityProvider = me._container.getService("identityProvider");
59
60 if (!securityAuthority)
61 throw new Error("Sessions are not supported, no security authority is supplied");
62
63
64 // пытаемся получить текущую сессию
65 return when(securityAuthority.getSession(), function (session) {
66 if (session)
67 return session;
68
69 // пытаемся создать сессию с использованием
70 // пользователя из внешнего
71 // источника аутентификации
72 if (identityProvider) {
73 return when(identityProvider.getUserLogin(), function (login) {
74 if (login) {
75 return when(securityProvider.createUserIdentity(login, false, {
76 createUser: me._autoRegisterUsers
77 }), function (uid) {
78 return securityAuthority.initSession(uid);
79 }, function (err) {
80 console.log("Failed to create session for '" + login + "' ", err);
81 throw new ForbiddenException();
82 });
83 } else if (me._createAnonymousSession) {
84 return when(securityProvider.getAnonymousIdentity(), function (uid) {
85 return securityAuthority.initSession(uid);
86 });
87 } else {
88 throw new ForbiddenException();
89 }
90 });
91 } else if (me._createAnonymousSession) {
92 return when(securityProvider.getAnonymousIdentity(), function (uid) {
93 return securityAuthority.initSession(uid);
94 });
95 }
96
97 throw new ForbiddenException();
98 });
99 }
100 });
101 }); No newline at end of file
@@ -0,0 +1,40
1 {
2 "extends": "tslint:recommended",
3 "rules": {
4 "align": [
5 true,
6 "parameters",
7 "statements"
8 ],
9 "interface-name": [false],
10 "max-line-length": [ true, 185 ],
11 "member-access": false,
12 "member-ordering": [
13 false,
14 "variables-before-functions"
15 ],
16 "no-bitwise": false,
17 "no-empty": false,
18 "no-namespace": false,
19 "no-string-literal": false,
20 "ordered-imports": false,
21 "one-line": [
22 true,
23 "check-open-brace",
24 "check-catch",
25 "check-whitespace"
26 ],
27 "object-literal-sort-keys": false,
28 "trailing-comma": [
29 true,
30 {
31 "singleline": "never",
32 "multiline": "never"
33 }
34 ],
35 "variable-name": false,
36 "curly": false,
37 "array-type": false,
38 "arrow-parens": [true, "ban-single-arg-parens"]
39 }
40 } No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now