Skip to content
This repository was archived by the owner on Oct 16, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions packages/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@
"lint": "eslint src/**/*"
},
"devDependencies": {
"@agile-ts/core": "file:../core"
"@agile-ts/utils": "file:../utils"
},
"peerDependencies": {
"@agile-ts/core": "^0.0.14"
"dependencies": {
"@agile-ts/utils": "^0.0.1"
},
"publishConfig": {
"access": "public"
Expand Down
2 changes: 1 addition & 1 deletion packages/api/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { clone, copy, defineConfig, isValidObject } from '@agile-ts/core';
import { clone, copy, defineConfig, isValidObject } from '@agile-ts/utils';

export default class API {
public config: ApiConfig;
Expand Down
8 changes: 8 additions & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@
"test:coverage": "jest --coverage",
"lint": "eslint src/**/*"
},
"devDependencies": {
"@agile-ts/logger": "file:../logger",
"@agile-ts/utils": "file:../utils"
},
"dependencies": {
"@agile-ts/utils": "^0.0.1",
"@agile-ts/logger": "^0.0.1"
},
"publishConfig": {
"access": "public"
},
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
// !! All internal Agile modules must be imported from here!!

// Logger
export * from './logger';
export * from '@agile-ts/logger';

// Utils
export * from './utils';
export * from '@agile-ts/utils';

// Agile
export * from './agile';
Expand Down
326 changes: 8 additions & 318 deletions packages/core/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,102 +1,13 @@
import { Agile, Observer, Collection } from './internal';
import {
Agile,
Observer,
Collection,
normalizeArray,
isFunction,
} from './internal';

//=========================================================================================================
// Copy
//=========================================================================================================
/**
* @internal
* Creates a fresh copy of an Array/Object
* https://www.samanthaming.com/tidbits/70-3-ways-to-clone-objects/
* @param value - Array/Object that gets copied
*/
export function copy<T = any>(value: T): T {
// Extra checking '!value' because 'typeof null === object'
if (!value) return value;

// Ignore everything that is no object or array
const valConstructorName = Object.getPrototypeOf(value).constructor.name;
if (!['object', 'array'].includes(valConstructorName.toLowerCase()))
return value;

let temp;
const newObject: any = Array.isArray(value) ? [] : {};
for (const property in value) {
temp = value[property];
newObject[property] = typeof temp === 'object' ? copy(temp) : temp;
}
return newObject as T;
}

//=========================================================================================================
// Is Valid Object
//=========================================================================================================
/**
* @internal
* Checks if passed value is a valid Object
* https://stackoverflow.com/questions/12996871/why-does-typeof-array-with-objects-return-object-and-not-array
* @param value - Value that is tested for its correctness
* @param considerArray - Whether Arrays should be considered as object
*/
export function isValidObject(value: any, considerArray = false): boolean {
function isHTMLElement(obj: any) {
try {
return obj instanceof HTMLElement;
} catch (e) {
return (
typeof obj === 'object' &&
obj.nodeType === 1 &&
typeof obj.style === 'object' &&
typeof obj.ownerDocument === 'object'
);
}
}

return (
value !== null &&
typeof value === 'object' &&
!isHTMLElement(value) &&
(considerArray ? true : !Array.isArray(value))
);
}

//=========================================================================================================
// Includes Array
//=========================================================================================================
/**
* @internal
* Check if array1 contains all elements of array2
* @param array1 - Array 1
* @param array2 - Array 2
*/
export function includesArray<DataType = any>(
array1: Array<DataType>,
array2: Array<DataType>
): boolean {
return array2.every((element) => array1.includes(element));
}

//=========================================================================================================
// Normalize Array
//=========================================================================================================
/**
* @internal
* Transforms Item/s to an Item Array
* @param items - Item/s that gets transformed to an Array
* @param config - Config
*/
export function normalizeArray<DataType = any>(
items?: DataType | Array<DataType>,
config: { createUndefinedArray?: boolean } = {}
): Array<DataType> {
config = defineConfig(config, {
createUndefinedArray: false, // If it should return [] or [undefined] if the passed Item is undefined
});
if (!items && !config.createUndefinedArray) return [];
return Array.isArray(items) ? items : [items as DataType];
}

//=========================================================================================================
// Get Instance
// Get Agile Instance
//=========================================================================================================
/**
* @internal
Expand Down Expand Up @@ -172,227 +83,6 @@ export function extractObservers(instances: any): Array<Observer | undefined> {
return instancesArray;
}

//=========================================================================================================
// Is Function
//=========================================================================================================
/**
* @internal
* Checks if value is a function
* @param value - Value that gets tested if its a function
*/
export function isFunction(value: any): boolean {
return typeof value === 'function';
}

//=========================================================================================================
// Is Async Function
//=========================================================================================================
/**
* @internal
* Checks if value is an async function
* @param value - Value that gets tested if its an async function
*/
export function isAsyncFunction(value: any): boolean {
const valueString = value.toString();
return (
isFunction(value) &&
(value.constructor.name === 'AsyncFunction' ||
valueString.includes('__awaiter'))
);
}

//=========================================================================================================
// Is Json String
//=========================================================================================================
/**
* @internal
* Checks if value is valid JsonString
* @param value - Value that gets checked
*/
export function isJsonString(value: any): boolean {
if (typeof value !== 'string') return false;
try {
JSON.parse(value);
} catch (e) {
return false;
}
return true;
}

//=========================================================================================================
// Define Config
//=========================================================================================================
/**
* @internal
* Merges default values/properties into config object
* @param config - Config object that receives default values
* @param defaults - Default values object that gets merged into config object
* @param overwriteUndefinedProperties - If undefined Properties in config gets overwritten by the default value
*/
export function defineConfig<ConfigInterface = Object>(
config: ConfigInterface,
defaults: Object,
overwriteUndefinedProperties?: boolean
): ConfigInterface {
if (overwriteUndefinedProperties === undefined)
overwriteUndefinedProperties = true;

if (overwriteUndefinedProperties) {
const finalConfig = { ...defaults, ...config };
for (const key in finalConfig)
if (finalConfig[key] === undefined) finalConfig[key] = defaults[key];
return finalConfig;
}

return { ...defaults, ...config };
}

//=========================================================================================================
// Flat Merge
//=========================================================================================================
/**
* @internal
* @param addNewProperties - Adds new properties to source Object
*/
export interface FlatMergeConfigInterface {
addNewProperties?: boolean;
}

/**
* @internal
* Merges items into object, be aware that the merge will only happen at the top level of the object.
* Initially it adds new properties of the changes object into the source object.
* @param source - Source object
* @param changes - Changes that get merged into the source object
* @param config - Config
*/
export function flatMerge<DataType = Object>(
source: DataType,
changes: Object,
config: FlatMergeConfigInterface = {}
): DataType {
config = defineConfig(config, {
addNewProperties: true,
});

// Copy Source to avoid References
const _source = copy<DataType>(source);
if (!_source) return _source;

// Merge Changes Object into Source Object
const keys = Object.keys(changes);
keys.forEach((property) => {
if (!config.addNewProperties && !_source[property]) return;
_source[property] = changes[property];
});

return _source;
}

//=========================================================================================================
// Equals
//=========================================================================================================
/**
* @internal
* Check if two values are equal
* @param value1 - First Value
* @param value2 - Second Value
*/
export function equal(value1: any, value2: any): boolean {
return value1 === value2 || JSON.stringify(value1) === JSON.stringify(value2);
}

//=========================================================================================================
// Not Equals
//=========================================================================================================
/**
* @internal
* Checks if two values aren't equal
* @param value1 - First Value
* @param value2 - Second Value
*/
export function notEqual(value1: any, value2: any): boolean {
return !equal(value1, value2);
}

//=========================================================================================================
// Generate Id
//=========================================================================================================
/**
* @internal
* Generates random Id
* @param length - Length of generated Id
*/
export function generateId(length?: number): string {
const characters =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const charactersLength = characters.length;
let result = '';
if (!length) length = 5;
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}

//=========================================================================================================
// Create Array From Object
//=========================================================================================================
/**
* @internal
* Transforms Object to Array
* @param object - Object that gets transformed
*/
export function createArrayFromObject<P = any>(object: {
[key: string]: P;
}): Array<{ key: string; instance: P }> {
const array: Array<{ key: string; instance: P }> = [];
for (const key in object) {
array.push({
key: key,
instance: object[key],
});
}
return array;
}

//=========================================================================================================
// Clone
//=========================================================================================================
/**
* @internal
* Clones a Class
* @param instance - Instance of Class you want to clone
*/
export function clone<T = any>(instance: T): T {
// Clone Class
const objectCopy: T = Object.create(Object.getPrototypeOf(instance));
const objectClone = Object.assign(objectCopy, instance);

// Copy Properties of Class to remove flat references
for (const key in objectClone) objectClone[key] = copy(objectClone[key]);

return objectClone;
}

//=========================================================================================================
// Remove Properties
//=========================================================================================================
/**
* @internal
* Removes properties from Object
* @param object - Object from which the properties get removed
* @param properties - Properties that get removed from the object
*/
export function removeProperties<T = Object>(
object: T,
properties: Array<string>
): T {
const copiedObject = copy(object);
properties.map((property) => delete copiedObject[property]);
return copiedObject;
}

//=========================================================================================================
// Global Bind
//=========================================================================================================
Expand Down
2 changes: 1 addition & 1 deletion packages/core/tests/unit/collection/collection.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
ComputedTracker,
StatePersistent,
} from '../../../src';
import * as Utils from '../../../src/utils';
import * as Utils from '@agile-ts/utils';
import mockConsole from 'jest-mock-console';

jest.mock('../../../src/collection/collection.persistent');
Expand Down
Loading