├── server
├── src
│ ├── app-root.ts
│ ├── filters
│ │ ├── readme.md
│ │ ├── index.ts
│ │ ├── kb-not-found-exception.filter.spec.ts
│ │ ├── kb-validation-exception.filter.spec.ts
│ │ ├── kb-not-found-exception.filter.ts
│ │ └── kb-validation-exception.filter.ts
│ ├── events
│ │ ├── readme.md
│ │ ├── index.ts
│ │ ├── events.module.ts
│ │ ├── events.gateway.spec.ts
│ │ └── events.gateway.ts
│ ├── main.ts
│ ├── tasks
│ │ ├── index.ts
│ │ ├── readme.md
│ │ ├── tasks.service.ts
│ │ └── tasks.service.spec.ts
│ ├── app
│ │ ├── readme.md
│ │ ├── index.ts
│ │ ├── app.module.ts
│ │ ├── app.controller.ts
│ │ ├── app.service.ts
│ │ └── app.controller.spec.ts
│ ├── decorators
│ │ ├── readme.md
│ │ ├── index.ts
│ │ ├── kb-api-validation-error-response.decorator.ts
│ │ ├── get-all.decorator.ts
│ │ ├── kb-post.decorator.ts
│ │ ├── get-one.decorator.ts
│ │ ├── kb-delete.decorator.ts
│ │ ├── kb-put.decorator.ts
│ │ └── kb-patch.decorator.ts
│ ├── api
│ │ ├── readme.md
│ │ ├── index.ts
│ │ ├── api.module.ts
│ │ ├── product
│ │ │ ├── product.module.ts
│ │ │ ├── product.service.ts
│ │ │ └── product.controller.ts
│ │ ├── api.controller.spec.ts
│ │ └── api.controller.ts
│ ├── abstracts
│ │ ├── index.ts
│ │ ├── readme.md
│ │ ├── base.model.abstract.ts
│ │ └── base.service.abstract.ts
│ ├── models
│ │ ├── index.ts
│ │ ├── readme.md
│ │ ├── api.model.ts
│ │ ├── public-error.model.ts
│ │ └── product.model.ts
│ ├── bootstrap-application.ts
│ └── swagger.ts
├── .eslintignore
├── nest-cli.json
├── tsconfig.build.json
├── test-tools
│ └── jest.setup.ts
├── test
│ ├── __snapshots__
│ │ └── app.e2e-spec.ts.snap
│ ├── utils.ts
│ ├── app.e2e-spec.ts
│ ├── jest-e2e.json
│ ├── sockets.e2e-spec.ts
│ └── socket.service.ts
├── tsconfig.json
├── .eslintrc.js
├── README.md
└── package.json
├── client
├── src
│ ├── assets
│ │ └── .gitkeep
│ ├── app
│ │ ├── app.component.scss
│ │ ├── app.component.html
│ │ ├── app-routing.module.ts
│ │ ├── app.component.ts
│ │ ├── app.module.ts
│ │ ├── app.component.spec.ts
│ │ └── angular-material
│ │ │ └── angular-material.module.ts
│ ├── environments
│ │ ├── environment.prod.ts
│ │ └── environment.ts
│ ├── favicon.ico
│ ├── main.ts
│ ├── index.html
│ ├── test.ts
│ ├── styles.scss
│ └── polyfills.ts
├── proxy.conf.json
├── .editorconfig
├── tsconfig.app.json
├── e2e
│ ├── src
│ │ ├── app.po.ts
│ │ └── app.e2e-spec.ts
│ ├── tsconfig.json
│ └── protractor.conf.js
├── tsconfig.spec.json
├── tsconfig.json
├── .gitignore
├── .browserslistrc
├── README.md
├── .eslintrc.js
├── karma.conf.js
├── package.json
├── tslint.json
└── angular.json
├── .commit-template
├── .github
├── FUNDING.yml
├── workflows
│ ├── build.yml
│ ├── release.yml
│ ├── e2e-tests.yml
│ ├── api-tests.yml
│ ├── server-unit-tests.yml
│ └── client-unit-tests.yml
└── CODEOWNERS
├── tools
├── readme.md
├── scripts
│ ├── replace-template-string.js
│ ├── get-all-contributors.js
│ └── prune-untrackted-branches.js
├── initialize.js
└── data
│ └── names-dictionary.js
├── commitlint.config.js
├── .vscode
├── extensions.json
├── settings.json
└── tasks.json
├── .all-contributorsrc
├── .devcontainer
├── docker-compose.yml
├── Dockerfile
└── devcontainer.json
├── .gitignore
├── TEMPLATE_INSTRUCTIONS.md
├── README.md
└── package.json
/server/src/app-root.ts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/src/assets/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/src/app/app.component.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.commit-template:
--------------------------------------------------------------------------------
1 | type(scope): subject
2 |
3 | description
--------------------------------------------------------------------------------
/server/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | lib
3 | .eslintrc.js
--------------------------------------------------------------------------------
/server/src/filters/readme.md:
--------------------------------------------------------------------------------
1 | ## FILTERS
2 |
3 | Nest.js filters
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | custom:
2 | - https://paypal.me/thatkookooguy?locale.x=en_US
--------------------------------------------------------------------------------
/server/nest-cli.json:
--------------------------------------------------------------------------------
1 | {
2 | "collection": "@nestjs/schematics",
3 | "sourceRoot": "src"
4 | }
5 |
--------------------------------------------------------------------------------
/client/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/client/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kibibit/kb-server-client-template/master/client/src/favicon.ico
--------------------------------------------------------------------------------
/server/src/events/readme.md:
--------------------------------------------------------------------------------
1 | ## EVENTS
2 |
3 | This module is in charge of everything related to async events on top of websockets
--------------------------------------------------------------------------------
/server/src/main.ts:
--------------------------------------------------------------------------------
1 | import { bootstrap } from './bootstrap-application';
2 |
3 | bootstrap()
4 | .then((app) => app.listen(10102));
5 |
--------------------------------------------------------------------------------
/server/src/tasks/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @file Automatically generated by barrelsby.
3 | */
4 |
5 | export * from './tasks.service';
6 |
--------------------------------------------------------------------------------
/server/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
4 | }
5 |
--------------------------------------------------------------------------------
/server/src/app/readme.md:
--------------------------------------------------------------------------------
1 | ## APP
2 |
3 | The main app definition. Everything is defined here (all the submodules are imported in this module as well).
--------------------------------------------------------------------------------
/server/src/decorators/readme.md:
--------------------------------------------------------------------------------
1 | ## DECORATORS
2 |
3 | Mostly using `applyDecorators` to combine decorators that are commonly being used together across the project
--------------------------------------------------------------------------------
/server/src/api/readme.md:
--------------------------------------------------------------------------------
1 | ## API
2 |
3 | Contains the main API of the application under `/api`. As a rule of thumbs, each part of the api should be a module of itself.
--------------------------------------------------------------------------------
/server/src/events/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @file Automatically generated by barrelsby.
3 | */
4 |
5 | export * from './events.gateway';
6 | export * from './events.module';
7 |
--------------------------------------------------------------------------------
/client/proxy.conf.json:
--------------------------------------------------------------------------------
1 | {
2 | "/api/*": {
3 | "target": "http://localhost:10102",
4 | "secure": false,
5 | "logLevel": "debug",
6 | "changeOrigin": true
7 | }
8 | }
--------------------------------------------------------------------------------
/server/src/abstracts/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @file Automatically generated by barrelsby.
3 | */
4 |
5 | export * from './base.model.abstract';
6 | export * from './base.service.abstract';
7 |
--------------------------------------------------------------------------------
/server/src/app/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @file Automatically generated by barrelsby.
3 | */
4 |
5 | export * from './app.controller';
6 | export * from './app.module';
7 | export * from './app.service';
8 |
--------------------------------------------------------------------------------
/server/src/filters/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @file Automatically generated by barrelsby.
3 | */
4 |
5 | export * from './kb-not-found-exception.filter';
6 | export * from './kb-validation-exception.filter';
7 |
--------------------------------------------------------------------------------
/server/src/models/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @file Automatically generated by barrelsby.
3 | */
4 |
5 | export * from './api.model';
6 | export * from './product.model';
7 | export * from './public-error.model';
8 |
--------------------------------------------------------------------------------
/server/src/events/events.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { EventsGateway } from './events.gateway';
3 |
4 | @Module({
5 | providers: [EventsGateway]
6 | })
7 | export class EventsModule {}
8 |
--------------------------------------------------------------------------------
/server/src/models/readme.md:
--------------------------------------------------------------------------------
1 | ## MODELS
2 |
3 | Every `item` in our system should be a model. We should have two types of items: persistent items, which will be saved to the database, and non-persistent items, which resign in-memory
--------------------------------------------------------------------------------
/server/src/tasks/readme.md:
--------------------------------------------------------------------------------
1 | ## TASKS
2 |
3 | This module is in charge of everything related to scheduled tasks. Think of cron jobs that the application should perform. This can include database backup, or updating data once a day and things like that
--------------------------------------------------------------------------------
/server/test-tools/jest.setup.ts:
--------------------------------------------------------------------------------
1 | const nativeConsoleError = global.console.warn;
2 |
3 | global.console.warn = (...args) => {
4 | if (args.join('').includes('Using Unsupported mongoose version')) {
5 | return;
6 | }
7 | return nativeConsoleError(...args);
8 | }
9 |
--------------------------------------------------------------------------------
/server/src/filters/kb-not-found-exception.filter.spec.ts:
--------------------------------------------------------------------------------
1 | import { KbNotFoundExceptionFilter } from '@kb-filters';
2 |
3 | describe('NotFoundExceptionFilterFilter', () => {
4 | it('should be defined', () => {
5 | expect(new KbNotFoundExceptionFilter('')).toBeDefined();
6 | });
7 | });
8 |
--------------------------------------------------------------------------------
/server/src/filters/kb-validation-exception.filter.spec.ts:
--------------------------------------------------------------------------------
1 | import { KbValidationExceptionFilter } from '@kb-filters';
2 |
3 | describe('KbValidationExceptionFilter', () => {
4 | it('should be defined', () => {
5 | expect(new KbValidationExceptionFilter()).toBeDefined();
6 | });
7 | });
8 |
--------------------------------------------------------------------------------
/tools/readme.md:
--------------------------------------------------------------------------------
1 | ## APPLICATION TOOLS
2 |
3 | This folder contains tools that can be used while developing.
4 |
5 | The most obvious one is the script to initialize this template.
6 | But you also have things like `prune-untrackted-branches` to delete branches
7 | that already appeared in the cloud and got deleted.
--------------------------------------------------------------------------------
/server/src/api/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @file Automatically generated by barrelsby.
3 | */
4 |
5 | export * from './api.controller';
6 | export * from './api.module';
7 | export * from './product/product.controller';
8 | export * from './product/product.module';
9 | export * from './product/product.service';
10 |
--------------------------------------------------------------------------------
/client/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 | kibibit Client Side
3 |
4 |
5 |
6 |
Start Writing your app!
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/server/src/abstracts/readme.md:
--------------------------------------------------------------------------------
1 | ## ABSTRACTS
2 |
3 | Contains all the abstracts of this project. Usually these are common parts of the application that repeat a lot (like services for database items or the database items themselves).
4 |
5 | Currently, this contain an abstract base service for DB items, and a base DB model to define new models based on.
--------------------------------------------------------------------------------
/client/src/app/app-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { RouterModule, Routes } from '@angular/router';
3 |
4 | const routes: Routes = [];
5 |
6 | @NgModule({
7 | imports: [RouterModule.forRoot(routes, { relativeLinkResolution: 'legacy' })],
8 | exports: [RouterModule]
9 | })
10 | export class AppRoutingModule { }
11 |
--------------------------------------------------------------------------------
/client/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see https://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.ts]
12 | quote_type = single
13 |
14 | [*.md]
15 | max_line_length = off
16 | trim_trailing_whitespace = false
17 |
--------------------------------------------------------------------------------
/client/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "./tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "./out-tsc/app",
6 | "types": []
7 | },
8 | "files": [
9 | "src/main.ts",
10 | "src/polyfills.ts"
11 | ],
12 | "include": [
13 | "src/**/*.d.ts"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/client/e2e/src/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, by, element } from 'protractor';
2 |
3 | export class AppPage {
4 | navigateTo(): Promise {
5 | return browser.get(browser.baseUrl) as Promise;
6 | }
7 |
8 | getTitleText(): Promise {
9 | return element(by.css('app-root #site-name'))
10 | .getText() as Promise;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/client/e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "../tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "../out-tsc/e2e",
6 | "module": "commonjs",
7 | "target": "es2018",
8 | "types": [
9 | "jasmine",
10 | "jasminewd2",
11 | "node"
12 | ]
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/server/src/decorators/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @file Automatically generated by barrelsby.
3 | */
4 |
5 | export * from './get-all.decorator';
6 | export * from './get-one.decorator';
7 | export * from './kb-api-validation-error-response.decorator';
8 | export * from './kb-delete.decorator';
9 | export * from './kb-patch.decorator';
10 | export * from './kb-post.decorator';
11 | export * from './kb-put.decorator';
12 |
--------------------------------------------------------------------------------
/commitlint.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [ '@commitlint/config-angular' ],
3 | rules: {
4 | 'type-enum': [
5 | 2,
6 | 'always', [
7 | 'build',
8 | 'chore',
9 | 'ci',
10 | 'docs',
11 | 'feat',
12 | 'fix',
13 | 'perf',
14 | 'refactor',
15 | 'revert',
16 | 'style',
17 | 'test'
18 | ]
19 | ]
20 | }
21 | };
--------------------------------------------------------------------------------
/server/src/tasks/tasks.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, Logger } from '@nestjs/common';
2 | import { Cron } from '@nestjs/schedule';
3 |
4 | @Injectable()
5 | export class TasksService {
6 | private readonly logger = new Logger(TasksService.name);
7 |
8 | /** Run every minute */
9 | @Cron('* * * * *')
10 | handleCron() {
11 | this.logger.debug('Scheduled task example: Called once every minute');
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/client/src/main.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from '@angular/core';
2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3 |
4 | import { AppModule } from './app/app.module';
5 | import { environment } from './environments/environment';
6 |
7 | if (environment.production) {
8 | enableProdMode();
9 | }
10 |
11 | platformBrowserDynamic().bootstrapModule(AppModule)
12 | .catch(err => console.error(err));
13 |
--------------------------------------------------------------------------------
/client/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "./tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "./out-tsc/spec",
6 | "types": [
7 | "jasmine"
8 | ]
9 | },
10 | "files": [
11 | "src/test.ts",
12 | "src/polyfills.ts"
13 | ],
14 | "include": [
15 | "src/**/*.spec.ts",
16 | "src/**/*.d.ts"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/server/src/api/api.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { MongooseModule } from '@nestjs/mongoose';
3 |
4 | import { ApiController } from './api.controller';
5 | import { ProductModule } from './product/product.module';
6 |
7 | @Module({
8 | controllers: [ApiController],
9 | imports: [
10 | MongooseModule.forRoot('mongodb://localhost:27017'),
11 | ProductModule
12 | ]
13 | })
14 | export class ApiModule {}
15 |
--------------------------------------------------------------------------------
/server/src/decorators/kb-api-validation-error-response.decorator.ts:
--------------------------------------------------------------------------------
1 | import { applyDecorators } from '@nestjs/common';
2 | import { ApiResponse } from '@nestjs/swagger';
3 |
4 | import { PublicError } from '@kb-models';
5 |
6 | export const KbApiValidateErrorResponse = () => {
7 | return applyDecorators(
8 | ApiResponse({
9 | description: 'Some validation error as accured on the given model',
10 | status: 405,
11 | type: PublicError
12 | })
13 | );
14 | };
15 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "rbbit.typescript-hero",
4 | "coenraads.bracket-pair-colorizer",
5 | "wix.vscode-import-cost",
6 | "orta.vscode-jest",
7 | "dbaeumer.vscode-eslint",
8 | "actboy168.tasks",
9 | "johnpapa.vscode-peacock",
10 | "angular.ng-template",
11 | "abhijoybasak.nestjs-files",
12 | "eamodio.gitlens",
13 | "codeandstuff.package-json-upgrade",
14 | "mongodb.mongodb-vscode",
15 | "ms-azuretools.vscode-docker",
16 | "jock.svg"
17 | ]
18 | }
--------------------------------------------------------------------------------
/client/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { webConsolelogo } from '@kibibit/consologo';
3 |
4 | @Component({
5 | selector: 'app-root',
6 | templateUrl: './app.component.html',
7 | styleUrls: ['./app.component.scss']
8 | })
9 | export class AppComponent {
10 | title = 'kibibit client';
11 |
12 | constructor() {
13 | webConsolelogo('kibibit client template', [
14 | 'kibibit server-client template',
15 | 'change this up in app.component.ts'
16 | ]);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: "Build"
2 |
3 | on:
4 | pull_request:
5 | push:
6 | branches:
7 | - master
8 | - next
9 |
10 | jobs:
11 | build:
12 | name: Build Production
13 | runs-on: ubuntu-latest
14 | steps:
15 | - name: Checkout commit
16 | uses: actions/checkout@v2
17 | - name: Setup Node.js
18 | uses: actions/setup-node@v1
19 | with:
20 | node-version: 12
21 | - name: Build
22 | run: |
23 | npm install
24 | npm run build
--------------------------------------------------------------------------------
/server/test/__snapshots__/app.e2e-spec.ts.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`AppController (e2e) /api (GET) API Information 1`] = `
4 | Object {
5 | "author": "thatkookooguy ",
6 | "bugs": "https://github.com/Kibibit/kb-server-client-template/issues",
7 | "description": "",
8 | "license": "MIT",
9 | "name": "@kibibit/kb-server-client-template",
10 | "repository": "https://github.com/Kibibit/kb-server-client-template.git",
11 | "version": "0.0.0-development",
12 | }
13 | `;
14 |
--------------------------------------------------------------------------------
/server/src/tasks/tasks.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 |
3 | import { TasksService } from '@kb-tasks';
4 |
5 | describe('TasksService', () => {
6 | let service: TasksService;
7 |
8 | beforeEach(async () => {
9 | const module: TestingModule = await Test.createTestingModule({
10 | providers: [TasksService]
11 | }).compile();
12 |
13 | service = module.get(TasksService);
14 | });
15 |
16 | it('should be defined', () => {
17 | expect(service).toBeDefined();
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/server/src/events/events.gateway.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 |
3 | import { EventsGateway } from '@kb-events';
4 |
5 | describe('EventsGateway', () => {
6 | let gateway: EventsGateway;
7 |
8 | beforeEach(async () => {
9 | const module: TestingModule = await Test.createTestingModule({
10 | providers: [EventsGateway]
11 | }).compile();
12 |
13 | gateway = module.get(EventsGateway);
14 | });
15 |
16 | it('should be defined', () => {
17 | expect(gateway).toBeDefined();
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/server/src/models/api.model.ts:
--------------------------------------------------------------------------------
1 | import { ApiProperty } from '@nestjs/swagger';
2 |
3 | export class ApiInfo {
4 | @ApiProperty()
5 | name: string;
6 |
7 | @ApiProperty()
8 | description: string;
9 |
10 | @ApiProperty()
11 | version: string;
12 |
13 | @ApiProperty()
14 | license: string;
15 |
16 | @ApiProperty()
17 | repository: string;
18 |
19 | @ApiProperty()
20 | author: string;
21 |
22 | @ApiProperty()
23 | bugs: string;
24 |
25 | constructor(partial: Partial = {}) {
26 | Object.assign(this, partial);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/server/src/api/product/product.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { MongooseModule } from '@nestjs/mongoose';
3 |
4 | import { Product } from '../../models/product.model';
5 | import { ProductController } from './product.controller';
6 | import { ProductService } from './product.service';
7 |
8 | @Module({
9 | imports: [
10 | MongooseModule.forFeature([
11 | { name: Product.modelName, schema: Product.schema }
12 | ])
13 | ],
14 | providers: [ProductService],
15 | controllers: [ProductController]
16 | })
17 | export class ProductModule {}
18 |
--------------------------------------------------------------------------------
/server/src/decorators/get-all.decorator.ts:
--------------------------------------------------------------------------------
1 | import {
2 | applyDecorators,
3 | ClassSerializerInterceptor,
4 | Get,
5 | UseInterceptors
6 | } from '@nestjs/common';
7 | import { ApiOkResponse, ApiOperation } from '@nestjs/swagger';
8 |
9 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
10 | export function GetAll(model: any, path?: string | string[]) {
11 | return applyDecorators(
12 | Get(path),
13 | ApiOperation({ summary: `Get all ${ model.name }` }),
14 | ApiOkResponse({ description: `Return a list of all ${ model.name }s` }),
15 | UseInterceptors(ClassSerializerInterceptor)
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/server/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { ScheduleModule } from '@nestjs/schedule';
3 |
4 | import { ApiModule } from '@kb-api';
5 | import { EventsGateway, EventsModule } from '@kb-events';
6 | import { TasksService } from '@kb-tasks';
7 |
8 | import { AppController } from './app.controller';
9 | import { AppService } from './app.service';
10 |
11 | @Module({
12 | imports: [
13 | ApiModule,
14 | ScheduleModule.forRoot(),
15 | EventsModule
16 | ],
17 | controllers: [AppController],
18 | providers: [AppService, TasksService, EventsGateway]
19 | })
20 | export class AppModule {}
21 |
--------------------------------------------------------------------------------
/client/tsconfig.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "compileOnSave": false,
4 | "compilerOptions": {
5 | "baseUrl": "./",
6 | "outDir": "./dist/out-tsc",
7 | "sourceMap": true,
8 | "declaration": false,
9 | "downlevelIteration": true,
10 | "experimentalDecorators": true,
11 | "moduleResolution": "node",
12 | "importHelpers": true,
13 | "target": "es2015",
14 | "module": "es2020",
15 | "lib": [
16 | "es2018",
17 | "dom"
18 | ]
19 | },
20 | "angularCompilerOptions": {
21 | "disableTypeScriptVersionCheck": true
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/server/src/models/public-error.model.ts:
--------------------------------------------------------------------------------
1 | import { HttpStatus } from '@nestjs/common';
2 | import { ApiProperty } from '@nestjs/swagger';
3 |
4 | export class PublicError {
5 | @ApiProperty()
6 | statusCode: HttpStatus;
7 |
8 | // toISOString formatted Date
9 | @ApiProperty()
10 | timestamp: string;
11 |
12 | @ApiProperty()
13 | path: string;
14 |
15 | @ApiProperty()
16 | name: string;
17 |
18 | @ApiProperty({
19 | oneOf: [{ type: 'string' }, { type: '[string]' }]
20 | })
21 | error: string | string[];
22 |
23 | constructor(partial: Partial = {}) {
24 | Object.assign(this, partial);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/server/src/api/api.controller.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 |
3 | import { ApiController } from '@kb-api';
4 |
5 | describe('ApiController', () => {
6 | let controller: ApiController;
7 |
8 | beforeEach(async () => {
9 | const module: TestingModule = await Test.createTestingModule({
10 | controllers: [ApiController]
11 | }).compile();
12 |
13 | controller = module.get(ApiController);
14 | });
15 |
16 | it('should be defined', () => {
17 | expect(controller).toBeDefined();
18 | });
19 |
20 | it.skip('should return package.json object', () => {
21 | expect(controller.getAPI()).toBe('API');
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/server/src/app/app.controller.ts:
--------------------------------------------------------------------------------
1 | import { Controller, Get, Res } from '@nestjs/common';
2 | import { ApiOkResponse, ApiOperation } from '@nestjs/swagger';
3 | import { Response } from 'express';
4 | import { join } from 'path';
5 |
6 | import { AppService } from './app.service';
7 |
8 | @Controller()
9 | export class AppController {
10 | constructor(private readonly appService: AppService) {}
11 |
12 | @Get()
13 | @ApiOperation({ summary: 'Get Web Client (HTML)' })
14 | @ApiOkResponse({
15 | description: 'Returns the Web Client\'s HTML File'
16 | })
17 | sendWebClient(@Res() res: Response): void {
18 | res.sendFile(join(this.appService.appRoot, '/dist/client/index.html'));
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/client/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
3 | // The list of file replacements can be found in `angular.json`.
4 |
5 | export const environment = {
6 | production: false
7 | };
8 |
9 | /*
10 | * For easier debugging in development mode, you can import the following file
11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
12 | *
13 | * This import should be commented out in production mode because it will have a negative impact
14 | * on performance if an error is thrown.
15 | */
16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI.
17 |
--------------------------------------------------------------------------------
/client/e2e/src/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { browser, logging } from 'protractor';
2 |
3 | import { AppPage } from './app.po';
4 |
5 | describe('workspace-project App', () => {
6 | let page: AppPage;
7 |
8 | beforeEach(() => {
9 | page = new AppPage();
10 | });
11 |
12 | it('should display welcome message', () => {
13 | page.navigateTo();
14 | expect(page.getTitleText()).toEqual('kibibit Client Side');
15 | });
16 |
17 | afterEach(async () => {
18 | // Assert that there are no errors emitted from the browser
19 | const logs = await browser.manage().logs().get(logging.Type.BROWSER);
20 | expect(logs).not.toContain(jasmine.objectContaining({
21 | level: logging.Level.SEVERE
22 | } as logging.Entry));
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/server/src/app/app.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 | import findRoot from 'find-root';
3 | import { pathExistsSync, readJSONSync } from 'fs-extra';
4 | import { join } from 'path';
5 |
6 | @Injectable()
7 | export class AppService {
8 | appRoot = findRoot(__dirname, (dir) => {
9 | const packagePath = join(dir, 'package.json');
10 | const isPackageJsonExists = pathExistsSync(packagePath);
11 |
12 | if (isPackageJsonExists) {
13 | const packageContent = readJSONSync(packagePath, { encoding: 'utf8' });
14 | if (['server', 'client'].indexOf(packageContent.name) < 0) {
15 | return true;
16 | }
17 | }
18 |
19 | return false;
20 | })
21 |
22 | getHello(): string {
23 | return 'Hello World!';
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 | on:
3 | push:
4 | branches:
5 | - dummy
6 | # - master
7 | # - next
8 | jobs:
9 | release:
10 | name: Release
11 | runs-on: ubuntu-18.04
12 | steps:
13 | - name: Checkout commit
14 | uses: actions/checkout@v2
15 | with:
16 | fetch-depth: 0
17 | - name: Setup Node.js
18 | uses: actions/setup-node@v1
19 | with:
20 | node-version: 12
21 | - name: Install dependencies
22 | run: |
23 | npm ci
24 | npm install
25 | npm run build
26 | - name: Release
27 | env:
28 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
29 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
30 | run: npm run semantic-release
--------------------------------------------------------------------------------
/client/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
2 | import { BrowserModule } from '@angular/platform-browser';
3 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
4 |
5 | import {
6 | AngularMaterialModule
7 | } from './angular-material/angular-material.module';
8 | import { AppRoutingModule } from './app-routing.module';
9 | import { AppComponent } from './app.component';
10 |
11 | @NgModule({
12 | declarations: [
13 | AppComponent
14 | ],
15 | imports: [
16 | BrowserModule,
17 | AppRoutingModule,
18 | BrowserAnimationsModule,
19 | AngularMaterialModule
20 | ],
21 | providers: [],
22 | bootstrap: [AppComponent],
23 | schemas: [CUSTOM_ELEMENTS_SCHEMA]
24 | })
25 | export class AppModule { }
26 |
--------------------------------------------------------------------------------
/server/src/decorators/kb-post.decorator.ts:
--------------------------------------------------------------------------------
1 | import {
2 | applyDecorators,
3 | ClassSerializerInterceptor,
4 | Post,
5 | UseInterceptors
6 | } from '@nestjs/common';
7 | import { ApiCreatedResponse, ApiOperation } from '@nestjs/swagger';
8 |
9 | import { KbApiValidateErrorResponse } from '@kb-decorators';
10 |
11 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
12 | export function KbPost(type: any, path?: string | string[]) {
13 | return applyDecorators(
14 | Post(path),
15 | ApiOperation({ summary: `Create a new ${ type.name }` }),
16 | ApiCreatedResponse({
17 | description: `The ${ type.name } has been successfully created.`,
18 | type
19 | }),
20 | KbApiValidateErrorResponse(),
21 | UseInterceptors(ClassSerializerInterceptor)
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # This is a comment.
2 | # Each line is a file pattern followed by one or more owners.
3 |
4 | # These owners will be the default owners for everything in
5 | # the repo. Unless a later match takes precedence,
6 | # @global-owner1 and @global-owner2 will be requested for
7 | # review when someone opens a pull request.
8 | * @thatkookooguy @ortichon @dunaevsky @zimgil
9 |
10 | # Server Owners
11 | /server/ @thatkookooguy
12 |
13 | # Client Owners
14 | /client/ @thatkookooguy
15 |
16 | # Achievements Owners
17 | # These are the people that understand the pattern to create
18 | # and work with achievements
19 | /achievements/ @thatkookooguy @ortichon @dunaevsky @zimgil
20 |
21 | # Build\Github Actions Owners
22 | /tools/ @thatkookooguy
23 | /.github/ @thatkookooguy
24 | /.devcontainer/ @thatkookooguy
25 | /.vscode/ @thatkookooguy
26 |
--------------------------------------------------------------------------------
/client/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 | # Only exists if Bazel was run
8 | /bazel-out
9 |
10 | # dependencies
11 | /node_modules
12 |
13 | # profiling files
14 | chrome-profiler-events*.json
15 | speed-measure-plugin*.json
16 |
17 | # IDEs and editors
18 | /.idea
19 | .project
20 | .classpath
21 | .c9/
22 | *.launch
23 | .settings/
24 | *.sublime-workspace
25 |
26 | # IDE - VSCode
27 | .vscode/*
28 | !.vscode/settings.json
29 | !.vscode/tasks.json
30 | !.vscode/launch.json
31 | !.vscode/extensions.json
32 | .history/*
33 |
34 | # misc
35 | /.sass-cache
36 | /connect.lock
37 | /coverage
38 | /libpeerconnection.log
39 | npm-debug.log
40 | yarn-error.log
41 | testem.log
42 | /typings
43 |
44 | # System Files
45 | .DS_Store
46 | Thumbs.db
47 |
--------------------------------------------------------------------------------
/server/src/models/product.model.ts:
--------------------------------------------------------------------------------
1 | import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
2 | import { prop as PersistInDb } from '@typegoose/typegoose';
3 | import { Exclude, Expose } from 'class-transformer';
4 | import { IsNotEmpty } from 'class-validator';
5 |
6 | import { BaseModel } from '../abstracts/base.model.abstract';
7 |
8 | @Exclude()
9 | export class Product extends BaseModel {
10 |
11 | @Expose()
12 | @IsNotEmpty()
13 | @ApiProperty()
14 | @PersistInDb()
15 | name: string;
16 |
17 | @Expose()
18 | @ApiPropertyOptional()
19 | @PersistInDb()
20 | description: string;
21 |
22 | @Expose()
23 | @IsNotEmpty()
24 | @ApiProperty()
25 | @PersistInDb()
26 | price: number;
27 |
28 | constructor(partial: Partial = {}) {
29 | super();
30 | Object.assign(this, partial);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/client/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Client
6 |
7 |
11 |
16 |
20 |
24 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/server/src/api/product/product.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 | import { InjectModel } from '@nestjs/mongoose';
3 | import { ReturnModelType } from '@typegoose/typegoose';
4 |
5 | import { BaseService } from '../../abstracts/base.service.abstract';
6 | import { Product } from '../../models/product.model';
7 |
8 | @Injectable()
9 | export class ProductService extends BaseService {
10 | constructor(
11 | @InjectModel(Product.modelName)
12 | private readonly productModel: ReturnModelType,
13 | ) {
14 | super(productModel, Product);
15 | }
16 |
17 | async findByName(name: string): Promise {
18 | const dbProduct = await this.findOne({ name }).exec();
19 |
20 | if (!dbProduct) {
21 | return;
22 | }
23 |
24 | return new Product(dbProduct.toObject());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/server/src/decorators/get-one.decorator.ts:
--------------------------------------------------------------------------------
1 | import {
2 | applyDecorators,
3 | ClassSerializerInterceptor,
4 | Get,
5 | UseInterceptors
6 | } from '@nestjs/common';
7 | import {
8 | ApiBadRequestResponse,
9 | ApiNotFoundResponse,
10 | ApiOkResponse,
11 | ApiOperation
12 | } from '@nestjs/swagger';
13 |
14 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
15 | export function GetOne(type: any, path?: string | string[]) {
16 | return applyDecorators(
17 | Get(path),
18 | ApiOperation({ summary: `Get an existing ${ type.name }` }),
19 | ApiOkResponse({ description: `Return a single ${ type.name }`, type }),
20 | ApiNotFoundResponse({ description: `${ type.name } not found` }),
21 | ApiBadRequestResponse({ description: 'Invalid identifier supplied' }),
22 | UseInterceptors(ClassSerializerInterceptor)
23 | );
24 | }
25 |
--------------------------------------------------------------------------------
/client/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads
2 | // recursively all the .spec and framework files
3 | import 'zone.js/dist/zone-testing';
4 |
5 | import { getTestBed } from '@angular/core/testing';
6 | import {
7 | BrowserDynamicTestingModule,
8 | platformBrowserDynamicTesting
9 | } from '@angular/platform-browser-dynamic/testing';
10 |
11 |
12 | declare const require: {
13 | context(path: string, deep?: boolean, filter?: RegExp): {
14 | keys(): string[];
15 | (id: string): T;
16 | };
17 | };
18 |
19 | // First, initialize the Angular testing environment.
20 | getTestBed().initTestEnvironment(
21 | BrowserDynamicTestingModule,
22 | platformBrowserDynamicTesting()
23 | );
24 | // Then we find all the tests.
25 | const context = require.context('./', true, /\.spec\.ts$/);
26 | // And load the modules.
27 | context.keys().map(context);
28 |
--------------------------------------------------------------------------------
/server/src/decorators/kb-delete.decorator.ts:
--------------------------------------------------------------------------------
1 | import {
2 | applyDecorators,
3 | ClassSerializerInterceptor,
4 | Delete,
5 | UseInterceptors
6 | } from '@nestjs/common';
7 | import {
8 | ApiBadRequestResponse,
9 | ApiNotFoundResponse,
10 | ApiOkResponse,
11 | ApiOperation
12 | } from '@nestjs/swagger';
13 |
14 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
15 | export function KbDelete(type: any, path?: string | string[]) {
16 | return applyDecorators(
17 | Delete(path),
18 | ApiOperation({
19 | summary: `Delete an existing ${ type.name }`
20 | }),
21 | ApiOkResponse({ type: type, description: `${ type.name } deleted` }),
22 | ApiNotFoundResponse({
23 | description: `${ type.name } not found`
24 | }),
25 | ApiBadRequestResponse({ description: 'Invalid identifier supplied' }),
26 | UseInterceptors(ClassSerializerInterceptor)
27 | );
28 | }
29 |
--------------------------------------------------------------------------------
/client/.browserslistrc:
--------------------------------------------------------------------------------
1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
2 | # For additional information regarding the format and rule options, please see:
3 | # https://github.com/browserslist/browserslist#queries
4 |
5 | # For the full list of supported browsers by the Angular framework, please see:
6 | # https://angular.io/guide/browser-support
7 |
8 | # You can see what browsers were selected by your queries by running:
9 | # npx browserslist
10 |
11 | last 1 Chrome version
12 | last 1 Firefox version
13 | last 2 Edge major versions
14 | last 2 Safari major versions
15 | last 2 iOS major versions
16 | Firefox ESR
17 | not IE 9-10 # Angular support for IE 9-10 has been deprecated and will be removed as of Angular v11. To opt-in, remove the 'not' prefix on this line.
18 | not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line.
19 |
--------------------------------------------------------------------------------
/server/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "declaration": true,
5 | "removeComments": true,
6 | "emitDecoratorMetadata": true,
7 | "experimentalDecorators": true,
8 | "allowSyntheticDefaultImports": true,
9 | "target": "es2017",
10 | "sourceMap": true,
11 | "esModuleInterop": true,
12 | "outDir": "../dist/server",
13 | "baseUrl": "./",
14 | "paths": {
15 | "@kb-server": [ "src/*" ],
16 | "@kb-models": [ "src/models/index" ],
17 | "@kb-abstracts": [ "src/abstracts/index" ],
18 | "@kb-decorators": [ "src/decorators/index" ],
19 | "@kb-filters": [ "src/filters/index" ],
20 | "@kb-events": [ "src/events/index" ],
21 | "@kb-tasks": [ "src/tasks/index" ],
22 | "@kb-app": [ "src/app/index" ],
23 | "@kb-api": [ "src/api/index" ]
24 |
25 | },
26 | "incremental": true,
27 | "skipLibCheck": true
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/server/src/app/app.controller.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { mockResponse } from 'jest-mock-req-res';
3 |
4 | import { AppController, AppService } from '@kb-app';
5 |
6 | describe('AppController', () => {
7 | let appController: AppController;
8 |
9 | beforeEach(async () => {
10 | const app: TestingModule = await Test.createTestingModule({
11 | controllers: [AppController],
12 | providers: [AppService]
13 | }).compile();
14 |
15 | appController = app.get(AppController);
16 | });
17 |
18 | describe('root', () => {
19 | it('should return an HTML page', async () => {
20 | const mocRes = mockResponse();
21 | appController.sendWebClient(mocRes);
22 | expect(mocRes.sendFile.mock.calls.length).toBe(1);
23 | const param = mocRes.sendFile.mock.calls[0][0] as string;
24 | expect(param.endsWith('dist/client/index.html')).toBeTruthy();
25 | });
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/server/src/filters/kb-not-found-exception.filter.ts:
--------------------------------------------------------------------------------
1 | import {
2 | ArgumentsHost,
3 | Catch,
4 | ExceptionFilter,
5 | NotFoundException
6 | } from '@nestjs/common';
7 | import { join } from 'path';
8 |
9 | @Catch(NotFoundException)
10 | export class KbNotFoundExceptionFilter implements ExceptionFilter {
11 | constructor(private readonly appRoot: string) {}
12 |
13 | catch(exception: NotFoundException, host: ArgumentsHost) {
14 | const ctx = host.switchToHttp();
15 | const response = ctx.getResponse();
16 | const request = ctx.getRequest();
17 | const path: string = request.path;
18 |
19 | if (path.startsWith('/api/')) {
20 | response.status(exception.getStatus()).json({
21 | statusCode: exception.getStatus(),
22 | name: exception.name,
23 | error: exception.message
24 | });
25 |
26 | return;
27 | }
28 |
29 | response.sendFile(
30 | join(this.appRoot, './dist/client/index.html')
31 | );
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/.all-contributorsrc:
--------------------------------------------------------------------------------
1 | {
2 | "projectName": "kb-server-client-template",
3 | "projectOwner": "Kibibit",
4 | "repoType": "github",
5 | "repoHost": "https://github.com",
6 | "files": [
7 | "README.md"
8 | ],
9 | "badgeTemplate": "
-orange.svg?style=flat-square\" alt=\"All Contributors\">",
10 | "imageSize": 100,
11 | "commit": false,
12 | "commitConvention": "angular",
13 | "contributors": [
14 | {
15 | "login": "Thatkookooguy",
16 | "name": "Neil Kalman",
17 | "avatar_url": "https://avatars3.githubusercontent.com/u/10427304?v=4",
18 | "profile": "http://thatkookooguy.kibibit.io/",
19 | "contributions": [
20 | "code",
21 | "doc",
22 | "design",
23 | "maintenance",
24 | "infra",
25 | "test"
26 | ]
27 | }
28 | ],
29 | "contributorsPerLine": 7,
30 | "skipCi": true
31 | }
32 |
--------------------------------------------------------------------------------
/.github/workflows/e2e-tests.yml:
--------------------------------------------------------------------------------
1 | name: E2E Tests
2 | on:
3 | workflow_dispatch:
4 | pull_request:
5 | paths:
6 | - 'client/**'
7 | push:
8 | branches:
9 | - master
10 | - next
11 | jobs:
12 | unittest:
13 | name: E2E Tests
14 | runs-on: ubuntu-18.04
15 | steps:
16 | - name: Checkout Commit
17 | uses: actions/checkout@v2
18 | with:
19 | fetch-depth: 0
20 | - name: Setup Node.js
21 | uses: actions/setup-node@v1
22 | with:
23 | node-version: 12
24 | - name: Start MongoDB
25 | uses: supercharge/mongodb-github-action@1.3.0
26 | with:
27 | mongodb-version: 4.2
28 | - name: Install Dependencies
29 | run: |
30 | npm ci
31 | npm install
32 | - name: Build
33 | run: npm run build --if-present
34 | - name: Run E2E Tests
35 | env:
36 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
37 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
38 | run: npm run test:e2e
--------------------------------------------------------------------------------
/server/src/decorators/kb-put.decorator.ts:
--------------------------------------------------------------------------------
1 | import {
2 | applyDecorators,
3 | ClassSerializerInterceptor,
4 | Put,
5 | UseInterceptors
6 | } from '@nestjs/common';
7 | import {
8 | ApiBadRequestResponse,
9 | ApiNotFoundResponse,
10 | ApiOkResponse,
11 | ApiOperation
12 | } from '@nestjs/swagger';
13 |
14 | import { KbApiValidateErrorResponse } from '@kb-decorators';
15 |
16 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
17 | export function KbPut(type: any, path?: string | string[]) {
18 | return applyDecorators(
19 | Put(path),
20 | ApiOperation({
21 | summary: `Update an existing ${ type.name }`,
22 | description: `Expects a full ${ type.name }`
23 | }),
24 | ApiOkResponse({ type: type, description: `${ type.name } updated` }),
25 | ApiNotFoundResponse({
26 | description: `${ type.name } not found`
27 | }),
28 | ApiBadRequestResponse({ description: 'Invalid identifier supplied' }),
29 | KbApiValidateErrorResponse(),
30 | UseInterceptors(ClassSerializerInterceptor)
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/server/src/decorators/kb-patch.decorator.ts:
--------------------------------------------------------------------------------
1 | import {
2 | applyDecorators,
3 | ClassSerializerInterceptor,
4 | Patch,
5 | UseInterceptors
6 | } from '@nestjs/common';
7 | import {
8 | ApiBadRequestResponse,
9 | ApiNotFoundResponse,
10 | ApiOkResponse,
11 | ApiOperation
12 | } from '@nestjs/swagger';
13 |
14 | import { KbApiValidateErrorResponse } from '@kb-decorators';
15 |
16 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
17 | export function KbPatch(type: any, path?: string | string[]) {
18 | return applyDecorators(
19 | Patch(path),
20 | ApiOperation({
21 | summary: `Update an existing ${ type.name }`,
22 | description: `Expects a partial ${ type.name }`
23 | }),
24 | ApiOkResponse({ type: type, description: `${ type.name } updated` }),
25 | ApiNotFoundResponse({
26 | description: `${ type.name } not found`
27 | }),
28 | ApiBadRequestResponse({ description: 'Invalid identifier supplied' }),
29 | KbApiValidateErrorResponse(),
30 | UseInterceptors(ClassSerializerInterceptor)
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/server/src/filters/kb-validation-exception.filter.ts:
--------------------------------------------------------------------------------
1 | import {
2 | ArgumentsHost,
3 | BadRequestException,
4 | Catch,
5 | ExceptionFilter,
6 | HttpStatus
7 | } from '@nestjs/common';
8 |
9 | import { PublicError } from '@kb-models';
10 |
11 | @Catch(BadRequestException)
12 | export class KbValidationExceptionFilter implements ExceptionFilter {
13 | catch(exception: BadRequestException, host: ArgumentsHost) {
14 | console.error(exception);
15 | const ctx = host.switchToHttp();
16 | const response = ctx.getResponse();
17 | const request = ctx.getRequest();
18 |
19 | response
20 | .status(HttpStatus.METHOD_NOT_ALLOWED)
21 | // you can manipulate the response here
22 | .json(new PublicError({
23 | statusCode: HttpStatus.METHOD_NOT_ALLOWED,
24 | timestamp: new Date().toISOString(),
25 | path: request.url,
26 | name: 'BadRequestException',
27 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
28 | error: (exception.getResponse() as any).message as string[]
29 | }));
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/client/README.md:
--------------------------------------------------------------------------------
1 | # Client
2 |
3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 10.2.0.
4 |
5 | ## Development server
6 |
7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
8 |
9 | ## Code scaffolding
10 |
11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
12 |
13 | ## Build
14 |
15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
16 |
17 | ## Running unit tests
18 |
19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
20 |
21 | ## Running end-to-end tests
22 |
23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
24 |
25 | ## Further help
26 |
27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
28 |
--------------------------------------------------------------------------------
/server/src/bootstrap-application.ts:
--------------------------------------------------------------------------------
1 | import { terminalConsoleLogo } from '@kibibit/consologo';
2 | import { ValidationPipe } from '@nestjs/common';
3 | import { NestFactory } from '@nestjs/core';
4 | import { NestExpressApplication } from '@nestjs/platform-express';
5 | import { WsAdapter } from '@nestjs/platform-ws';
6 | import { join } from 'path';
7 |
8 | import { AppModule, AppService } from '@kb-app';
9 | import { KbNotFoundExceptionFilter } from '@kb-filters';
10 |
11 | import { Swagger } from './swagger';
12 |
13 | const appRoot = new AppService().appRoot;
14 |
15 | export async function bootstrap(): Promise {
16 | terminalConsoleLogo('kibibit server template', [
17 | 'change this in server/src/bootstrap-application.ts'
18 | ]);
19 | const app = await NestFactory.create(AppModule);
20 | app.useWebSocketAdapter(new WsAdapter(app))
21 | app.useGlobalFilters(new KbNotFoundExceptionFilter(appRoot));
22 | app.useGlobalPipes(new ValidationPipe());
23 | app.useStaticAssets(join(appRoot, './dist/client'));
24 |
25 | await Swagger.addSwagger(app);
26 |
27 | return app;
28 | }
29 |
--------------------------------------------------------------------------------
/server/test/utils.ts:
--------------------------------------------------------------------------------
1 | import { INestApplication } from '@nestjs/common/interfaces';
2 | import { TestingModule } from '@nestjs/testing/testing-module';
3 | import * as bodyParser from 'body-parser';
4 | import express from 'express';
5 |
6 | import { SocketService } from './socket.service';
7 |
8 | export class Utils {
9 | public static socket: SocketService;
10 | private static server: express.Express;
11 | private static app: INestApplication;
12 | private static module: TestingModule;
13 |
14 | public static async startServer(testingModule: TestingModule) {
15 | this.module = testingModule;
16 | this.server = express();
17 | this.server.use(bodyParser.json());
18 | this.app = await testingModule.createNestApplication();
19 | await this.app.init();
20 | }
21 |
22 | public static async createSocket(defer = false) {
23 | await this.app.listen(10109);
24 | this.socket = new SocketService(defer);
25 |
26 | return this.socket;
27 | }
28 | public static async closeApp() {
29 | this.socket.close();
30 | await this.app.close();
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/client/e2e/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | // Protractor configuration file, see link for more information
3 | // https://github.com/angular/protractor/blob/master/lib/config.ts
4 |
5 | const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter');
6 |
7 | /**
8 | * @type { import("protractor").Config }
9 | */
10 | exports.config = {
11 | allScriptsTimeout: 11000,
12 | specs: [
13 | './src/**/*.e2e-spec.ts'
14 | ],
15 | capabilities: {
16 | browserName: 'chrome',
17 | chromeOptions: {
18 | args: [ '--headless', '--disable-gpu', '--window-size=800,600', '--log-level=3', '--no-sandbox' ]
19 | }
20 | },
21 | directConnect: true,
22 | baseUrl: 'http://localhost:10101/',
23 | framework: 'jasmine',
24 | jasmineNodeOpts: {
25 | showColors: true,
26 | defaultTimeoutInterval: 30000,
27 | print: function() {}
28 | },
29 | onPrepare() {
30 | require('ts-node').register({
31 | project: require('path').join(__dirname, './tsconfig.json')
32 | });
33 | jasmine.getEnv().addReporter(new SpecReporter({
34 | spec: {
35 | displayStacktrace: StacktraceOption.PRETTY
36 | }
37 | }));
38 | }
39 | };
--------------------------------------------------------------------------------
/server/test/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { INestApplication } from '@nestjs/common';
2 | import { Test, TestingModule } from '@nestjs/testing';
3 | import request from 'supertest';
4 |
5 | import { AppModule } from '@kb-app';
6 |
7 | describe('AppController (e2e)', () => {
8 | let app: INestApplication;
9 | let server;
10 |
11 | beforeEach(async () => {
12 | const moduleFixture: TestingModule = await Test.createTestingModule({
13 | imports: [AppModule]
14 | }).compile();
15 |
16 | app = moduleFixture.createNestApplication();
17 | await app.init();
18 | server = app.getHttpServer();
19 | });
20 |
21 | afterEach(async() => {
22 | await app.close();
23 | });
24 |
25 | test('/api (GET) API Information', async () => {
26 | const response = await request(server).get('/api');
27 | expect(response.status).toBe(200);
28 | expect(response.body).toMatchSnapshot();
29 | });
30 |
31 | test('/ (GET) HTML of client application', async () => {
32 | const response = await request(server).get('/');
33 | // console.log(response.body);
34 | expect(response.status).toBe(200);
35 | expect(response.type).toMatch(/html/);
36 | });
37 | });
38 |
--------------------------------------------------------------------------------
/server/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: '@typescript-eslint/parser',
3 | parserOptions: {
4 | project: 'tsconfig.json',
5 | sourceType: 'module',
6 | },
7 | plugins: ['@typescript-eslint/eslint-plugin'],
8 | extends: [
9 | 'plugin:@typescript-eslint/eslint-recommended',
10 | 'plugin:@typescript-eslint/recommended',
11 | ],
12 | ignorePatterns: [ '.eslintrc.js' ],
13 | root: true,
14 | env: {
15 | node: true,
16 | jest: true,
17 | },
18 | rules: {
19 | 'eol-last': [ 2, 'windows' ],
20 | 'comma-dangle': [ 'error', 'never' ],
21 | 'max-len': [ 'error', { 'code': 80, "ignoreComments": true } ],
22 | 'quotes': ["error", "single"],
23 | '@typescript-eslint/no-empty-interface': 'error',
24 | '@typescript-eslint/member-delimiter-style': 'error',
25 | '@typescript-eslint/explicit-function-return-type': 'off',
26 | '@typescript-eslint/explicit-module-boundary-types': 'off',
27 | '@typescript-eslint/naming-convention': [
28 | "error",
29 | {
30 | "selector": "interface",
31 | "format": ["PascalCase"],
32 | "custom": {
33 | "regex": "^I[A-Z]",
34 | "match": true
35 | }
36 | }
37 | ]
38 | }
39 | };
40 |
--------------------------------------------------------------------------------
/.github/workflows/api-tests.yml:
--------------------------------------------------------------------------------
1 | name: API Tests
2 | on:
3 | workflow_dispatch:
4 | pull_request:
5 | paths:
6 | - 'server/**'
7 | push:
8 | branches:
9 | - master
10 | - next
11 | jobs:
12 | unittest:
13 | name: API Tests
14 | runs-on: ubuntu-18.04
15 | steps:
16 | - name: Checkout Commit
17 | uses: actions/checkout@v2
18 | with:
19 | fetch-depth: 0
20 | - name: Setup Node.js
21 | uses: actions/setup-node@v1
22 | with:
23 | node-version: 12
24 | - name: Start MongoDB
25 | uses: supercharge/mongodb-github-action@1.3.0
26 | with:
27 | mongodb-version: 4.2
28 | - name: Install Dependencies
29 | run: |
30 | npm ci
31 | npm install
32 | - name: Build
33 | run: npm run build --if-present
34 | - name: Run API Tests
35 | env:
36 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
37 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
38 | run: npm run test:api
39 | - name: Archive test results & coverage
40 | uses: actions/upload-artifact@v2
41 | with:
42 | name: server-unit-tests-code-coverage-report
43 | path: test-results/api
--------------------------------------------------------------------------------
/server/test/jest-e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "moduleFileExtensions": ["js", "json", "ts"],
3 | "rootDir": ".",
4 | "testEnvironment": "node",
5 | "testRegex": ".e2e-spec.ts$",
6 | "transform": {
7 | "^.+\\.(t|j)s$": "ts-jest"
8 | },
9 | "setupFilesAfterEnv": [
10 | "../test-tools/jest.setup.ts"
11 | ],
12 | "coverageDirectory": "../../test-results/api/coverage",
13 | "reporters": [
14 | "default",
15 | [
16 | "jest-stare",
17 | {
18 | "resultDir": "../test-results/api",
19 | "reportTitle": "jest-stare!",
20 | "additionalResultsProcessors": [
21 | "jest-junit"
22 | ],
23 | "coverageLink": "./coverage/lcov-report/index.html"
24 | }
25 | ]
26 | ],
27 | "moduleNameMapper": {
28 | "^@kb-server$": "/../src",
29 | "^@kb-models$": "/../src/models/index",
30 | "^@kb-abstracts$": "/../src/abstracts/index",
31 | "^@kb-decorators$": "/../src/decorators/index",
32 | "^@kb-filters$": "/../src/filters/index",
33 | "^@kb-api$": "/../src/api/index",
34 | "^@kb-events$": "/../src/events/index",
35 | "^@kb-app$": "/../src/app/index",
36 | "^@kb-tasks": "/../src/tasks/index"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/.github/workflows/server-unit-tests.yml:
--------------------------------------------------------------------------------
1 | name: Server Unit Tests
2 | on:
3 | workflow_dispatch:
4 | pull_request:
5 | paths:
6 | - 'server/**'
7 | push:
8 | paths:
9 | - 'server/**'
10 |
11 | jobs:
12 | serverunittest:
13 | name: Server Unit Tests
14 | runs-on: ubuntu-18.04
15 | steps:
16 | - name: Checkout Commit
17 | uses: actions/checkout@v2
18 | with:
19 | fetch-depth: 0
20 | - name: Setup Node.js
21 | uses: actions/setup-node@v1
22 | with:
23 | node-version: 12
24 | - name: Install Dependencies
25 | run: |
26 | npm ci
27 | npm install
28 | - name: Build
29 | run: npm run build --if-present
30 | - name: Run Server Tests
31 | env:
32 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
33 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
34 | run: npm run test:server-unit
35 | - name: Archive test results & coverage
36 | uses: actions/upload-artifact@v2
37 | with:
38 | name: server-unit-tests-code-coverage-report
39 | path: test-results/server
40 | # - name: Code Coverage Report
41 | # uses: romeovs/lcov-reporter-action@v0.2.16
42 | # with:
43 | # github-token: ${{ secrets.GITHUB_TOKEN }}
44 | # lcov-file: coverage/server/lcov.info
--------------------------------------------------------------------------------
/.github/workflows/client-unit-tests.yml:
--------------------------------------------------------------------------------
1 | name: Client Unit Tests
2 | on:
3 | workflow_dispatch:
4 | pull_request:
5 | paths:
6 | - 'client/**'
7 | push:
8 | paths:
9 | - 'client/**'
10 |
11 | jobs:
12 | clientunittest:
13 | name: Client Unit Tests
14 | runs-on: ubuntu-18.04
15 | steps:
16 | - name: Checkout Commit
17 | uses: actions/checkout@v2
18 | with:
19 | fetch-depth: 0
20 | - name: Setup Node.js
21 | uses: actions/setup-node@v1
22 | with:
23 | node-version: 12
24 | - name: Install Dependencies
25 | run: |
26 | npm ci
27 | npm install
28 | - name: Build
29 | run: npm run build --if-present
30 | - name: Run Client Tests
31 | env:
32 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
33 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
34 | run: npm run test:client-unit
35 | - name: Archive test Results & Coverage
36 | uses: actions/upload-artifact@v2
37 | with:
38 | name: server-unit-tests-code-coverage-report
39 | path: test-results/client
40 | # - name: Code Coverage Report
41 | # uses: romeovs/lcov-reporter-action@v0.2.16
42 | # with:
43 | # github-token: ${{ secrets.GITHUB_TOKEN }}
44 | # lcov-file: coverage/client/report-html/lcov.info
--------------------------------------------------------------------------------
/client/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed } from '@angular/core/testing';
2 | import { RouterTestingModule } from '@angular/router/testing';
3 |
4 | import {
5 | AngularMaterialModule
6 | } from './angular-material/angular-material.module';
7 | import { AppComponent } from './app.component';
8 |
9 | describe('AppComponent', () => {
10 | beforeEach(async () => {
11 | await TestBed.configureTestingModule({
12 | imports: [
13 | RouterTestingModule,
14 | AngularMaterialModule
15 | ],
16 | declarations: [
17 | AppComponent
18 | ]
19 | }).compileComponents();
20 | });
21 |
22 | it('should create the app', () => {
23 | const fixture = TestBed.createComponent(AppComponent);
24 | const app = fixture.componentInstance;
25 | expect(app).toBeTruthy();
26 | });
27 |
28 | it(`should have as title 'kibibit client'`, () => {
29 | const fixture = TestBed.createComponent(AppComponent);
30 | const app = fixture.componentInstance;
31 | expect(app.title).toEqual('kibibit client');
32 | });
33 |
34 | it('should render title', () => {
35 | const fixture = TestBed.createComponent(AppComponent);
36 | fixture.detectChanges();
37 | const compiled = fixture.nativeElement;
38 | expect(compiled.querySelector('#site-name').textContent)
39 | .toContain('kibibit Client Side');
40 | });
41 | });
42 |
--------------------------------------------------------------------------------
/tools/scripts/replace-template-string.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-var-requires */
2 | const replace = require('replace-in-file');
3 |
4 | const projectNameArgs = process.argv.slice(2);
5 | const projectName = projectNameArgs[projectNameArgs.length - 1];
6 |
7 | if (!projectName) {
8 | throw new Error('must pass a project name');
9 | } else {
10 | console.log(projectName);
11 | // return;
12 | }
13 |
14 | const readmeFile = {
15 | files: './README.md',
16 | from: /kb-server-client-template/g,
17 | to: projectName,
18 | };
19 |
20 | const contributorsFile = {
21 | files: './.all-contributorsrc',
22 | from: /kb-server-client-template/g,
23 | to: projectName,
24 | };
25 |
26 | const packageFile = {
27 | files: './package.json',
28 | from: /kb-server-client-template/g,
29 | to: projectName,
30 | };
31 |
32 | const packageLockFile = {
33 | files: './package-lock.json',
34 | from: /kb-server-client-template/g,
35 | to: projectName,
36 | };
37 |
38 |
39 |
40 | (async () => {
41 | try {
42 | let results = [];
43 | results.push(await replace(readmeFile));
44 | results.push(await replace(packageFile));
45 | results.push(await replace(packageLockFile));
46 | results.push(await replace(contributorsFile));
47 | console.log('Replacement results:', results);
48 | }
49 | catch (error) {
50 | console.error('Error occurred:', error);
51 | }
52 | })();
53 |
--------------------------------------------------------------------------------
/server/src/abstracts/base.model.abstract.ts:
--------------------------------------------------------------------------------
1 | import { buildSchema, prop as PersistInDb } from '@typegoose/typegoose';
2 | import { classToPlain, Exclude, Expose } from 'class-transformer';
3 | import { Schema } from 'mongoose';
4 |
5 | @Exclude()
6 | export abstract class BaseModel {
7 | @PersistInDb()
8 | createdDate?: Date; // provided by timestamps
9 | @Expose()
10 | @PersistInDb()
11 | updatedDate?: Date; // provided by timestamps
12 |
13 | // @Expose({ name: 'id' })
14 | // @Transform(({ value }) => value && value.toString())
15 | // tslint:disable-next-line: variable-name
16 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
17 | _id?: any;
18 |
19 | id?: string; // is actually model._id getter
20 |
21 | // tslint:disable-next-line: variable-name
22 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
23 | _v?: any;
24 |
25 | // add more to a base model if you want.
26 |
27 | toJSON() {
28 | return classToPlain(this);
29 | }
30 |
31 | toString() {
32 | return JSON.stringify(this.toJSON());
33 | }
34 |
35 | static get schema(): Schema {
36 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
37 | return buildSchema(this as any, {
38 | timestamps: true,
39 | toJSON: {
40 | getters: true,
41 | virtuals: true
42 | }
43 | });
44 | }
45 |
46 | static get modelName(): string {
47 | return this.name;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/server/src/events/events.gateway.ts:
--------------------------------------------------------------------------------
1 | import { Logger } from '@nestjs/common';
2 | import {
3 | MessageBody,
4 | OnGatewayConnection,
5 | SubscribeMessage,
6 | WebSocketGateway,
7 | WebSocketServer,
8 | WsResponse
9 | } from '@nestjs/websockets';
10 | import { from, Observable } from 'rxjs';
11 | import { map } from 'rxjs/operators';
12 | import { Server } from 'socket.io';
13 |
14 | @WebSocketGateway()
15 | export class EventsGateway implements OnGatewayConnection {
16 | private logger: Logger = new Logger('AppGateway');
17 |
18 | @WebSocketServer()
19 | server: Server;
20 |
21 | constructor() {
22 | this.logger.log('testing, testing...');
23 | }
24 |
25 | @SubscribeMessage('message')
26 | handleMessage(@MessageBody() data: unknown): WsResponse {
27 | this.logger.log('got here!');
28 |
29 | return {
30 | event: 'message-response',
31 | data
32 | };
33 | }
34 |
35 | @SubscribeMessage('events')
36 | onEvent(/* @MessageBody() data: unknown */): Observable> {
37 | const event = 'events';
38 | const response = [1, 2, 3];
39 |
40 | return from(response).pipe(
41 | map(data => ({ event, data }))
42 | );
43 | }
44 |
45 | handleDisconnect() {
46 | this.logger.log('Client disconnected:');
47 | // console.log(client);
48 | }
49 |
50 | handleConnection() {
51 | this.logger.log('Client connected:');
52 | // console.log(client);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/server/src/api/api.controller.ts:
--------------------------------------------------------------------------------
1 | import { Controller, Get, Logger } from '@nestjs/common';
2 | import { ApiOkResponse, ApiOperation } from '@nestjs/swagger';
3 | import { readJSON } from 'fs-extra';
4 | import { chain } from 'lodash';
5 | import { join } from 'path';
6 |
7 | import { AppService } from '@kb-app';
8 | import { ApiInfo } from '@kb-models';
9 |
10 | @Controller('api')
11 | export class ApiController {
12 | readonly appRoot = new AppService().appRoot;
13 |
14 | private readonly logger = new Logger(ApiController.name);
15 |
16 | @Get()
17 | @ApiOperation({ summary: 'Get API Information' })
18 | @ApiOkResponse({
19 | description: 'Returns API info as a JSON',
20 | type: ApiInfo
21 | })
22 | async getAPI() {
23 | const packageInfo = await readJSON(
24 | join(this.appRoot, './package.json')
25 | );
26 | const details = new ApiInfo(
27 | chain(packageInfo)
28 | .pick([
29 | 'name',
30 | 'description',
31 | 'version',
32 | 'license',
33 | 'repository',
34 | 'author',
35 | 'bugs'
36 | ])
37 | .mapValues((val) => val.url ? val.url : val)
38 | .value()
39 | );
40 | this.logger.log('Api information requested');
41 | return details;
42 | }
43 |
44 | @Get('/nana')
45 | @ApiOperation({
46 | deprecated: true
47 | })
48 | async deprecationTest() {
49 | return new Promise((resolve) => setTimeout(() => resolve('hello'), 60000));
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/client/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "root": true,
3 | "ignorePatterns": [
4 | "projects/**/*"
5 | ],
6 | "overrides": [
7 | {
8 | "files": [
9 | "*.ts"
10 | ],
11 | "parserOptions": {
12 | "project": [
13 | "tsconfig.json",
14 | "e2e/tsconfig.json"
15 | ],
16 | "tsconfigRootDir": __dirname,
17 | "createDefaultProgram": true
18 | },
19 | "extends": [
20 | "plugin:@angular-eslint/ng-cli-compat",
21 | "plugin:@angular-eslint/ng-cli-compat--formatting-add-on",
22 | "plugin:@angular-eslint/template/process-inline-templates"
23 | ],
24 | "rules": {
25 | 'eol-last': [ 2, 'windows' ],
26 | 'comma-dangle': [ 'error', 'never' ],
27 | 'max-len': [ 'error', { 'code': 80, "ignoreComments": true } ],
28 | "@angular-eslint/component-selector": [
29 | "error",
30 | {
31 | "type": "element",
32 | "prefix": "app",
33 | "style": "kebab-case"
34 | }
35 | ],
36 | "@angular-eslint/directive-selector": [
37 | "error",
38 | {
39 | "type": "attribute",
40 | "prefix": "app",
41 | "style": "camelCase"
42 | }
43 | ]
44 | }
45 | },
46 | {
47 | "files": [
48 | "*.html"
49 | ],
50 | "extends": [
51 | "plugin:@angular-eslint/template/recommended"
52 | ],
53 | "rules": {}
54 | }
55 | ]
56 | };
57 |
--------------------------------------------------------------------------------
/.devcontainer/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 |
3 | services:
4 | app:
5 | build:
6 | context: .
7 | dockerfile: Dockerfile
8 | args:
9 | # [Choice] Node.js version: 14, 12, 10
10 | VARIANT: 14
11 | # On Linux, you may need to update USER_UID and USER_GID below if not your local UID is not 1000.
12 | USER_UID: 1000
13 | USER_GID: 1000
14 |
15 | volumes:
16 | - ..:/workspace:cached
17 |
18 | # Overrides default command so things don't shut down after the process ends.
19 | command: sleep infinity
20 |
21 | # Runs app on the same network as the database container, allows "forwardPorts" in devcontainer.json function.
22 | network_mode: service:db
23 |
24 | # Uncomment the next line to use a non-root user for all processes.
25 | # user: node
26 |
27 | # Use "forwardPorts" in **devcontainer.json** to forward an app port locally.
28 | # (Adding the "ports" property to this file will not forward from a Codespace.)
29 |
30 | db:
31 | image: mongo:latest
32 | restart: unless-stopped
33 | volumes:
34 | - mongodb-data:/data/db
35 |
36 | # Uncomment to change startup options
37 | # environment:
38 | # MONGO_INITDB_ROOT_USERNAME: root
39 | # MONGO_INITDB_ROOT_PASSWORD: example
40 | # MONGO_INITDB_DATABASE: your-database-here
41 |
42 | # Add "forwardPorts": ["27017"] to **devcontainer.json** to forward MongoDB locally.
43 | # (Adding the "ports" property to this file will not forward from a Codespace.)
44 |
45 | volumes:
46 | mongodb-data:
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "peacock.color": "#3334f4",
3 | "editor.tabSize": 2,
4 | "editor.insertSpaces": true,
5 | "editor.detectIndentation": false,
6 | "editor.rulers": [80],
7 | "editor.fontFamily": "Hack, Menlo, Monaco, 'Courier New', monospace",
8 | "editor.matchBrackets": "always",
9 | // Terminal
10 | "terminal.integrated.fontFamily": "Hack, Menlo, Monaco, 'Courier New', monospace",
11 | "terminal.integrated.fontSize": 14,
12 | // Workbench
13 | "workbench.colorTheme": "Andromeda",
14 | "workbench.editor.showIcons": true,
15 | "workbench.iconTheme": "vs-seti",
16 | // Bracket Pair Colorizer
17 | "bracketPairColorizer.colorMode": "Consecutive",
18 | "bracketPairColorizer.forceUniqueOpeningColor": true,
19 | "bracketPairColorizer.showBracketsInGutter": true,
20 | "window.title": "${activeEditorShort}${separator}${rootName} [kibibit]",
21 | "typescriptHero.imports.stringQuoteStyle": "'",
22 | "typescriptHero.imports.grouping": [
23 | "Plains",
24 | "Modules",
25 | "/^@kb-/",
26 | "Workspace"
27 |
28 | ],
29 | "typescriptHero.imports.organizeOnSave": true,
30 | "typescriptHero.imports.multiLineTrailingComma": false,
31 | "typescriptHero.imports.multiLineWrapThreshold": 80,
32 | "typescriptHero.imports.insertSpaceBeforeAndAfterImportBraces": true,
33 | "typescriptHero.imports.insertSemicolons": true,
34 | "editor.codeActionsOnSave": {
35 | "source.fixAll.eslint": true,
36 | },
37 | "eslint.format.enable": true,
38 | "vsicons.presets.angular": true,
39 | "eslint.workingDirectories": [
40 | "client/",
41 | "server/"
42 | ],
43 | "svg.preview.background": "black"
44 | }
--------------------------------------------------------------------------------
/server/test/sockets.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { observe } from 'rxjs-marbles/jest';
3 | import { take, tap } from 'rxjs/operators';
4 |
5 | import { AppModule } from '@kb-app';
6 |
7 | import { SocketService } from './socket.service';
8 | import { Utils } from './utils';
9 |
10 | describe('Web Socket Events (e2e)', () => {
11 | let socket: SocketService;
12 |
13 | beforeEach(async () => {
14 | const testingModule: TestingModule = await Test.createTestingModule({
15 | imports: [AppModule]
16 | }).compile();
17 |
18 | await Utils.startServer(testingModule);
19 | // each test need a new socket connection
20 | socket = await Utils.createSocket();
21 | });
22 |
23 | afterEach(async () => {
24 | // each test need to release the connection for next
25 | await Utils.closeApp();
26 | });
27 |
28 | test('socket connection', observe(() => {
29 | return socket.once('connect')
30 | .pipe(tap(() => expect(true).toBeTruthy()));
31 | }));
32 |
33 | test('Events topic', observe(() => {
34 | let counter = 1;
35 |
36 | socket
37 | .once('connect')
38 | .pipe(tap(() => socket.emit('events', { test: 'test' })))
39 | .subscribe();
40 |
41 | return socket.on('events')
42 | .pipe(
43 | take(3),
44 | tap(data => expect(data).toBe(counter++))
45 | );
46 | }));
47 |
48 | test('Message topic', observe(() => {
49 | const messageData = { test: 'test' };
50 | socket
51 | .once('connect')
52 | .pipe(tap(() => socket.emit('message', messageData)))
53 | .subscribe();
54 |
55 | return socket.once('message-response')
56 | .pipe(tap(data => expect(data).toEqual(messageData)));
57 | }));
58 | });
59 |
--------------------------------------------------------------------------------
/tools/scripts/get-all-contributors.js:
--------------------------------------------------------------------------------
1 | const gitlog = require('gitlog').default;
2 | const { join } = require('path');
3 | const githubUsername = require('github-username');
4 | const shell = require('shelljs');
5 | const { forEach, chain } = require('lodash');
6 | const { readJson } = require('fs-extra');
7 |
8 |
9 | (async () => {
10 | const allContributorsConfig = await readJson(join(__dirname, '..', '/.all-contributorsrc'));
11 | const data = gitlog({
12 | repo: join(__dirname, '..'),
13 | fields: ["authorName", "authorEmail", "authorDate"]
14 | });
15 |
16 | let result = {};
17 |
18 | for (const commit of data) {
19 | const isCode = commit.files.find((item) => item.startsWith('server/src') || item.startsWith('client/src'));
20 | const isInfra = commit.files.find((item) => item.startsWith('.github/') || item.startsWith('tools/'));
21 | const isTests = commit.files.find((item) => item.endsWith('.spec.ts'));
22 | let types = [];
23 | if (isCode) { types.push('code'); }
24 | if (isInfra) { types.push('infra'); }
25 | if (isTests) { types.push('test'); }
26 |
27 | if (!result[commit.authorEmail]) {
28 | const githubLogin = await githubUsername(commit.authorEmail);
29 | result[commit.authorEmail] = {
30 | githubLogin,
31 | types
32 | };
33 | } else {
34 | result[commit.authorEmail].types = result[commit.authorEmail].types.concat(types);
35 | }
36 | }
37 |
38 | forEach(result, (person, email) => {
39 | const existing = chain(allContributorsConfig.contributors)
40 | .find((item) => item.login === person.githubLogin)
41 | .get('contributions', [])
42 | .value();
43 | const types = chain(person.types.concat(existing)).uniq().sortBy().value();
44 | const command = `npm run contributors:add ${ person.githubLogin } ${ types.join(',') }`;
45 | console.log(command);
46 | shell.exec(command, { cwd: __dirname });
47 | })
48 | })();
--------------------------------------------------------------------------------
/client/src/styles.scss:
--------------------------------------------------------------------------------
1 |
2 | // Custom Theming for Angular Material
3 | // For more information: https://material.angular.io/guide/theming
4 | @import '~@angular/material/theming';
5 | // Plus imports for other components in your app.
6 |
7 | // Define a custom typography config that overrides the font-family as well as the
8 | // `headlines` and `body-1` levels.
9 | $custom-typography: mat-typography-config(
10 | $font-family: "'Comfortaa', cursive",
11 | $headline: mat-typography-level(32px, 48px, 700),
12 | $body-1: mat-typography-level(16px, 24px, 500)
13 | );
14 |
15 |
16 | // Include the common styles for Angular Material. We include this here so that you only
17 | // have to load a single css file for Angular Material in your app.
18 | // Be sure that you only ever include this mixin once!
19 | @include mat-core($custom-typography);
20 |
21 | // Define the palettes for your theme using the Material Design palettes available in palette.scss
22 | // (imported above). For each palette, you can optionally specify a default, lighter, and darker
23 | // hue. Available color palettes: https://material.io/design/color/
24 | $client-primary: mat-palette($mat-indigo);
25 | $client-accent: mat-palette($mat-pink, A200, A100, A400);
26 |
27 | // The warn palette is optional (defaults to red).
28 | $client-warn: mat-palette($mat-red);
29 |
30 | // Create the theme object. A theme consists of configurations for individual
31 | // theming systems such as "color" or "typography".
32 | $client-theme: mat-dark-theme((
33 | color: (
34 | primary: $client-primary,
35 | accent: $client-accent,
36 | warn: $client-warn,
37 | )
38 | ));
39 |
40 | // Include theme styles for core and each component used in your app.
41 | // Alternatively, you can import and @include the theme mixins for each component
42 | // that you are using.
43 | @include angular-material-theme($client-theme);
44 |
45 | /* You can add global styles to this file, and also import other style files */
46 |
47 | html, body { height: 100%; }
48 | body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; background: #212121; }
49 |
--------------------------------------------------------------------------------
/tools/initialize.js:
--------------------------------------------------------------------------------
1 | const { terminalConsoleLogo } = require('@kibibit/consologo');
2 | const inquirer = require('inquirer');
3 | const Foswig = require('foswig').default;
4 | const Sentencer = require('sentencer');
5 | const Names = require('./data/names-dictionary');
6 | const { kebabCase, toLower } = require('lodash');
7 | const packageNameRegex = require('package-name-regex');
8 | // const imageToAscii = require('image-to-ascii');
9 |
10 | //load the words into the markov chain
11 | const chain = new Foswig(3, Names.dictionary);
12 |
13 | // imageToAscii("https://octodex.github.com/images/octofez.png", (err, converted) => {
14 | // console.log(err || converted);
15 | // });
16 |
17 | const questions = [
18 | {
19 | type: 'input',
20 | name: 'projectName',
21 | message: 'What is the name of this new project?',
22 | transformer(input) { return `@kibibit/${ input }`; },
23 | validate(input) {
24 | return packageNameRegex.test(input) || `@kibibit/${ input } is not a valid NPM package name`;
25 | },
26 | default: '@kibibit/' + toLower(chain.generate({
27 | minLength: 5,
28 | maxLength: 10,
29 | allowDuplicates: false
30 | }))
31 | },
32 | {
33 | type: 'input',
34 | name: 'projectDescription',
35 | message: 'Please write a short description for this project',
36 | default: Sentencer.make('This is {{ an_adjective }} {{ noun }}.')
37 | },
38 | {
39 | type: 'input',
40 | name: 'author',
41 | message: 'Project owner\\author',
42 | default: 'githubusername '
43 | },
44 | {
45 | type: 'input',
46 | name: 'githubRepo',
47 | message: 'Paste a url to a github repo or a user/repo pair',
48 | default: 'kibibit/achievibit'
49 | }
50 | ];
51 |
52 | (async () => {
53 | terminalConsoleLogo('Start a new project', 'Answer these questions to start a new project');
54 | const answers = await inquirer.prompt(questions);
55 |
56 | answers.projectName = answers.projectName.startsWith('@kibibit/') ?
57 | answers.projectName : `@kibibit/${ answers.projectName }`;
58 |
59 | answers.projectNameSafe = kebabCase(answers.projectName);
60 |
61 | console.log(answers);
62 | })();
--------------------------------------------------------------------------------
/client/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage'),
13 | require('@angular-devkit/build-angular/plugins/karma'),
14 | require('karma-spec-reporter'),
15 | require('karma-htmlfile-reporter')
16 | ],
17 | client: {
18 | captureConsole: false,
19 | clearContext: false // leave Jasmine Spec Runner output visible in browser
20 | },
21 | preprocessors: {
22 | 'src/**/*!(test|spec).ts': ['coverage']
23 | },
24 | // coverageIstanbulReporter: {
25 | // dir: require('path').join(__dirname, '../coverage/client'),
26 | // reports: ['html', 'lcovonly', 'text-summary'],
27 | // fixWebpackSourcePaths: true
28 | // },
29 | coverageReporter: {
30 | // type : 'html',
31 | dir : '../test-results/client/coverage',
32 | reporters: [
33 | { type: 'lcov', subdir: 'report-lcov' },
34 | { type: 'html', subdir: 'report-html' }
35 | ],
36 | fixWebpackSourcePaths: true
37 | },
38 |
39 | reporters: ['spec', 'html'],
40 | htmlReporter: {
41 | outputFile: '../test-results/client/index.html',
42 |
43 | // Optional
44 | pageTitle: 'Client-Side Unit Tests',
45 | subPageTitle: 'A summary of test results',
46 | groupSuites: true,
47 | useCompactStyle: true,
48 | useLegacyStyle: true,
49 | showOnlyFailed: false
50 | },
51 | port: 9876,
52 | colors: true,
53 | logLevel: config.LOG_INFO,
54 | autoWatch: true,
55 | browsers: [ 'Chrome_without_sandbox' ],
56 | customLaunchers: {
57 | Chrome_without_sandbox: {
58 | base: 'ChromeHeadless',
59 | flags: [ '--no-sandbox' ] // with sandbox it fails under Docker
60 | }
61 | },
62 | singleRun: false,
63 | restartOnFileChange: true
64 | });
65 | };
66 |
--------------------------------------------------------------------------------
/server/src/swagger.ts:
--------------------------------------------------------------------------------
1 | import { INestApplication } from '@nestjs/common';
2 | import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
3 | import axios from 'axios';
4 |
5 | interface ISwaggerMethod {
6 | get: (attr: string) => string;
7 | }
8 |
9 | export class Swagger {
10 | static title = 'API Docs example';
11 | static swaggerPath = 'api/docs';
12 | static config = new DocumentBuilder()
13 | .setTitle(Swagger.title)
14 | .setDescription('The API description')
15 | .setVersion('1.0')
16 | .setContact(
17 | 'thatkookooguy',
18 | 'github.com/thatkookooguy',
19 | 'thatkookooguy@kibibit.io'
20 | )
21 | .addTag(
22 | 'product',
23 | [
24 | 'Product is an example module to show how to ',
25 | 'add **swagger** documentation'
26 | ].join(''),
27 | {
28 | url: 'https://github.com/kibibit',
29 | description: 'See Docs'
30 | }
31 | )
32 | .build();
33 |
34 | static getOperationsSorter() {
35 | return (a: ISwaggerMethod, b: ISwaggerMethod) => {
36 | const methodsOrder = [
37 | 'get',
38 | 'post',
39 | 'put',
40 | 'patch',
41 | 'delete',
42 | 'options',
43 | 'trace'
44 | ];
45 | let result =
46 | methodsOrder.indexOf( a.get('method') ) -
47 | methodsOrder.indexOf( b.get('method') );
48 |
49 | if (result === 0) {
50 | result = a.get('path').localeCompare(b.get('path'));
51 | }
52 |
53 | return result;
54 | }
55 | }
56 |
57 | static async addSwagger(app: INestApplication) {
58 | const document = SwaggerModule.createDocument(app, Swagger.config);
59 | const swaggerCssResponse = await axios
60 | .get('https://kibibit.io/kibibit-assets/swagger/swagger.css');
61 | const customCss = swaggerCssResponse.data;
62 |
63 | SwaggerModule.setup(Swagger.swaggerPath, app, document, {
64 | customSiteTitle: Swagger.title,
65 | customCss,
66 | customJs: '//kibibit.io/kibibit-assets/swagger/swagger.js',
67 | swaggerOptions: {
68 | docExpansion: 'none',
69 | apisSorter: 'alpha',
70 | operationsSorter: Swagger.getOperationsSorter()
71 | }
72 | });
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/.devcontainer/Dockerfile:
--------------------------------------------------------------------------------
1 | # See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.158.0/containers/typescript-node/.devcontainer/base.Dockerfile
2 |
3 | # [Choice] Node.js version: 14, 12, 10
4 | ARG VARIANT="14-buster"
5 | FROM mcr.microsoft.com/vscode/devcontainers/typescript-node:0-${VARIANT}
6 |
7 | # Install MongoDB command line tools
8 | ARG MONGO_TOOLS_VERSION=4.2
9 | RUN curl -sSL "https://www.mongodb.org/static/pgp/server-${MONGO_TOOLS_VERSION}.asc" | (OUT=$(apt-key add - 2>&1) || echo $OUT) \
10 | && echo "deb http://repo.mongodb.org/apt/debian $(lsb_release -cs)/mongodb-org/${MONGO_TOOLS_VERSION} main" | tee /etc/apt/sources.list.d/mongodb-org-${MONGO_TOOLS_VERSION}.list \
11 | && apt-get update && export DEBIAN_FRONTEND=noninteractive \
12 | && apt-get install -y mongodb-org-tools mongodb-org-shell \
13 | && apt-get clean -y && rm -rf /var/lib/apt/lists/*
14 |
15 | # Install Chrome Driver
16 | RUN wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | apt-key add -
17 | RUN echo 'deb http://dl.google.com/linux/chrome/deb/ stable main' >> /etc/apt/sources.list
18 | RUN apt-get update && apt-get install --no-install-recommends -y google-chrome-stable
19 |
20 | # Update args in docker-compose.yaml to set the UID/GID of the "node" user.
21 | ARG USER_UID=1000
22 | ARG USER_GID=$USER_UID
23 | RUN if [ "$USER_GID" != "1000" ] || [ "$USER_UID" != "1000" ]; then groupmod --gid $USER_GID node && usermod --uid $USER_UID --gid $USER_GID node; fi
24 |
25 | # [Optional] Uncomment this section to install additional OS packages.
26 | RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
27 | && apt-get -y install --no-install-recommends zsh
28 | RUN wget https://github.com/robbyrussell/oh-my-zsh/raw/master/tools/install.sh -O - | zsh || true
29 | RUN wget https://raw.githubusercontent.com/zakaziko99/agnosterzak-ohmyzsh-theme/master/agnosterzak.zsh-theme -P /home/node/.oh-my-zsh/themes
30 | RUN sed -i 's/ZSH\_THEME\=\".*\"/ZSH_THEME\=\"agnosterzak\"/' /home/node/.zshrc
31 | RUN echo "git config --global core.editor \"code --wait\"" >> /home/node/.zshrc
32 |
33 | # [Optional] Uncomment if you want to install an additional version of node using nvm
34 | # ARG EXTRA_NODE_VERSION=10
35 | # RUN su node -c "source /usr/local/share/nvm/nvm.sh && nvm install ${EXTRA_NODE_VERSION}"
36 |
37 | # [Optional] Uncomment if you want to install more global node packages
38 | RUN su node -c "npm install -g @angular/cli @nestjs/cli"
39 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | lib
2 | test-results
3 |
4 | # Logs
5 | logs
6 | *.log
7 | npm-debug.log*
8 | yarn-debug.log*
9 | yarn-error.log*
10 | lerna-debug.log*
11 |
12 | # Diagnostic reports (https://nodejs.org/api/report.html)
13 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
14 |
15 | # Runtime data
16 | pids
17 | *.pid
18 | *.seed
19 | *.pid.lock
20 |
21 | # Directory for instrumented libs generated by jscoverage/JSCover
22 | lib-cov
23 |
24 | # Coverage directory used by tools like istanbul
25 | coverage
26 | *.lcov
27 |
28 | # nyc test coverage
29 | .nyc_output
30 |
31 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
32 | .grunt
33 |
34 | # Bower dependency directory (https://bower.io/)
35 | bower_components
36 |
37 | # node-waf configuration
38 | .lock-wscript
39 |
40 | # Compiled binary addons (https://nodejs.org/api/addons.html)
41 | build/Release
42 |
43 | # Dependency directories
44 | node_modules/
45 | jspm_packages/
46 |
47 | # Snowpack dependency directory (https://snowpack.dev/)
48 | web_modules/
49 |
50 | # TypeScript cache
51 | *.tsbuildinfo
52 |
53 | # Optional npm cache directory
54 | .npm
55 |
56 | # Optional eslint cache
57 | .eslintcache
58 |
59 | # Microbundle cache
60 | .rpt2_cache/
61 | .rts2_cache_cjs/
62 | .rts2_cache_es/
63 | .rts2_cache_umd/
64 |
65 | # Optional REPL history
66 | .node_repl_history
67 |
68 | # Output of 'npm pack'
69 | *.tgz
70 |
71 | # Yarn Integrity file
72 | .yarn-integrity
73 |
74 | # dotenv environment variables file
75 | .env
76 | .env.test
77 |
78 | # parcel-bundler cache (https://parceljs.org/)
79 | .cache
80 | .parcel-cache
81 |
82 | # Next.js build output
83 | .next
84 | out
85 |
86 | # Nuxt.js build / generate output
87 | .nuxt
88 | dist
89 |
90 | # Gatsby files
91 | .cache/
92 | # Comment in the public line in if your project uses Gatsby and not Next.js
93 | # https://nextjs.org/blog/next-9-1#public-directory-support
94 | # public
95 |
96 | # vuepress build output
97 | .vuepress/dist
98 |
99 | # Serverless directories
100 | .serverless/
101 |
102 | # FuseBox cache
103 | .fusebox/
104 |
105 | # DynamoDB Local files
106 | .dynamodb/
107 |
108 | # TernJS port file
109 | .tern-port
110 |
111 | # Stores VSCode versions used for testing VSCode extensions
112 | .vscode-test
113 |
114 | # yarn v2
115 | .yarn/cache
116 | .yarn/unplugged
117 | .yarn/build-state.yml
118 | .yarn/install-state.gz
119 | .pnp.*
120 |
--------------------------------------------------------------------------------
/TEMPLATE_INSTRUCTIONS.md:
--------------------------------------------------------------------------------
1 | # Template Usage Overview
2 |
3 | ## Starting Up
4 |
5 | ### Prequisits
6 | - [vscode](https://code.visualstudio.com/)
7 | - [Docker](https://www.docker.com/) (**OPTIONAL**)
8 |
9 | ### Install the repo
10 | 1. On your local computer, copy the repo using `git clone`
11 | 2. Open the repository in vscode.
12 | 3. you can work in the following modes:
13 | - **[Devcontainer](https://code.visualstudio.com/docs/remote/containers)** - Create a docker container that runs the app with all the tools and extensions necassery. vscode will popup a notification to ask you if it should re-open the project inside a devcontainer. This will also start a mongodb database for the app to use against
14 | - **open locally** - run the repo locally. Should work but you might have some OS differences at edge-cases. You will need to provice a mongodb database link for the app to work against a database.
15 | 4. Open a terminal and run `npm install` inside the main folder. This will install all dependencies.
16 |
17 | ### Rename to your project name
18 | After the dependencies are installed, you need to change the project name from `kb-server-client-template` to whatever project you're developing. In order to do that, you can run:
19 | ```
20 | npm run init
21 | ```
22 | This will replace the important parts of the templates to your project name (which should be npm compliant).
23 |
24 | ## Run in Development mode
25 |
26 | ### Server
27 | In the main repo folder, run `npm run start:server` to start the server
28 | in watch mode. The server will be available at `localhost:10102`.
29 |
30 | API swagger documentation is available at `localhost:10102/api/docs` (or through the client proxy).
31 |
32 | A `Product` module is included as an example for a full module\service\controller feature with db.
33 |
34 | ### Client
35 | In the main repo folder, run `npm run start:client` to start the client in local proxy mode. The client will be available at `localhost:10101`. This will re-route all rest calls to
36 | `localhost:10101/api` to the server application running on `localhost:10102`.
37 |
38 | ## Run Production App
39 | In the main repo folder, run the following commands
40 | ```
41 | npm run build
42 | node .
43 | ```
44 |
45 | ## Run tests
46 |
47 | ### Server Unit-Tests
48 | In vscode, at the footer, click on `TESTS: Server Unit-tests and Coverage` or run `npm run test:server-unit`
49 | ### Client Unit-tests
50 | In vscode, at the footer, click on `TESTS: Client Unit-tests and Coverage` or run `npm run test:client-unit`
51 | ### API Tests
52 | In vscode, at the footer, click on `TESTS: API-tests` or run `npm run test:api`
53 | ### E2E Tests
54 | In vscode, at the footer, click on `TESTS: E2E-tests` or run `npm run test:e2e`
--------------------------------------------------------------------------------
/server/test/socket.service.ts:
--------------------------------------------------------------------------------
1 | import { Observable } from 'rxjs';
2 | import { debounceTime, first, share, tap } from 'rxjs/operators';
3 | import { connect, Socket } from 'socket.io-client';
4 |
5 | enum NATIVE_EVENTS {
6 | CONNECT = 'connect',
7 | DISCONNECT = 'disconnect',
8 | ERROR = 'error',
9 | RECONNECT = 'reconnect',
10 | }
11 |
12 | export class SocketService {
13 | private host = 'ws://localhost:10109';
14 | private events$: Record> = {};
15 | socket: typeof Socket;
16 |
17 | errors$: Observable;
18 | isConnected$: Observable;
19 |
20 | constructor(defer?: boolean) {
21 | this.socket = connect(
22 | this.host,
23 | {
24 | autoConnect: !defer,
25 | forceNew: true
26 | },
27 | );
28 |
29 | this.errors$ = this.on('disconnect-reason');
30 | this.on(NATIVE_EVENTS.DISCONNECT)
31 | .pipe(
32 | tap(reason => `disconnected: ${reason}`),
33 | debounceTime(5000),
34 | )
35 | .subscribe(reason => {
36 | if (reason === 'io server disconnect') {
37 | }
38 | });
39 | }
40 |
41 | on(event: string): Observable {
42 | if (this.events$[event]) {
43 | return this.events$[event];
44 | }
45 |
46 | this.events$[event] = new Observable(observer => {
47 | this.socket.on(event, observer.next.bind(observer));
48 |
49 | return () => {
50 | this.socket.off(event);
51 | delete this.events$[event];
52 | };
53 | }).pipe(share());
54 |
55 | return this.events$[event];
56 | }
57 |
58 | once(event: string): Observable {
59 | return this.on(event).pipe(first());
60 | }
61 | emit(event: string, data?: T, ack?: false): void;
62 | // This overloading is not working in TS 3.0.1
63 | // emit(event: string, data?: any, ack?: true): Observable;
64 | emit(event: string, data?: T, ack?: true): Observable;
65 | emit(
66 | event: string,
67 | data?: T,
68 | ack?: boolean
69 | ): void | Observable {
70 | if (ack) {
71 | return new Observable(observer => {
72 | this.socket.emit(event, data, observer.next.bind(observer));
73 | }).pipe(first());
74 | } else {
75 | this.socket.emit(event, data);
76 | }
77 | }
78 |
79 | open() {
80 | this.socket.connect();
81 | }
82 |
83 | close() {
84 | this.socket.disconnect();
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "client",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "ng": "ng",
6 | "start": "ng serve --port 10101 --poll=2000",
7 | "start:proxy": "ng serve --port 10101 --proxy-config ./proxy.conf.json --poll=2000",
8 | "build": "ng build",
9 | "test:watch": "ng test",
10 | "test": "ng test --no-watch --browsers Chrome_without_sandbox",
11 | "test:cov": "ng test --no-watch --code-coverage --browsers Chrome_without_sandbox",
12 | "test:headed": "ng test --no-watch --code-coverage --browsers=Chrome",
13 | "lint": "ng lint",
14 | "lint:fix": "ng lint client --fix",
15 | "lint:html": "prettyhtml ./src/**/*.html --tab-width 2 --print-width 80 --wrapAttributes true --sortAttributes",
16 | "e2e": "ng e2e",
17 | "e2e:headed": "ng e2e --browsers Chrome"
18 | },
19 | "private": true,
20 | "dependencies": {
21 | "@angular/animations": "~11.1.2",
22 | "@angular/cdk": "^11.1.2",
23 | "@angular/common": "~11.1.2",
24 | "@angular/compiler": "~11.1.2",
25 | "@angular/core": "~11.1.2",
26 | "@angular/forms": "~11.1.2",
27 | "@angular/material": "^11.1.2",
28 | "@angular/platform-browser": "~11.1.2",
29 | "@angular/platform-browser-dynamic": "~11.1.2",
30 | "@angular/router": "~11.1.2",
31 | "@kibibit/consologo": "^1.2.0",
32 | "rxjs": "~6.6.0",
33 | "tslib": "^2.0.0",
34 | "zone.js": "~0.10.2"
35 | },
36 | "devDependencies": {
37 | "@angular-devkit/build-angular": "~0.1101.4",
38 | "@angular-eslint/builder": "1.2.0",
39 | "@angular-eslint/eslint-plugin": "1.2.0",
40 | "@angular-eslint/eslint-plugin-template": "1.2.0",
41 | "@angular-eslint/schematics": "1.2.0",
42 | "@angular-eslint/template-parser": "1.2.0",
43 | "@angular/cli": "^11.1.4",
44 | "@angular/compiler-cli": "~11.1.2",
45 | "@starptech/prettyhtml": "^0.10.0",
46 | "@types/jasmine": "~3.5.0",
47 | "@types/jasminewd2": "~2.0.3",
48 | "@types/node": "^14.14.25",
49 | "@typescript-eslint/eslint-plugin": "4.14.2",
50 | "@typescript-eslint/parser": "4.14.2",
51 | "codelyzer": "^6.0.0",
52 | "eslint": "^7.19.0",
53 | "eslint-plugin-import": "2.22.1",
54 | "eslint-plugin-jsdoc": "31.6.1",
55 | "eslint-plugin-prefer-arrow": "1.2.2",
56 | "jasmine-core": "~3.6.0",
57 | "jasmine-spec-reporter": "~5.0.0",
58 | "karma": "^5.2.3",
59 | "karma-chrome-launcher": "~3.1.0",
60 | "karma-coverage": "^2.0.3",
61 | "karma-coverage-istanbul-reporter": "~3.0.2",
62 | "karma-jasmine": "~4.0.0",
63 | "karma-jasmine-html-reporter": "^1.5.0",
64 | "karma-spec-reporter": "0.0.32",
65 | "protractor": "~7.0.0",
66 | "ts-node": "~9.1.1",
67 | "tslint": "~6.1.0",
68 | "typescript": "~4.1.3"
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/client/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable max-len */
2 | /**
3 | * This file includes polyfills needed by Angular and is loaded before the app.
4 | * You can add your own extra polyfills to this file.
5 | *
6 | * This file is divided into 2 sections:
7 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
8 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
9 | * file.
10 | *
11 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
12 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
13 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
14 | *
15 | * Learn more in https://angular.io/guide/browser-support
16 | */
17 |
18 | /***************************************************************************************************
19 | * BROWSER POLYFILLS
20 | */
21 |
22 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */
23 | // import 'classlist.js'; // Run `npm install --save classlist.js`.
24 |
25 | /**
26 | * Web Animations `@angular/platform-browser/animations`
27 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
28 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
29 | */
30 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`.
31 |
32 | /**
33 | * By default, zone.js will patch all possible macroTask and DomEvents
34 | * user can disable parts of macroTask/DomEvents patch by setting following flags
35 | * because those flags need to be set before `zone.js` being loaded, and webpack
36 | * will put import in the top of bundle, so user need to create a separate file
37 | * in this directory (for example: zone-flags.ts), and put the following flags
38 | * into that file, and then add the following code before importing zone.js.
39 | * import './zone-flags';
40 | *
41 | * The flags allowed in zone-flags.ts are listed here.
42 | *
43 | * The following flags will work for all browsers.
44 | *
45 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
46 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
47 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
48 | *
49 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
50 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
51 | *
52 | * (window as any).__Zone_enable_cross_context_check = true;
53 | *
54 | */
55 |
56 | /***************************************************************************************************
57 | * Zone JS is required by default for Angular itself.
58 | */
59 | import 'zone.js/dist/zone'; // Included with Angular CLI.
60 |
61 |
62 | /***************************************************************************************************
63 | * APPLICATION IMPORTS
64 | */
65 |
--------------------------------------------------------------------------------
/client/src/app/angular-material/angular-material.module.ts:
--------------------------------------------------------------------------------
1 | import { OverlayModule } from '@angular/cdk/overlay';
2 | import { PortalModule } from '@angular/cdk/portal';
3 | import { CdkTreeModule } from '@angular/cdk/tree';
4 | import { CommonModule } from '@angular/common';
5 | import { NgModule } from '@angular/core';
6 | import { MatAutocompleteModule } from '@angular/material/autocomplete';
7 | import { MatBadgeModule } from '@angular/material/badge';
8 | import { MatButtonModule } from '@angular/material/button';
9 | import { MatButtonToggleModule } from '@angular/material/button-toggle';
10 | import { MatCardModule } from '@angular/material/card';
11 | import { MatCheckboxModule } from '@angular/material/checkbox';
12 | import { MatChipsModule } from '@angular/material/chips';
13 | import { MatRippleModule } from '@angular/material/core';
14 | import { MatDatepickerModule } from '@angular/material/datepicker';
15 | import { MatDividerModule } from '@angular/material/divider';
16 | import { MatExpansionModule } from '@angular/material/expansion';
17 | import { MatFormFieldModule } from '@angular/material/form-field';
18 | import { MatGridListModule } from '@angular/material/grid-list';
19 | import { MatIconModule } from '@angular/material/icon';
20 | import { MatInputModule } from '@angular/material/input';
21 | import { MatListModule } from '@angular/material/list';
22 | import { MatMenuModule } from '@angular/material/menu';
23 | import { MatPaginatorModule } from '@angular/material/paginator';
24 | import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
25 | import { MatRadioModule } from '@angular/material/radio';
26 | import { MatSelectModule } from '@angular/material/select';
27 | import { MatSidenavModule } from '@angular/material/sidenav';
28 | import { MatSnackBarModule } from '@angular/material/snack-bar';
29 | import { MatSortModule } from '@angular/material/sort';
30 | import { MatTableModule } from '@angular/material/table';
31 | import { MatTabsModule } from '@angular/material/tabs';
32 | import { MatToolbarModule } from '@angular/material/toolbar';
33 | import { MatTooltipModule } from '@angular/material/tooltip';
34 | import { MatTreeModule } from '@angular/material/tree';
35 |
36 |
37 | const materialModules = [
38 | CdkTreeModule,
39 | MatAutocompleteModule,
40 | MatButtonModule,
41 | MatCardModule,
42 | MatCheckboxModule,
43 | MatChipsModule,
44 | MatDividerModule,
45 | MatExpansionModule,
46 | MatIconModule,
47 | MatInputModule,
48 | MatListModule,
49 | MatMenuModule,
50 | MatProgressSpinnerModule,
51 | MatPaginatorModule,
52 | MatRippleModule,
53 | MatSelectModule,
54 | MatSidenavModule,
55 | MatSnackBarModule,
56 | MatSortModule,
57 | MatTableModule,
58 | MatTabsModule,
59 | MatToolbarModule,
60 | MatFormFieldModule,
61 | MatButtonToggleModule,
62 | MatTreeModule,
63 | OverlayModule,
64 | PortalModule,
65 | MatBadgeModule,
66 | MatGridListModule,
67 | MatRadioModule,
68 | MatDatepickerModule,
69 | MatTooltipModule
70 | ];
71 |
72 | @NgModule({
73 | imports: [
74 | CommonModule,
75 | ...materialModules
76 | ],
77 | exports: [
78 | ...materialModules
79 | ]
80 | })
81 |
82 | export class AngularMaterialModule { }
83 |
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "tasks": [
4 | {
5 | "label": "dev:start",
6 | "detail": "Start both server & client for development",
7 | "dependsOn": [
8 | "dev:server",
9 | "dev:client-proxy"
10 | ],
11 | "problemMatcher": [],
12 | "options": {
13 | "statusbar": {
14 | "color": "#35AC5E"
15 | }
16 | }
17 | },
18 | {
19 | "label": "dev:client-proxy",
20 | "detail": "client in proxy\\watch mode",
21 | "type": "npm",
22 | "script": "start:proxy",
23 | "path": "client/",
24 | "problemMatcher": [],
25 | "presentation": {
26 | "reveal": "always",
27 | "panel": "shared",
28 | "group": "devwatch"
29 | },
30 | "options": {
31 | "statusbar": {
32 | "hide": true,
33 | "color": "#35AC5E"
34 | }
35 | }
36 | },
37 | {
38 | "label": "dev:server",
39 | "detail": "server in watch mode",
40 | "type": "npm",
41 | "script": "start:server",
42 | "problemMatcher": [],
43 | "presentation": {
44 | "reveal": "always",
45 | "panel": "shared",
46 | "group": "devwatch"
47 | },
48 | "options": {
49 | "statusbar": {
50 | "hide": true,
51 | "color": "#35AC5E"
52 | }
53 | }
54 | },
55 | {
56 | "type": "npm",
57 | "script": "build",
58 | "group": "build",
59 | "problemMatcher": [],
60 | "label": "Build Production",
61 | "detail": "Will build both Server & Client. Output in ./dist/",
62 | "options": {
63 | "statusbar": {
64 | "color": "#209CEE"
65 | }
66 | }
67 | },
68 | {
69 | "type": "npm",
70 | "script": "test:cov",
71 | "path": "server/",
72 | "problemMatcher": [],
73 | "label": "TESTS: Server",
74 | "detail": "Run Server Unit-tests and Coverage",
75 | "options": {
76 | "statusbar": {
77 | "color": "#FFDD57"
78 | }
79 | }
80 | },
81 | {
82 | "type": "npm",
83 | "script": "test:cov",
84 | "path": "client/",
85 | "group": "test",
86 | "label": "TEST: Client",
87 | "detail": "Run Client Unit-tests and Coverage",
88 | "isBackground": true,
89 | "problemMatcher": "$karma-jasmine2",
90 | "options": {
91 | "statusbar": {
92 | "color": "#FFDD57"
93 | }
94 | }
95 | },
96 | {
97 | "type": "npm",
98 | "script": "test:e2e",
99 | "path": "server/",
100 | "problemMatcher": [],
101 | "label": "TESTS: API",
102 | "detail": "Run API Tests (server e2e)",
103 | "options": {
104 | "statusbar": {
105 | "color": "#FFDD57"
106 | }
107 | }
108 | },
109 | {
110 | "type": "npm",
111 | "script": "e2e",
112 | "path": "client/",
113 | "problemMatcher": [],
114 | "label": "TESTS: E2E",
115 | "detail": "Run E2E tests (client e2e. includes HTML interaction)",
116 | "options": {
117 | "statusbar": {
118 | "color": "#FFDD57"
119 | }
120 | }
121 | },
122 |
123 | ]
124 | }
--------------------------------------------------------------------------------
/server/src/api/product/product.controller.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Body,
3 | Controller,
4 | Logger,
5 | NotFoundException,
6 | Param,
7 | UseFilters
8 | } from '@nestjs/common';
9 | import { ApiTags } from '@nestjs/swagger';
10 |
11 | import {
12 | GetAll,
13 | GetOne,
14 | KbDelete,
15 | KbPatch,
16 | KbPost,
17 | KbPut
18 | } from '@kb-decorators';
19 | import { KbValidationExceptionFilter } from '@kb-filters';
20 | import { Product } from '@kb-models';
21 |
22 | import { ProductService } from './product.service';
23 |
24 | @Controller('api/product')
25 | @ApiTags('product')
26 | @UseFilters(new KbValidationExceptionFilter())
27 | export class ProductController {
28 | private readonly logger = new Logger(ProductController.name);
29 |
30 | constructor(private readonly productService: ProductService) {}
31 |
32 | @GetAll(Product)
33 | async getAllProducts() {
34 | const productsDB = await this.productService.findAllAsync();
35 | const products = productsDB
36 | .map((productDb) => new Product(productDb.toObject()));
37 |
38 | return products;
39 | }
40 |
41 | @GetOne(Product, ':name')
42 | async getProduct(@Param('name') name: string) {
43 | const product = await this.productService.findByName(name);
44 |
45 | if (!product) {
46 | throw new NotFoundException(`Product with name ${ name } not found`);
47 | }
48 |
49 | // will show secret fields as well!
50 | this.logger.log('Full Product');
51 | // will log only public fields!
52 | this.logger.log(product);
53 | // DANGER! WILL LOG EVERYTHING!
54 | console.log(product);
55 |
56 | // will only include exposed fields
57 | return product;
58 | }
59 |
60 | @KbPost(Product)
61 | async createProduct(@Body() item: Product) {
62 |
63 | const product = await this.productService.create(item);
64 |
65 | return product
66 | }
67 |
68 | @KbPatch(Product, ':name')
69 | async changeProduct(
70 | @Param('name') name: string,
71 | @Body() changes: Product
72 | ) {
73 | const existingProductDB = await this.productService.findOneAsync({ name });
74 |
75 | if (!existingProductDB) {
76 | throw new NotFoundException(`Product with name ${ name } not found`);
77 | }
78 |
79 | const existingProduct = new Product(existingProductDB.toObject());
80 | const product = await this.productService.updateAsync({
81 | ...existingProduct,
82 | ...changes
83 | })
84 | return product.toObject();
85 | }
86 |
87 | @KbPut(Product, ':name')
88 | async changeProduct2(
89 | @Param('name') name: string,
90 | @Body() changes: Product
91 | ) {
92 | const existingProductDB = await this.productService.findOneAsync({ name });
93 | const existingProduct = new Product(existingProductDB.toObject());
94 | const product = await this.productService.updateAsync({
95 | ...existingProduct,
96 | ...changes
97 | })
98 | return product.toObject();
99 | }
100 |
101 | @KbDelete(Product, ':name')
102 | async deleteProduct(@Param('name') name: string) {
103 | const product = await this.productService.deleteAsync({ name });
104 |
105 | const parsed = new Product(product.toObject());
106 |
107 | this.logger.log(parsed);
108 |
109 | // will only include exposed fields
110 | return parsed;
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/server/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | [travis-image]: https://api.travis-ci.org/nestjs/nest.svg?branch=master
6 | [travis-url]: https://travis-ci.org/nestjs/nest
7 | [linux-image]: https://img.shields.io/travis/nestjs/nest/master.svg?label=linux
8 | [linux-url]: https://travis-ci.org/nestjs/nest
9 |
10 | A progressive Node.js framework for building efficient and scalable server-side applications, heavily inspired by Angular.
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
26 |
27 | ## Description
28 |
29 | [Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
30 |
31 | ## Installation
32 |
33 | ```bash
34 | $ npm install
35 | ```
36 |
37 | ## Running the app
38 |
39 | ```bash
40 | # development
41 | $ npm run start
42 |
43 | # watch mode
44 | $ npm run start:dev
45 |
46 | # production mode
47 | $ npm run start:prod
48 | ```
49 |
50 | ## Test
51 |
52 | ```bash
53 | # unit tests
54 | $ npm run test
55 |
56 | # e2e tests
57 | $ npm run test:e2e
58 |
59 | # test coverage
60 | $ npm run test:cov
61 | ```
62 |
63 | ## Support
64 |
65 | Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
66 |
67 | ## Stay in touch
68 |
69 | - Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
70 | - Website - [https://nestjs.com](https://nestjs.com/)
71 | - Twitter - [@nestframework](https://twitter.com/nestframework)
72 |
73 | ## License
74 |
75 | Nest is [MIT licensed](LICENSE).
76 |
--------------------------------------------------------------------------------
/server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "server",
3 | "version": "0.0.1",
4 | "description": "",
5 | "author": "",
6 | "private": true,
7 | "license": "UNLICENSED",
8 | "scripts": {
9 | "prebuild": "rimraf dist",
10 | "build": "nest build",
11 | "generate-barrels": "barrelsby --delete -d ./src -l below -q --exclude spec.ts",
12 | "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
13 | "start": "nest start",
14 | "start:dev": "nest start --watch",
15 | "start:debug": "nest start --debug --watch",
16 | "start:prod": "node dist/main",
17 | "lint": "eslint -c ./.eslintrc.js \"{src,apps,libs,test}/**/*.ts\"",
18 | "lint:fix": "eslint -c ./.eslintrc.js \"{src,apps,libs,test}/**/*.ts\" --fix",
19 | "test": "jest",
20 | "test:watch": "jest --watch --verbose",
21 | "test:cov": "jest --coverage --verbose",
22 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
23 | "test:e2e": "jest --config ./test/jest-e2e.json --verbose --detectOpenHandles --coverage"
24 | },
25 | "dependencies": {},
26 | "devDependencies": {
27 | "@nestjs/cli": "^7.5.4",
28 | "@nestjs/schematics": "^7.2.7",
29 | "@nestjs/testing": "^7.6.11",
30 | "@types/express": "^4.17.11",
31 | "@types/fs-extra": "^9.0.7",
32 | "@types/jest": "^26.0.20",
33 | "@types/lodash": "^4.14.168",
34 | "@types/node": "^14.14.25",
35 | "@types/socket.io-client": "^1.4.36",
36 | "@types/supertest": "^2.0.10",
37 | "@typescript-eslint/eslint-plugin": "^4.14.2",
38 | "@typescript-eslint/parser": "^4.14.2",
39 | "barrelsby": "^2.2.0",
40 | "eslint": "^7.19.0",
41 | "eslint-config-prettier": "^7.2.0",
42 | "eslint-plugin-prettier": "^3.3.1",
43 | "jest": "^26.6.3",
44 | "jest-mock-req-res": "^1.0.2",
45 | "prettier": "^2.2.1",
46 | "rxjs-marbles": "^6.0.1",
47 | "supertest": "^6.1.3",
48 | "ts-jest": "^26.5.0",
49 | "ts-loader": "^8.0.16",
50 | "ts-node": "^9.1.1",
51 | "tsconfig-paths": "^3.9.0",
52 | "typescript": "^4.1.3"
53 | },
54 | "jest": {
55 | "moduleFileExtensions": [
56 | "js",
57 | "json",
58 | "ts"
59 | ],
60 | "rootDir": "src",
61 | "testRegex": ".*\\.spec\\.ts$",
62 | "transform": {
63 | "^.+\\.(t|j)s$": "ts-jest"
64 | },
65 | "setupFilesAfterEnv": [
66 | "../test-tools/jest.setup.ts"
67 | ],
68 | "collectCoverageFrom": [
69 | "**/*.(t|j)s",
70 | "!**/*.decorator.ts",
71 | "!**/index.ts",
72 | "!**/*.module.ts"
73 | ],
74 | "reporters": [
75 | "default",
76 | [
77 | "jest-stare",
78 | {
79 | "resultDir": "../test-results/server",
80 | "reportTitle": "jest-stare!",
81 | "additionalResultsProcessors": [
82 | "jest-junit"
83 | ],
84 | "coverageLink": "./coverage/lcov-report/index.html"
85 | }
86 | ]
87 | ],
88 | "coverageDirectory": "../../test-results/server/coverage",
89 | "testEnvironment": "node",
90 | "moduleNameMapper": {
91 | "^@kb-server$": "",
92 | "^@kb-models$": "/models/index",
93 | "^@kb-abstracts$": "/abstracts/index",
94 | "^@kb-decorators$": "/decorators/index",
95 | "^@kb-filters$": "/filters/index",
96 | "^@kb-api$": "/api/index",
97 | "^@kb-events$": "/events/index",
98 | "^@kb-app$": "/app/index",
99 | "^@kb-tasks": "/tasks/index"
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | @kibibit/kb-server-client-template
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | DESCRIPTION
30 |
31 |
32 |
33 | ## Installation
34 | ## Usage
35 |
36 | ## Contributors ✨
37 |
38 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
39 |
40 |
41 |
42 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind are welcome!
54 |
55 | ## Stay in touch
56 |
57 | - Author - [Neil Kalman](https://github.com/thatkookooguy)
58 | - Website - [https://github.com/kibibit](https://github.com/kibibit)
59 | - StackOverflow - [thatkookooguy](https://stackoverflow.com/users/1788884/thatkookooguy)
60 | - Twitter - [@thatkookooguy](https://twitter.com/thatkookooguy)
61 | - Twitter - [@kibibit_opensrc](https://twitter.com/kibibit_opensrc)
62 |
--------------------------------------------------------------------------------
/client/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "tslint:recommended",
3 | "rulesDirectory": [
4 | "codelyzer"
5 | ],
6 | "rules": {
7 | "align": {
8 | "options": [
9 | "parameters",
10 | "statements"
11 | ]
12 | },
13 | "array-type": false,
14 | "arrow-return-shorthand": true,
15 | "curly": true,
16 | "deprecation": {
17 | "severity": "warning"
18 | },
19 | "eofline": true,
20 | "import-blacklist": [
21 | true,
22 | "rxjs/Rx"
23 | ],
24 | "import-spacing": true,
25 | "indent": {
26 | "options": [
27 | "spaces"
28 | ]
29 | },
30 | "max-classes-per-file": false,
31 | "max-line-length": [
32 | true,
33 | 140
34 | ],
35 | "member-ordering": [
36 | true,
37 | {
38 | "order": [
39 | "static-field",
40 | "instance-field",
41 | "static-method",
42 | "instance-method"
43 | ]
44 | }
45 | ],
46 | "no-console": [
47 | true,
48 | "debug",
49 | "info",
50 | "time",
51 | "timeEnd",
52 | "trace"
53 | ],
54 | "no-empty": false,
55 | "no-inferrable-types": [
56 | true,
57 | "ignore-params"
58 | ],
59 | "no-non-null-assertion": true,
60 | "no-redundant-jsdoc": true,
61 | "no-switch-case-fall-through": true,
62 | "no-var-requires": false,
63 | "object-literal-key-quotes": [
64 | true,
65 | "as-needed"
66 | ],
67 | "quotemark": [
68 | true,
69 | "single"
70 | ],
71 | "semicolon": {
72 | "options": [
73 | "always"
74 | ]
75 | },
76 | "space-before-function-paren": {
77 | "options": {
78 | "anonymous": "never",
79 | "asyncArrow": "always",
80 | "constructor": "never",
81 | "method": "never",
82 | "named": "never"
83 | }
84 | },
85 | "typedef": [
86 | true,
87 | "call-signature"
88 | ],
89 | "typedef-whitespace": {
90 | "options": [
91 | {
92 | "call-signature": "nospace",
93 | "index-signature": "nospace",
94 | "parameter": "nospace",
95 | "property-declaration": "nospace",
96 | "variable-declaration": "nospace"
97 | },
98 | {
99 | "call-signature": "onespace",
100 | "index-signature": "onespace",
101 | "parameter": "onespace",
102 | "property-declaration": "onespace",
103 | "variable-declaration": "onespace"
104 | }
105 | ]
106 | },
107 | "variable-name": {
108 | "options": [
109 | "ban-keywords",
110 | "check-format",
111 | "allow-pascal-case"
112 | ]
113 | },
114 | "whitespace": {
115 | "options": [
116 | "check-branch",
117 | "check-decl",
118 | "check-operator",
119 | "check-separator",
120 | "check-type",
121 | "check-typecast"
122 | ]
123 | },
124 | "component-class-suffix": true,
125 | "contextual-lifecycle": true,
126 | "directive-class-suffix": true,
127 | "no-conflicting-lifecycle": true,
128 | "no-host-metadata-property": true,
129 | "no-input-rename": true,
130 | "no-inputs-metadata-property": true,
131 | "no-output-native": true,
132 | "no-output-on-prefix": true,
133 | "no-output-rename": true,
134 | "no-outputs-metadata-property": true,
135 | "template-banana-in-box": true,
136 | "template-no-negated-async": true,
137 | "use-lifecycle-interface": true,
138 | "use-pipe-transform-interface": true,
139 | "directive-selector": [
140 | true,
141 | "attribute",
142 | "app",
143 | "camelCase"
144 | ],
145 | "component-selector": [
146 | true,
147 | "element",
148 | "app",
149 | "kebab-case"
150 | ]
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/.devcontainer/devcontainer.json:
--------------------------------------------------------------------------------
1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
2 | // https://github.com/microsoft/vscode-dev-containers/tree/v0.158.0/containers/typescript-node
3 | {
4 | "name": "kb-server-client-template",
5 | "dockerComposeFile": "docker-compose.yml",
6 | "service": "app",
7 | "workspaceFolder": "/workspace",
8 | // Set *default* container specific settings.json values on container create.
9 | "settings": {
10 | "terminal.integrated.shell.linux": "/bin/zsh",
11 | "peacock.color": "#3334f4",
12 | "editor.tabSize": 2,
13 | "editor.insertSpaces": true,
14 | "editor.detectIndentation": false,
15 | "editor.rulers": [80],
16 | "editor.fontFamily": "Hack, Menlo, Monaco, 'Courier New', monospace",
17 | "editor.matchBrackets": "always",
18 | // Terminal
19 | "terminal.integrated.fontFamily": "Hack, Menlo, Monaco, 'Courier New', monospace",
20 | "terminal.integrated.fontSize": 14,
21 | // Workbench
22 | "workbench.colorTheme": "Andromeda",
23 | "workbench.editor.showIcons": true,
24 | "workbench.iconTheme": "vs-seti",
25 | // Bracket Pair Colorizer
26 | "bracketPairColorizer.colorMode": "Consecutive",
27 | "bracketPairColorizer.forceUniqueOpeningColor": true,
28 | "bracketPairColorizer.showBracketsInGutter": true,
29 | "window.title": "${activeEditorShort}${separator}${rootName} [kibibit]",
30 | "typescriptHero.imports.stringQuoteStyle": "'",
31 | "typescriptHero.imports.grouping": [
32 | "Plains",
33 | "Modules",
34 | "/^@kb-/",
35 | "Workspace"
36 |
37 | ],
38 | "typescriptHero.imports.organizeOnSave": true,
39 | "typescriptHero.imports.multiLineTrailingComma": false,
40 | "typescriptHero.imports.multiLineWrapThreshold": 80,
41 | "typescriptHero.imports.insertSpaceBeforeAndAfterImportBraces": true,
42 | "typescriptHero.imports.insertSemicolons": true,
43 | "editor.codeActionsOnSave": {
44 | "source.fixAll.eslint": true,
45 | },
46 | "eslint.format.enable": true,
47 | "vsicons.presets.angular": true,
48 | "eslint.workingDirectories": [
49 | "client/",
50 | "server/"
51 | ]
52 | },
53 |
54 | // Add the IDs of extensions you want installed when the container is created.
55 | "extensions": [
56 | "dbaeumer.vscode-eslint",
57 | "rbbit.typescript-hero",
58 | "coenraads.bracket-pair-colorizer",
59 | "orta.vscode-jest",
60 | "wix.vscode-import-cost",
61 | "actboy168.tasks",
62 | "johnpapa.vscode-peacock",
63 | "angular.ng-template",
64 | "abhijoybasak.nestjs-files",
65 | "eamodio.gitlens",
66 | "codeandstuff.package-json-upgrade",
67 | "mongodb.mongodb-vscode",
68 | "ms-azuretools.vscode-docker",
69 | "jock.svg"
70 | ],
71 |
72 | // Use 'forwardPorts' to make a list of ports inside the container available locally.
73 | "forwardPorts": [
74 | 10101, // dev client
75 | 10102, // dev server
76 | 27017 // mongodb instance
77 | ],
78 | "portsAttributes": {
79 | "10101": {
80 | "label": "Client",
81 | "onAutoForward": "notify"
82 | },
83 | "10102": {
84 | "label": "Server",
85 | "onAutoForward": "notify"
86 | },
87 | "27017": {
88 | "label": "MongoDB",
89 | "onAutoForward": "notify"
90 | },
91 | "9876": {
92 | "label": "TESTS: Client Unit Tests",
93 | "onAutoForward": "notify"
94 | },
95 | "4200": {
96 | "label": "TESTS: Client E2E",
97 | "onAutoForward": "notify"
98 | },
99 | },
100 |
101 | // Use 'postCreateCommand' to run commands after the container is created.
102 | // "postCreateCommand": "yarn install",
103 | // "mounts": [
104 | // "source=${localWorkspaceFolderBasename}-node_modules,target=${containerWorkspaceFolder}/node_modules,type=volume"
105 | // ],
106 |
107 | // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
108 | "remoteUser": "node"
109 | }
--------------------------------------------------------------------------------
/client/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "version": 1,
4 | "newProjectRoot": "projects",
5 | "projects": {
6 | "client": {
7 | "projectType": "application",
8 | "schematics": {
9 | "@schematics/angular:component": {
10 | "style": "scss"
11 | }
12 | },
13 | "root": "",
14 | "sourceRoot": "src",
15 | "prefix": "app",
16 | "architect": {
17 | "build": {
18 | "builder": "@angular-devkit/build-angular:browser",
19 | "options": {
20 | "outputPath": "../dist/client",
21 | "index": "src/index.html",
22 | "main": "src/main.ts",
23 | "polyfills": "src/polyfills.ts",
24 | "tsConfig": "tsconfig.app.json",
25 | "aot": true,
26 | "assets": [
27 | "src/favicon.ico",
28 | "src/assets"
29 | ],
30 | "styles": [
31 | "src/styles.scss"
32 | ],
33 | "scripts": []
34 | },
35 | "configurations": {
36 | "production": {
37 | "fileReplacements": [
38 | {
39 | "replace": "src/environments/environment.ts",
40 | "with": "src/environments/environment.prod.ts"
41 | }
42 | ],
43 | "optimization": true,
44 | "outputHashing": "all",
45 | "sourceMap": false,
46 | "extractCss": true,
47 | "namedChunks": false,
48 | "extractLicenses": true,
49 | "vendorChunk": false,
50 | "buildOptimizer": true,
51 | "budgets": [
52 | {
53 | "type": "initial",
54 | "maximumWarning": "2mb",
55 | "maximumError": "5mb"
56 | },
57 | {
58 | "type": "anyComponentStyle",
59 | "maximumWarning": "6kb",
60 | "maximumError": "10kb"
61 | }
62 | ]
63 | }
64 | }
65 | },
66 | "serve": {
67 | "builder": "@angular-devkit/build-angular:dev-server",
68 | "options": {
69 | "browserTarget": "client:build"
70 | },
71 | "configurations": {
72 | "production": {
73 | "browserTarget": "client:build:production"
74 | }
75 | }
76 | },
77 | "extract-i18n": {
78 | "builder": "@angular-devkit/build-angular:extract-i18n",
79 | "options": {
80 | "browserTarget": "client:build"
81 | }
82 | },
83 | "test": {
84 | "builder": "@angular-devkit/build-angular:karma",
85 | "options": {
86 | "main": "src/test.ts",
87 | "polyfills": "src/polyfills.ts",
88 | "tsConfig": "tsconfig.spec.json",
89 | "karmaConfig": "karma.conf.js",
90 | "assets": [
91 | "src/favicon.ico",
92 | "src/assets"
93 | ],
94 | "styles": [
95 | "src/styles.scss"
96 | ],
97 | "scripts": []
98 | }
99 | },
100 | "lint": {
101 | "builder": "@angular-eslint/builder:lint",
102 | "options": {
103 | "lintFilePatterns": [
104 | "src/**/*.ts",
105 | "src/**/*.html"
106 | ]
107 | }
108 | },
109 | "e2e": {
110 | "builder": "@angular-devkit/build-angular:protractor",
111 | "options": {
112 | "protractorConfig": "e2e/protractor.conf.js",
113 | "devServerTarget": "client:serve"
114 | },
115 | "configurations": {
116 | "production": {
117 | "devServerTarget": "client:serve:production"
118 | }
119 | }
120 | }
121 | }
122 | }
123 | },
124 | "defaultProject": "client"
125 | }
126 |
--------------------------------------------------------------------------------
/server/src/abstracts/base.service.abstract.ts:
--------------------------------------------------------------------------------
1 | import { InternalServerErrorException } from '@nestjs/common';
2 | import { DocumentType, ReturnModelType } from '@typegoose/typegoose';
3 | import { AnyParamConstructor } from '@typegoose/typegoose/lib/types';
4 | import { MongoError } from 'mongodb';
5 | import { Document, DocumentQuery, Query, Types } from 'mongoose';
6 |
7 | import { BaseModel } from './base.model.abstract';
8 |
9 |
10 | type QueryList = DocumentQuery<
11 | Array>,
12 | DocumentType
13 | >;
14 | type QueryItem = DocumentQuery<
15 | DocumentType,
16 | DocumentType
17 | >;
18 |
19 | export abstract class BaseService {
20 | protected model: ReturnModelType>;
21 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
22 | protected class: any;
23 |
24 | protected constructor(
25 | model: ReturnModelType>,
26 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
27 | serviceClass: any
28 | ) {
29 | this.model = model;
30 | this.class = serviceClass;
31 | }
32 |
33 | protected static throwMongoError(err: MongoError): void {
34 | throw new InternalServerErrorException(err, err.errmsg);
35 | }
36 |
37 | protected static toObjectId(id: string): Types.ObjectId {
38 | try {
39 | return Types.ObjectId(id);
40 | } catch (e) {
41 | this.throwMongoError(e);
42 | }
43 | }
44 |
45 | createModel(doc?: Partial): T {
46 | return new this.model(doc);
47 | }
48 |
49 | findAll(filter = {}): QueryList {
50 | return this.model.find(filter);
51 | }
52 |
53 | async findAllAsync(filter = {}): Promise>> {
54 | try {
55 | return await this.findAll(filter).exec();
56 | } catch (e) {
57 | BaseService.throwMongoError(e);
58 | }
59 | }
60 |
61 | findOne(filter = {}): QueryItem {
62 | return this.model.findOne(filter);
63 | }
64 |
65 | async findOneAsync(filter = {}): Promise> {
66 | try {
67 | return await this.findOne(filter).exec();
68 | } catch (e) {
69 | BaseService.throwMongoError(e);
70 | }
71 | }
72 |
73 | findById(id: string): QueryItem {
74 | return this.model.findById(BaseService.toObjectId(id));
75 | }
76 |
77 | async findByIdAsync(id: string): Promise> {
78 | try {
79 | return await this.findById(id).exec();
80 | } catch (e) {
81 | BaseService.throwMongoError(e);
82 | }
83 | }
84 |
85 | async create(item: T): Promise {
86 | try {
87 | const persistedItem = await this.model.create(item);
88 | return new this.class(persistedItem.toObject());
89 | } catch (e) {
90 | BaseService.throwMongoError(e);
91 | }
92 | }
93 |
94 | delete(filter = {}): QueryItem {
95 | return this.model.findOneAndDelete(filter);
96 | }
97 |
98 | async deleteAsync(filter = {}): Promise> {
99 | try {
100 | return await this.delete(filter).exec();
101 | } catch (e) {
102 | BaseService.throwMongoError(e);
103 | }
104 | }
105 |
106 | deleteById(id: string): QueryItem {
107 | return this.model.findByIdAndDelete(BaseService.toObjectId(id));
108 | }
109 |
110 | async deleteByIdAsync(id: string): Promise> {
111 | try {
112 | return await this.deleteById(id).exec();
113 | } catch (e) {
114 | BaseService.throwMongoError(e);
115 | }
116 | }
117 |
118 | update(item: Partial): QueryItem {
119 | return this.model.findByIdAndUpdate(BaseService.toObjectId(item.id), item, {
120 | new: true
121 | });
122 | }
123 |
124 | async updateAsync(item: Partial): Promise> {
125 | try {
126 | return await this.update(item).exec();
127 | } catch (e) {
128 | BaseService.throwMongoError(e);
129 | }
130 | }
131 |
132 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
133 | count(filter = {}): Query> {
134 | return this.model.count(filter);
135 | }
136 |
137 | async countAsync(filter = {}): Promise {
138 | try {
139 | return await this.count(filter);
140 | } catch (e) {
141 | BaseService.throwMongoError(e);
142 | }
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@kibibit/kb-server-client-template",
3 | "version": "0.0.0-development",
4 | "description": "",
5 | "main": "dist/server/main.js",
6 | "scripts": {
7 | "prune-branches": "node ./tools/scripts/prune-untrackted-branches.js",
8 | "contributors:all": "cross-env HUSKY_SKIP_HOOKS=1 node ./tools/get-all-contributors.js",
9 | "contributors:add": "cross-env HUSKY_SKIP_HOOKS=1 all-contributors add",
10 | "contributors:generate": "cross-env HUSKY_SKIP_HOOKS=1 all-contributors generate",
11 | "install": "npm run install:server && npm run install:client",
12 | "install:client": "cd client && npm install",
13 | "install:server": "cd server && npm install",
14 | "build": "npm run build:server && npm run build:client",
15 | "build:client": "cd ./client && npm run build",
16 | "build:server": "cd ./server && npm run build",
17 | "start:client": "cd ./client && npm run start",
18 | "start:server": "cd ./server && npm run start:dev",
19 | "init": "node ./tools/replace-template-string.js",
20 | "generate-barrels": "barrelsby --delete -d ./src -l below -q",
21 | "semantic-release:setup": "semantic-release-cli setup",
22 | "lint": "eslint \"{src,apps,libs,test}/**/*.ts\"",
23 | "lint:fix": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
24 | "test": "echo \"Error: no test specified\" && exit 1",
25 | "test:server-unit": "cd server && npm run test:cov",
26 | "test:client-unit": "cd client && npm run test:cov",
27 | "test:api": "cd server && npm run test:e2e",
28 | "test:e2e": "cd client && npm run e2e",
29 | "semantic-release": "semantic-release"
30 | },
31 | "author": "thatkookooguy ",
32 | "license": "MIT",
33 | "devDependencies": {
34 | "@commitlint/cli": "^11.0.0",
35 | "@commitlint/config-angular": "^11.0.0",
36 | "@commitlint/config-conventional": "^11.0.0",
37 | "@types/cron": "^1.7.2",
38 | "@types/find-root": "^1.1.2",
39 | "@types/mongoose": "^5.10.3",
40 | "@types/socket.io": "^2.1.13",
41 | "@typescript-eslint/eslint-plugin": "^4.14.2",
42 | "@typescript-eslint/parser": "^4.14.2",
43 | "all-contributors-cli": "^6.20.0",
44 | "cli-table": "^0.3.6",
45 | "commitizen": "^4.2.3",
46 | "cross-env": "^7.0.3",
47 | "cz-conventional-changelog": "^3.3.0",
48 | "cz-conventional-changelog-emoji": "^0.1.0",
49 | "eslint": "^7.19.0",
50 | "foswig": "^3.0.1",
51 | "github-username": "^6.0.0",
52 | "gitlog": "^4.0.4",
53 | "husky": "^4.3.8",
54 | "inquirer": "^8.0.0",
55 | "jest-stare": "^2.2.1",
56 | "karma-htmlfile-reporter": "^0.3.8",
57 | "ncp": "^2.0.0",
58 | "package-name-regex": "^1.0.11",
59 | "replace-in-file": "^6.2.0",
60 | "semantic-release": "^17.3.8",
61 | "semantic-release-cli": "^5.4.3",
62 | "sentencer": "^0.2.1",
63 | "shelljs": "^0.8.4",
64 | "simple-git": "^2.37.0",
65 | "ts-node": "^9.1.1",
66 | "typescript": "^4.1.3"
67 | },
68 | "repository": {
69 | "type": "git",
70 | "url": "https://github.com/Kibibit/kb-server-client-template.git"
71 | },
72 | "bugs": {
73 | "url": "https://github.com/Kibibit/kb-server-client-template/issues"
74 | },
75 | "dependencies": {
76 | "@kibibit/consologo": "^1.2.0",
77 | "@kibibit/kb-error": "^1.0.3",
78 | "@nestjs/common": "^7.6.11",
79 | "@nestjs/core": "^7.6.11",
80 | "@nestjs/mongoose": "^7.0.4",
81 | "@nestjs/platform-express": "^7.6.11",
82 | "@nestjs/platform-socket.io": "^7.6.15",
83 | "@nestjs/platform-ws": "^7.6.15",
84 | "@nestjs/schedule": "^0.4.3",
85 | "@nestjs/swagger": "^4.7.12",
86 | "@nestjs/websockets": "^7.6.15",
87 | "@typegoose/typegoose": "^7.6.0",
88 | "axios": "^0.21.1",
89 | "class-transformer": "^0.3.2",
90 | "class-validator": "^0.13.1",
91 | "find-root": "^1.1.0",
92 | "fs-extra": "^9.1.0",
93 | "lodash": "^4.17.20",
94 | "mongoose": "^5.10.19",
95 | "reflect-metadata": "^0.1.13",
96 | "rimraf": "^3.0.2",
97 | "rxjs": "^6.6.3",
98 | "swagger-ui-express": "^4.1.6"
99 | },
100 | "config": {
101 | "commitizen": {
102 | "path": "./node_modules/cz-conventional-changelog-emoji"
103 | }
104 | },
105 | "husky": {
106 | "hooks": {
107 | "prepare-commit-msg": "exec < /dev/tty && git cz --hook || true",
108 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
109 | }
110 | },
111 | "publishConfig": {
112 | "access": "public"
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/tools/scripts/prune-untrackted-branches.js:
--------------------------------------------------------------------------------
1 | const simpleGit = require('simple-git/promise');
2 | const { chain, trim, map } = require('lodash');
3 | const inquirer = require('inquirer');
4 | const Table = require('cli-table');
5 |
6 | const git = simpleGit();
7 | const remoteInfoRegex = /^\[(.*?)\]\s/g;
8 | const MAIN_BRANCHES = [
9 | 'master',
10 | 'main'
11 | ];
12 |
13 | const chars = {
14 | 'top': '═', 'top-mid': '╤', 'top-left': '╔', 'top-right': '╗'
15 | , 'bottom': '═', 'bottom-mid': '╧', 'bottom-left': '╚', 'bottom-right': '╝'
16 | , 'left': '║', 'left-mid': '╟', 'mid': '─', 'mid-mid': '┼'
17 | , 'right': '║', 'right-mid': '╢', 'middle': '│'
18 | };
19 |
20 | (async () => {
21 | try {
22 | await git.fetch(['-p']); // prune? is it necassery?
23 | const branchSummaryResult = await git.branch(['-vv']);
24 | const localBranches = branchSummaryResult.branches;
25 | const localBranchesWithGoneRemotes = chain(localBranches)
26 | .filter((item) => !MAIN_BRANCHES.includes(item.name))
27 | .forEach((item) => {
28 | // console.log('an item appeared!', item);
29 | const remoteInfo = item.label.match(remoteInfoRegex);
30 |
31 | if (remoteInfo) {
32 | const parsedRemoteInfo = trim(remoteInfo[0], '[] ');
33 | const isMerged = parsedRemoteInfo.endsWith(': gone');
34 | const remoteBranchName = parsedRemoteInfo.replace(': gone', '');
35 |
36 | item.isMerged = isMerged;
37 | item.remoteName = remoteBranchName;
38 | }
39 | })
40 | .filter((item) => item.isMerged)
41 | .value();
42 | const branchNames = chain(localBranchesWithGoneRemotes).map('name').value();
43 |
44 | if (!branchNames.length) {
45 | console.log('PROJECT IS CLEAN! WELL DONE!');
46 | process.exit(0);
47 | }
48 |
49 | // interaction!
50 | const table = new Table({
51 | head: ['Branch Name', 'Origin Branch Name', 'Origin Branch Status'],
52 | chars
53 | });
54 |
55 | localBranchesWithGoneRemotes.forEach((item) => table.push([item.name, item.remoteName, 'GONE']));
56 |
57 | console.log(`${table.toString()}\n`);
58 |
59 | const ACTION_ANSWERS = {
60 | PRUNE_ALL: 'prune all gone branches',
61 | SELECT_BRANCHES: 'Selected Individual branches to delete'
62 | }
63 |
64 | const answers = await inquirer
65 | .prompt([
66 | {
67 | type: 'list',
68 | name: 'whatToDo',
69 | message: 'These branches have been deleted from the origin. What do you want to do with them?',
70 | choices: chain(ACTION_ANSWERS).values().value()
71 | }
72 | ]);
73 |
74 | if (answers.whatToDo === ACTION_ANSWERS.PRUNE_ALL) {
75 | await moveToAnotherBranchIfNeeded(branchSummaryResult, branchNames);
76 |
77 | const result = await git.deleteLocalBranches(branchNames, true);
78 | console.log('DONE');
79 | chain(result.all)
80 | .map((item) => `${ item.branch }: ${ item.success ? 'DELETED' : 'FAILED' }`)
81 | .forEach((item) => console.log(item));
82 | return;
83 | }
84 |
85 | if (answers.whatToDo === ACTION_ANSWERS.SELECT_BRANCHES) {
86 | const answers = await inquirer
87 | .prompt([
88 | {
89 | type: 'checkbox',
90 | message: 'These branches have been deleted from the origin. Do you want to prune them?',
91 | name: 'pruneBranches',
92 | choices: branchNames
93 | }
94 | ]);
95 |
96 | await moveToAnotherBranchIfNeeded(branchSummaryResult, branchNames);
97 |
98 | const result = await Promise.all(answers.pruneBranches.map((branchName) => git.deleteLocalBranch(branchName, true)));
99 | console.log('DONE');
100 | console.log(result);
101 | chain(result.all)
102 | .map((item) => `${ item.branch }: ${ item.success ? 'DELETED' : 'FAILED' }`)
103 | .forEach((item) => console.log(item));
104 | return;
105 | }
106 | } catch (err) {
107 | console.error(err);
108 | process.exit(1);
109 | }
110 | })();
111 |
112 | async function moveToAnotherBranchIfNeeded(branchSummaryResult, branchesToDelete) {
113 | const suspectedMainBranch = branchSummaryResult.all.find((branchName) => MAIN_BRANCHES.includes(branchName));
114 | const currentCheckedoutBranch = branchSummaryResult.current;
115 |
116 | // console.log('main branch:', suspectedMainBranch);
117 |
118 | if (branchesToDelete.includes(currentCheckedoutBranch)) {
119 | console.warn(`trying to delete checkedout branch ${ currentCheckedoutBranch }. moving to ${ suspectedMainBranch }`);
120 | await git.checkout(suspectedMainBranch);
121 | }
122 | }
--------------------------------------------------------------------------------
/tools/data/names-dictionary.js:
--------------------------------------------------------------------------------
1 | exports.dictionary = [
2 | "coke",
3 | "bootstrap",
4 | "ikSelect",
5 | "selectik",
6 | "tinycon",
7 | "foswiki",
8 | "socketstream",
9 | "elFinder",
10 | "basket.js",
11 | "jaws",
12 | "tinyhook",
13 | "Sozi",
14 | "Zeppelin",
15 | "Propilex",
16 | "fontomas",
17 | "lernanta",
18 | "Sentimental",
19 | "sink.js",
20 | "towerjs.org",
21 | "cloud9",
22 | "openlayers",
23 | "phonegap",
24 | "prototype",
25 | "APIConnect",
26 | "collusion",
27 | "less.js",
28 | "nodeclub",
29 | "gaia",
30 | "CodeMirror2",
31 | "arbor",
32 | "scriptish",
33 | "stylebot",
34 | "experiments",
35 | "backbone",
36 | "ember.js",
37 | "zotero",
38 | "jStorage",
39 | "richard",
40 | "duino",
41 | "underscore",
42 | "mediaQueryBookmarklet",
43 | "morpheus",
44 | "cryptocat",
45 | "backbone.marionette",
46 | "Modernizr",
47 | "supersized",
48 | "animatable",
49 | "connect",
50 | "jshint",
51 | "turn.js",
52 | "impress.js",
53 | "emscripten",
54 | "WAT",
55 | "wysihtml5",
56 | "node",
57 | "FitVids.js",
58 | "deployinator",
59 | "kartograph.js",
60 | "broadway",
61 | "tako",
62 | "passport",
63 | "docs",
64 | "nanoblocks",
65 | "dwb",
66 | "jake",
67 | "galleria",
68 | "ShareJS",
69 | "d3",
70 | "MasteringJQuery",
71 | "express",
72 | "history.js",
73 | "R2",
74 | "EaselJS",
75 | "Iconic",
76 | "KineticJS",
77 | "semantic.gs",
78 | "buster",
79 | "chosen",
80 | "pdf.js",
81 | "wapama",
82 | "transparency",
83 | "droparea",
84 | "jade",
85 | "shelljs",
86 | "autodafe",
87 | "cdnjs",
88 | "antiscroll",
89 | "esprima",
90 | "Unslider",
91 | "mocha",
92 | "three.js",
93 | "spine",
94 | "fluidinfo.js",
95 | "nutzdemo",
96 | "funcunit",
97 | "scriptaculous",
98 | "nohm",
99 | "jitsu",
100 | "timePicker",
101 | "markdowndeep",
102 | "maqetta",
103 | "qunit",
104 | "formhub",
105 | "studio3",
106 | "picturefill",
107 | "zepto",
108 | "suite",
109 | "scrollpath",
110 | "mulberry",
111 | "FlightDeck",
112 | "notifer.js",
113 | "jbrowse",
114 | "jappix",
115 | "yui3",
116 | "Geolib",
117 | "bootstrap4gwt",
118 | "patio",
119 | "CATMAID",
120 | "Respond",
121 | "jScrollPane",
122 | "faye",
123 | "lesscss.org",
124 | "ThreeNodes.js",
125 | "dgrid",
126 | "gfx",
127 | "Reel",
128 | "jsbin",
129 | "progit",
130 | "jurlp",
131 | "liquid.js",
132 | "browserid",
133 | "py2js",
134 | "locomotive",
135 | "translators",
136 | "moment",
137 | "png.js",
138 | "ace",
139 | "hypernotes",
140 | "Scribite",
141 | "noty",
142 | "use.js",
143 | "Idle.js",
144 | "Haraka",
145 | "ql.io",
146 | "Leaflet",
147 | "UglifyJS",
148 | "maptail",
149 | "wro4j",
150 | "WikipediaMobile",
151 | "dialect",
152 | "angular.js",
153 | "translate.js",
154 | "gury",
155 | "i18next",
156 | "mapknitter",
157 | "jsperanto",
158 | "mustache.js",
159 | "numeric",
160 | "Phraseanet",
161 | "MathJax",
162 | "AnythingSlider",
163 | "html5demos",
164 | "wysihat",
165 | "showoff",
166 | "NES",
167 | "J3D",
168 | "JSLint",
169 | "scrumblr",
170 | "chaplin",
171 | "jsduck",
172 | "uikit",
173 | "chartjunk",
174 | "tquery",
175 | "recanvas",
176 | "Amanda",
177 | "MotionCAPTCHA",
178 | "sproutcore",
179 | "recollection",
180 | "Kalendae",
181 | "visualsearch",
182 | "jPlayer",
183 | "TiShadow",
184 | "binaergewitter.github.com",
185 | "lionbars",
186 | "hexagame",
187 | "Namespace.js",
188 | "howtonode.org",
189 | "jasmine",
190 | "Sinon.JS",
191 | "Mandible",
192 | "Mandible2",
193 | "JSJaC",
194 | "AntiMap",
195 | "whiskey",
196 | "EurekaJ",
197 | "github",
198 | "uptime",
199 | "tesseract",
200 | "At.js",
201 | "reveal.js",
202 | "grunt",
203 | "ltx",
204 | "jstat",
205 | "Skeleton",
206 | "facetview",
207 | "restler",
208 | "Espresso",
209 | "faux",
210 | "envisionjs",
211 | "hieroglyph",
212 | "nervous",
213 | "ed.gridpak",
214 | "blossom",
215 | "Crafty",
216 | "examples",
217 | "WeiboSDK",
218 | "Tattoo",
219 | "easyXDM",
220 | "tweet",
221 | "h5Validate",
222 | "curl",
223 | "topcube",
224 | "yiiext.github.com",
225 | "onyx",
226 | "PDFObject",
227 | "SlickGrid",
228 | "enyo",
229 | "microjs.com",
230 | "stanfordacm.github.com",
231 | "readabilitySAX",
232 | "sjcl",
233 | "data",
234 | "node.couch.js",
235 | "umd",
236 | "CodePen",
237 | "engine.io",
238 | "melonJS",
239 | "help.github.com",
240 | "jQTouch",
241 | "Hotot",
242 | "jath",
243 | "mathquill",
244 | "fullcalendar",
245 | "nodester",
246 | "Sprite3D.js",
247 | "CloudEdit",
248 | "money.js",
249 | "gmap3",
250 | "node.io",
251 | "MiniCart",
252 | "tilemill",
253 | "RPG",
254 | "bbclonemail",
255 | "amber",
256 | "jRumble",
257 | "paper.js",
258 | "fulcrum",
259 | "todomvc",
260 | "mongojs",
261 | "NewsBlur",
262 | "vexchords",
263 | "Aardwolf",
264 | "asyncblock",
265 | "buzz",
266 | "Locker",
267 | "AncientBeast",
268 | "collective.github.com",
269 | "hammer.js",
270 | "puffer",
271 | "sigma.js",
272 | "subway",
273 | "carrier",
274 | "csslint",
275 | "EpicEditor",
276 | "Font.js",
277 | "SyntaxHighlighter",
278 | "TinyNav.js",
279 | "backstrapp",
280 | "datacouch",
281 | "transform",
282 | "cycle",
283 | "cufon",
284 | "Breakout",
285 | "highlight.js",
286 | "CanvasLoader",
287 | "mailcheck",
288 | "ejs",
289 | "autosize",
290 | "idiomatic.js",
291 | "bouncy",
292 | "rickshaw",
293 | "ringojs",
294 | "haibu",
295 | "quarkjs",
296 | "TheodoRogerCmsBundle",
297 | "jQ.Mobi",
298 | "nodemon",
299 | "BrowserQuest",
300 | "knockout",
301 | "Laro",
302 | "smokescreen",
303 | "snack",
304 | "requirejs",
305 | "QueryLoader2",
306 | "filtrify",
307 | "jslider",
308 | "knockout.mapping",
309 | "JAK",
310 | "hook.io",
311 | "cloudifysource.github.com",
312 | "jcarousel",
313 | "Tachyon",
314 | "expresso",
315 | "lectric",
316 | "core",
317 | "Han",
318 | "Lungo.js",
319 | "bonzo",
320 | "npm",
321 | "rainbow",
322 | "jQuery.superLabels",
323 | "fancyBox",
324 | "elRTE",
325 | "remo",
326 | "Monocle",
327 | "socket.io",
328 | "wireit",
329 | "bean",
330 | "libredocs",
331 | "backbone.modelbinding",
332 | "geonode",
333 | "jsdoc",
334 | "Bazar",
335 | "apf",
336 | "deployd",
337 | "natural",
338 | "gm",
339 | "leafclusterer",
340 | "ni",
341 | "jsDAV",
342 | "cockpit.js",
343 | "Proj4Leaflet",
344 | "winston",
345 | "xsschef",
346 | "kanso",
347 | "TypedJS",
348 | "klass",
349 | "backbonetutorials",
350 | "pl",
351 | "hnmobile",
352 | "Impressionist",
353 | "DropKick",
354 | "nios",
355 | "create",
356 | "cartodb",
357 | "website",
358 | "fireworks",
359 | "Ender",
360 | "quicksearch",
361 | "zoomooz",
362 | "poserver",
363 | "swipe",
364 | "mViewer",
365 | "mongoose",
366 | "deck.js",
367 | "siwapp",
368 | "ImageMapster",
369 | "firebug",
370 | "procstreams",
371 | "jsPlumb",
372 | "cube",
373 | "Timeline",
374 | "Slatwall",
375 | "pouchdb",
376 | "statsd",
377 | "x3dom",
378 | "mailparser",
379 | "MaSha",
380 | "Cocktail",
381 | "curtain.js",
382 | "Flotr2",
383 | "topup",
384 | "tty.js",
385 | "volo",
386 | "planet",
387 | "Wappalyzer",
388 | "imgg",
389 | "deferred",
390 | "jsrender",
391 | "fbgraph",
392 | "stsh",
393 | "TrelloScrum",
394 | "node.gzip",
395 | "inliner",
396 | "Sequence",
397 | "chromeless",
398 | "taffydb",
399 | "buildy",
400 | "isotope",
401 | "soda",
402 | "store.js",
403 | "Slides",
404 | "KickJS",
405 | "kissy",
406 | "modules",
407 | "superagent",
408 | "stacey",
409 | "xitrum",
410 | "highcharts.com",
411 | "jsconsole",
412 | "seajs",
413 | "cromagjs",
414 | "getUserMedia.js",
415 | "davis.js",
416 | "phpjs",
417 | "nightlytt",
418 | "jscex",
419 | "Thruk",
420 | "jqGrid",
421 | "robnyman.github.com",
422 | "html5test",
423 | "WebSonic",
424 | "jsdeferred",
425 | "erjjones.github.com",
426 | "mxui",
427 | "ViMbAdmin",
428 | "git.js",
429 | "FitText.js",
430 | "wnframework",
431 | "vimfiles",
432 | "coveraje",
433 | "jmpress.js",
434 | "learningthreejs.com",
435 | "tiddlywiki",
436 | "Camera",
437 | "inputex",
438 | "audiojs",
439 | "soulmate.js",
440 | "ccdump",
441 | "webshim",
442 | "GloveBox",
443 | "nodemock",
444 | "tipsy",
445 | "gbone.js",
446 | "threefab",
447 | "H5F",
448 | "versionator",
449 | "DeliveryTracker",
450 | "pngy",
451 | "wtfjs",
452 | "Backbone.SharePoint",
453 | "gzippo",
454 | "sandbox",
455 | "Copper",
456 | "swfobject",
457 | "tinymce",
458 | "qooxdoo",
459 | "rlightbox2",
460 | "ezjscore",
461 | "building",
462 | "playpainter",
463 | "liteAccordion",
464 | "alleup",
465 | "globalize",
466 | "BookClub",
467 | "qTip2",
468 | "jPages",
469 | "smoothie",
470 | "immortal",
471 | "raphael",
472 | "orbital",
473 | "infiniverse",
474 | "Ext.ux.Router",
475 | "mercury",
476 | "karl",
477 | "perl6.org",
478 | "inflickity",
479 | "Sugar",
480 | "sizzle",
481 | "hogan.js",
482 | "substance",
483 | "jQRangeSlider",
484 | "imagesloaded",
485 | "html5dashboard",
486 | "o2.js",
487 | "twitter.github.com",
488 | "abaaso",
489 | "Tangle",
490 | "move.js",
491 | "water",
492 | "selectorgadget",
493 | "artwork",
494 | "streamlinejs",
495 | "validity",
496 | "nodeunit",
497 | "PhotoSwipe",
498 | "winkstart",
499 | "shareabouts",
500 | "actionHero",
501 | "mockJSON",
502 | "PxLoader",
503 | "nimbupani.github.com",
504 | "chico",
505 | "js2coffee",
506 | "ttTools",
507 | "script.js",
508 | "DeftJS",
509 | "aws2js",
510 | "pulley",
511 | "Products.TinyMCE",
512 | "ResponsiveSlides.js",
513 | "marked",
514 | "diveintohtml5",
515 | "mozmill",
516 | "zeromq.node",
517 | "noodletalk",
518 | "agility",
519 | "URI.js",
520 | "contextify",
521 | "nanoScrollerJS",
522 | "formalize",
523 | "Hasher",
524 | "compact",
525 | "json3",
526 | "yuidocjs",
527 | "watch",
528 | "form",
529 | "webidl.js",
530 | "datajam",
531 | "frontend",
532 | "eol",
533 | "openbadges",
534 | "Log.io",
535 | "async",
536 | "jslitmus",
537 | "areweplayingyet",
538 | "billy",
539 | "repl.it",
540 | "WebGL101",
541 | "cdf",
542 | "toc",
543 | "scriptular",
544 | "dochub",
545 | "bananabomber",
546 | "libcanvas",
547 | "ws",
548 | "scrolldeck.js",
549 | "html5boilerplate.com",
550 | "accessifyhtml5.js",
551 | "Pagify",
552 | "CubicVR.js",
553 | "should.js",
554 | "1.x",
555 | "underscore.string",
556 | "scrollorama",
557 | "geddy",
558 | "jsviews",
559 | "MQTT.js",
560 | "sagecell",
561 | "jribbble",
562 | "datavore",
563 | "openwebapps",
564 | "Thinktecture.IdentityModel.Http",
565 | "linguist",
566 | "fleet",
567 | "W",
568 | "scrolly",
569 | "radio",
570 | "backbone.iobind",
571 | "pure",
572 | "HighRoller",
573 | "320andup",
574 | "dom.js",
575 | "fileupload",
576 | "plates",
577 | "scala.github.com",
578 | "jqfundamentals",
579 | "classList.js",
580 | "WatchMen",
581 | "Monorail.js",
582 | "clicktoplugin",
583 | "bigdesk",
584 | "ichabod",
585 | "ueberDB",
586 | "coweb",
587 | "PiplMesh",
588 | "Seriously.js",
589 | "XRegExp",
590 | "benchmark.js",
591 | "fabric.js",
592 | "ghostdriver",
593 | "procouch",
594 | "when",
595 | "orbited2",
596 | "dojo",
597 | "everyauth",
598 | "csso",
599 | "ZeParser",
600 | "Swipe",
601 | "sequelize",
602 | "tablesorter",
603 | "foresight.js",
604 | "butter",
605 | "cytoscapeweb",
606 | "messenger.js",
607 | "nools",
608 | "taberareloo",
609 | "bamboo",
610 | "youtify",
611 | "nodrr",
612 | "textualizer",
613 | "html2canvas",
614 | "ui",
615 | "molt",
616 | "NodObjC",
617 | "dance",
618 | "Anemometer",
619 | "sumorio",
620 | "r.js",
621 | "Services.bundle",
622 | "WebSqlSync",
623 | "Backbone.localStorage",
624 | "roy",
625 | "Perspectives",
626 | "instachrome",
627 | "jslm32",
628 | "Noisy",
629 | "heatmap.js",
630 | "top3c",
631 | "gitterb",
632 | "evroneCrop",
633 | "jwcrypto",
634 | "BitmapData.js",
635 | "jsper",
636 | "issues",
637 | "browserbuild",
638 | "backbone.paginator",
639 | "headjs",
640 | "gmaps",
641 | "codo",
642 | "nude.js",
643 | "papermill",
644 | "wire",
645 | "jump",
646 | "CocoaChineseDoc",
647 | "dabblet",
648 | "tabletop",
649 | "masonry",
650 | "lowkick",
651 | "roundabout",
652 | "autopagerize",
653 | "memchaser",
654 | "jGallery",
655 | "tracer",
656 | "Gritter",
657 | "backbone.layoutmanager",
658 | "jsOAuth",
659 | "spazcore",
660 | "glfx.js",
661 | "saepythondevguide",
662 | "SignalR",
663 | "FlexSlider",
664 | "jugglingdb",
665 | "tweereal",
666 | "humongous",
667 | "Shortly",
668 | "LatticeGrid",
669 | "cramp",
670 | "QuickWeb",
671 | "Emberwind",
672 | "calipso",
673 | "SimpLESS",
674 | "augment.js",
675 | "xui",
676 | "Nodemailer",
677 | "uniform",
678 | "WebIRC",
679 | "std.js",
680 | "queue",
681 | "iscroll",
682 | "Tilt",
683 | "hookbox",
684 | "step",
685 | "sap",
686 | "zcov",
687 | "creationix",
688 | "Socialite",
689 | "speak.js",
690 | "knox",
691 | "p5",
692 | "cconf.github.com",
693 | "agilefant",
694 | "EntityJS",
695 | "polymaps",
696 | "jGauge",
697 | "reified",
698 | "juggernaut",
699 | "localStore",
700 | "turntable.fm",
701 | "koshinuke",
702 | "yslow",
703 | "keymaster",
704 | "akihabara",
705 | "stellar.js",
706 | "whenever.js",
707 | "kissyteam.github.com",
708 | "vrome",
709 | "elasticsearch.github.com",
710 | "html5please",
711 | "gdash",
712 | "ezautosave",
713 | "hekyll",
714 | "TEditor",
715 | "session.js",
716 | "propelorm.github.com",
717 | "suit",
718 | "flurid",
719 | "staticdocs",
720 | "fbootstrapp",
721 | "livestyle",
722 | "firequery",
723 | "gameQuery",
724 | "stylus",
725 | "WAF",
726 | "jQCloud",
727 | "slidedown",
728 | "easie",
729 | "nvd3",
730 | "Desligado",
731 | "flatiron",
732 | "agent8ball",
733 | "consolidate.js",
734 | "ezstyleeditor",
735 | "unify",
736 | "ZenCodingNetBeansPlugin",
737 | "handlebars.js",
738 | "ezoe",
739 | "MiniSub",
740 | "Dorrie",
741 | "Lettering.js",
742 | "request",
743 | "jessie",
744 | "tatami",
745 | "msgboy",
746 | "PaintbrushJS",
747 | "jsonld.js",
748 | "jstree",
749 | "strophejs",
750 | "bubbletree",
751 | "prettyPrint.js",
752 | "springy",
753 | "CSSS",
754 | "threads",
755 | "RiverTrail",
756 | "ntwitter",
757 | "multiselect",
758 | "amplify",
759 | "HNHalfLife",
760 | "casperjs",
761 | "Selenium2",
762 | "websocket.io",
763 | "messageformat.js",
764 | "h5ai",
765 | "gazel",
766 | "bootstrap.stylus",
767 | "placeholder",
768 | "OpenTreeMap",
769 | "jQueryAutocompletePlugin",
770 | "jsdom",
771 | "enchant.js",
772 | "leihs",
773 | "jsrepl",
774 | "timepicker",
775 | "markdownR",
776 | "toast",
777 | "railsyardcms",
778 | "digitalfoosball",
779 | "yepnope.js",
780 | "TransformJS",
781 | "shadowbox",
782 | "bxslider",
783 | "callsite",
784 | "candy",
785 | "nodeslide",
786 | "pattern",
787 | "jwysiwyg",
788 | "union",
789 | "bgiframe",
790 | "dragon",
791 | "panda",
792 | "wax",
793 | "datui",
794 | "crossfilter",
795 | "LazyBoy",
796 | "doubleSuggest",
797 | "ezie",
798 | "atomjs",
799 | "bsmSelect",
800 | "Kickstart",
801 | "backboneboilerplate",
802 | "xiaotiantian",
803 | "helenus",
804 | "3dtransforms",
805 | "documentcloud",
806 | "postal.js",
807 | "limejs",
808 | "redback",
809 | "wrapup",
810 | "jomohojs",
811 | "App",
812 | "dustjs",
813 | "nodePhpSessions",
814 | "SilkJS",
815 | "faker",
816 | "diskusagereports",
817 | "holla",
818 | "canjs",
819 | "twss.js",
820 | "Kickstrap",
821 | "luca",
822 | "pjscrape",
823 | "requestpolicy",
824 | "osmus",
825 | "SlidingDrawer",
826 | "superslides",
827 | "Speeqe",
828 | "joli.api.js",
829 | "jax",
830 | "bbb",
831 | "joli.js",
832 | "pages.github.com",
833 | "Selene",
834 | "bbUI.js",
835 | "JFavicon",
836 | "lazyload",
837 | "jscache",
838 | "tinker",
839 | "Glisse.js",
840 | "KoGrid",
841 | "composer.js",
842 | "OpenESPI",
843 | "adapt",
844 | "PagedList",
845 | "Chatty",
846 | "dracula",
847 | "jQuery.fracs",
848 | "embedjs",
849 | "TermKit",
850 | "colorbox",
851 | "couchtato",
852 | "Evergreen",
853 | "arquillian.github.com",
854 | "Frameless",
855 | "Acebug",
856 | "nodeblog",
857 | "mootools2",
858 | "Mu",
859 | "vimium",
860 | "spin.js",
861 | "Blueberry",
862 | "X",
863 | "JSARToolKit",
864 | "jihua",
865 | "jqiphoneslide",
866 | "EventEmitter2",
867 | "sgn",
868 | "bvjs",
869 | "djblog",
870 | "iLepra",
871 | "Scale",
872 | "parcycle",
873 | "openphin",
874 | "jQuery.fn.autoResize",
875 | "LiipMonitorBundle",
876 | "ice",
877 | "minispade",
878 | "gmap",
879 | "caterwaul",
880 | "websocketpp",
881 | "q",
882 | "filtrr",
883 | "ElyCharts",
884 | "gcli",
885 | "mothereffinganimatedgif",
886 | "EventVat",
887 | "boomerang",
888 | "html5Widgets",
889 | "spraycan",
890 | "Moth",
891 | "almond",
892 | "mercadolibre.js",
893 | "members",
894 | "uglifier",
895 | "virtua",
896 | "matchMedia.js",
897 | "FeatherGL",
898 | "mana",
899 | "jit",
900 | "kue",
901 | "BigText",
902 | "prime",
903 | "jDataView",
904 | "glee",
905 | "gladius",
906 | "GeoExplorer",
907 | "duckduckgo",
908 | "dailyjs",
909 | "base12",
910 | "waitForImages",
911 | "mustache.couch.js",
912 | "chai",
913 | "SoundManager2",
914 | "minion",
915 | "hurl",
916 | "Blocks",
917 | "code52.github.com",
918 | "platform.js",
919 | "Tempus",
920 | "PIE",
921 | "NuGetDocs",
922 | "rabbit.js",
923 | "Toolkit",
924 | "CodeBook",
925 | "xstats.js",
926 | "uranium",
927 | "HearsayRequireJSBundle",
928 | "Raphael.FreeTransform",
929 | "SwipeView",
930 | "prefixfree",
931 | "Kizzy",
932 | "BasicsOfThreeJS",
933 | "Punchy",
934 | "sagenb",
935 | "forever",
936 | "cors",
937 | "pegjs",
938 | "NodeBeginnerBook",
939 | "db.js",
940 | "carto",
941 | "sitequery",
942 | "tweetanium",
943 | "OpenKaya",
944 | "resumable.js",
945 | "treehugger",
946 | "pophealth.github.com",
947 | "pixastic",
948 | "monomi",
949 | "Growl",
950 | "thimble",
951 | "jrac",
952 | "shumway",
953 | "DataTables",
954 | "somajs",
955 | "accounting.js",
956 | "nipster",
957 | "shapado",
958 | "snucode",
959 | "openmind",
960 | "jobberrails",
961 | "g.raphael",
962 | "Alice",
963 | "FireTray",
964 | "StickyTableHeaders",
965 | "heatcanvas",
966 | "paynode",
967 | "jslint.vim",
968 | "jsHtmlToText",
969 | "gxp",
970 | "OSMTM",
971 | "infusion",
972 | "resttesttest",
973 | "blacksmith",
974 | "folder2sprite",
975 | "cssFx",
976 | "html5shiv",
977 | "jqTransform",
978 | "orion.client",
979 | "zip.js",
980 | "Extensible",
981 | "slides.htm",
982 | "csswarp.js",
983 | "knockback",
984 | "shoulda.js",
985 | "cannon.js",
986 | "jsshell",
987 | "Cutter.js",
988 | "dygraphs",
989 | "Portamento",
990 | "trycatch",
991 | "node.couchapp.js",
992 | "videoPlayer",
993 | "Squire",
994 | "rcarousel",
995 | "DaumEditor",
996 | "opencomparison",
997 | "codersociety.github.com",
998 | "sparks.js",
999 | "joyride",
1000 | "wru",
1001 | "greasemonkey",
1002 | "kohai",
1003 | "RaccoonBlog",
1004 | "RESTClient",
1005 | "rrestjs",
1006 | "ursa",
1007 | "imgareaselect",
1008 | "wheat",
1009 | "PubSubJS",
1010 | "TitanTricks",
1011 | "slither",
1012 | "art",
1013 | "backbone.validations",
1014 | "backbone.validation",
1015 | "list",
1016 | "shim",
1017 | "jslintmate",
1018 | "KitchenSinkiPad",
1019 | "html5Preloader.js",
1020 | "hiro",
1021 | "orbit",
1022 | "IndexedDB",
1023 | "Do",
1024 | "Notificon",
1025 | "gut",
1026 | "sylvester",
1027 | "IDBWrapper",
1028 | "youkuhtml5playerbookmark",
1029 | "MyExtensions",
1030 | "audiolib.js",
1031 | "dictionary",
1032 | "TableDnD",
1033 | "backbone.memento",
1034 | "sqlabs",
1035 | "unveil",
1036 | "webfontloader",
1037 | "JX",
1038 | "Adplayer",
1039 | "include.js",
1040 | "backbone.io",
1041 | "Arctic.js",
1042 | "Ext.data.proxy.IndexedDB",
1043 | "LooseLeaf",
1044 | "trisano",
1045 | "validate.js",
1046 | "backbone.analytics",
1047 | "Overscroll",
1048 | "curriculum",
1049 | "stats.js",
1050 | "CAAT",
1051 | "dropshado.ws",
1052 | "jqTree",
1053 | "shred",
1054 | "nedis",
1055 | "support",
1056 | "treesaver",
1057 | "jsdiff",
1058 | "MailCheckerMinus",
1059 | "locust",
1060 | "JQtextile",
1061 | "iceScrum",
1062 | "dox",
1063 | "mxn",
1064 | "cannoli",
1065 | "SNORQL",
1066 | "socketbug",
1067 | "Clamp.js",
1068 | "wikistream",
1069 | "Capsule",
1070 | "MashupsLibrary",
1071 | "jslinuxstorage",
1072 | "ndoc",
1073 | "owp",
1074 | "BazingaExposeTranslationBundle",
1075 | "devnull",
1076 | "shower",
1077 | "spine.app",
1078 | "tooltipsy",
1079 | "keepasshttp",
1080 | "dreadnot",
1081 | "readium",
1082 | "nodelint",
1083 | "localtodos",
1084 | "valentine",
1085 | "sameplace",
1086 | "fun",
1087 | "Jcrop",
1088 | "trafficcone",
1089 | "FastLegS",
1090 | "reveal",
1091 | "synapse",
1092 | "matador",
1093 | "quicksand",
1094 | "steal",
1095 | "vintageJS",
1096 | "flot",
1097 | "WebIntents",
1098 | "Kibana",
1099 | "labs",
1100 | "BabelExt",
1101 | "reMarked.js",
1102 | "sausage",
1103 | "jclock",
1104 | "joostina",
1105 | "Sample.RSS",
1106 | "lawnchair",
1107 | "Sample.Todo",
1108 | "nodemanual.org",
1109 | "xstyle",
1110 | "blockui",
1111 | "ICanHaz.js",
1112 | "corner",
1113 | "Smartupdater",
1114 | "OpenACD",
1115 | "Struct",
1116 | "FCBKcomplete",
1117 | "mongorito",
1118 | "HelloPhoneGap",
1119 | "irc",
1120 | "ical.js",
1121 | "ixf",
1122 | "geo",
1123 | "query",
1124 | "read",
1125 | "utile",
1126 | "TraceKit",
1127 | "Fuse.js",
1128 | "gas",
1129 | "lua.js",
1130 | "queryIndexedDB",
1131 | "diva.js",
1132 | "kickoff",
1133 | "backbone.offline",
1134 | "Jed",
1135 | "lightningjs",
1136 | "cluster",
1137 | "tomatoes",
1138 | "zoom.js",
1139 | "selectToAutocomplete",
1140 | "ioreader",
1141 | "datePicker",
1142 | "contracts.coffee",
1143 | "turing.js",
1144 | "json2html",
1145 | "maki",
1146 | "coa",
1147 | "anvil.js",
1148 | "jsGameSoup",
1149 | "pyzotero",
1150 | "DropZone",
1151 | "yeti",
1152 | "jQuery.Gantt",
1153 | "audia",
1154 | "Glimpse",
1155 | "EventEmitter",
1156 | "CrossSlide",
1157 | "doctorjs",
1158 | "asciifi",
1159 | "toxiclibsjs",
1160 | "jstouchslide",
1161 | "mediaelement",
1162 | "CSS1K",
1163 | "corejs",
1164 | "facebox",
1165 | "browserjs",
1166 | "mechanic",
1167 | "jMediaelement",
1168 | "proper",
1169 | "spidernode",
1170 | "tilestream",
1171 | "axel",
1172 | "webservice.js",
1173 | "juiceui",
1174 | "www.luffy.cx",
1175 | "nssocket",
1176 | "HoverBattles",
1177 | "kyuri",
1178 | "reds",
1179 | "Znode",
1180 | "Laudio",
1181 | "jTweetsAnywhere",
1182 | "Faker.js",
1183 | "Movuca",
1184 | "bootbox",
1185 | "cepmon",
1186 | "raintpl3",
1187 | "iisnode",
1188 | "response.js",
1189 | "fin",
1190 | "js.class",
1191 | "fingerpoken",
1192 | "jsparse",
1193 | "clarinet",
1194 | "fmpstandards",
1195 | "zendblog",
1196 | "gridy",
1197 | "bloog",
1198 | "jQR",
1199 | "declarative",
1200 | "grumble.js",
1201 | "iviewer",
1202 | "webSlide",
1203 | "neo4js",
1204 | "lokka",
1205 | "js.io",
1206 | "websockify",
1207 | "Pot.js",
1208 | "develop.github.com",
1209 | "lostdecade.github.com",
1210 | "abba",
1211 | "roominator",
1212 | "smoke.js",
1213 | "sprite.js",
1214 | "xdate",
1215 | "Fireworks",
1216 | "bonescript",
1217 | "difflet",
1218 | "JSONSelect",
1219 | "webxray",
1220 | "expressling",
1221 | "oegyscroll",
1222 | "glDatePicker",
1223 | "Keyboard",
1224 | "dropfile",
1225 | "noVNC",
1226 | "MigrationsMap.net",
1227 | "alice",
1228 | "seaport",
1229 | "load.js",
1230 | "PopulationPyramid.net",
1231 | "jsHelpers",
1232 | "npm2debian",
1233 | "wunderlist",
1234 | "CIP",
1235 | "Magic",
1236 | "thegrid",
1237 | "ZIA",
1238 | "CeeBox",
1239 | "swig",
1240 | "plone.app.deco",
1241 | "foounit",
1242 | "DGSE",
1243 | "AnythingZoomer",
1244 | "cradle",
1245 | "reqwest",
1246 | "vmcjs",
1247 | "Bleextop",
1248 | "knockout.aspnetmvcdemos",
1249 | "bowser",
1250 | "jScroll",
1251 | "dnode",
1252 | "zest",
1253 | "twig.js",
1254 | "sisyphus",
1255 | "tasket",
1256 | "ncluster",
1257 | "KnobKnob",
1258 | "frank",
1259 | "narcissus",
1260 | "IodineGBA",
1261 | "wander",
1262 | "dromaeo",
1263 | "plasm.js",
1264 | "nib",
1265 | "PubMed",
1266 | "JPlus",
1267 | "mosaic",
1268 | "bones",
1269 | "datetime",
1270 | "hinclude",
1271 | "Fool.js",
1272 | "protos",
1273 | "Regions.js",
1274 | "Jester",
1275 | "Overthrow",
1276 | "control",
1277 | "Morse.js",
1278 | "jsGB",
1279 | "TransportDublin",
1280 | "jquip",
1281 | "doT",
1282 | "rt",
1283 | "always",
1284 | "Queuebert",
1285 | "role",
1286 | "mediafrontpage",
1287 | "nTunes",
1288 | "pleft",
1289 | "graphiti",
1290 | "Audiolet",
1291 | "backrub",
1292 | "com.woltlab.wcf.message",
1293 | "PikaChoose",
1294 | "ipadpeek",
1295 | "piratescript",
1296 | "JSAMF",
1297 | "Tutorials",
1298 | "syn",
1299 | "CodeHaacks",
1300 | "Codestrong",
1301 | "Flickable.js",
1302 | "units.js",
1303 | "jsonview",
1304 | "persistencejs",
1305 | "hyve",
1306 | "microblog",
1307 | "Windshaft",
1308 | "ssu",
1309 | "EasyWebsocket",
1310 | "porthole",
1311 | "documentjs",
1312 | "javascriptmvc",
1313 | "jamal",
1314 | "inject",
1315 | "HandlebarJS",
1316 | "dmv",
1317 | "trollicons",
1318 | "Nustache",
1319 | "jo",
1320 | "livereload",
1321 | "VisualEvent",
1322 | "Ghost.py",
1323 | "boastful",
1324 | "superfastmatch",
1325 | "selectivizr",
1326 | "LearningRegistry",
1327 | "useragent",
1328 | "jQuery.twinkle",
1329 | "piler",
1330 | "assetgraph",
1331 | "LucidJS",
1332 | "jOrgChart",
1333 | "erkie.github.com",
1334 | "LivelyKernel",
1335 | "emotiface",
1336 | "pop",
1337 | "tornskel",
1338 | "vows",
1339 | "websocket.MQ",
1340 | "vtp",
1341 | "twitter",
1342 | "Avisota",
1343 | "masteringnode",
1344 | "microevent.js",
1345 | "now",
1346 | "pushover",
1347 | "OpenMercury",
1348 | "quickui",
1349 | "mmstats",
1350 | "visage",
1351 | "glow",
1352 | "YiiBlocks",
1353 | "Lu",
1354 | "needle",
1355 | "dijit",
1356 | "dojox",
1357 | "brisket",
1358 | "picky",
1359 | "youRhere",
1360 | "nock",
1361 | "todos",
1362 | "upnode",
1363 | "mobiscroll",
1364 | "Tagedit",
1365 | "TextboxList",
1366 | "LABjs",
1367 | "feather",
1368 | "jcanvas",
1369 | "director",
1370 | "ER",
1371 | "Struct.js",
1372 | "spm",
1373 | "LightWrite",
1374 | "Emphasis",
1375 | "musicalpha",
1376 | "lavish",
1377 | "compose",
1378 | "SquishIt",
1379 | "distribute",
1380 | "JAIL",
1381 | "openpixels",
1382 | "meteor",
1383 | "pacman",
1384 | "knockout.live.plugin",
1385 | "Radios",
1386 | "tagmate",
1387 | "ndb",
1388 | "FlixelJS",
1389 | "exhibit3",
1390 | "LanguageFundamentals",
1391 | "entityspaces.js",
1392 | "BigDecimal.js",
1393 | "expect.js",
1394 | "beardcomb",
1395 | "virtualjoystick.js",
1396 | "Plinth",
1397 | "henrik.nyh.se",
1398 | "middlefiddle",
1399 | "html5sql",
1400 | "raty",
1401 | "waterbear",
1402 | "palette",
1403 | "hborecycling",
1404 | "RapidFTR",
1405 | "wherestheparty",
1406 | "Backbone.Subset",
1407 | "nide",
1408 | "SURVIVOR",
1409 | "craftyjstut",
1410 | "PrusaMendel",
1411 | "orderly.js",
1412 | "protovis",
1413 | "bloomfilter.js",
1414 | "c3",
1415 | "jSlide",
1416 | "lscache",
1417 | "DragDrop",
1418 | "Squirrel.js",
1419 | "Algorithms",
1420 | "holman.github.com",
1421 | "NicknameTabComplete",
1422 | "spec",
1423 | "Radio",
1424 | "jqNode",
1425 | "redirectory",
1426 | "placeholder.js",
1427 | "hummingbird",
1428 | "jelly",
1429 | "MovingBoxes",
1430 | "javascript.tmbundle",
1431 | "graph.tk",
1432 | "zkybase",
1433 | "jsftp",
1434 | "Dataview.js",
1435 | "domready",
1436 | "CuteTime",
1437 | "jsqrcode",
1438 | "nclosure",
1439 | "csonv.js",
1440 | "tablefilter",
1441 | "Core",
1442 | "styleguide",
1443 | "VIE",
1444 | "eve",
1445 | "pageflipper",
1446 | "vexflow",
1447 | "slabText",
1448 | "WebViewer",
1449 | "html5media",
1450 | "namespace",
1451 | "bogart",
1452 | "gee",
1453 | "3gears",
1454 | "kataspace",
1455 | "katajs",
1456 | "tween.js",
1457 | "dsp.js",
1458 | "ranviermud",
1459 | "runloop",
1460 | "jszip",
1461 | "WKTouch",
1462 | "shake.js",
1463 | "book",
1464 | "autoComplete.js",
1465 | "breizhcamp",
1466 | "Downloadify",
1467 | "bitarray.js",
1468 | "uedit",
1469 | "jog",
1470 | "flarevideo",
1471 | "QRCode",
1472 | "mindmaps",
1473 | "perldoc.perl.org",
1474 | "exhibitor",
1475 | "jQuery.Tagify",
1476 | "commander.js",
1477 | "BiSON.js",
1478 | "biwascheme",
1479 | "Knockout.Extensions",
1480 | "AppBoilerplate",
1481 | "surface",
1482 | "RESTduino",
1483 | "kitchensink",
1484 | "realie",
1485 | "keysnail",
1486 | "CalVis3",
1487 | "SocialBootstrapApi",
1488 | "coco",
1489 | "remoteStorage.js",
1490 | "cubes",
1491 | "simplemodal",
1492 | "jack",
1493 | "LexikTranslationBundle",
1494 | "metro.css",
1495 | "healthjs",
1496 | "seamcarving",
1497 | "nullmq",
1498 | "restconsole",
1499 | "PacketQ",
1500 | "logule",
1501 | "DMS",
1502 | "zenircbot",
1503 | "modul8",
1504 | "d3talk",
1505 | "mintrayr",
1506 | "autoproxy",
1507 | "d3x",
1508 | "sammy",
1509 | "warp",
1510 | "viewporter",
1511 | "EventSource",
1512 | "TrafficCop",
1513 | "boilerstrap",
1514 | "apollo",
1515 | "jsface",
1516 | "Dashboard",
1517 | "jQuery.countdown",
1518 | "mongo3",
1519 | "GLGE",
1520 | "Captionator",
1521 | "lab",
1522 | "zoom",
1523 | "monte",
1524 | "gridx",
1525 | "shell",
1526 | "sibilant",
1527 | "journey",
1528 | "breakpoints",
1529 | "jqPagination",
1530 | "coapp.org",
1531 | "cassis",
1532 | "multifox",
1533 | "modules.zendframework.com",
1534 | "HouseAgent",
1535 | "tap.js",
1536 | "jsonlintdotcom",
1537 | "VisualPHPUnit",
1538 | "livequery",
1539 | "jison",
1540 | "Odin",
1541 | "jui",
1542 | "tiddlyspace",
1543 | "mozillians",
1544 | "formaline",
1545 | "TiddlyWiki5",
1546 | "Boomgraph",
1547 | "localModel",
1548 | "tag",
1549 | "bigpipe",
1550 | "kidsruby",
1551 | "mochaui",
1552 | "Sticky",
1553 | "jmapping",
1554 | "TouchScroll",
1555 | "Peanutty",
1556 | "strata",
1557 | "doctor",
1558 | "Flow",
1559 | "angularjs.org",
1560 | "css3please",
1561 | "EnvJasmine",
1562 | "redmon",
1563 | "RedmineThemePixelCookers",
1564 | "NicEdit",
1565 | "Backbone.Meninges",
1566 | "Scribl",
1567 | "AudioStreamer",
1568 | "nano",
1569 | "Siege",
1570 | "tobi",
1571 | "shellshape",
1572 | "rubeque",
1573 | "math",
1574 | "djenofdjango",
1575 | "jq.carousel",
1576 | "kindeditor",
1577 | "Control",
1578 | "feedme.js",
1579 | "12306",
1580 | "filter.js",
1581 | "MilkChart",
1582 | "fluid960gs",
1583 | "chromesniffer",
1584 | "profxmpp",
1585 | "PyOfWave",
1586 | "Steppe",
1587 | "AwesomeChartJS",
1588 | "Hoverizr",
1589 | "rewriter",
1590 | "WikiSocium",
1591 | "startpage.rwrt",
1592 | "wob.js",
1593 | "tracksapp.github.com",
1594 | "nerve",
1595 | "kranium",
1596 | "snapbird",
1597 | "treestyletab",
1598 | "magi",
1599 | "prepareTransition",
1600 | "geoext",
1601 | "RdvZ",
1602 | "ipull",
1603 | "weld",
1604 | "plax",
1605 | "InlineEditor",
1606 | "SuperChromePass",
1607 | "libreprojects",
1608 | "punycode.js",
1609 | "slugr",
1610 | "NodeSSH",
1611 | "beamjs",
1612 | "melpa",
1613 | "luna",
1614 | "mpns",
1615 | "SlideShow",
1616 | "flapjax",
1617 | "loupe",
1618 | "currency.io",
1619 | "pocketchangeapp",
1620 | "passifox",
1621 | "nodepad",
1622 | "backpusher",
1623 | "cqrsjourney.github.com",
1624 | "remark",
1625 | "amplesdk",
1626 | "pmxdr",
1627 | "iGesture",
1628 | "JsChilicat",
1629 | "samplemvc",
1630 | "samples",
1631 | "nwmatcher",
1632 | "Vanadium",
1633 | "banking.js",
1634 | "bullet",
1635 | "jsdifflib",
1636 | "qwery",
1637 | "smoosh",
1638 | "techradar",
1639 | "KeyCandy",
1640 | "stapes",
1641 | "webdriverjs",
1642 | "tile5",
1643 | "revolver",
1644 | "jsUri",
1645 | "knockout.namespaces",
1646 | "dispatch",
1647 | "thomasdavis.github.com",
1648 | "LiipVieBundle",
1649 | "Booktype",
1650 | "getpython3.com",
1651 | "ignition",
1652 | "xauth",
1653 | "objs",
1654 | "PixelPerfect",
1655 | "jQuery.threesixty",
1656 | "jQote2",
1657 | "crossroads.js",
1658 | "metrics",
1659 | "MagicTouch",
1660 | "design.io",
1661 | "rsted",
1662 | "esmorph",
1663 | "SubAppDemo",
1664 | "enablePlaceholder",
1665 | "gordon",
1666 | "processingjstool",
1667 | "ARti",
1668 | "anchor",
1669 | "keysocket",
1670 | "ChesterGL",
1671 | "shortcut.io",
1672 | "waldo",
1673 | "logref",
1674 | "stoopid",
1675 | "dynamo",
1676 | "itsalltext",
1677 | "browsermirror",
1678 | "DVL",
1679 | "WTFEngine",
1680 | "MapCraft",
1681 | "mooeditable",
1682 | "polymapper",
1683 | "prof",
1684 | "boilerplate",
1685 | "uki",
1686 | "panorama360",
1687 | "Apprise",
1688 | "gamejs",
1689 | "coffeejade",
1690 | "pega.io",
1691 | "xmpp4moz",
1692 | "interleave",
1693 | "Granule",
1694 | "xjst",
1695 | "gamepad.js",
1696 | "Coulisse",
1697 | "publisher.js",
1698 | "GraphGL",
1699 | "up",
1700 | "response",
1701 | "stack",
1702 | "lilac",
1703 | "JSDev",
1704 | "pystar",
1705 | "dashboard",
1706 | "kartograph.org",
1707 | "vogue",
1708 | "freshereditor",
1709 | "himera",
1710 | "inca",
1711 | "ansi.js",
1712 | "qq",
1713 | "soca",
1714 | "codermn",
1715 | "Ext.ux.touch.grid",
1716 | "cleanslate",
1717 | "pathjs",
1718 | "jslint4java",
1719 | "revalidator",
1720 | "QuickSlides",
1721 | "bujagali",
1722 | "railwayjs.com",
1723 | "prettyphoto",
1724 | "jecookie",
1725 | "mockery",
1726 | "CSS3D.js",
1727 | "nimble",
1728 | "tablefixedheader",
1729 | "Codeshelver",
1730 | "vvvv.js",
1731 | "slang",
1732 | "twinkle",
1733 | "supplement.js",
1734 | "nwm",
1735 | "mongous",
1736 | "Firemacs",
1737 | "instiki",
1738 | "cloudsave",
1739 | "GithubSharp",
1740 | "libgit2.github.com",
1741 | "wymeditor",
1742 | "BeerMe",
1743 | "snob",
1744 | "scroller",
1745 | "ScrollFix",
1746 | "OverflowScrollingFix",
1747 | "nodegit",
1748 | "dryice",
1749 | "jparallax",
1750 | "tappable",
1751 | "csshttprequest",
1752 | "screenfull.js",
1753 | "jQRange",
1754 | "recline",
1755 | "iodocs",
1756 | "xmppjs",
1757 | "roco",
1758 | "qwrap",
1759 | "efrepo",
1760 | "tree",
1761 | "wildfire",
1762 | "localStorageDB",
1763 | "input.js",
1764 | "jsmockito",
1765 | "move",
1766 | "FluentIL",
1767 | "cachirulovalleydirectory",
1768 | "StaticSite",
1769 | "XWMM",
1770 | "introducinghtml5",
1771 | "gooddata.github.com",
1772 | "MockHttpRequest",
1773 | "peity",
1774 | "Kernel.js",
1775 | "scrumboard",
1776 | "BrickPile",
1777 | "cssSandpaper",
1778 | "JigLibJS2",
1779 | "shareDesk",
1780 | "clusterhub",
1781 | "archetypes",
1782 | "webforms2",
1783 | "interact",
1784 | "color",
1785 | "brain",
1786 | "ccss",
1787 | "html5",
1788 | "nexpect",
1789 | "therabbithole",
1790 | "Chili",
1791 | "ralph",
1792 | "highlight",
1793 | "wolfram",
1794 | "modulargrid",
1795 | "vnc.js",
1796 | "tcp.js",
1797 | "jsgrep",
1798 | "livemap",
1799 | "shardjs",
1800 | "OpenTag",
1801 | "pinify",
1802 | "Shipyard",
1803 | "openhercules",
1804 | "scraper",
1805 | "sofa",
1806 | "Resizer",
1807 | "propagit",
1808 | "webactors",
1809 | "NodeChat",
1810 | "extra",
1811 | "canvas",
1812 | "layout",
1813 | "leviroutes",
1814 | "torquebox.org",
1815 | "spludo",
1816 | "Enterprise",
1817 | "launch",
1818 | "gissues",
1819 | "node.dbslayer.js",
1820 | "ms.js",
1821 | "Awwation",
1822 | "spark",
1823 | "fermata",
1824 | "ReviewR",
1825 | "rocketcharts",
1826 | "miniTip",
1827 | "pow",
1828 | "Touchy.js",
1829 | "webjs",
1830 | "CSSAnimation",
1831 | "gapvis",
1832 | "satisfy",
1833 | "microjungle",
1834 | "chromus",
1835 | "deCSS3",
1836 | "fathom",
1837 | "jukebox",
1838 | "slippy",
1839 | "greatbigcrane",
1840 | "shining",
1841 | "Springboard",
1842 | "sip.js",
1843 | "nakedpassword",
1844 | "Bundler",
1845 | "htracr",
1846 | "grasshopper",
1847 | "asEvented",
1848 | "scroll.js",
1849 | "rubythankful",
1850 | "huboard",
1851 | "machina.js",
1852 | "ape",
1853 | "hubticle.github.com",
1854 | "filter",
1855 | "nfe",
1856 | "AceGWT",
1857 | "csg.js",
1858 | "kneath.github.com",
1859 | "tryocaml",
1860 | "jsfx",
1861 | "FlipPics",
1862 | "ready.js",
1863 | "mongode",
1864 | "minimal",
1865 | "inflect",
1866 | "lightgl.js",
1867 | "postmark.js",
1868 | "foxjs",
1869 | "moofx",
1870 | "pd",
1871 | "float.js",
1872 | "mote",
1873 | "log.js",
1874 | "Rubex",
1875 | "SVG.toDataURL",
1876 | "finance",
1877 | "vibou.gTile",
1878 | "webfs",
1879 | "traceur",
1880 | "activejs",
1881 | "eyeballs.js",
1882 | "stalker",
1883 | "futures",
1884 | "evercookie",
1885 | "microwave",
1886 | "ybuild",
1887 | "spider",
1888 | "Search",
1889 | "trackiffer",
1890 | "Backbonist",
1891 | "KaboomJS",
1892 | "Geometry.js",
1893 | "music.js",
1894 | "jsmad",
1895 | "elmo",
1896 | "XAudioJS",
1897 | "pcmdata.js",
1898 | "binary.js",
1899 | "player",
1900 | "counter",
1901 | "Badger",
1902 | "quora",
1903 | "slidy",
1904 | "10er10",
1905 | "quill",
1906 | "hashgrid",
1907 | "stack.io",
1908 | "simplegraph",
1909 | "loadrunner",
1910 | "rekapi",
1911 | "iframework",
1912 | "jsmidi",
1913 | "josediazgonzalez.com",
1914 | "shifty",
1915 | "EngageNet",
1916 | "Love",
1917 | "playdar.js",
1918 | "debug",
1919 | "favs",
1920 | "JSCORM",
1921 | "monocles",
1922 | "stativus",
1923 | "dynamicaudio.js",
1924 | "dynamoDB",
1925 | "sift.js",
1926 | "JSV",
1927 | "emacsbook",
1928 | "railsapps.github.com",
1929 | "escodegen",
1930 | "TicTacToe",
1931 | "lucid",
1932 | "IronJS",
1933 | "txn",
1934 | "nodeQuery",
1935 | "philogl",
1936 | "brewer.js",
1937 | "TiHandson",
1938 | "outlet",
1939 | "mongee",
1940 | "axonome",
1941 | "cookies",
1942 | "store",
1943 | "TryRuby",
1944 | "javelin",
1945 | "Booking",
1946 | "cft",
1947 | "colors.js",
1948 | "keycode.js",
1949 | "NBlog",
1950 | "myformbuilder",
1951 | "EnhanceJS",
1952 | "codesurgeon",
1953 | "Zoombox",
1954 | "wheresmyschoolbus",
1955 | "resourceful",
1956 | "wmd",
1957 | "ncore",
1958 | "NinjaKit",
1959 | "raphael.serialize",
1960 | "cucumis",
1961 | "thorax",
1962 | "cacique",
1963 | "maps.stamen.com",
1964 | "yate",
1965 | "hijs",
1966 | "rdf.js",
1967 | "talks",
1968 | "drtwoot",
1969 | "noUiSlider",
1970 | "spine.todos",
1971 | "Craftyslide",
1972 | "Joose",
1973 | "tabzilla",
1974 | "formfactor",
1975 | "cde",
1976 | "openstreetbugs",
1977 | "rotate3Di",
1978 | "sourcekit",
1979 | "nestor",
1980 | "MavensMate",
1981 | "mothereff.in",
1982 | "emphasize.js",
1983 | "behavior",
1984 | "randexp.js",
1985 | "Parallaxjs",
1986 | "fstream",
1987 | "wrapjs",
1988 | "maphilight",
1989 | "SourceSquare",
1990 | "Ext.ux.touch.Rating",
1991 | "Spritemapper",
1992 | "davidblog",
1993 | "testosterone",
1994 | "tombloo",
1995 | "slickback",
1996 | "CodeMirror",
1997 | "Maple.js",
1998 | "BackboneJsAndFullCalendar",
1999 | "joint",
2000 | "keydown",
2001 | "Template.Tabbed",
2002 | "Slidifier",
2003 | "dynode",
2004 | "cookbook",
2005 | "protocol",
2006 | "nodejs.ir",
2007 | "codex",
2008 | "nyroModal",
2009 | "eyes.js",
2010 | "CrashReporterDemo",
2011 | "blend",
2012 | "css3test",
2013 | "WeScheme",
2014 | "Ext.ux.TouchCalendar",
2015 | "dew",
2016 | "jsii",
2017 | "blocker",
2018 | "owtf",
2019 | "sticky",
2020 | "youtubeplaylist",
2021 | "Sphere",
2022 | "graphite",
2023 | "twit",
2024 | "slick",
2025 | "pietimer",
2026 | "muralapp",
2027 | "TypeSequence",
2028 | "nconf",
2029 | "pathmenu.js",
2030 | "synchrotron",
2031 | "science.js",
2032 | "donut",
2033 | "askken",
2034 | "erljs",
2035 | "jsonlint",
2036 | "jsPDF",
2037 | "html5support",
2038 | "audiodata",
2039 | "has.js",
2040 | "griffin.editor",
2041 | "delegate",
2042 | "FlexiColorPicker",
2043 | "box2d.js",
2044 | "wmuSlider",
2045 | "Articles",
2046 | "nue",
2047 | "roto",
2048 | "konphyg",
2049 | "Rumpetroll",
2050 | "gaebar",
2051 | "sawpf",
2052 | "Dandelion",
2053 | "orbium",
2054 | "Happy.js",
2055 | "gurupi.org",
2056 | "Lungo.Sugars",
2057 | "kaiten",
2058 | "three.dart",
2059 | "scrollability",
2060 | "KickAssets",
2061 | "cowl",
2062 | "jsnes",
2063 | "JSBoy",
2064 | "SpecsFor",
2065 | "import",
2066 | "Extendables",
2067 | "swinger",
2068 | "shapesmith",
2069 | "occupywallst",
2070 | "binb",
2071 | "api.js",
2072 | "ircd.js",
2073 | "impactConnect",
2074 | "tempo",
2075 | "CodeStoryStep2",
2076 | "ElementStack",
2077 | "html5slider",
2078 | "alokmenghrajani.github.com",
2079 | "scenejs",
2080 | "gerbil",
2081 | "404",
2082 | "schema",
2083 | "instapoppin",
2084 | "BootstrapMVC",
2085 | "ThreeDots",
2086 | "eventproxy",
2087 | "postmessage",
2088 | "audionode",
2089 | "JSONStream",
2090 | "ducttape",
2091 | "expound",
2092 | "jWorkflow",
2093 | "lifesinger.github.com",
2094 | "In",
2095 | "fugue",
2096 | "monwarp",
2097 | "runjs",
2098 | "marmot",
2099 | "simplesmtp",
2100 | "Bookmarkly",
2101 | "cansecurity",
2102 | "TiMetro",
2103 | "injectr",
2104 | "mobilemenu",
2105 | "spartify",
2106 | "jKey",
2107 | "UserInfuser",
2108 | "functools",
2109 | "diggy",
2110 | "ymacs",
2111 | "TiWinSlider",
2112 | "player.vas3k.ru",
2113 | "Kirin",
2114 | "diagonalFade",
2115 | "GSet",
2116 | "conductor",
2117 | "fastFrag",
2118 | "ghterm",
2119 | "geowebcache",
2120 | "bolt",
2121 | "gitdocs",
2122 | "Crunch",
2123 | "jschannel",
2124 | "Balloons.IO",
2125 | "nodeigniter",
2126 | "GLOW",
2127 | "threex",
2128 | "taboo",
2129 | "agiletickets",
2130 | "volkszaehler.org",
2131 | "haml.js",
2132 | "tijscore",
2133 | "jsmirrors",
2134 | "polyfills",
2135 | "sh.js",
2136 | "hypher",
2137 | "jiveapps",
2138 | "lumbar",
2139 | "statusdashboard",
2140 | "scrubyt",
2141 | "worzone",
2142 | "rockwood",
2143 | "flexie",
2144 | "FaxJs",
2145 | "KitchenSink",
2146 | "mixr",
2147 | "cpm",
2148 | "CalEnder",
2149 | "lrthw.github.com",
2150 | "Lockets",
2151 | "capt",
2152 | "Pajinate",
2153 | "TiStoreKit",
2154 | "imageCache",
2155 | "minimatch",
2156 | "sherpa",
2157 | "vidchat",
2158 | "short",
2159 | "slidify",
2160 | "cleditor",
2161 | "jQueryFileUpload.Net",
2162 | "syze",
2163 | "jspp",
2164 | "emile",
2165 | "jazz",
2166 | "conkeror",
2167 | "terrificjs",
2168 | "QuoJS",
2169 | "webhookit",
2170 | "botio",
2171 | "nbl",
2172 | "JSON.minify",
2173 | "960gridder",
2174 | "confess",
2175 | "oauthorizer",
2176 | "polychart.js",
2177 | "do",
2178 | "androjs",
2179 | "protolicious",
2180 | "Son",
2181 | "cascade",
2182 | "cliff",
2183 | "BroadleafCommerceDemoSite",
2184 | "JSManipulate",
2185 | "vapor.js",
2186 | "polyfill.js",
2187 | "IntroD3",
2188 | "jsshaper",
2189 | "reddit.tv",
2190 | "IPCNode",
2191 | "NextJS",
2192 | "osgjs",
2193 | "borschik",
2194 | "darkstrap",
2195 | "def.js",
2196 | "Timecop.js",
2197 | "classtalk",
2198 | "MooPlay",
2199 | "mozrepl",
2200 | "daemon.node",
2201 | "TodoList",
2202 | "TeamFeeder",
2203 | "underscore.Deferred",
2204 | "judge",
2205 | "scrollbar",
2206 | "gauss",
2207 | "Markup.js",
2208 | "VivaGraphJS",
2209 | "echonest.streamgraphing.org",
2210 | "measures",
2211 | "userChrome.js",
2212 | "perstore",
2213 | "sparkseditor",
2214 | "X2",
2215 | "JSLINQ",
2216 | "html5edit",
2217 | "Fotorama",
2218 | "scotch",
2219 | "nestedSortable",
2220 | "combohandler",
2221 | "barista",
2222 | "matcha",
2223 | "streamgraph.js",
2224 | "hailwhale",
2225 | "ak",
2226 | "dropshare",
2227 | "OriginJS",
2228 | "jshint.vim",
2229 | "edgelisp",
2230 | "fusebox",
2231 | "choreographer",
2232 | "graphquire",
2233 | "bob",
2234 | "prospector",
2235 | "todolist",
2236 | "releasenotes",
2237 | "buzzbird",
2238 | "1kg.org",
2239 | "jlayout",
2240 | "TiGrid",
2241 | "jbar",
2242 | "dollardom",
2243 | "vision",
2244 | "dnsserver.js",
2245 | "ncp",
2246 | "filer.js",
2247 | "morestreams",
2248 | "MvcContrib",
2249 | "quixe",
2250 | "Donatello",
2251 | "just",
2252 | "metamorph.js",
2253 | "form2js",
2254 | "visibility.js",
2255 | "limestone",
2256 | "stevelosh",
2257 | "detectmobile.js",
2258 | "Flags",
2259 | "setochka",
2260 | "ingenioJS",
2261 | "zoey",
2262 | "ocanvas",
2263 | "timvc",
2264 | "Ajaxmanager",
2265 | "ain",
2266 | "deckem",
2267 | "ExamplesByMesh",
2268 | "berlinjs.org",
2269 | "goto",
2270 | "modalbox",
2271 | "finchjs",
2272 | "MassiveCover",
2273 | "nodeEventStore",
2274 | "OverlappingMarkerSpiderfier",
2275 | "Delivery.js",
2276 | "twitterlib",
2277 | "diveintopython3",
2278 | "SafariKeywordSearch",
2279 | "html5validator",
2280 | "wowhead",
2281 | "V5",
2282 | "watchman",
2283 | "twicli",
2284 | "firmin",
2285 | "w3fools",
2286 | "Timed",
2287 | "hub.me",
2288 | "milkshake",
2289 | "brite",
2290 | "DartVectorMath",
2291 | "burst",
2292 | "neuron",
2293 | "hnet",
2294 | "respect.github.com",
2295 | "bindWithDelay",
2296 | "nodepress",
2297 | "eidogo",
2298 | "OzJS",
2299 | "onde",
2300 | "entropy.js",
2301 | "invoke",
2302 | "darkbox",
2303 | "fab",
2304 | "JSinJS",
2305 | "mondrian",
2306 | "dalliance",
2307 | "CraftyBox2D",
2308 | "mdoc",
2309 | "TiDrop",
2310 | "Jang",
2311 | "es5.github.com",
2312 | "browserl",
2313 | "nodesynth",
2314 | "cooltips",
2315 | "jellyfish",
2316 | "bloody.go",
2317 | "recursive",
2318 | "Prevel",
2319 | "npmjs.org",
2320 | "circleplayer",
2321 | "clientside",
2322 | "aceditable",
2323 | "Cuore.js",
2324 | "sdoc",
2325 | "jtab",
2326 | "osmplayer",
2327 | "narrowthegapp",
2328 | "capsela",
2329 | "mobipick",
2330 | "phonegapcontacts",
2331 | "ligament.js",
2332 | "jsgif",
2333 | "JSONH",
2334 | "play.js",
2335 | "company",
2336 | "site",
2337 | "sqwish",
2338 | "apejs",
2339 | "suggestr.js",
2340 | "datadigitizer",
2341 | "kanade",
2342 | "vegas",
2343 | "srchr",
2344 | "tasscss",
2345 | "wink",
2346 | "jSlideto",
2347 | "ngist",
2348 | "touche",
2349 | "domcrypt",
2350 | "serve",
2351 | "wreq",
2352 | "jeesh",
2353 | "exValidation",
2354 | "kratko.js",
2355 | "ping",
2356 | "harmony",
2357 | "aToolTip",
2358 | "jslim",
2359 | "kerning.js",
2360 | "spah",
2361 | "cereal",
2362 | "twerp",
2363 | "blode",
2364 | "NodeMUD",
2365 | "Mobile",
2366 | "tmpltr",
2367 | "password123",
2368 | "fedena",
2369 | "media",
2370 | "domlint",
2371 | "js3",
2372 | "emailjs",
2373 | "movethewebforward",
2374 | "simplepager",
2375 | "Blog",
2376 | "FlashJS",
2377 | "browsertiles",
2378 | "exemplos",
2379 | "sly",
2380 | "detexify",
2381 | "multiview",
2382 | "link.js",
2383 | "system.js",
2384 | "GoogleMapsColorizr",
2385 | "mongoq",
2386 | "chain.js",
2387 | "boxbox",
2388 | "hbs",
2389 | "deuxdrop",
2390 | "batch",
2391 | "URLON",
2392 | "seed",
2393 | "unicons",
2394 | "planetarium",
2395 | "complete",
2396 | "qrx",
2397 | "escort",
2398 | "Golingo",
2399 | "demos",
2400 | "jsconfus",
2401 | "MyGeoCloud",
2402 | "awesomecomplete",
2403 | "initr",
2404 | "8ball",
2405 | "rock.js",
2406 | "cupboard",
2407 | "hypervm",
2408 | "jarTT",
2409 | "ECUI",
2410 | "battleship",
2411 | "simpledb",
2412 | "eventreactor",
2413 | "lepture.com",
2414 | "scripty2",
2415 | "backnode",
2416 | "openseadragon",
2417 | "GammaJS",
2418 | "SampleProjects",
2419 | "cardorizer",
2420 | "petrify",
2421 | "nariya",
2422 | "ROMManagerManifest",
2423 | "JSONPath",
2424 | "stickySectionHeaders",
2425 | "sleight",
2426 | "wormz",
2427 | "newsmonger",
2428 | "jQueryPlugins",
2429 | "blowfish.js",
2430 | "SenchaTouch2MVCHelloworld",
2431 | "ClassIE",
2432 | "MembershipStarterKit",
2433 | "djangode",
2434 | "vbench",
2435 | "visual6502",
2436 | "StackExchangeScripts",
2437 | "windmill",
2438 | "bricks",
2439 | "Thinktecture.Web.Http",
2440 | "nstore",
2441 | "regula",
2442 | "rePublish",
2443 | "Ux.locale.Manager",
2444 | "TiAir",
2445 | "ico",
2446 | "inherits",
2447 | "logging",
2448 | "kibo",
2449 | "chromeEyeDropper",
2450 | "CNPROG",
2451 | "thoonk.js",
2452 | "forms",
2453 | "flexnav",
2454 | "FotoRatan",
2455 | "SharpLinter",
2456 | "TiBall",
2457 | "jsref",
2458 | "couchforms",
2459 | "gowiththeflow.js",
2460 | "guides",
2461 | "todo",
2462 | "cli",
2463 | "Calloway",
2464 | "npmtop",
2465 | "quora2",
2466 | "oia",
2467 | "stackvm",
2468 | "Viper",
2469 | "jTag",
2470 | "forge",
2471 | "jCanvaScript",
2472 | "searchjs",
2473 | "sass.js",
2474 | "gitpaste",
2475 | "kettu",
2476 | "VECNIK",
2477 | "jsRayTracer",
2478 | "fatty",
2479 | "prototype.node.js",
2480 | "FireMobileSimulator",
2481 | "DOMination",
2482 | "futon4mongo",
2483 | "jush",
2484 | "pocketio",
2485 | "fileUploader",
2486 | "MobileWebAppsExamples",
2487 | "nextVal",
2488 | "morf",
2489 | "tartJS",
2490 | "miftree",
2491 | "moorte",
2492 | "Ext.ux.Exporter",
2493 | "clojuredocs",
2494 | "cssbeautify",
2495 | "firegestures",
2496 | "friar",
2497 | "CSSOM",
2498 | "ko.editables",
2499 | "dronejs",
2500 | "Playr",
2501 | "jinja.js",
2502 | "RestBugs",
2503 | "iTunes",
2504 | "githubarchive.org",
2505 | "rawkets",
2506 | "leanModal.js",
2507 | "fontmetrics.js",
2508 | "farbtastic",
2509 | "cujo",
2510 | "SimpleCQRS",
2511 | "fayer",
2512 | "jwerty",
2513 | "unicodetiles.js",
2514 | "servitude",
2515 | "JSSceneGraph",
2516 | "echo",
2517 | "JsonDiffPatch",
2518 | "MailCheckerPlus",
2519 | "l10n.js",
2520 | "structr.js",
2521 | "Niceforms",
2522 | "resistance",
2523 | "genData",
2524 | "geekslides",
2525 | "whiskers.js",
2526 | "comb",
2527 | "narwhal",
2528 | "GoogleClientLogin",
2529 | "myfavoritebeer.org",
2530 | "timekeeper",
2531 | "concrete",
2532 | "lingo",
2533 | "spaces",
2534 | "Meow",
2535 | "heso",
2536 | "Skytoop",
2537 | "upbeat",
2538 | "airport",
2539 | "SkitterSlideshow",
2540 | "muddy",
2541 | "firesass",
2542 | "WebGLU",
2543 | "FoBo",
2544 | "blingalytics",
2545 | "sudosocial",
2546 | "dombuilder",
2547 | "chromewithtwitter",
2548 | "FlodJS",
2549 | "pentabarf",
2550 | "nodeload",
2551 | "canvascropper",
2552 | "textshadow",
2553 | "shindig",
2554 | "stepy",
2555 | "ChromeToPaper",
2556 | "gritter",
2557 | "readability.py",
2558 | "haz",
2559 | "thingiview.js",
2560 | "birthdaypicker",
2561 | "hexlib",
2562 | "droppy",
2563 | "jsgamebench",
2564 | "tween",
2565 | "environ",
2566 | "authom",
2567 | "Texy.js",
2568 | "Gitview",
2569 | "magix",
2570 | "floom",
2571 | "Shibastagram",
2572 | "webui",
2573 | "speller",
2574 | "Janis",
2575 | "ySchool",
2576 | "specit",
2577 | "kwestion",
2578 | "writeCapture",
2579 | "NodeDeploy",
2580 | "SoundBankPlayer",
2581 | "com.woltlab.wcf.ckeditor",
2582 | "chmsee",
2583 | "selfish",
2584 | "NST",
2585 | "primer",
2586 | "D8",
2587 | "Training",
2588 | "jshtml",
2589 | "Backbone.amplify",
2590 | "rubyconf.tw",
2591 | "FlashCanvas",
2592 | "webglearth",
2593 | "slideNote",
2594 | "dillinger",
2595 | "JSONplus",
2596 | "liquidmetal",
2597 | "TeamCitySharp",
2598 | "usim",
2599 | "remoteagent",
2600 | "www",
2601 | "mibbu",
2602 | "smartr",
2603 | "ConsoleDummy.js",
2604 | "jqVideoBox",
2605 | "openpgpjs",
2606 | "message",
2607 | "fbdc",
2608 | "RouletteTok",
2609 | "run.js",
2610 | "async.js",
2611 | "Autolinker.js",
2612 | "btvwag.github.com",
2613 | "bibliotype",
2614 | "notestack",
2615 | "rotor.js",
2616 | "Pokki",
2617 | "Deluge.app",
2618 | "Raphael.Export",
2619 | "httpmock",
2620 | "webattle.js",
2621 | "LocalStorageDB.js",
2622 | "PlatformDemo",
2623 | "fbgl1",
2624 | "newtwitter",
2625 | "upstage",
2626 | "dropup",
2627 | "buftabs",
2628 | "ImpactStorage",
2629 | "shorty",
2630 | "drev",
2631 | "firmata",
2632 | "Convergence",
2633 | "phnx",
2634 | "escapes.js",
2635 | "node.websocket.js",
2636 | "telemote",
2637 | "spine.contacts",
2638 | "HowIsTheWeather",
2639 | "cqs",
2640 | "nog",
2641 | "infuser",
2642 | "mooMasonry",
2643 | "audionode.js",
2644 | "AjaxIM",
2645 | "CeraBox",
2646 | "lmd",
2647 | "task.js",
2648 | "zztmmo",
2649 | "teagrams",
2650 | "im.kayac.com",
2651 | "MorningStar",
2652 | "maddy",
2653 | "FileSaver.js",
2654 | "InstantSprite",
2655 | "background",
2656 | "mcms",
2657 | "slides",
2658 | "tamejs",
2659 | "polyglot",
2660 | "arclite",
2661 | "callme",
2662 | "Saito.js",
2663 | "bsonspec.org",
2664 | "LazyLoad",
2665 | "jsonparse",
2666 | "ttplus",
2667 | "MultiBox",
2668 | "ImageMenu",
2669 | "spksrc",
2670 | "cartoset",
2671 | "jasmid",
2672 | "scenario",
2673 | "RouteMap",
2674 | "ChuckNorrisException",
2675 | "MinPubSub",
2676 | "Appmator",
2677 | "jQswipe",
2678 | "customSelect",
2679 | "station",
2680 | "cartagen",
2681 | "CodeSnippet",
2682 | "HBFav",
2683 | "clay.js",
2684 | "Ext.layout.AccordionLayout",
2685 | "riab",
2686 | "nodejuice",
2687 | "react",
2688 | "mosaiqy",
2689 | "TheRenderEngine",
2690 | "TableTools",
2691 | "sideline",
2692 | "nodes",
2693 | "hoard",
2694 | "Pause",
2695 | "mediaboxAdvanced",
2696 | "echowaves",
2697 | "PinScroll",
2698 | "virgil",
2699 | "combyne.js",
2700 | "jQuery.placeHoldize",
2701 | "toocool",
2702 | "evernode",
2703 | "Editr",
2704 | "pjax",
2705 | "TabMemFree",
2706 | "LambdaJS",
2707 | "Colors",
2708 | "searchbox",
2709 | "ycChrome",
2710 | "syntaxhl",
2711 | "popupwindow",
2712 | "Fomatto",
2713 | "otherinbox",
2714 | "popcorn.kernel",
2715 | "vlandham.github.com",
2716 | "rotp",
2717 | "popcorn.sequence",
2718 | "postmile",
2719 | "Datejs",
2720 | "ForceDotBundle",
2721 | "jpgjs",
2722 | "Injector.js",
2723 | "xmlhttprequest",
2724 | "KeyboardJS",
2725 | "rdflib.js",
2726 | "pavlov",
2727 | "Thumbnails",
2728 | "zip",
2729 | "transformie",
2730 | "USTORE.js",
2731 | "errs",
2732 | "spell",
2733 | "NQUnit",
2734 | "ubiquity",
2735 | "canvasfilters",
2736 | "sequencer.js",
2737 | "svarx",
2738 | "html5bytebeat",
2739 | "vertebrae",
2740 | "JSGestureRecognizer",
2741 | "140medley",
2742 | "stream.js",
2743 | "Persistence",
2744 | "jazzrecord",
2745 | "lookups",
2746 | "openoverflow",
2747 | "NinjaScript",
2748 | "magaka",
2749 | "Helium",
2750 | "watermark.js",
2751 | "boxy",
2752 | "AutoObjectDocumentation",
2753 | "Apricot",
2754 | "grafico",
2755 | "thumbs.js",
2756 | "ccc",
2757 | "tinyalert",
2758 | "Templator",
2759 | "manor",
2760 | "Watchlist",
2761 | "pages",
2762 | "youtube5",
2763 | "TabCloud",
2764 | "lanyard",
2765 | "worklog",
2766 | "ROCA",
2767 | "alphaTab",
2768 | "parchment",
2769 | "github.commits.widget",
2770 | "armstrong.hatband",
2771 | "ValidateSimple",
2772 | "Ext.ux.TouchGridPanel",
2773 | "CanvasDisplayList",
2774 | "replicate",
2775 | "routes.js",
2776 | "tmpload",
2777 | "2011.beercamp.com",
2778 | "timesheets.js",
2779 | "mustache.github.com",
2780 | "Glog",
2781 | "lsjs",
2782 | "konamicode",
2783 | "startthedark",
2784 | "mochikit",
2785 | "FileDownLoadApp",
2786 | "myngo",
2787 | "redcouch",
2788 | "qleelulu.github.com",
2789 | "hornet",
2790 | "filed",
2791 | "tdl",
2792 | "summerTorrent",
2793 | "jefe",
2794 | "newebe",
2795 | "soundmanager2",
2796 | "feedback",
2797 | "primarywall",
2798 | "firepicker",
2799 | "color.js",
2800 | "colorwheel",
2801 | "wsbench",
2802 | "Typesetter.js",
2803 | "nodecpt.github.com",
2804 | "Sonic",
2805 | "foundation7",
2806 | "basecampjs",
2807 | "rx",
2808 | "jcoffeescript",
2809 | "follow",
2810 | "sizeit.js",
2811 | "timeframe",
2812 | "library",
2813 | "simpli5",
2814 | "playgrub",
2815 | "Ext.ux.touch.ListPullRefresh",
2816 | "node.ws.js",
2817 | "chriseppstein.github.com",
2818 | "mColorPicker",
2819 | "nodedc.github.com",
2820 | "happypointer",
2821 | "TiPopover",
2822 | "malsup.github.com",
2823 | "coconut",
2824 | "Raphael.JSON",
2825 | "peanut",
2826 | "chocolatechip",
2827 | "rosnodejs",
2828 | "thingjs",
2829 | "goggles",
2830 | "ckanjs",
2831 | "jSnake",
2832 | "html5tutorial",
2833 | "prompt",
2834 | "ChatSocks",
2835 | "hotspots",
2836 | "chickenfoot",
2837 | "cssParentSelector",
2838 | "Nodetuts",
2839 | "browser",
2840 | "enums",
2841 | "SproutCoreNative",
2842 | "dat.globe",
2843 | "ResponsiveThumbnailGallery",
2844 | "urljs",
2845 | "whoops",
2846 | "oorja",
2847 | "tipp",
2848 | "backbone.couchdb.js",
2849 | "clientcide",
2850 | "reformed",
2851 | "Turan",
2852 | "matchme",
2853 | "skywriter",
2854 | "ircloggr",
2855 | "api",
2856 | "Foundation",
2857 | "glossy",
2858 | "Codeita",
2859 | "resume.github.com",
2860 | "Ockley",
2861 | "spine.bitly",
2862 | "Absolution",
2863 | "preware",
2864 | "TheBox",
2865 | "flickrbomb",
2866 | "fastclick",
2867 | "Ajax.JSONRequest",
2868 | "kinout",
2869 | "OpaDo",
2870 | "dmWidgetGalleryPlugin",
2871 | "seventhings",
2872 | "spinners",
2873 | "userscripts",
2874 | "hydra",
2875 | "epub",
2876 | "streamie",
2877 | "OperaLink.js",
2878 | "Robohash",
2879 | "Exercise",
2880 | "pintura",
2881 | "activo",
2882 | "fredit",
2883 | "JaDE",
2884 | "nodeshot",
2885 | "MobileReadyFramework",
2886 | "fireunit",
2887 | "yui2",
2888 | "pfc",
2889 | "Ext.ux.Cover",
2890 | "tekkub.github.com",
2891 | "dr.js",
2892 | "nodecraft",
2893 | "Porter",
2894 | "jsctags",
2895 | "js.js",
2896 | "Handy.sugar",
2897 | "dom3d",
2898 | "JSTouchController",
2899 | "suncalc",
2900 | "redminetoolbar",
2901 | "amp",
2902 | "jQuery.emoji",
2903 | "opentodo",
2904 | "hellonode",
2905 | "s6",
2906 | "googlephonefix",
2907 | "MicroCache.js",
2908 | "microphysics.js",
2909 | "dizzy.js",
2910 | "NodeInterval",
2911 | "calendar",
2912 | "emoncms3",
2913 | "PNGStore",
2914 | "Incrementable",
2915 | "mapquery",
2916 | "celeri",
2917 | "customTabBar",
2918 | "TitaniORM",
2919 | "tempalias",
2920 | "TitaniumCachedImageView",
2921 | "CacheManager",
2922 | "whatajong",
2923 | "screencasts",
2924 | "nomnom",
2925 | "Atlas",
2926 | "PathFinding.js",
2927 | "RWThemeKit",
2928 | "relevancy.js",
2929 | "soldash",
2930 | "tail.io",
2931 | "rtvote",
2932 | "mobileGmap",
2933 | "eagerfeet",
2934 | "iFrame",
2935 | "nodeCQRS",
2936 | "ShortScroll",
2937 | "Knockout.Unobtrusive",
2938 | "sydjs",
2939 | "anticontainer",
2940 | "StoryNavigator",
2941 | "Presentations",
2942 | "oui",
2943 | "snoopy",
2944 | "Gatling",
2945 | "ExtJS.ux.HtmlEditor.Plugins",
2946 | "inventory",
2947 | "readability",
2948 | "Tabbed",
2949 | "browserbible",
2950 | "eyedee.me",
2951 | "kestrelweb",
2952 | "packnode",
2953 | "schema.js",
2954 | "oatv",
2955 | "Nowtable",
2956 | "Pik6",
2957 | "patternizer.js",
2958 | "mapchat",
2959 | "testling",
2960 | "jsup",
2961 | "horaa",
2962 | "floatingFixed",
2963 | "RavenGallery",
2964 | "dusterjs",
2965 | "usmap",
2966 | "lens",
2967 | "gmailr",
2968 | "jqapi",
2969 | "bespinclient",
2970 | "holler",
2971 | "retrospectiveapp",
2972 | "eson",
2973 | "simplerpg",
2974 | "SonatajQueryBundle",
2975 | "jQuery.Switchable",
2976 | "reticulatorjs",
2977 | "Eirene",
2978 | "workshop",
2979 | "twostroke",
2980 | "Tutti",
2981 | "BeerShift",
2982 | "bootstrap.js",
2983 | "pre3d",
2984 | "databases",
2985 | "SlideBlast",
2986 | "jQuery.BlackAndWhite",
2987 | "elastic",
2988 | "beeline",
2989 | "DirtyShare",
2990 | "jsContract",
2991 | "AppLayout",
2992 | "brew",
2993 | "reflect.js",
2994 | "elasticfox",
2995 | "QUnitTestRunnerPlugin",
2996 | "easey",
2997 | "moni",
2998 | "cdlre",
2999 | "timout",
3000 | "ceng202hestek",
3001 | "LayerStyles",
3002 | "spray",
3003 | "Beseda",
3004 | "socialcalc",
3005 | "metajack.im",
3006 | "jsdefer",
3007 | "xkcdfools",
3008 | "pmrpc",
3009 | "println",
3010 | "mixin",
3011 | "cast",
3012 | "dynts",
3013 | "minj",
3014 | "webglreport",
3015 | "StickyScroll",
3016 | "KitchenSinkDesktop",
3017 | "util",
3018 | "hoxy",
3019 | "bulldog",
3020 | "Aidori",
3021 | "abcsWriter",
3022 | "monstars.js",
3023 | "pjs",
3024 | "stick",
3025 | "dominiate",
3026 | "CallbackBillingPlugin",
3027 | "articles",
3028 | "recorder.js",
3029 | "webloop",
3030 | "CoderDeck",
3031 | "svgboilerplate",
3032 | "Colony",
3033 | "protobot",
3034 | "bless.js",
3035 | "notepages",
3036 | "hapi",
3037 | "Kwicks",
3038 | "glue.js",
3039 | "lonelytype",
3040 | "presentations",
3041 | "Face.js",
3042 | "plate",
3043 | "choicescript",
3044 | "broadcast",
3045 | "nlogger",
3046 | "MicroStream",
3047 | "markview",
3048 | "deck.rb",
3049 | "textus",
3050 | "xmlview",
3051 | "NodeCombo",
3052 | "drupal",
3053 | "okshadow",
3054 | "Thoth",
3055 | "KnockoutJSLive",
3056 | "jLinq",
3057 | "later",
3058 | "BazingaJqueryUiBundle",
3059 | "Tweetanium",
3060 | "traer.physics.js",
3061 | "nodestalker",
3062 | "Sumon",
3063 | "c3dl",
3064 | "springsprout",
3065 | "mio",
3066 | "panojs",
3067 | "kaffeine",
3068 | "blueprint",
3069 | "cubee.github.com",
3070 | "UglifyCSS",
3071 | "collective",
3072 | "jaml",
3073 | "Reston",
3074 | "userChromeJS",
3075 | "AjaxStack",
3076 | "frameworks",
3077 | "lsd",
3078 | "WebGitNet",
3079 | "trackyt.net",
3080 | "Riddle.js",
3081 | "rimraf",
3082 | "touchdown",
3083 | "sparklines.js",
3084 | "taskr",
3085 | "evently",
3086 | "GeoExt4",
3087 | "helicopter",
3088 | "ethercalc",
3089 | "lightnode",
3090 | "WebCraft",
3091 | "djax",
3092 | "yourworldoftext",
3093 | "djangotodos",
3094 | "omnibar",
3095 | "MGSE",
3096 | "LiveFilter",
3097 | "poshytip",
3098 | "CatchGame",
3099 | "webConsole",
3100 | "easteregg.in",
3101 | "Templify",
3102 | "nopt",
3103 | "CrossBrowse",
3104 | "strftime",
3105 | "undum",
3106 | "HistoryManager",
3107 | "RTree",
3108 | "json",
3109 | "Sheethub",
3110 | "smoothgallery",
3111 | "arc.js",
3112 | "gitmarks",
3113 | "CrowLib",
3114 | "corpus",
3115 | "cuepoint",
3116 | "smus.com",
3117 | "capty",
3118 | "drag2up",
3119 | "drupal.api.js",
3120 | "skybase",
3121 | "redis2json",
3122 | "tryhaskell",
3123 | "kibi",
3124 | "maxim.github.com",
3125 | "jianmian",
3126 | "nodewiki",
3127 | "StartupDataTrends",
3128 | "iOSEmoji",
3129 | "xlsuite",
3130 | "stackview",
3131 | "PixelPlatformer",
3132 | "sctable",
3133 | "vimari",
3134 | "craftscape",
3135 | "rediskit",
3136 | "transpochoices",
3137 | "cornerz",
3138 | "puppet",
3139 | "jarvis",
3140 | "Jerk",
3141 | "red",
3142 | "FrenchPress.sugar",
3143 | "Qanban",
3144 | "Scrumie",
3145 | "box2dnode",
3146 | "AutoSuggest",
3147 | "dejavis",
3148 | "syntux",
3149 | "tapestry5inaction",
3150 | "Booki",
3151 | "js",
3152 | "djangobase",
3153 | "CoffeeCoffee",
3154 | "string",
3155 | "Mouse.Touch",
3156 | "nodules",
3157 | "boilerplates",
3158 | "wd",
3159 | "mongolia",
3160 | "fcbkListSelection",
3161 | "couchpubtato",
3162 | "twebz",
3163 | "elmah.mvc.controller",
3164 | "uncommonjs",
3165 | "a3",
3166 | "hotdot",
3167 | "MaDe",
3168 | "dfm.github.com",
3169 | "try.redis",
3170 | "jshamcrest",
3171 | "say.js",
3172 | "Dagron",
3173 | "TitaniumSamples",
3174 | "teleport",
3175 | "suggest",
3176 | "thingler",
3177 | "Postman",
3178 | "ukijs.org",
3179 | "tilelive.js",
3180 | "wallet",
3181 | "nabe",
3182 | "filternet",
3183 | "DDDSample.Net",
3184 | "Macto",
3185 | "firerainbow",
3186 | "firelogger",
3187 | "yproject",
3188 | "mimelib",
3189 | "RealTimeMonitor",
3190 | "flensed",
3191 | "quip",
3192 | "rfc3339date.js",
3193 | "NavigationController",
3194 | "amdefine",
3195 | "lessless",
3196 | "fire",
3197 | "webshell",
3198 | "spotlight.js",
3199 | "facyBox",
3200 | "crux",
3201 | "htmlcanvas",
3202 | "RxJSKoans",
3203 | "karait",
3204 | "pastebin",
3205 | "ExpandingTextareas",
3206 | "siobench",
3207 | "hotglue2",
3208 | "Leaflet.zoomslider",
3209 | "yaselect",
3210 | "cssx",
3211 | "nTenjin",
3212 | "thunder",
3213 | "fidel",
3214 | "Anychrome.crx",
3215 | "Tweetable",
3216 | "dbemitter",
3217 | "adventure",
3218 | "jQuery.zip2addr",
3219 | "MIDI.js",
3220 | "asset",
3221 | "escrito",
3222 | "stash",
3223 | "Tasks",
3224 | "radioSwitch",
3225 | "cluster.exception",
3226 | "delorean.js",
3227 | "m2node",
3228 | "BIMsurfer",
3229 | "jsBVH",
3230 | "bangbang",
3231 | "resty",
3232 | "raphael.draggable",
3233 | "speakeasy",
3234 | "modplug",
3235 | "tinkerbin",
3236 | "chromeshack",
3237 | "kern.js",
3238 | "RaphaelViz",
3239 | "RaphaelViews",
3240 | "ineedmoretime",
3241 | "spectrum",
3242 | "TipTip",
3243 | "RacerJS",
3244 | "Events.js",
3245 | "AsciiTracer",
3246 | "LightFace",
3247 | "jmeta",
3248 | "webtreemap",
3249 | "intercom",
3250 | "livecss",
3251 | "swimlanes",
3252 | "CoolClock",
3253 | "Ext.ux.Printer",
3254 | "sight",
3255 | "cssess",
3256 | "courtside",
3257 | "thewall",
3258 | "nseg",
3259 | "tadagraph",
3260 | "Safari960",
3261 | "zAccordion",
3262 | "jsmodplayer",
3263 | "Sanitize.js",
3264 | "spine.infinite",
3265 | "everyjs.com",
3266 | "picoCSS",
3267 | "OpenPCR",
3268 | "Gitgraph",
3269 | "knvs",
3270 | "filereader.js",
3271 | "etch",
3272 | "powereditor",
3273 | "TreeBundle",
3274 | "sfxr.js",
3275 | "ClojureCoffeeScriptGame",
3276 | "endash",
3277 | "jgrowl",
3278 | "photosnear.me",
3279 | "chainvas",
3280 | "HelloPhoneGap1.0",
3281 | "totem",
3282 | "uilayer",
3283 | "jscouch",
3284 | "contour",
3285 | "chirp",
3286 | "jqm.carousel",
3287 | "grockets",
3288 | "newforms",
3289 | "thumbreel",
3290 | "jezebel",
3291 | "jsAvroPhonetic",
3292 | "QTransform",
3293 | "R.js",
3294 | "Levenshtein",
3295 | "jackdaw",
3296 | "Skull.io",
3297 | "nodechat",
3298 | "tumblrrr",
3299 | "ADsafe",
3300 | "javascript",
3301 | "scielobooks",
3302 | "yabble",
3303 | "buckets",
3304 | "michromeformats",
3305 | "tcx2nikeplus",
3306 | "jskata",
3307 | "DJS",
3308 | "canvasthumber",
3309 | "Cordova",
3310 | "jim",
3311 | "io",
3312 | "mturk",
3313 | "stitches",
3314 | "eCSStender.js",
3315 | "underscore.date",
3316 | "PrincetonStartWeek",
3317 | "caustic",
3318 | "livecomments.go",
3319 | "gtween.js",
3320 | "learn.js",
3321 | "birdhouse",
3322 | "blog.js",
3323 | "pulverizr",
3324 | "albumdy",
3325 | "fusejs",
3326 | "aboutme",
3327 | "arduinojs",
3328 | "yuno",
3329 | "audiojedit",
3330 | "Crude",
3331 | "Nyars",
3332 | "githubbub",
3333 | "JigLibJS",
3334 | "spade",
3335 | "backbone.CQRS",
3336 | "rell",
3337 | "neovigator",
3338 | "happen",
3339 | "soapjs",
3340 | "prenup",
3341 | "tquery.jsartoolkit",
3342 | "msgpack.js",
3343 | "cloudpanic",
3344 | "tweetfilter.github.com",
3345 | "Ext.LocaleManager",
3346 | "couchmail",
3347 | "multiplayer",
3348 | "rubyonrio",
3349 | "Tranquil",
3350 | "sideflow",
3351 | "mariohtml5",
3352 | "Play20StartApp",
3353 | "spark2",
3354 | "arrispwgen",
3355 | "NoSQLite",
3356 | "UITest",
3357 | "LinkifyURL",
3358 | "ICanHandlebarz.js",
3359 | "PoliteCaptcha",
3360 | "rhatter",
3361 | "WKSlider",
3362 | "iphone.css",
3363 | "jorder",
3364 | "require",
3365 | "fargo",
3366 | "mashi",
3367 | "remora",
3368 | "Selfsurfing",
3369 | "inception",
3370 | "blog",
3371 | "mapsonastick",
3372 | "banzai",
3373 | "PaperCube",
3374 | "multiplayerchess.com",
3375 | "AndroidGrocerySync",
3376 | "jQuery.birdseye",
3377 | "tweetstream",
3378 | "Skylight",
3379 | "jSquares",
3380 | "ExtJS.ux.DataDrop",
3381 | "Ext.ux.filebrowser",
3382 | "jWizard",
3383 | "ghostwriter",
3384 | "qTip1",
3385 | "meryl",
3386 | "wordcloud",
3387 | "flipBook",
3388 | "Html5MvcTemplates",
3389 | "html5expense",
3390 | "asereje",
3391 | "tutorials",
3392 | "oms",
3393 | "JxLib",
3394 | "Pano",
3395 | "mumbl",
3396 | "geocoder",
3397 | "firefly",
3398 | "noladex.org",
3399 | "cssie",
3400 | "dyluni",
3401 | "vertex.js",
3402 | "rifgraf",
3403 | "cron.js",
3404 | "delivR",
3405 | "popupjs",
3406 | "Presidents",
3407 | "wormhole",
3408 | "cfg.js",
3409 | "NavSimple",
3410 | "MooTune",
3411 | "tabswitcher",
3412 | "Flexigrid",
3413 | "logger.js",
3414 | "wobot",
3415 | "Raphaelle",
3416 | "osc4node",
3417 | "spassky",
3418 | "VLCcontrols",
3419 | "qbe",
3420 | "XcodeAPI",
3421 | "enchantMapEditor",
3422 | "vim.safariextension",
3423 | "restmvc.js",
3424 | "schampignon",
3425 | "ranger",
3426 | "jReject",
3427 | "client.express.js",
3428 | "nserv",
3429 | "graphitejs",
3430 | "docbook.github.com",
3431 | "pokernode",
3432 | "BlobBuilder.js",
3433 | "csster",
3434 | "turtlewax",
3435 | "reed",
3436 | "xregexp",
3437 | "Grid",
3438 | "nerdie",
3439 | "animatePNG",
3440 | "backbone.js",
3441 | "ofxNodejs",
3442 | "Numbas",
3443 | "codenode",
3444 | "chattrr",
3445 | "drty",
3446 | "vex",
3447 | "motionjs",
3448 | "AdaWiki2",
3449 | "bookreader",
3450 | "jQuery.html5loader",
3451 | "Responsly.js",
3452 | "hook.js",
3453 | "jchess",
3454 | "polypage",
3455 | "BrandAnalytics",
3456 | "JsBarcode",
3457 | "GDLogTool",
3458 | "stereo",
3459 | "donortrust",
3460 | "pilgrim",
3461 | "service.js",
3462 | "Dropp",
3463 | "MvcIntegrationTestFramework",
3464 | "TinyColor",
3465 | "foobar3000",
3466 | "Redbeard",
3467 | "twirlymol",
3468 | "editablegrid",
3469 | "visibly.js",
3470 | "WebAL",
3471 | "jsandbox",
3472 | "PP.js",
3473 | "Brochurno",
3474 | "melt",
3475 | "trinity",
3476 | "cygnuscms",
3477 | "dowhen",
3478 | "Facelist",
3479 | "imsto",
3480 | "bluff",
3481 | "Extremes",
3482 | "jQuery.keyboard",
3483 | "ffffallback",
3484 | "readygxp",
3485 | "WebGLCraft",
3486 | "jsts",
3487 | "TileJSON",
3488 | "jssvggraph",
3489 | "uploader",
3490 | "Formly",
3491 | "Ext.ux.touch.InfiniteCarousel",
3492 | "minimal.js",
3493 | "extjs",
3494 | "email.js",
3495 | "acewidget",
3496 | "dui",
3497 | "ModuleJS",
3498 | "ExtJS.ux.GMapPanel",
3499 | "Focus",
3500 | "olelo",
3501 | "livevalidation",
3502 | "Simples.SkypeChatStyle",
3503 | "mobilefuton",
3504 | "selleck",
3505 | "hedgehog",
3506 | "terminus",
3507 | "PaintWeb",
3508 | "groovywebconsole",
3509 | "CSSAnimationKeyframeEvents",
3510 | "lyonrb",
3511 | "smoothscroll",
3512 | "aries",
3513 | "big",
3514 | "growlhub",
3515 | "gitforked",
3516 | "parsely",
3517 | "taconite",
3518 | "VFW",
3519 | "WebWorksQNXWidgets",
3520 | "logan",
3521 | "raindrop",
3522 | ];
3523 |
--------------------------------------------------------------------------------