diff --git a/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/.gitignore b/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/.gitignore new file mode 100644 index 0000000..7879096 --- /dev/null +++ b/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/.gitignore @@ -0,0 +1,3 @@ +/node_modules/ +**/*.js +**/*.d.ts diff --git a/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/.npmignore b/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/.npmignore new file mode 100644 index 0000000..74dbfa8 --- /dev/null +++ b/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/.npmignore @@ -0,0 +1,2 @@ +!/*.js +!/*.d.ts diff --git a/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/LICENSE.txt b/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/LICENSE.txt new file mode 100644 index 0000000..0bdc196 --- /dev/null +++ b/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/LICENSE.txt @@ -0,0 +1,12 @@ +Copyright (c) .NET Foundation. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +these files except in compliance with the License. You may obtain a copy of the +License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. diff --git a/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/package.json b/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/package.json new file mode 100644 index 0000000..5954823 --- /dev/null +++ b/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/package.json @@ -0,0 +1,32 @@ +{ + "name": "aspnet-angular", + "version": "0.1.0", + "description": "Helpers for using Angular in ASP.NET Core projects", + "main": "index.js", + "scripts": { + "prepublish": "rimraf *.d.ts && tsc && echo 'Finished building NPM package \"aspnet-angular\"'", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "https://github.com/aspnet/JavaScriptServices.git" + }, + "author": "Microsoft", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/aspnet/JavaScriptServices/issues" + }, + "devDependencies": { + "@angular/common": "^4.3.2", + "@angular/core": "^4.3.2", + "@angular/http": "^4.3.2", + "@angular/platform-browser": "^4.3.2", + "rimraf": "^2.6.1", + "typescript": "^2.4.2", + "rxjs": "^5.4.2", + "zone.js": "^0.8.16" + }, + "peerDependencies": { + "@angular/core": "^4.2.5 || ^5.0.0-beta" + } +} diff --git a/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/src/HttpWithStateTransfer.ts b/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/src/HttpWithStateTransfer.ts new file mode 100644 index 0000000..517dd7e --- /dev/null +++ b/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/src/HttpWithStateTransfer.ts @@ -0,0 +1,94 @@ +import { Provider, NgModule, Inject } from '@angular/core'; +import { Headers, Http, ResponseOptions, RequestOptionsArgs, Response } from '@angular/http'; +import { Observable } from 'rxjs/Observable'; +import 'rxjs/add/observable/of'; +import 'rxjs/add/operator/map'; +const globalSerializedStateKey = 'HTTP_STATE_TRANSFER'; +const backingStoreDIToken = 'HTTP_STATE_BACKING_STORE'; + +export interface CacheOptions { + permanent: boolean; +} + +export interface CachedHttpResponse { + headers: { [name: string]: any } | null; + status: number; + statusText: string | null; + text: string; + url: string; +} + +export type BackingStore = { [key: string]: CachedHttpResponse }; + +export class HttpWithStateTransfer { + private backingStore: BackingStore; + private http: Http; + + constructor(@Inject(Http) http: Http, @Inject(backingStoreDIToken) backingStore: BackingStore) { + this.http = http; + this.backingStore = backingStore; + } + + public stateForTransfer(): any { + return { [globalSerializedStateKey]: this.backingStore }; + } + + public get(url: string, options?: CacheOptions, requestOptions?: RequestOptionsArgs): Observable { + return this.getCachedResponse(/* cacheKey */ url, () => this.http.get(url, requestOptions), options); + } + + private getCachedResponse(cacheKey: string, provider: () => Observable, options?: CacheOptions): Observable { + // By default, the cache is only used for the *first* client-side read. So, we're only performing + // a one-time transfer of server-side response to the client. If you want to keep and reuse cached + // responses continually during server-side and client-side execution, set 'permanent' to 'true. + const isClient = typeof window !== 'undefined'; + const isPermanent = options && options.permanent; + + const allowReadFromCache = isClient || isPermanent; + if (allowReadFromCache && this.backingStore.hasOwnProperty(cacheKey)) { + const cachedValue = this.backingStore[cacheKey]; + if (!isPermanent) { + delete this.backingStore[cacheKey]; + } + return Observable.of(new Response(new ResponseOptions({ + body: cachedValue.text, + headers: new Headers(cachedValue.headers), + status: cachedValue.status, + url: cachedValue.url + }))); + } + + return provider() + .map(response => { + const allowWriteToCache = !isClient || isPermanent; + if (allowWriteToCache) { + this.backingStore[cacheKey] = { + headers: response.headers ? response.headers.toJSON() : null, + status: response.status, + statusText: response.statusText, + text: response.text(), + url: response.url + }; + } + + return response; + }); + } +} + +export function defaultBackingStoreFactory() { + const transferredData = typeof window !== 'undefined' ? (window as any)[globalSerializedStateKey] : null; + return transferredData || {}; +} + +@NgModule({ + providers: [ + // The backing store is a separate DI service so you could override exactly how it gets + // transferred from server to client + { provide: backingStoreDIToken, useFactory: defaultBackingStoreFactory }, + + { provide: HttpWithStateTransfer, useClass: HttpWithStateTransfer }, + ] +}) +export class HttpWithStateTransferModule { +} diff --git a/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/src/index.ts b/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/src/index.ts new file mode 100644 index 0000000..93d2185 --- /dev/null +++ b/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/src/index.ts @@ -0,0 +1 @@ +export * from './HttpWithStateTransfer'; diff --git a/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/tsconfig.json b/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/tsconfig.json new file mode 100644 index 0000000..22a6c26 --- /dev/null +++ b/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "experimentalDecorators": true, + "moduleResolution": "node", + "module": "commonjs", + "target": "es5", + "declaration": true, + "outDir": ".", + "lib": ["es2015", "dom"] + }, + "files": [ + "src/index.ts" + ], + "exclude": [ + "node_modules" + ] +}