Commit 2783f91f authored by nanahira's avatar nanahira

first

parent a6f0cda7
# compiled output
/dist
/node_modules
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# OS
.DS_Store
# Tests
/coverage
/.nyc_output
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
/data
/output
/config.yaml
.git*
Dockerfile
.dockerignore
/tests
webpack.config.js
dist/*
build/*
*.js
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
root: true,
env: {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js'],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
};
# compiled output
/dist
/node_modules
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# OS
.DS_Store
# Tests
/coverage
/.nyc_output
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
/data
/output
/config.yaml
stages:
- build
- combine
- deploy
variables:
GIT_DEPTH: "1"
CONTAINER_TEST_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
CONTAINER_TEST_ARM_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-arm
CONTAINER_TEST_X86_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-x86
CONTAINER_RELEASE_IMAGE: $CI_REGISTRY_IMAGE:latest
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
build-x86:
stage: build
tags:
- docker
script:
- TARGET_IMAGE=$CONTAINER_TEST_X86_IMAGE
- docker build --pull -t $TARGET_IMAGE .
- docker push $TARGET_IMAGE
build-arm:
stage: build
tags:
- docker-arm
script:
- TARGET_IMAGE=$CONTAINER_TEST_ARM_IMAGE
- docker build --pull -t $TARGET_IMAGE .
- docker push $TARGET_IMAGE
combine:
stage: combine
tags:
- docker
script:
- TARGET_IMAGE=$CONTAINER_TEST_IMAGE
- SOURCE_IMAGE_2=$CONTAINER_TEST_ARM_IMAGE
- SOURCE_IMAGE_1=$CONTAINER_TEST_X86_IMAGE
- docker pull $SOURCE_IMAGE_1
- docker pull $SOURCE_IMAGE_2
- docker manifest create $TARGET_IMAGE --amend $SOURCE_IMAGE_1 --amend
$SOURCE_IMAGE_2
- docker manifest push $TARGET_IMAGE
deploy_latest:
stage: deploy
tags:
- docker
script:
- TARGET_IMAGE=$CONTAINER_RELEASE_IMAGE
- SOURCE_IMAGE=$CONTAINER_TEST_IMAGE
- docker pull $SOURCE_IMAGE
- docker tag $SOURCE_IMAGE $TARGET_IMAGE
- docker push $TARGET_IMAGE
only:
- master
deploy_tag:
stage: deploy
tags:
- docker
script:
- TARGET_IMAGE=$CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
- SOURCE_IMAGE=$CONTAINER_TEST_IMAGE
- docker pull $SOURCE_IMAGE
- docker tag $SOURCE_IMAGE $TARGET_IMAGE
- docker push $TARGET_IMAGE
only:
- tags
stages:
- install
- build
- deploy
variables:
GIT_DEPTH: "1"
npm_ci:
stage: install
tags:
- linux
script:
- npm ci
artifacts:
paths:
- node_modules
.build_base:
stage: build
tags:
- linux
dependencies:
- npm_ci
build:
extends: .build_base
script: npm run build
artifacts:
paths:
- dist/
upload_to_minio:
stage: deploy
dependencies:
- build
tags:
- linux
script:
- aws s3 --endpoint=https://minio.momobako.com:9000 sync --delete dist/ s3://nanahira/path
only:
- master
stages:
- install
- build
- deploy
variables:
GIT_DEPTH: "1"
npm_ci:
stage: install
tags:
- linux
script:
- npm ci
artifacts:
paths:
- node_modules
.build_base:
stage: build
tags:
- linux
dependencies:
- npm_ci
build:
extends:
- .build_base
script:
- npm run build
artifacts:
paths:
- dist/
unit-test:
extends:
- .build_base
script:
- npm run test
deploy_npm:
stage: deploy
dependencies:
- build
tags:
- linux
script:
- apt update;apt -y install coreutils
- echo $NPMRC | base64 --decode > ~/.npmrc
- npm publish . || true
only:
- master
/install-npm.sh
.git*
/data
/output
/config.yaml
.idea
.dockerignore
Dockerfile
/src
/coverage
/tests
/dist/tests
{
"singleQuote": true,
"trailingComma": "all"
}
\ No newline at end of file
FROM node:lts-bullseye-slim as base
LABEL Author="Nanahira <nanahira@momobako.com>"
RUN apt update && apt -y install python3 build-essential && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /var/log/*
WORKDIR /usr/src/app
COPY ./package*.json ./
FROM base as builder
RUN npm ci && npm cache clean --force
COPY . ./
RUN npm run build
FROM base
ENV NODE_ENV production
RUN npm ci && npm cache clean --force
COPY --from=builder /usr/src/app/dist ./dist
CMD [ "npm", "start" ]
The MIT License (MIT)
Copyright (c) 2021 Nanahira
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
#!/bin/bash
npm install --save-dev \
@types/node \
typescript \
'@typescript-eslint/eslint-plugin@^4.28.2' \
'@typescript-eslint/parser@^4.28.2 '\
'eslint@^7.30.0' \
'eslint-config-prettier@^8.3.0' \
'eslint-plugin-prettier@^3.4.0' \
prettier \
jest \
@types/jest \
ts-jest \
rimraf
This diff is collapsed.
{
"name": "cordis-decorators",
"description": "Decorator implementation of [cordis](https://github.com/shigma/cordis) .",
"version": "1.0.0",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"lint": "eslint --fix .",
"build": "rimraf dist && tsc",
"test": "jest --passWithNoTests",
"start": "node dist/index.js"
},
"repository": {
"type": "git",
"url": "https://code.mycard.moe/3rdeye/cordis-decorators.git"
},
"author": "Nanahira <nanahira@momobako.com>",
"license": "MIT",
"keywords": [],
"bugs": {
"url": "https://code.mycard.moe/3rdeye/cordis-decorators/issues"
},
"homepage": "https://code.mycard.moe/3rdeye/cordis-decorators",
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "tests",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
},
"devDependencies": {
"@types/jest": "^28.1.4",
"@types/lodash": "^4.14.182",
"@types/mustache": "^4.1.3",
"@types/node": "^18.0.3",
"@typescript-eslint/eslint-plugin": "^4.33.0",
"@typescript-eslint/parser": "^4.33.0",
"eslint": "^7.32.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^3.4.1",
"jest": "^28.1.2",
"prettier": "^2.7.1",
"rimraf": "^3.0.2",
"ts-jest": "^28.0.5",
"typescript": "^4.7.4"
},
"peerDependencies": {
"cordis": "^2.0.6",
"schemastery": "^3.4.3"
},
"dependencies": {
"lodash": "^4.17.21",
"mustache": "^4.2.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.5.6",
"schemastery-gen": "^3.1.14",
"typed-reflector": "^1.0.10"
}
}
import { defaultRegistrar } from '../default-registrar';
export const { Isolate, UsingService } = defaultRegistrar.scopeDecorators();
export const { UsePlugin } = defaultRegistrar.methodDecorators();
export * from './common';
export * from './plugin';
import { defaultRegistrar } from '../default-registrar';
import { Context } from 'cordis';
import { PluginRegistrar } from '../def/plugin';
const pluginDecorators = defaultRegistrar.pluginDecorators();
export const {
If,
For,
PluginName,
PluginSchema,
Reusable,
Provide,
InjectContext,
InjectConfig,
InjectParent,
Caller,
} = pluginDecorators;
export const Fork = <Ctx extends Context>(
fork: PluginRegistrar.PluginClass<Ctx>,
) => pluginDecorators.Fork(fork);
export function Inject(name?: string, addUsing?: boolean): PropertyDecorator;
export function Inject(addUsing?: boolean): PropertyDecorator;
export function Inject(...args: [(string | boolean)?, boolean?]) {
return pluginDecorators.Inject(...args);
}
export * from './interfaces';
export type Awaitable<T> = [T] extends [Promise<unknown>] ? T : T | Promise<T>;
export type TypedMethodDecorator<F extends (...args: any[]) => any> = <
T extends F,
>(
// eslint-disable-next-line @typescript-eslint/ban-types
target: Object,
propertyKey: string | symbol,
descriptor: TypedPropertyDescriptor<T>,
) => void;
export type FunctionParam<F extends (...args: any[]) => any> = F extends (
...args: infer R
) => any
? R
: never;
export type FunctionReturn<F extends (...args: any[]) => any> = F extends (
...args: any[]
) => infer R
? R
: never;
export type PickEventFunction<M, K extends keyof M = keyof M> = M[K] extends (
...args: any[]
) => any
? (...args: FunctionParam<M[K]>) => Awaitable<FunctionReturn<M[K]>>
: M[K];
export type ParamRenderer = <T>(v: T) => T;
export interface ControlTypeMap {
if: boolean;
for: Iterable<Record<string, any>>;
}
export type Condition<R, T = any, Ext extends any[] = []> = (
o: T,
...ext: Ext
) => R;
export interface ControlType<
T extends keyof ControlTypeMap = keyof ControlTypeMap,
> {
type: T;
condition: Condition<ControlTypeMap[T], any, [Record<string, any>]>;
}
import { Context, Plugin } from 'cordis';
import Schema from 'schemastery';
import { ClassType } from 'schemastery-gen';
import { Registrar } from '../registrar';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace PluginRegistrar {
export type PluginClass<Ctx extends Context, C = any, P = any> = new (
ctx: Ctx,
config: C,
) => P;
export type SystemInjectFun<Ctx extends Context> = <T = any>(
obj: PluginMeta<Ctx, T>,
cl: PluginClass<Ctx>,
) => any;
export interface PluginRegistrationOptions<Ctx extends Context, T = any> {
name?: string;
schema?: Schema<T> | ClassType<T>;
Config?: Schema<T> | ClassType<T>;
using?: Registrar.ServiceName<Ctx>[];
reusable?: boolean;
}
export interface PluginMeta<Ctx extends Context, T = any> {
__ctx: Ctx;
__config: T;
__pluginOptions: PluginRegistrationOptions<Ctx, T>;
__promisesToWaitFor: Promise<void>[];
__disposables: (() => void)[];
}
export type PluginOptions<T extends Plugin> = boolean | Plugin.Config<T>;
export interface PluginDefinitionExact<
Ctx extends Context,
T extends Plugin<Ctx>,
> {
plugin: T;
options?: boolean | PluginOptions<T>;
}
export interface PluginDefinitionName {
plugin: string;
options?: any;
}
export type PluginDefinition<Ctx extends Context, T extends Plugin = any> =
| PluginDefinitionExact<Ctx, T>
| PluginDefinitionName;
}
export function PluginDef<Ctx extends Context>(
name: string,
options?: any,
): PluginRegistrar.PluginDefinitionName;
export function PluginDef<Ctx extends Context, T extends Plugin>(
plugin: T,
options?: PluginRegistrar.PluginOptions<T>,
): PluginRegistrar.PluginDefinitionExact<Ctx, T>;
export function PluginDef<Ctx extends Context, T extends Plugin>(
plugin: T,
options?: PluginRegistrar.PluginOptions<T>,
): PluginRegistrar.PluginDefinition<Ctx, T> {
return { plugin, options };
}
export interface LifecycleEvents {
onApply?(): void;
onConnect?(): void | Promise<void>;
onDisconnect?(): void | Promise<void>;
onFork?(instance: any): void | Promise<void>;
onForkDisconnect?(instance: any): void | Promise<void>;
}
import { Registrar } from './registrar';
import { Context } from 'cordis';
export const defaultRegistrar = new Registrar(Context);
import { Context } from 'cordis';
import { Registrar } from './registrar';
import { generateRenderer, renderObject } from './utility/render-object';
import { extractObjectMethod } from './utility/utility';
import { ControlType } from './def';
import { from, Observable, ObservableInput, ObservedValueOf, of } from 'rxjs';
import _ from 'lodash';
type RecursiveUnwrapObservable<T> = T extends ObservableInput<any>
? RecursiveUnwrapObservable<ObservedValueOf<T>>
: T;
export class RegistrarAspect<Ctx extends Context, T = any> {
constructor(
private registrar: Registrar<Ctx>,
private obj: T,
private view: Record<any, any> = {},
) {}
getAllFieldsToRegister() {
const arr = this.registrar.reflector
.getArray('CordisRegisterKeys', this.obj)
.filter((k) => typeof k === 'string');
return arr as (keyof T & string)[];
}
getScopeContext(
ctx: Ctx,
key?: keyof T & string,
extraView: Record<any, any> = {},
autoScope = true,
) {
if (key && autoScope) {
ctx = this.getScopeContext(ctx);
}
const contextFilters = this.registrar.reflector.getArray(
'CordisContextTransformer',
this.obj,
key,
);
const r = generateRenderer({ ...this.view, ...extraView });
return this.registrar.transformContext(ctx, contextFilters, r);
}
registerMethod(
ctx: Ctx,
key: keyof T & string,
extraView: Record<any, any> = {},
): Registrar.RegisterResult<Ctx, T> {
const data = this.registrar.reflector.get('CordisRegister', this.obj, key);
if (!data) return;
const result = data.action(
this.getScopeContext(ctx, key, extraView, false),
extractObjectMethod(this.obj, key),
...renderObject(data.args, { ...this.view, ...extraView }),
);
return { ...data, key: key, result };
}
private registerWithLoopControl(
ctx: Ctx,
key: keyof T & string,
stack: ControlType[],
existing: Record<string, any> = {},
): Registrar.RegisterResult<Ctx, T>[] {
if (!stack.length) {
const result = this.registerMethod(ctx, key, existing);
return result ? [result] : [];
}
const rest = [...stack];
const control = rest.pop();
switch (control.type) {
case 'if':
if (!(control as ControlType<'if'>).condition(this, existing))
return [];
return this.registerWithLoopControl(ctx, key, rest, existing);
case 'for':
return Array.from(
(control as ControlType<'for'>).condition(this, existing),
).flatMap((item) =>
this.registerWithLoopControl(ctx, key, rest, {
...existing,
...item,
}),
);
}
}
private runLayersWith<R extends ObservableInput<any>>(
ctx: Ctx,
cb: Registrar.ContextFunction<Ctx, R>,
layers: Registrar.ContextCallbackLayer<Ctx>[],
): Observable<RecursiveUnwrapObservable<R>> {
const rest = [...layers];
const layer = rest.pop();
if (!layer) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return of(cb(ctx));
}
return new Observable((subscriber) => {
layer(ctx, async (nextCtx) => {
if (!rest.length) {
const result = cb(nextCtx);
if (result) {
from(result).subscribe({
next: (v) => subscriber.next(v),
// no error
// no complete
});
}
} else {
this.runLayersWith(nextCtx, cb, rest).subscribe(subscriber);
}
});
});
}
private runLayers<R extends ObservableInput<any>>(
ctx: Ctx,
cb: Registrar.ContextFunction<Ctx, R>,
key?: keyof T,
) {
const layers = this.registrar.reflector.getArray(
'CordisContextLayers',
this.obj,
key as string,
);
return this.runLayersWith(ctx, cb, layers);
}
registerFor(ctx: Ctx, key: keyof T & string) {
const stack = this.registrar.reflector.getArray(
'CordisControl',
this.obj,
key,
);
return this.runLayers(
ctx,
(innerCtx) => this.registerWithLoopControl(innerCtx, key, stack),
key,
);
}
register(ctx: Ctx) {
const keys = this.getAllFieldsToRegister();
return this.runLayers(ctx, (innerCtx) =>
keys.map((key) => this.registerFor(innerCtx, key)),
);
}
performTopActions(
ctx: Ctx,
autoScope = false,
extraView: Record<any, any> = {},
) {
if (autoScope) {
ctx = this.getScopeContext(ctx);
}
const actions = _.uniq(
this.registrar.reflector.getArray('CordisTopLevelAction', this.obj),
);
const renderer = generateRenderer({ ...this.view, ...extraView });
actions.forEach((action) => action(ctx, this.obj, renderer));
}
}
This diff is collapsed.
import Mustache from 'mustache';
import _ from 'lodash';
import { ParamRenderer } from '../def';
export function renderObject<T = any>(
object: T,
view: any,
visited?: Set<any>,
): T;
export function renderObject(object: any, view: any, visited: Set<any>): any {
if (!view || !object) {
return object;
}
visited ??= new Set();
if (typeof object === 'string') {
return Mustache.render(object, view, undefined, { escape: (v) => v });
}
if (visited.has(object)) {
return object;
}
if (Array.isArray(object)) {
visited.add(object);
return (object as any[]).map((item) => renderObject(item, view, visited));
}
if (typeof object === 'object') {
visited.add(object);
return _.mapValues(object, (value) => renderObject(value, view, visited));
}
return object;
}
export function generateRenderer(view: any): ParamRenderer {
return (v) => renderObject(v, view);
}
type AnyFunction = (...args: any[]) => any;
export function extractObjectMethod<T, K extends keyof T>(
o: T,
key: K,
): T[K] extends AnyFunction ? T[K] : AnyFunction {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return (...args: any[]) => o[key](...args);
}
describe('Sample test.', () => {
it('should pass', () => {
expect(true).toBe(true);
});
});
{
"compilerOptions": {
"outDir": "dist",
"module": "commonjs",
"target": "es2021",
"esModuleInterop": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"declaration": true,
"sourceMap": true
},
"compileOnSave": true,
"allowJs": true,
"include": [
"*.ts",
"src/**/*.ts",
"test/**/*.ts",
"tests/**/*.ts"
]
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment