diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..e8c83bd --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,15 @@ +# These are supported funding model platforms + +github: [angular-package] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: angularpackage # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: [ + 'https://checkout.revolut.com/pay/048b10a3-0e10-42c8-a917-e3e9cb4c8e29', + 'https://donate.stripe.com/dR614hfDZcJE3wAcMM' +] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3a183d6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,45 @@ +# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files. + +# Compiled output +/dist +/tmp +/out-tsc +/bazel-out + +# Node +/node_modules +npm-debug.log +yarn-error.log + +# IDEs and editors +.idea/ +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# Visual Studio Code +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history/* + +# Miscellaneous +/.angular/cache +.sass-cache/ +/connect.lock +/coverage +/libpeerconnection.log +testem.log +/typings + +# System files +.DS_Store +Thumbs.db + +*.ignore* +temp \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..cb3ede5 --- /dev/null +++ b/README.md @@ -0,0 +1,200 @@ + + + + +## @typescript-package/collection + + +[![npm version][typescript-package-npm-badge-svg]][typescript-package-npm-badge] +[![GitHub issues][typescript-package-badge-issues]][typescript-package-issues] +[![GitHub license][typescript-package-badge-license]][typescript-package-license] + +A **lightweight** TypeScript package for data collection. + +## Table of contents + +- [Installation](#installation) +- [Api](#api) + - **Abstract** + - [`CollectionBase`](#collectionbase) + - [`CollectionCore`](#collectioncore) + - **Adapters** + - [`SetAdapter`](#setadapter) + - **Concrete** + - [`Collection`](#collection) +- [Contributing](#contributing) +- [Code of Conduct](code-of-conduct) +- [Git](#git) + - [Commit](#commit) + - [Versioning](#versioning) +- [License](#license) + +## Installation + +```bash +npm install @typescript-package/collection --save-peer +``` + +## Api + +```typescript +import { + // Abstract. + CollectionCore, + CollectionBase, + // Concrete. + Collection, + // Adapters. + SetAdapter +} from '@typescript-package/collection'; +``` + +### Abstract + +### `CollectionBase` + +The base functionality abstraction for collections. + +```typescript +import { CollectionBase } from '@typescript-package/collection'; +``` + +[`CollectionBase`](https://github.com/typescript-package/hooks/blob/main/src/core/lib/collection.base.ts) + +### `CollectionCore` + +The core abstract class for `Type` collections of elements `Element` type. + +```typescript +import { CollectionCore } from '@typescript-package/collection'; +``` + +[`CollectionCore`](https://github.com/typescript-package/hooks/blob/main/src/core/lib/collection.core.ts) + +### Adapters + +### `SetAdapter` + +The Set collection adapter. + +```typescript +import { SetAdapter } from '@typescript-package/collection'; +``` + +[`SetAdapter`](https://github.com/typescript-package/hooks/blob/main/src/adapter/lib/set.adapter.ts) + +### Concrete + +### `Collection` + +The collection concrete class with adapter support. + +```typescript +import { Collection, SetAdapter } from '@typescript-package/collection'; + +const collection = new Collection(SetAdapter, 1, 2, 3); + +// Adds. +collection.add(27, 29, 31, 33); +// Deletes. +collection.delete(29, 31); + +console.log(`size: `, collection.size); // Output: 5 +``` + +[`Collection`](https://github.com/typescript-package/hooks/blob/main/src/lib/collection.ts) + +## Contributing + +Your contributions are valued! If you'd like to contribute, please feel free to submit a pull request. Help is always appreciated. + +## Support + +If you find this package useful and would like to support its and general development, you can contribute through one of the following payment methods. Your support helps maintain the packages and continue adding new. + +Support via: + +- [Stripe](https://donate.stripe.com/dR614hfDZcJE3wAcMM) +- [Revolut](https://checkout.revolut.com/pay/048b10a3-0e10-42c8-a917-e3e9cb4c8e29) +- [GitHub](https://github.com/sponsors/angular-package/sponsorships?sponsor=sciborrudnicki&tier_id=83618) +- [DonorBox](https://donorbox.org/become-a-sponsor-to-the-angular-package?default_interval=o) +- [Patreon](https://www.patreon.com/checkout/angularpackage?rid=0&fan_landing=true&view_as=public) + +or via Trust Wallet + +- [XLM](https://link.trustwallet.com/send?coin=148&address=GAFFFB7H3LG42O6JA63FJDRK4PP4JCNEOPHLGLLFH625X2KFYQ4UYVM4) +- [USDT (BEP20)](https://link.trustwallet.com/send?coin=20000714&address=0xA0c22A2bc7E37C1d5992dFDFFeD5E6f9298E1b94&token_id=0x55d398326f99059fF775485246999027B3197955) +- [ETH](https://link.trustwallet.com/send?coin=60&address=0xA0c22A2bc7E37C1d5992dFDFFeD5E6f9298E1b94) +- [BTC](https://link.trustwallet.com/send?coin=0&address=bc1qnf709336tfl57ta5mfkf4t9fndhx7agxvv9svn) +- [BNB](https://link.trustwallet.com/send?coin=20000714&address=0xA0c22A2bc7E37C1d5992dFDFFeD5E6f9298E1b94) + +Thanks for your support! + +## Code of Conduct + +By participating in this project, you agree to follow **[Code of Conduct](https://www.contributor-covenant.org/version/2/1/code_of_conduct/)**. + +## GIT + +### Commit + +Please follow the following commit message conventions: + +- [AngularJS Git Commit Message Conventions][git-commit-angular] +- [Karma Git Commit Msg][git-commit-karma] +- [Conventional Commits][git-commit-conventional] + +### Versioning + +The package follows [Semantic Versioning 2.0.0][git-semver] for all releases. The versioning format is: + +**Given a version number MAJOR.MINOR.PATCH, increment the:** + +- MAJOR version when you make incompatible API changes, +- MINOR version when you add functionality in a backwards-compatible manner, and +- PATCH version when you make backwards-compatible bug fixes. + +Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format. + +**FAQ** +How should I deal with revisions in the 0.y.z initial development phase? + +> The simplest thing to do is start your initial development release at 0.1.0 and then increment the minor version for each subsequent release. + +How do I know when to release 1.0.0? + +> If your software is being used in production, it should probably already be 1.0.0. If you have a stable API on which users have come to depend, you should be 1.0.0. If you’re worrying a lot about backwards compatibility, you should probably already be 1.0.0. + +## License + +MIT © typescript-package ([license][typescript-package-license]) + + + + [typescript-package-badge-issues]: https://img.shields.io/github/issues/typescript-package/collection + [isscript-package-badge-forks]: https://img.shields.io/github/forks/typescript-package/collection + [typescript-package-badge-stars]: https://img.shields.io/github/stars/typescript-package/collection + [typescript-package-badge-license]: https://img.shields.io/github/license/typescript-package/collection + + [typescript-package-issues]: https://github.com/typescript-package/collection/issues + [typescript-package-forks]: https://github.com/typescript-package/collection/network + [typescript-package-license]: https://github.com/typescript-package/collection/blob/master/LICENSE + [typescript-package-stars]: https://github.com/typescript-package/collection/stargazers + + + + + [typescript-package-npm-badge-svg]: https://badge.fury.io/js/@typescript-package%2Fcollection.svg + [typescript-package-npm-badge]: https://badge.fury.io/js/@typescript-package%2Fcollection + + +[git-semver]: http://semver.org/ + + +[git-commit-angular]: https://gist.github.com/stephenparish/9941e89d80e2bc58a153 +[git-commit-karma]: http://karma-runner.github.io/0.10/dev/git-commit-msg.html +[git-commit-conventional]: https://www.conventionalcommits.org/en/v1.0.0/ diff --git a/ng-package.json b/ng-package.json new file mode 100644 index 0000000..4564092 --- /dev/null +++ b/ng-package.json @@ -0,0 +1,8 @@ +{ + "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../dist/collection", + "lib": { + "entryFile": "src/public-api.ts" + }, + "keepLifecycleScripts": true +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..35fc94d --- /dev/null +++ b/package-lock.json @@ -0,0 +1,79 @@ +{ + "name": "@typescript-package/collection", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@typescript-package/collection", + "version": "0.0.1", + "funding": [ + { + "type": "individual", + "url": "https://checkout.revolut.com/pay/048b10a3-0e10-42c8-a917-e3e9cb4c8e29" + } + ], + "license": "MIT", + "peerDependencies": { + "@typedly/collection": "^1.1.0" + } + }, + "node_modules/@typedly/collection": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@typedly/collection/-/collection-1.1.0.tgz", + "integrity": "sha512-Xt3F2LUhWi0wxIUBFEr05ZiafJRhO2Rw6Ee7kVoJCZEh7CeWFldNejd3HzogVezK9rvo8yhBBnphwlEYlE9eSg==", + "funding": [ + { + "type": "stripe", + "url": "https://donate.stripe.com/dR614hfDZcJE3wAcMM" + }, + { + "type": "revolut", + "url": "https://checkout.revolut.com/pay/048b10a3-0e10-42c8-a917-e3e9cb4c8e29" + } + ], + "license": "MIT", + "peer": true, + "peerDependencies": { + "@typedly/data": "^2.0.0" + } + }, + "node_modules/@typedly/constructor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@typedly/constructor/-/constructor-1.0.0.tgz", + "integrity": "sha512-MFww4huWIfRpPjr2Gz001XKdYzfTe3tW94+EzXKwhiqnwrPApOazpjM5NG8GYP9etKUlmAqVIFEc0xclSwfRDg==", + "funding": [ + { + "type": "stripe", + "url": "https://donate.stripe.com/dR614hfDZcJE3wAcMM" + }, + { + "type": "revolut", + "url": "https://checkout.revolut.com/pay/048b10a3-0e10-42c8-a917-e3e9cb4c8e29" + } + ], + "license": "MIT", + "peer": true + }, + "node_modules/@typedly/data": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@typedly/data/-/data-2.0.0.tgz", + "integrity": "sha512-l2IPH7E1atzqiSuUaMcUSFR87QKnpxk9QewkhsMuowVApPCdFH6snEb/3r/PAyFk2kS0Ho9Ej6j56zcG7kcUaA==", + "funding": [ + { + "type": "stripe", + "url": "https://donate.stripe.com/dR614hfDZcJE3wAcMM" + }, + { + "type": "revolut", + "url": "https://checkout.revolut.com/pay/048b10a3-0e10-42c8-a917-e3e9cb4c8e29" + } + ], + "license": "MIT", + "peer": true, + "peerDependencies": { + "@typedly/constructor": "^1.0.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..d6f81f8 --- /dev/null +++ b/package.json @@ -0,0 +1,43 @@ +{ + "name": "@typescript-package/collection", + "version": "0.0.1", + "author": "wwwdev.io ", + "description": "A lightweight TypeScript library for data collection.", + "license": "MIT", + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org" + }, + "peerDependencies": { + "@typedly/collection": "^1.1.0" + }, + "scripts": { + "prepublishOnly": "npm run pkg && npm run clean", + "pkg": "npm pkg delete dependencies", + "clean": "npm pkg delete scripts" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/typescript-package/collection.git" + }, + "bugs": { + "url": "https://github.com/typescript-package/collection/issues" + }, + "keywords": [ + "@typescript-package", + "@typescript-package/collection", + "CollectionBase", + "CollectionCore" + ], + "funding": [ + { + "type": "stripe", + "url": "https://donate.stripe.com/dR614hfDZcJE3wAcMM" + }, + { + "type": "individual", + "url": "https://checkout.revolut.com/pay/048b10a3-0e10-42c8-a917-e3e9cb4c8e29" + } + ], + "sideEffects": false +} diff --git a/src/adapter/index.ts b/src/adapter/index.ts new file mode 100644 index 0000000..8b46f1a --- /dev/null +++ b/src/adapter/index.ts @@ -0,0 +1 @@ +export { SetAdapter } from './lib'; \ No newline at end of file diff --git a/src/adapter/lib/index.ts b/src/adapter/lib/index.ts new file mode 100644 index 0000000..44cbed7 --- /dev/null +++ b/src/adapter/lib/index.ts @@ -0,0 +1 @@ +export { SetAdapter } from './set.adapter'; \ No newline at end of file diff --git a/src/adapter/lib/set.adapter.ts b/src/adapter/lib/set.adapter.ts new file mode 100644 index 0000000..17f2e47 --- /dev/null +++ b/src/adapter/lib/set.adapter.ts @@ -0,0 +1,75 @@ +// Interface. +import { CollectionAdapter } from '@typedly/collection'; +/** + * @description The Set collection adapter. + * @export + * @class SetAdapter + * @template Element The type of the elements in the Set. + * @template [Type=Set] The type of the underlying Set collection. + * @implements {CollectionAdapter} + */ +export class SetAdapter< + Element, + Type = Set +> implements CollectionAdapter { + public version: string = '1.0.0'; + + get [Symbol.toStringTag](): string { + return 'SetAdapter'; + } + + get [Symbol.iterator](): Iterator { + return (this.#collection as unknown as Set)[Symbol.iterator](); + } + + + protected get collection(): Type { + return this.#collection; + } + + public get size(): number { + return (this.collection as unknown as Set).size; + } + + public get value(): Type { + return this.collection as unknown as Type; + } + + #collection: Type; + + constructor(...collection: Element[]) { + this.#collection = new Set(collection) as Type; + } + + public add(element: Element): this { + return (this.collection as unknown as Set).add(element), this; + } + + public clear(): this { + return (this.collection as unknown as Set).clear(), this; + } + + public destroy(): this { + return this.clear(), (this.#collection = null as any), this; + } + + public delete(element: Element): boolean { + return (this.collection as unknown as Set).delete(element); + } + + public forEach(callbackfn: (element: Element, element2: Element, collection: CollectionAdapter) => void, thisArg?: any): this { + return (this.collection as unknown as Set).forEach((value, value2) => callbackfn.call(thisArg, value, value2, this as any)), this; + } + + public has(element: Element): boolean { + return (this.collection as unknown as Set).has(element); + } + + public lock(): this { + return Object.freeze(this.collection), this; + } + + public set(value: Type): this { + return (this.#collection = value), this; + } +} diff --git a/src/core/index.ts b/src/core/index.ts new file mode 100644 index 0000000..b6d5db8 --- /dev/null +++ b/src/core/index.ts @@ -0,0 +1,4 @@ +export { + CollectionBase, + CollectionCore, +} from './lib'; \ No newline at end of file diff --git a/src/core/lib/collection.base.ts b/src/core/lib/collection.base.ts new file mode 100644 index 0000000..1fa34b2 --- /dev/null +++ b/src/core/lib/collection.base.ts @@ -0,0 +1,85 @@ +// Abstract. +import { CollectionCore } from './collection.core'; +// Interface. +import { CollectionAdapter } from '@typedly/collection'; +/** + * @description The base functionality abstraction for collections. + * @export + * @abstract + * @class CollectionBase + * @template E Element type in collection. + * @template T Type of the collection. + * @template {CollectionAdapter} A Type of the adapter. + * @extends {CollectionCore} + */ +export abstract class CollectionBase< + E, + T, + A extends CollectionAdapter +> extends CollectionCore { + + public get [Symbol.toStringTag](): string { + return 'Collection'; + } + + public get [Symbol.iterator](): Iterator { + return (this.#adapter.value as unknown as any)[Symbol.iterator](); + } + + /** + * @description The protected getter and setter for the adapter. + * @protected + * @type {A} + */ + public get adapter() { + return this.#adapter; + } + + public get size() { + return this.#adapter.size; + } + + public get value() { + return this.#adapter.value; + } + + #adapter: A; + + constructor( + adapter: new (...args: any[]) => A, + ...elements: E[] + ) { + super(); + this.#adapter = new adapter(...elements) as unknown as A; + } + + public add(...element: E[]): this { + return element.forEach(e => this.#adapter.add(e)), this; + } + public clear(): this { + return this.#adapter.clear(), this; + } + public clone(): CollectionCore { + return new (this.constructor as any)(this.#adapter.value, this.#adapter); + } + public destroy(): this { + return this.clear(), + this.#adapter = null as any, + this; + } + public delete(...element: E[]): boolean { + return element.every(e => this.#adapter.delete(e)); + } + public forEach(callbackfn: (element: E, element2: E, collection: CollectionCore) => void, thisArg?: any): this { + return this.#adapter.forEach(callbackfn as any, thisArg), this; + } + public has(element: E): boolean { + return this.#adapter.has(element); + } + public lock(): this { + return this.#adapter.lock?.(), this; + } + public set(value: T): this { + return this.#adapter.set(value), this; + } +} diff --git a/src/core/lib/collection.core.ts b/src/core/lib/collection.core.ts new file mode 100644 index 0000000..8baa6d4 --- /dev/null +++ b/src/core/lib/collection.core.ts @@ -0,0 +1,25 @@ +// Interface. +import { CollectionShape } from '@typedly/collection'; +/** + * @description The core abstract class for `Type` collections of elements `Element` type. + * @export + * @abstract + * @class CollectionCore + * @template Element type in collection. + * @template Type of the collection. + * @implements {CollectionShape} + */ +export abstract class CollectionCore implements CollectionShape { + abstract get [Symbol.toStringTag](): string; + abstract get [Symbol.iterator](): Iterator; + abstract value: Type; + abstract add(...element: Element[]): this; + abstract clear(): this; + abstract delete(...element: Element[]): boolean; + abstract destroy(): this; + abstract forEach(callbackfn: (element: Element, element2: Element, collection: CollectionShape) => void, thisArg?: any): void; + abstract has(element: Element): boolean; + abstract lock(): this; + abstract set(value: Type): this; + abstract readonly size: number; +} diff --git a/src/core/lib/index.ts b/src/core/lib/index.ts new file mode 100644 index 0000000..ad95d79 --- /dev/null +++ b/src/core/lib/index.ts @@ -0,0 +1,3 @@ +// Abstract. +export { CollectionBase } from './collection.base'; +export { CollectionCore } from './collection.core'; \ No newline at end of file diff --git a/src/lib/collection.class.ts b/src/lib/collection.class.ts new file mode 100644 index 0000000..5d703bc --- /dev/null +++ b/src/lib/collection.class.ts @@ -0,0 +1,20 @@ +// Abstract. +import { CollectionBase } from "../core"; +// Interface. +import { CollectionAdapter } from "@typedly/collection"; +/** + * @description The collection concrete class with adapter support. + * @export + * @class Collection + * @template E The type of elements in the collection. + * @template {new (...args: any[]) => CollectionAdapter} A The type of the adapter. + * @extends {CollectionBase>} + */ +export class Collection< + E, + A extends new (...args: any[]) => CollectionAdapter +> extends CollectionBase> { + constructor(adapter: A, ...elements: E[]) { + super(adapter as any, ...elements); + } +} diff --git a/src/lib/index.ts b/src/lib/index.ts new file mode 100644 index 0000000..47bf6bd --- /dev/null +++ b/src/lib/index.ts @@ -0,0 +1 @@ +export { Collection } from './collection.class'; \ No newline at end of file diff --git a/src/public-api.ts b/src/public-api.ts new file mode 100644 index 0000000..5726ca1 --- /dev/null +++ b/src/public-api.ts @@ -0,0 +1,16 @@ +/* + * Public API Surface of collection + */ + +export { + SetAdapter +} from './adapter'; + +export { + CollectionBase, + CollectionCore, +} from './core'; + +export { + Collection +} from './lib'; diff --git a/src/test/collection.spec.ts b/src/test/collection.spec.ts new file mode 100644 index 0000000..a45dfc7 --- /dev/null +++ b/src/test/collection.spec.ts @@ -0,0 +1,23 @@ +import { SetAdapter } from "../adapter"; +import { Collection } from "../lib"; + +const collection = new Collection(SetAdapter, 1, 2, 3); + +// Adds. +collection.add(27, 29, 31, 33); +// Deletes. +collection.delete(29, 31); + +console.log(`size: `, collection.size); // Output: 5 + +describe("Collection SetAdapter", () => { + test("has method works correctly", () => { + expect(collection.has(27)).toBe(true); + expect(collection.has(29)).toBe(false); + }); + + test("clear method works correctly", () => { + collection.clear(); + expect(collection.size).toBe(0); + }); +}); diff --git a/tsconfig.lib.json b/tsconfig.lib.json new file mode 100644 index 0000000..edb0551 --- /dev/null +++ b/tsconfig.lib.json @@ -0,0 +1,18 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/lib", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "types": [] + }, + "include": [ + "src/**/*.ts" + ], + "exclude": [ + "**/*.spec.ts" + ] +} diff --git a/tsconfig.lib.prod.json b/tsconfig.lib.prod.json new file mode 100644 index 0000000..9215caa --- /dev/null +++ b/tsconfig.lib.prod.json @@ -0,0 +1,11 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "extends": "./tsconfig.lib.json", + "compilerOptions": { + "declarationMap": false + }, + "angularCompilerOptions": { + "compilationMode": "partial" + } +} diff --git a/tsconfig.spec.json b/tsconfig.spec.json new file mode 100644 index 0000000..769f8bf --- /dev/null +++ b/tsconfig.spec.json @@ -0,0 +1,16 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/spec", + "types": [ + "vitest/globals" + ] + }, + "include": [ + "src/**/*.ts", + "src/**/*.d.ts", + "src/**/*.spec.ts" + ] +}