Socket
Socket
Sign inDemoInstall

dependency-injection-cat

Package Overview
Dependencies
Maintainers
1
Versions
67
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

dependency-injection-cat - npm Package Compare versions

Comparing version 0.0.2 to 0.0.3

typescript-helpers/node-source-descriptor/ExportDeclarationWithoutClauseAndModuleSpecifier.d.ts

4

decorators/Bean.js

@@ -10,6 +10,4 @@ "use strict";

throw new Error('Trying to use Bean property without configured di-container, or not in class');
return function (target, propertyKey, descriptor) {
throw new Error('Trying to use Bean property without configured di-container, or not in class');
};
return function (target, propertyKey, descriptor) { };
}
exports.Bean = Bean;
{
"name": "dependency-injection-cat",
"version": "0.0.2",
"version": "0.0.3",
"main": "index.js",

@@ -5,0 +5,0 @@ "types": "index.d.ts",

@@ -0,1 +1,2 @@

![npm](https://img.shields.io/npm/v/dependency-injection-cat?style=flat)
# dependency-injection-cat

@@ -5,9 +6,27 @@

the Dependency Inversion pattern with Dependency Injection
<!-- toc -->
- [Installation](#installation)
* [Config options](#config-options)
* [Configuration with Webpack](#configuration-with-webpack)
* [Configuration with ttypescript](#configuration-with-ttypescript)
- [Usage](#usage)
- [Bean](#bean)
* [Rules](#bean-rules)
* [Syntax](#bean-syntax)
* [Configuration object](#bean-configuration-object)
- [Qualifier](#qualifier)
* [What is it?](#what-is-it)
* [Rules](#qualifier-rules)
* [Syntax](#qualifier-syntax)
- [Container](#container)
* [Rules](#container-rules)
* [Syntax](#container-syntax)
<!-- tocstop -->
## Installation
Install with yarn
**Yarn**
```bash
yarn add dependency-injection-cat
```
Install with npm
**NPM**
```bash

@@ -17,5 +36,86 @@ npm install dependency-injection-cat

#### Config options
```ts
interface TransformerConfig {
diConfigPattern?: string; // Glob pattern, default value = '**/*.diconfig.ts'
ignorePatterns?: string[]; // Array of Glob patterns, default value = ['**/node_modules/**']
}
```
#### Configuration with Webpack
Dependency Injection Cat supports transpileOnly mode for faster builds! [More Info](https://github.com/TypeStrong/ts-loader#transpileonly)
<br/>
With Webpack, You can use any TypeScript-related loader that supports custom transformers, e.g. awesome-typescript-loader or ts-loader
<h5>webpack.config.js</h5>
```js
const dependencyInjectionCatTransformer = require('dependency-injection-cat/transformer').default;
module.exports = {
// ...
module: {
rules: [
{
test: /\.ts$/,
loader: 'ts-loader', // or 'awesome-typescript-loader'
options: {
transpileOnly: true, // If set transpileOnly: true, you're loosing TypeChecking
getCustomTransformers: program => ({
before: [
dependencyInjectionCatTransformer(program)
]
})
}
}
]
}
};
```
<h5>With custom options</h5>
In webpack.config.js you can pass a second parameter to the transformer:
```js
before: [
dependencyInjectionCatTransformer(program, {
diConfigPattern: '**/config/**/*.diconfig.ts'
})
]
```
#### Configuration with ttypescript
Check out ttypescript's [README](https://github.com/cevek/ttypescript/blob/master/README.md) for more information
<h5>tsconfig.json</h5>
```json
{
"compilerOptions": {
"plugins": [
{
"transform": "dependency-injection-cat/transformer"
}
]
}
}
```
<h5>With custom options</h5>
```json
{
"compilerOptions": {
"plugins": [
{
"transform": "dependency-injection-cat/transformer",
"diConfigPattern": "**/config/**/*.diconfig.ts"
}
]
}
}
```
## Usage
Simple case
```typescript
```ts
// requesters.diconfig.ts

@@ -57,4 +157,175 @@ import { Bean, Qualifier } from 'dependency-injection-cat';

requester.makeRequest....
requester.makeRequest();
```
## Bean
<h4 id="bean-rules">Rules</h4>
<!-- toc -->
- Bean should be a class member (property or method)
- Beans should not have cyclic dependencies (it will throw compilation error)
- Beans should not have duplicate declarations
- Beans can be used only in **diconfig.ts** files (or files with another pattern declared in transformer config)
- Bean type should not be empty (it will throw compilation error)
- Bean type should not be primitive (it will throw compilation error)
- All bean dependencies should be typed, and type should not be primitive
<!-- tocstop -->
<h4 id="bean-syntax">Syntax</h4>
Beans support 2 kinds of syntax
<h5>First</h5>
```ts
import { Bean } from 'dependency-injection-cat';
class SuperClass {
//Without any dependencies
@Bean
someMethod(): Interface {
return new ImplementationOfInterface();
}
//With Bean dependencies
@Bean
someMethod(
dependency1: InterfaceOfDependency1,
dependency2: InterfaceOfDependency2,
): Interface {
return new ImplementationOfInterface(dependency1, dependency2);
}
//With Bean configuration
@Bean({ qualifier: 'someCoolImpl', scope: 'prototype' })
someMethod(
dependency1: InterfaceOfDependency1,
dependency2: InterfaceOfDependency2,
): Interface {
return new ImplementationOfInterface(dependency1, dependency2);
}
//Beans do not support arrow-functions methods, this will throw an error
@Bean
someMethod = (): Interface => new ImplementationOfInterface();
//Beans should have complex types, this will throw an error
@Bean
someMethod(): number | string | any {
return new ImplementationOfInterface();
}
//Should not have cyclyc dependencies, this will throw an error
@Bean
someMethod(
dependency1: InterfaceOfDependency1, //Cyclic dependency
): Interface {
return new ImplementationOfInterface(dependency1);
}
@Bean
someMethod2(
dependency1: Interface, //Cyclic dependency
): InterfaceOfDependency1 {
return new ImplementationOfDependency(dependency1);
}
}
```
<h5>Second</h5>
```ts
import { Bean } from 'dependency-injection-cat';
class SuperClass {
//If you don't need to pass specific dependencies in Bean, it will resolve all dependencies automatically
someBeanProperty = Bean<Interface>(ImplementationOfInterface);
//With Bean configuration
//First argument in Bean should always be implementation of interface, second is configuration object
//You should pass Bean type in generic
someBeanProperty = Bean<Interface>(ImplementationOfInterface, { qualifier: 'someCoolImpl' });
}
```
<h4 id="bean-configuration-object">Bean configuration object</h4>
```ts
interface BeanConfiguration {
//By default all beans are singleton, if you will set scope 'prototype' Bean will no longer be a singleton
scope?: 'prototype' | 'singleton';
//Read about Qualifiers and their rules below
qualifier?: string;
}
```
## Qualifier
<h4 id="what-is-it">What is it?</h4>
In fact, Qualifier it's just a name of Bean
You can use it, if you have a few different implementations of interface
<h4 id="qualifier-rules">Rules</h4>
<!-- toc -->
- Qualifier be a string, and should not be empty string
- Qualifier should not be dynamically calculated (no template strings, or references to constants/properties)
<!-- tocstop -->
<h4 id="qualifier-syntax">Syntax</h4>
```ts
import { Bean, Qualifier } from 'dependency-injection-cat';
class SuperClass {
//Correct example
@Bean
someMethod(
@Qualifier('someQualifier') dependency1: InterfaceOfDependency1,
): Interface {
return new ImplementationOfInterface();
}
}
```
```ts
//Wrong examples
import { Bean, Qualifier } from 'dependency-injection-cat';
const superQualifierName = 'superQualifierNameValue';
class SuperClass {
@Bean
someMethod(
@Qualifier(superQualifierName) dependency1: InterfaceOfDependency1,
): Interface {
return new ImplementationOfInterface();
}
@Bean
someMethod(
@Qualifier('') dependency1: InterfaceOfDependency1,
): Interface {
return new ImplementationOfInterface();
}
}
```
### Container
Container has only one method "get"
<h4 id="container-rules">Rules</h4>
<!-- toc -->
- You should pass type as generic in get method of Container
<!-- tocstop -->
<h4 id="container-syntax">Syntax</h4>
```ts
//Any TypeScript file in project
import { container } from 'dependency-injection-cat';
//Without Qualifier
const someBean = container.get<Interface>();
//With Qualifier
const someBean = container.get<Interface>('someQualifier');
```
## Author

@@ -61,0 +332,0 @@ * [**Artem Kornev**](https://github.com/artem1458)

@@ -10,5 +10,9 @@ "use strict";

var createFactories_1 = require("../factories/createFactories");
var SourceFilesCache_1 = require("../typescript-helpers/node-source-descriptor/SourceFilesCache");
var PathResolverCache_1 = require("../typescript-helpers/path-resolver/PathResolverCache");
exports.runCompile = function () {
TypeRegisterRepository_1.TypeRegisterRepository.clearRepository();
TypeDependencyRepository_1.TypeDependencyRepository.clearRepository();
SourceFilesCache_1.SourceFilesCache.clearCache();
PathResolverCache_1.PathResolverCache.clearCache();
ProgramRepository_1.ProgramRepository.initProgram();

@@ -15,0 +19,0 @@ registerTypes_1.registerTypes();

@@ -15,6 +15,4 @@ "use strict";

var classPropertyBeanTypeIdQualifier_1 = require("../typescript-helpers/type-id-qualifier/class-property-bean/classPropertyBeanTypeIdQualifier");
var node_source_descriptor_1 = require("../typescript-helpers/node-source-descriptor");
var typescript_1 = require("typescript");
var CompilerOptionsProvider_1 = require("../compiler-options-provider/CompilerOptionsProvider");
var PathResolver_1 = require("../typescript-helpers/path-resolver/PathResolver");
var getNodeSourceDescriptorDeep_1 = require("../typescript-helpers/node-source-descriptor/getNodeSourceDescriptorDeep");
var SourceFilesCache_1 = require("../typescript-helpers/node-source-descriptor/SourceFilesCache");
function registerDependencies() {

@@ -66,14 +64,7 @@ var program = ProgramRepository_1.ProgramRepository.program;

var sourceFile = node.getSourceFile();
var importSourceDescriptor = node_source_descriptor_1.getNodeSourceDescriptorFromImports(sourceFile, nameToFind);
if (importSourceDescriptor === undefined) {
var nodeSourceDescriptor = getNodeSourceDescriptorDeep_1.getNodeSourceDescriptorDeep(sourceFile, nameToFind);
if (nodeSourceDescriptor === null) {
throw new Error('Can not find import for bean implementation' + getClassMemberLocationMessage_1.getClassMemberLocationMessage(node));
}
var pathWithExt = PathResolver_1.PathResolver.resolveWithExtension(sourceFile.fileName, importSourceDescriptor.path);
var program = typescript_1.createProgram([pathWithExt], CompilerOptionsProvider_1.CompilerOptionsProvider.options);
//SETTING PARENT PROPERTY OF ALL NODES
program.getTypeChecker();
var file = program.getSourceFile(pathWithExt);
if (file === undefined) {
throw new Error("File not found in program, " + importSourceDescriptor.path);
}
var file = SourceFilesCache_1.SourceFilesCache.getSourceFileByPath(nodeSourceDescriptor.path);
var dependencies = [];

@@ -84,3 +75,3 @@ var classDeclaration = file.statements.find(function (it) {

}
if (it.name && it.name.escapedText.toString() === importSourceDescriptor.name) {
if (it.name && it.name.escapedText.toString() === nodeSourceDescriptor.name) {
return true;

@@ -91,3 +82,3 @@ }

if (classDeclaration === undefined) {
throw new Error("Can not find class declaration for " + importSourceDescriptor.name + " in file " + importSourceDescriptor.path);
throw new Error("Can not find class declaration for " + nodeSourceDescriptor.name + " in file " + nodeSourceDescriptor.path);
}

@@ -100,3 +91,3 @@ var constructor = classDeclaration.members.find(ts.isConstructorDeclaration);

if (parameter.type === undefined) {
throw new Error("All parameters in Class declaration that you are use in Bean property should have a type, " + importSourceDescriptor.name + " file " + importSourceDescriptor.path);
throw new Error("All parameters in Class declaration that you are use in Bean property should have a type, " + nodeSourceDescriptor.name + " file " + nodeSourceDescriptor.path);
}

@@ -103,0 +94,0 @@ try {

@@ -1,4 +0,4 @@

import ts from 'typescript';
import * as ts from 'typescript';
export interface CallExpressionWithTypeArguments extends ts.CallExpression {
typeArguments: ts.NodeArray<ts.TypeNode>;
}

@@ -1,3 +0,3 @@

import ts from 'typescript';
import * as ts from 'typescript';
import { CallExpressionWithTypeArguments } from './CallExpressionWithTypeArguments';
export declare function isCallExpressionWithTypeArguments(node: ts.Node): node is CallExpressionWithTypeArguments;

@@ -5,6 +5,6 @@ "use strict";

var tslib_1 = require("tslib");
var typescript_1 = tslib_1.__importDefault(require("typescript"));
var ts = tslib_1.__importStar(require("typescript"));
function isCallExpressionWithTypeArguments(node) {
return typescript_1.default.isCallExpression(node) && node.typeArguments !== undefined;
return ts.isCallExpression(node) && node.typeArguments !== undefined;
}
exports.isCallExpressionWithTypeArguments = isCallExpressionWithTypeArguments;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isContainerGetCall = void 0;
var isCallExpressionFromFile_1 = require("../call-expression/isCallExpressionFromFile");
var node_source_descriptor_1 = require("../node-source-descriptor");
var libraryName_1 = require("../../constants/libraryName");
var isCallExpressionFromFile_1 = require("../call-expression/isCallExpressionFromFile");
function isContainerGetCall(typeChecker, node) {

@@ -11,20 +10,8 @@ if (isCallExpressionFromFile_1.isCallExpressionFromFile(typeChecker, node, node.getSourceFile().fileName)) {

}
var fullExpression = node.expression.getText().split('.');
var fromImports = node_source_descriptor_1.getNodeSourceDescriptorFromImports(node.getSourceFile(), fullExpression[0]);
if (fromImports === undefined || libraryName_1.libraryName !== fromImports.path) {
var nodeSourceDescriptor = node_source_descriptor_1.getNodeSourceDescriptorDeep(node.getSourceFile(), node.expression.getText());
if (nodeSourceDescriptor === null) {
return false;
}
switch (fromImports.importType) {
case node_source_descriptor_1.ImportType.Named:
fullExpression[0] = fromImports.name;
break;
case node_source_descriptor_1.ImportType.Namespace:
fullExpression.splice(0, 1);
break;
case node_source_descriptor_1.ImportType.Default:
default:
return false;
}
return fullExpression.join('.') === 'container.get';
return nodeSourceDescriptor.name === 'container.get';
}
exports.isContainerGetCall = isContainerGetCall;

@@ -6,4 +6,4 @@ "use strict";

var ts = tslib_1.__importStar(require("typescript"));
var libraryName_1 = require("../../constants/libraryName");
var node_source_descriptor_1 = require("../node-source-descriptor");
var libraryName_1 = require("../../constants/libraryName");
function isBeanDecorator(decorator) {

@@ -21,8 +21,8 @@ var nameToFind = undefined;

}
var sourceDescriptor = node_source_descriptor_1.getNodeSourceDescriptorFromImports(decorator.getSourceFile(), nameToFind);
if (sourceDescriptor === undefined) {
var nodeSourceDescriptor = node_source_descriptor_1.getNodeSourceDescriptorDeep(decorator.getSourceFile(), nameToFind);
if (nodeSourceDescriptor === null) {
return false;
}
return sourceDescriptor.name === 'Bean' && sourceDescriptor.path === libraryName_1.libraryName;
return nodeSourceDescriptor.name === 'Bean' && nodeSourceDescriptor.path === libraryName_1.libraryName;
}
exports.isBeanDecorator = isBeanDecorator;

@@ -6,4 +6,4 @@ "use strict";

var ts = tslib_1.__importStar(require("typescript"));
var libraryName_1 = require("../../constants/libraryName");
var node_source_descriptor_1 = require("../node-source-descriptor");
var libraryName_1 = require("../../constants/libraryName");
function isClassPropertyBean(node) {

@@ -22,4 +22,4 @@ return ts.isPropertyDeclaration(node) && hasBeanCallExpression(node);

var nameToFind = initializer.expression.getText();
var nodeSourceDescriptor = node_source_descriptor_1.getNodeSourceDescriptorFromImports(node.getSourceFile(), nameToFind);
if (nodeSourceDescriptor === undefined) {
var nodeSourceDescriptor = node_source_descriptor_1.getNodeSourceDescriptorDeep(node.getSourceFile(), nameToFind);
if (nodeSourceDescriptor === null) {
return false;

@@ -26,0 +26,0 @@ }

@@ -6,4 +6,4 @@ "use strict";

var ts = tslib_1.__importStar(require("typescript"));
var libraryName_1 = require("../../constants/libraryName");
var node_source_descriptor_1 = require("../node-source-descriptor");
var libraryName_1 = require("../../constants/libraryName");
function isParameterQualifierDecorator(decoratorNode) {

@@ -15,8 +15,8 @@ var decoratorExpression = decoratorNode.expression;

var nameToFind = decoratorExpression.expression.getText();
var sourceDescriptor = node_source_descriptor_1.getNodeSourceDescriptorFromImports(decoratorNode.getSourceFile(), nameToFind);
if (sourceDescriptor === undefined) {
var nodeSourceDescriptor = node_source_descriptor_1.getNodeSourceDescriptorDeep(decoratorNode.getSourceFile(), nameToFind);
if (nodeSourceDescriptor === null) {
return false;
}
return sourceDescriptor.name === 'Qualifier' && sourceDescriptor.path === libraryName_1.libraryName;
return nodeSourceDescriptor.name === 'Qualifier' && nodeSourceDescriptor.path === libraryName_1.libraryName;
}
exports.isParameterQualifierDecorator = isParameterQualifierDecorator;

@@ -1,3 +0,2 @@

export * from './getNodeSourceDescriptorFromImports';
export * from './getTypeSourceDescriptorFromTopStatements';
export * from './types';
export * from './getNodeSourceDescriptorDeep';
export * from './INodeSourceDescriptor';
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
tslib_1.__exportStar(require("./getNodeSourceDescriptorFromImports"), exports);
tslib_1.__exportStar(require("./getTypeSourceDescriptorFromTopStatements"), exports);
tslib_1.__exportStar(require("./types"), exports);
tslib_1.__exportStar(require("./getNodeSourceDescriptorDeep"), exports);
tslib_1.__exportStar(require("./INodeSourceDescriptor"), exports);

@@ -5,3 +5,2 @@ export declare class PathResolver {

static resolveWithoutExtension(sourceFilePath: string, targetPath: string): string;
static resolveWithExtension(sourceFilePath: string, filePath: string): string;
}

@@ -6,14 +6,5 @@ "use strict";

var path_1 = tslib_1.__importDefault(require("path"));
var fs_1 = tslib_1.__importDefault(require("fs"));
var tsconfig_paths_1 = require("tsconfig-paths");
var isPathRelative_1 = require("../../utils/isPathRelative");
var CompilerOptionsProvider_1 = require("../../compiler-options-provider/CompilerOptionsProvider");
var extensionsToResolve = [
'.ts',
'.tsx',
'.d.ts',
'/index.ts',
'/index.tsx',
'/index.d.ts',
];
var PathResolver = /** @class */ (function () {

@@ -40,14 +31,4 @@ function PathResolver() {

};
//Use only for ts.createProgram!
PathResolver.resolveWithExtension = function (sourceFilePath, filePath) {
var withoutExt = this.resolveWithoutExtension(sourceFilePath, filePath);
var filesPaths = extensionsToResolve.map(function (it) { return withoutExt + it; });
var resolved = filesPaths.find(function (it) { return fs_1.default.existsSync(it); });
if (resolved === undefined) {
throw new Error("Can not resolve file " + filePath);
}
return resolved;
};
return PathResolver;
}());
exports.PathResolver = PathResolver;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.typeIdQualifierBase = void 0;
var tslib_1 = require("tslib");
var ts = tslib_1.__importStar(require("typescript"));
var TypeQualifierError_1 = require("./TypeQualifierError");
var parseTokens_1 = require("./parseTokens");
var node_source_descriptor_1 = require("../../node-source-descriptor");
var parseTokens_1 = require("./parseTokens");
var node_source_descriptor_2 = require("../../node-source-descriptor");
function typeIdQualifierBase(node) {
if (node.typeName === undefined) {
throw new Error(TypeQualifierError_1.TypeQualifierError.NoTypeNameFound);
}
var referTypeName = getReferTypeName(node.typeName);
var leftSideName = referTypeName.split('.')[0];
var typeName = getBaseTypeNameAndPath(node, leftSideName, referTypeName);
if (typeName === undefined) {
var sourceFile = node.getSourceFile();
var nameToFind = node.getText();
var nodeSourceDescriptor = node_source_descriptor_1.getNodeSourceDescriptorDeep(sourceFile, nameToFind);
if (nodeSourceDescriptor === null) {
throw new Error(TypeQualifierError_1.TypeQualifierError.CanNotGenerateType);
}
return typeName;
return "" + parseTokens_1.START_PATH_TOKEN + nodeSourceDescriptor.path + parseTokens_1.END_PATH_TOKEN + nodeSourceDescriptor.name;
}
exports.typeIdQualifierBase = typeIdQualifierBase;
function getBaseTypeNameAndPath(node, nameToFind, referTypeName) {
var sourceFile = node.getSourceFile();
var typeFromImport = node_source_descriptor_1.getNodeSourceDescriptorFromImports(sourceFile, nameToFind);
var typeFromStatement = node_source_descriptor_2.getTypeSourceDescriptorFromTopStatements(sourceFile, nameToFind);
if (typeFromImport && typeFromStatement && typeFromImport.name === typeFromStatement.name) {
throw new Error("Duplicate identifiers detected, when trying to resolve TypeReference " + nameToFind + ", Path " + sourceFile.fileName);
}
if (typeFromImport) {
var actualName = void 0;
switch (typeFromImport.importType) {
case node_source_descriptor_1.ImportType.Default:
actualName = 'default';
break;
case node_source_descriptor_1.ImportType.Namespace:
actualName = referTypeName.split('.').slice(1).join('.');
break;
case node_source_descriptor_1.ImportType.Named:
actualName = tslib_1.__spreadArrays([typeFromImport.name], referTypeName.split('.').slice(1)).join('.');
break;
default:
actualName = referTypeName;
}
return "" + parseTokens_1.START_PATH_TOKEN + typeFromImport.path + parseTokens_1.END_PATH_TOKEN + actualName;
}
if (typeFromStatement) {
//TODO Check for default export of classes
return "" + parseTokens_1.START_PATH_TOKEN + typeFromStatement.path + parseTokens_1.END_PATH_TOKEN + referTypeName;
}
return undefined;
}
function getReferTypeName(node, prevText) {
if (prevText === void 0) { prevText = ''; }
if (node.kind === ts.SyntaxKind.Identifier) {
var text = node.escapedText;
if (text === undefined) {
throw new Error('node.escapedText have no text');
}
return (text + "." + prevText).replace(/\.$/, '');
}
if (node.kind === ts.SyntaxKind.QualifiedName) {
var text = node.right.escapedText;
if (text === undefined) {
throw new Error('node.right.escapedText have no text');
}
return getReferTypeName(node.left, text.toString() + "." + prevText);
}
return prevText.replace(/\.$/, '');
}

@@ -1,1 +0,1 @@

export declare function isPathRelative(path: string): boolean;
export declare function isPathRelative(filePath: string): boolean;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isPathRelative = void 0;
function isPathRelative(path) {
return /^\.?\.\//.test(path);
var tslib_1 = require("tslib");
var path_1 = tslib_1.__importDefault(require("path"));
var lodash_1 = require("lodash");
var relativeRegexp = new RegExp("^..?" + lodash_1.escapeRegExp(path_1.default.sep));
function isPathRelative(filePath) {
return relativeRegexp.test(filePath);
}
exports.isPathRelative = isPathRelative;
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc