Can typescript external modules have circular dependencies? - module

It looks like this is not allowed. requireJS is throwing an error on the following (this post is different as it was resolved with internal modules):
element.ts:
import runProperties = require('./run-properties');
export class Element {
public static factory (element : IElement) : Element {
switch (element.type) {
case TYPE.RUN_PROPERTIES :
return new runProperties.RunProperties().deserialize(<runProperties.IRunProperties>element);
}
return null;
}
}
run-properties.ts:
import element = require('./element');
export class RunProperties extends element.Element implements IRunProperties {
}

No, modules can't have circular dependencies unless they are in the same file. Each file is being processed in sequence, synchronously, so the full file definition (including all of the exports for example) hasn't been completed when it goes to second file, which immediately tries to require/reference the first file, and so on.
Normally, you can break a circular dependency by introducing an interface or base class into a common definition file(s) (basically interfaces only) and having the other files use that as a common "interface" rather than directly referencing the classes. This is a typical pattern in many platforms.

I have same issue, I was able to fix it by creating factory class that allows registration of child classes and used Generics for instantiation.
Reference: https://www.typescriptlang.org/docs/handbook/generics.html#using-class-types-in-generics
See sample code below:
Base Class (abstract.control.ts)
export type AbstracControlOptions = {
key?:string;
}
export abstract class AbstractControl {
key:string;
constructor(options:AbstracControlOptions){
this.key = options.key;
}
}
Parent Class (container.ts)
import { AbstractControl, AbstracControlOptions } from './abstract.control';
import { Factory } from './factory';
export { AbstracControlOptions };
export abstract class Container extends AbstractControl {
children: AbstractControl[] = [];
constructor(options: AbstracControlOptions) {
super(options);
}
addChild(options: { type: string }) {
var Control:any = Factory.ControlMap[options.type];
if (Control) {
this.children.push(Factory.create(Control, options));
}
}
}
I don't have to import the child classes any more, because I'm using factory.ts to instantiate the child classes.
Factory Class(factory.ts)
import {AbstractControl, AbstracControlOptions} from './abstract.control';
type ControlMap<T extends AbstractControl> = {
[type:string]:T
};
export class Factory{
static ControlMap: ControlMap<any> = {};
static create<T extends AbstractControl>(c: { new ({}): T; }, options: AbstracControlOptions): T {
return new c(options);
}
}
Although class constructor seems to be called at c: { new ({}): T } but it does not actually calls it. But gets the reference to the constructor via new operator. The parameter {} to the constructor in my case is required because the base class AbstractControl requires it.
(1) Child Class(layout.ts)
import { Factory } from './factory';
import { Container, AbstracControlOptions } from './container';
export type LayoutlOptions = AbstracControlOptions & {
type:"layout";
}
export class Layout extends Container {
type: string = "layout";
constructor(options:LayoutlOptions) {
super(options);
}
}
Factory.ControlMap["layout"] = Layout;
(2) Child Class(repeater.ts)
import { Factory } from './factory'
import { Container, AbstracControlOptions } from './container';
export type RepeaterOptions = AbstracControlOptions & {
type: "repeater";
}
export class Repeater extends Container {
type: string = "repeater";
constructor(options:RepeaterOptions) {
super(options);
}
}
Factory.ControlMap["repeater"] = Repeater;

Related

Nestjs MiddlewareConsumer exclude path not working in Azure function

Exclude path does not work when I wrap it up into an Azure function. See ClientInfoModule snippet below.
exclude({ path :'validate', method : RequestMethod.POST })
Even though the validate is specified in the exclude, the middleware is still called for that route.
Am I missing something trivial here ?
I followed the documentation here : https://docs.nestjs.com/middleware#functional-middleware
Here is a top level snippet of my code below.
import { Injectable, NestMiddleware } from '#nestjs/common';
export class AuthMiddleware implements NestMiddleware {
...
}
----------
import { MiddlewareConsumer, Module, NestModule, RequestMethod } from '#nestjs/common';
export class ClientInfoModule implements NestModule{
configure(consumer: MiddlewareConsumer) {
consumer
.apply(AuthMiddleware)
.exclude(
{ path :'validate', method : RequestMethod.POST },
)
.forRoutes(ClientController, SomeOtherController);
}
}
----------
#Controller('validate')
export class ClientController {
constructor( ) {}
#Post()
async validateFingerprint(#Body() clientPayload: ClientPayload): Promise<ApiResponse>
{
... this should be excluded from the middleware
}
}

Problem converting Java class containing a static factory method to Kotlin

I have had no sucess trying to convert this to Kotlin and use it.
It works as Java, just not with my Kotlin (which I converted using IntelliJ Kotlin Plugin)
the problem appears to be this part
#PluginFactory
public static AnsiConsoleAppender createAppender(
I tried to add #JvmStatic and I get this error:
Unable to invoke factory method in class AnsiConsoleAppender for element AnsiConsoleAppender: java.lang.IllegalArgumentException: Parameter specified as non-null is null: method AnsiConsoleAppender$Companion.AnsiConsoleAppender, parameter filter java.lang.reflect.InvocationTargetException
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.appender.AppenderLoggingException;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.layout.PatternLayout;
import java.io.Serializable;
/**
* <h2>AnsiConsoleAppender</h2>
*
* <h3>notes</h3>
* <li>class name need not match the #Plugin name</li>
* <h3>more<h3/>
* <li>How to Create a Custom Appender in log4j2?</li>
*/
#Plugin(name="AnsiConsoleAppender", category="Core", elementType="appender", printObject=true)
public final class AnsiConsoleAppender extends AbstractAppender {
protected AnsiConsoleAppender(String name, Filter filter,
Layout<? extends Serializable> layout, final boolean ignoreExceptions) {
super(name, filter, layout, ignoreExceptions);
}
// The append method is where the appender does the work.
// Given a log event, you are free to do with it what you want.
// This example demonstrates:
// 1. Concurrency: this method may be called by multiple threads concurrently
// 2. How to use layouts
// 3. Error handling
//#Override
public void append(LogEvent event) {
try {
final byte[] bytes = getLayout().toByteArray(event);
// output code goes here
} catch (Exception ex) {
if (!ignoreExceptions()) throw new AppenderLoggingException(ex);
}
}
// Your custom appender needs to declare a factory method
// annotated with `#PluginFactory`. Log4j will parse the configuration
// and call this factory method to construct an appender instance with
// the configured attributes.
#PluginFactory
public static AnsiConsoleAppender createAppender(
#PluginAttribute("name") String name,
#PluginElement("Layout") Layout<? extends Serializable> layout,
#PluginElement("Filter") final Filter filter,
#PluginAttribute("otherAttribute") String otherAttribute) {
if (name == null) {
LOGGER.error("No name provided for AnsiConsoleAppenderImpl");
return null;
}
if (layout == null) {
layout = PatternLayout.createDefaultLayout();
}
return new AnsiConsoleAppender(name, filter, layout, true);
}
}
import org.apache.logging.log4j.core.AbstractLifeCycle
import org.apache.logging.log4j.core.Filter
import org.apache.logging.log4j.core.Layout
import org.apache.logging.log4j.core.LogEvent
import org.apache.logging.log4j.core.appender.AbstractAppender
import org.apache.logging.log4j.core.appender.AppenderLoggingException
import org.apache.logging.log4j.core.config.plugins.Plugin
import org.apache.logging.log4j.core.config.plugins.PluginAttribute
import org.apache.logging.log4j.core.config.plugins.PluginElement
import org.apache.logging.log4j.core.config.plugins.PluginFactory
import org.apache.logging.log4j.core.layout.PatternLayout
import java.io.Serializable
#Plugin(name = "AnsiConsoleAppender", category = "Core", elementType = "appender", printObject = true)
class AnsiConsoleAppender /*protected*/ constructor(
name: String, filter: Filter,
layout: Layout<out Serializable>, ignoreExceptions: Boolean
) : AbstractAppender(name, filter, layout, ignoreExceptions) {
// The append method is where the appender does the work.
// Given a log event, you are free to do with it what you want.
// This example demonstrates:
// 1. Concurrency: this method may be called by multiple threads concurrently
// 2. How to use layouts
// 3. Error handling
//#Override
override fun append(event: LogEvent) {
try {
val bytes = layout.toByteArray(event)
//AnsiColor.out(String(bytes), ColorMaps.ASTERIKSY, null, true)
} catch (ex: Exception) {
if (!ignoreExceptions()) throw AppenderLoggingException(ex)
}
}
companion object {
// Your custom appender needs to declare a factory method
// annotated with `#PluginFactory`. Log4j will parse the configuration
// and call this factory method to construct an appender instance with
// the configured attributes.
#JvmStatic
#PluginFactory
fun createAppender(
#PluginAttribute("name") name: String?,
#PluginElement("Layout") layout: Layout<out Serializable>?,
#PluginElement("Filter") filter: Filter,
#PluginAttribute("otherAttribute") otherAttribute: String
): AnsiConsoleAppender? {
val lay = layout ?: PatternLayout.createDefaultLayout()
if (name == null) {
AbstractLifeCycle.LOGGER.error("No name provided for AnsiConsoleAppenderImpl")
return null
}
return AnsiConsoleAppender(name, filter, lay, true)
}
}
}
what am I missing?
The exception says filter is null.
So it should be nullable type.
companion object {
#PluginFactory
#JvmStatic
fun createAppender(
#PluginAttribute("name") name: String,
#PluginElement("Layout") layout: Layout<out Serializable>,
#PluginElement("Filter") filter: Filter?,
#PluginAttribute("otherAttribute") otherAttribute: String?
): AnsiConsoleAppender {
In Kotlin factory methods are usually defined in a companion object of a class, e.g.:
class AnsiConsoleAppender : AbstractAppender() {
companion object {
// add JvmStatic annotation if you want to call this method from Java file
#JvmStatic
#PluginFactory
fun createAppender(/*params*/): AnsiConsoleAppender {
//...
}
}
}
More on companion object

inversify.js resolve by name

I want to create little CQRS framework for my Typescript Node.js project.
I wish to resolve class by it's name from inversify.js container.
I need something like that:
let x = container.get("Foo");
where Foo is regular class and container is Container object from inversify.js.
Is it possible to do what I want?
You should be able to do the following:
import { Container, injectable } from "inversify";
#injectable()
class Foo {
// ...
}
const container = new Container();
container.bind(Foo).toSelf();
const foo = container.get(Foo);
Or (if foo is the composition root):
import { Container, injectable } from "inversify";
#injectable()
class Foo {
// ...
}
const container = new Container();
const foo = container.resolve(Foo);

Specialized Singleton implementation

I am looking for specialized singleton implementation, probably I might be using wrong terminology and hence looking for expert suggestion. Here is my scenario:
There is common code which can be called by ComponentA or ComponentB. I need to push telemetry data from the common code. Telemetry needs to have information that whether this common code get called by ComponentA or ComponentB.
So common code will have just this line of code:
telemetry.pushData(this._area, data);
where this._area tells the telemetry data is getting pushed for which component
I need to push telemetry data from multiple places so it would be good if object got created once and used through out the code lifetime
One option I can think of passing component context to the common code which in mind doesn't look right, hence looking for suggestion what kind of pattern one should use in this case?
This is what I am thinking
// Telemetry.ts file present in shared code
export class Telemetry extends Singleton {
public constructor() {
super();
}
public static instance(): Telemetry {
return super.instance<Telemetry>(Telemetry);
}
public publishEvent(data): void {
if (!this.area) {
throw new Error("Error: Initialize telemetry class with right area");
}
pushtelemetryData(this.area, data);
}
public area: string;
}
// Create Telemetry object from component A
Telemetry.instance().area = "ComponentA";
// Shared code will call telemetry publishEvent
Telemetry.instance().publishEvent(data);
Thanks
It's not a good pattern to use in TypeScript where you would generally inject dependencies.
If you must absolutely do it then you can do it by faking it somewhat:
namespace Telemetry {
var instance : SingletonSomething;
export function push(data: Any) : void {
if (instance == null) {
instance = new SingletonSomething();
}
instance.push(data);
}
class SingletonSomething() { ... }
}
and then you could call
Telemetry.push(data);
You can imitate the singleton pattern in typescript easily:
class Telemetry {
private static instance: Telemetry;
public static getInstance(): Telemetry {
if (Telemetry.instance == null) {
Telemetry.instance = new Telemetry();
}
return Telemetry.instance;
}
...
}
If you have your code in some sort of closure (module, namespace, etc) then you can replace the static member with:
let telemetryInstance: Telemetry;
export class Telemetry {
public static getInstance(): Telemetry {
if (telemetryInstance == null) {
telemetryInstance = new Telemetry();
}
return telemetryInstance;
}
...
}
But then you can also replace the static method with:
let telemetryInstance: Telemetry;
export function getTelemetryInstance(): Telemetry {
if (telemetryInstance == null) {
telemetryInstance = new Telemetry();
}
return telemetryInstance;
}
export class Telemetry {
...
}
At this point, in case you are using some sort of closure, you might ask yourself if you really need the class at all?
If you use this as a module:
// telemetry.ts
export interface TelemetryData {
...
}
export function pushData(data: TelemetryData): void {
...
}
Then you get exactly what you're looking for, and this is more of the "javascript way" of doing it.
Edit
In the telemetry module there's no need to know the users of it.
If the Telemetry.pushData function needs to have information about the object that called it then define an interface for it:
// telemetry.ts
export interface TelemetryData {
...
}
export interface TelemetryComponent {
name: string;
...
}
export function pushData(data: TelemetryData, component: TelemetryComponent): void {
...
}
Then in the other modules, where you use it:
// someModule.ts
import * as Telemetry from "./telemetry";
class MyComponent implement Telemetry.TelemetryComponent {
// can also be a simple string property
public get name() {
return "MyComponent";
}
fn() {
...
Telemetry.pushData({ ... }, this);
}
}
2nd Edit
Because you are using a module system, your module files are enough to make singletons, there's no need for a class to achieve that.
You can do this:
// telemetry.ts
let area: string;
export interface TelemetryData {
...
}
export function setArea(usedArea: string) {
area = usedArea;
}
export function pushData(data: TelemetryData): void {
...
}
Then:
Telemetry.setArea("ComponentA");
...
Telemetry.publishEvent(data);
The telemetry module will be created only once per page, so you can treat the entire module as a singleton.
Export only the functions that are needed.

Why does Luxe/Flow quit unexpectedly after building with my PhysicsHandler class?

My PhysicsHandler class seems to be causing Luxe to quit unexpectedly, and I have no idea why.
Everything runs fine until I declare a class-variable, at which point it crashes a couple of seconds after loading. What's weird is that I have another class (InputHandler) that declares class-variables and runs fine. Not sure whether this is a problem with my code (somehow... ), Luxe, or Flow.
Main class:
import luxe.Input;
import luxe.Parcel;
import luxe.ParcelProgress;
import InputHandler;
import PhysicsHandler;
import Player;
enum GAME_STATE
{
play;
pause;
}
class Main extends luxe.Game {
var INPUT_HANDLER: InputHandler;
override function ready() {
var assetsParcel = new Parcel
({
textures:
[
{ id:"assets/block.png" },
{ id:"assets/background.png" }
]
});
new ParcelProgress
({
parcel : assetsParcel,
oncomplete : onAssetsLoaded
});
assetsParcel.load();
INPUT_HANDLER = new InputHandler();
INPUT_HANDLER.GameState = GAME_STATE.play;
}
private function onAssetsLoaded(_)
{
var player = new Player();
INPUT_HANDLER.setPlayerEntity(player);
}
override function update(dt:Float) {
INPUT_HANDLER.update();
}
}
InputHandler class:
import luxe.Input;
import luxe.Entity;
import Main;
class InputHandler
{
public var GameState: EnumValue;
private var player: Entity;
// functions, etc. below here...
}
PhysicsHandler class (the troublemaker... ):
import Main;
class PhysicsHandler
{
public var GameState: EnumValue;
}
This is all it takes to crash the game somehow. As you can see, I'm not even instantiating the PhysicsHandler class yet, just importing it.
Okay, so I was able to sort this with some help on the Snowkit forums. Apparently, Luxe doesn't play well with the latest version of hxcpp, so downgrading to 3.2.102 worked. Result.