@dismissible/nestjs-dismissible-item 0.0.2-canary.738340d.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,131 @@
1
+ # @dismissible/nestjs-dismissible-item
2
+
3
+ Core data models and factory for dismissible items in NestJS applications.
4
+
5
+ > **Part of the Dismissible API** - This library is part of the [Dismissible API](https://dismissible.io) ecosystem. Visit [dismissible.io](https://dismissible.io) for more information and documentation.
6
+
7
+ ## Overview
8
+
9
+ This library provides the foundational data structures for the Dismissible system:
10
+
11
+ - `DismissibleItemDto` - The core data transfer object representing a dismissible item
12
+ - `DismissibleItemFactory` - Factory for creating and manipulating dismissible items
13
+ - `BaseMetadata` - Type definition for item metadata
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install @dismissible/nestjs-dismissible-item
19
+ ```
20
+
21
+ ## Getting Started
22
+
23
+ ### Basic Usage
24
+
25
+ The `DismissibleItemDto` class represents a dismissible item with the following properties:
26
+
27
+ - `id` - Unique identifier for the item
28
+ - `userId` - User identifier who owns the item
29
+ - `createdAt` - Timestamp when the item was created
30
+ - `dismissedAt` - Optional timestamp when the item was dismissed
31
+ - `metadata` - Optional metadata object (type-safe with generics)
32
+
33
+ ```typescript
34
+ import { DismissibleItemDto, BaseMetadata } from '@dismissible/nestjs-dismissible-item';
35
+
36
+ interface MyMetadata extends BaseMetadata {
37
+ version: number;
38
+ category: string;
39
+ }
40
+
41
+ const item: DismissibleItemDto<MyMetadata> = {
42
+ id: 'welcome-banner',
43
+ userId: 'user-123',
44
+ createdAt: new Date(),
45
+ metadata: {
46
+ version: 2,
47
+ category: 'onboarding',
48
+ },
49
+ };
50
+ ```
51
+
52
+ ### Using the Factory
53
+
54
+ The `DismissibleItemFactory` provides methods for creating and manipulating items:
55
+
56
+ ```typescript
57
+ import { Module } from '@nestjs/common';
58
+ import {
59
+ DismissibleItemModule,
60
+ DismissibleItemFactory,
61
+ } from '@dismissible/nestjs-dismissible-item';
62
+
63
+ @Module({
64
+ imports: [DismissibleItemModule],
65
+ })
66
+ export class AppModule {}
67
+
68
+ // In your service
69
+ import { Injectable } from '@nestjs/common';
70
+ import { DismissibleItemFactory, DismissibleItemDto } from '@dismissible/nestjs-dismissible-item';
71
+
72
+ @Injectable()
73
+ export class MyService {
74
+ constructor(private readonly itemFactory: DismissibleItemFactory) {}
75
+
76
+ createItem(): DismissibleItemDto {
77
+ return this.itemFactory.create({
78
+ id: 'welcome-banner',
79
+ userId: 'user-123',
80
+ createdAt: new Date(),
81
+ });
82
+ }
83
+
84
+ dismissItem(item: DismissibleItemDto): DismissibleItemDto {
85
+ return this.itemFactory.createDismissed(item, new Date());
86
+ }
87
+
88
+ restoreItem(item: DismissibleItemDto): DismissibleItemDto {
89
+ return this.itemFactory.createRestored(item);
90
+ }
91
+ }
92
+ ```
93
+
94
+ ## API Reference
95
+
96
+ ### DismissibleItemDto
97
+
98
+ The main data transfer object for dismissible items.
99
+
100
+ ```typescript
101
+ class DismissibleItemDto<TMetadata extends BaseMetadata = BaseMetadata> {
102
+ id: string;
103
+ userId: string;
104
+ createdAt: Date;
105
+ dismissedAt?: Date;
106
+ metadata?: TMetadata;
107
+ }
108
+ ```
109
+
110
+ ### DismissibleItemFactory
111
+
112
+ Factory for creating and manipulating dismissible items.
113
+
114
+ #### Methods
115
+
116
+ - `create(options)` - Create a new item from options
117
+ - `clone(item)` - Create a clone of an existing item
118
+ - `createDismissed(item, dismissedAt)` - Create a dismissed version of an item
119
+ - `createRestored(item)` - Create a restored (non-dismissed) version of an item
120
+
121
+ ## Related Packages
122
+
123
+ This library is typically used alongside other Dismissible packages:
124
+
125
+ - `@dismissible/nestjs-dismissible` - Main dismissible service and module
126
+ - `@dismissible/nestjs-storage` - Storage interface and adapters
127
+ - `@dismissible/nestjs-postgres-storage` - PostgreSQL storage adapter
128
+
129
+ ## License
130
+
131
+ MIT
package/jest.config.ts ADDED
@@ -0,0 +1,10 @@
1
+ export default {
2
+ displayName: 'dismissible-item',
3
+ preset: '../../jest.preset.js',
4
+ testEnvironment: 'node',
5
+ transform: {
6
+ '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.json' }],
7
+ },
8
+ moduleFileExtensions: ['ts', 'js', 'html'],
9
+ coverageDirectory: '../../coverage/libs/dismissible-item',
10
+ };
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@dismissible/nestjs-dismissible-item",
3
+ "version": "0.0.2-canary.738340d.0",
4
+ "description": "Dismissible item library",
5
+ "main": "./src/index.js",
6
+ "types": "./src/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./src/index.mjs",
10
+ "require": "./src/index.js",
11
+ "types": "./src/index.d.ts"
12
+ }
13
+ },
14
+ "peerDependencies": {
15
+ "@nestjs/common": "^11.0.0"
16
+ },
17
+ "peerDependenciesMeta": {
18
+ "@nestjs/common": {
19
+ "optional": false
20
+ }
21
+ },
22
+ "keywords": [
23
+ "nestjs",
24
+ "dismissible",
25
+ "dismissible-item"
26
+ ],
27
+ "author": "",
28
+ "license": "MIT",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": ""
32
+ }
33
+ }
package/project.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "dismissible-item",
3
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
4
+ "sourceRoot": "libs/dismissible-item/src",
5
+ "projectType": "library",
6
+ "tags": [],
7
+ "targets": {
8
+ "build": {
9
+ "executor": "@nx/js:tsc",
10
+ "outputs": ["{options.outputPath}"],
11
+ "options": {
12
+ "outputPath": "dist/libs/dismissible-item",
13
+ "main": "libs/dismissible-item/src/index.ts",
14
+ "tsConfig": "libs/dismissible-item/tsconfig.lib.json",
15
+ "assets": ["libs/dismissible-item/package.json", "libs/dismissible-item/README.md"],
16
+ "generatePackageJson": true
17
+ }
18
+ },
19
+ "lint": {
20
+ "executor": "@nx/eslint:lint",
21
+ "outputs": ["{options.outputFile}"],
22
+ "options": {
23
+ "lintFilePatterns": ["libs/dismissible-item/**/*.ts"]
24
+ }
25
+ },
26
+ "test": {
27
+ "executor": "@nx/jest:jest",
28
+ "outputs": ["{workspaceRoot}/coverage/libs/dismissible-item"],
29
+ "options": {
30
+ "jestConfig": "libs/dismissible-item/jest.config.ts",
31
+ "passWithNoTests": true
32
+ }
33
+ },
34
+ "npm-publish": {
35
+ "executor": "nx:run-commands",
36
+ "options": {
37
+ "command": "npm publish --access public",
38
+ "cwd": "dist/libs/dismissible-item"
39
+ }
40
+ }
41
+ }
42
+ }
@@ -0,0 +1,124 @@
1
+ import { DismissibleItemFactory } from './dismissible-item.factory';
2
+ import { DismissibleItemDto } from './dismissible-item';
3
+
4
+ describe('DismissibleItemFactory', () => {
5
+ let factory: DismissibleItemFactory;
6
+
7
+ beforeEach(() => {
8
+ factory = new DismissibleItemFactory();
9
+ });
10
+
11
+ describe('create', () => {
12
+ it('should create a DismissibleItemDto instance', () => {
13
+ const item = factory.create({
14
+ id: 'test-id',
15
+ createdAt: new Date('2024-01-15T10:00:00.000Z'),
16
+ });
17
+
18
+ expect(item).toBeInstanceOf(DismissibleItemDto);
19
+ expect(item.id).toBe('test-id');
20
+ expect(item.createdAt).toEqual(new Date('2024-01-15T10:00:00.000Z'));
21
+ });
22
+
23
+ it('should handle optional properties when undefined', () => {
24
+ const item = factory.create({
25
+ id: 'test-id',
26
+ createdAt: new Date('2024-01-15T10:00:00.000Z'),
27
+ userId: undefined,
28
+ metadata: undefined,
29
+ dismissedAt: undefined,
30
+ });
31
+
32
+ expect(item.id).toBe('test-id');
33
+ expect(item.createdAt).toEqual(new Date('2024-01-15T10:00:00.000Z'));
34
+ expect(item.userId).toBeUndefined();
35
+ expect(item.metadata).toBeUndefined();
36
+ expect(item.dismissedAt).toBeUndefined();
37
+ });
38
+
39
+ it('should include optional properties when provided', () => {
40
+ const metadata = { version: 2, category: 'test' };
41
+ const item = factory.create({
42
+ id: 'test-id',
43
+ createdAt: new Date('2024-01-15T10:00:00.000Z'),
44
+ userId: 'user-123',
45
+ metadata,
46
+ dismissedAt: new Date('2024-01-15T12:00:00.000Z'),
47
+ });
48
+
49
+ expect(item.userId).toBe('user-123');
50
+ expect(item.metadata).toEqual(metadata);
51
+ expect(item.dismissedAt).toEqual(new Date('2024-01-15T12:00:00.000Z'));
52
+ });
53
+ });
54
+
55
+ describe('clone', () => {
56
+ it('should create an identical copy of an item', () => {
57
+ const original = factory.create({
58
+ id: 'test-id',
59
+ createdAt: new Date('2024-01-15T10:00:00.000Z'),
60
+ userId: 'user-123',
61
+ metadata: { key: 'value' },
62
+ });
63
+
64
+ const cloned = factory.clone(original);
65
+
66
+ expect(cloned).toBeInstanceOf(DismissibleItemDto);
67
+ expect(cloned).toEqual(original);
68
+ expect(cloned).not.toBe(original); // Different object reference
69
+ });
70
+ });
71
+
72
+ describe('createDismissed', () => {
73
+ it('should create a dismissed version with dismissedAt set', () => {
74
+ const original = factory.create({
75
+ id: 'test-id',
76
+ createdAt: new Date('2024-01-15T10:00:00.000Z'),
77
+ userId: 'user-123',
78
+ });
79
+
80
+ const dismissed = factory.createDismissed(original, new Date('2024-01-15T12:00:00.000Z'));
81
+
82
+ expect(dismissed).toBeInstanceOf(DismissibleItemDto);
83
+ expect(dismissed.id).toBe(original.id);
84
+ expect(dismissed.createdAt).toEqual(original.createdAt);
85
+ expect(dismissed.userId).toBe(original.userId);
86
+ expect(dismissed.dismissedAt).toEqual(new Date('2024-01-15T12:00:00.000Z'));
87
+ });
88
+ });
89
+
90
+ describe('createRestored', () => {
91
+ it('should create a restored version without dismissedAt', () => {
92
+ const dismissed = factory.create({
93
+ id: 'test-id',
94
+ createdAt: new Date('2024-01-15T10:00:00.000Z'),
95
+ userId: 'user-123',
96
+ dismissedAt: new Date('2024-01-15T12:00:00.000Z'),
97
+ });
98
+
99
+ const restored = factory.createRestored(dismissed);
100
+
101
+ expect(restored).toBeInstanceOf(DismissibleItemDto);
102
+ expect(restored.id).toBe(dismissed.id);
103
+ expect(restored.createdAt).toEqual(dismissed.createdAt);
104
+ expect(restored.userId).toBe(dismissed.userId);
105
+ expect(restored.dismissedAt).toBeUndefined();
106
+ });
107
+
108
+ it('should preserve metadata when restoring', () => {
109
+ const metadata = { key: 'value', count: 42 };
110
+ const dismissed = factory.create({
111
+ id: 'test-id',
112
+ createdAt: new Date('2024-01-15T10:00:00.000Z'),
113
+ userId: 'user-123',
114
+ metadata,
115
+ dismissedAt: new Date('2024-01-15T12:00:00.000Z'),
116
+ });
117
+
118
+ const restored = factory.createRestored(dismissed);
119
+
120
+ expect(restored.metadata).toEqual(metadata);
121
+ expect(restored.metadata).not.toBe(dismissed.metadata); // Different object reference
122
+ });
123
+ });
124
+ });
@@ -0,0 +1,81 @@
1
+ import { Injectable } from '@nestjs/common';
2
+ import { plainToInstance } from 'class-transformer';
3
+ import { BaseMetadata, DismissibleItemDto } from './dismissible-item';
4
+
5
+ /**
6
+ * Options for creating a dismissible item.
7
+ */
8
+ export interface ICreateDismissibleItemOptions<TMetadata extends BaseMetadata> {
9
+ id: string;
10
+ createdAt: Date;
11
+ userId?: string;
12
+ metadata?: TMetadata;
13
+ dismissedAt?: Date;
14
+ }
15
+
16
+ /**
17
+ * Factory for creating DismissibleItemDto instances.
18
+ * Uses class-transformer to ensure proper class instantiation.
19
+ */
20
+ @Injectable()
21
+ export class DismissibleItemFactory {
22
+ /**
23
+ * Create a new DismissibleItemDto instance from the provided options.
24
+ * Uses plainToInstance to ensure the result is a proper class instance.
25
+ */
26
+ create<TMetadata extends BaseMetadata = BaseMetadata>(
27
+ options: ICreateDismissibleItemOptions<TMetadata>,
28
+ ): DismissibleItemDto<TMetadata> {
29
+ return plainToInstance(DismissibleItemDto<TMetadata>, options, {
30
+ excludeExtraneousValues: false,
31
+ enableImplicitConversion: true,
32
+ });
33
+ }
34
+
35
+ /**
36
+ * Create a clone of an existing DismissibleItemDto.
37
+ */
38
+ clone<TMetadata extends BaseMetadata = BaseMetadata>(
39
+ item: DismissibleItemDto<TMetadata>,
40
+ ): DismissibleItemDto<TMetadata> {
41
+ return this.create({
42
+ id: item.id,
43
+ createdAt: item.createdAt,
44
+ userId: item.userId,
45
+ metadata: item.metadata,
46
+ dismissedAt: item.dismissedAt,
47
+ });
48
+ }
49
+
50
+ /**
51
+ * Create a dismissed version of an existing item.
52
+ */
53
+ createDismissed<TMetadata extends BaseMetadata = BaseMetadata>(
54
+ item: DismissibleItemDto<TMetadata>,
55
+ dismissedAt: Date,
56
+ ): DismissibleItemDto<TMetadata> {
57
+ return this.create({
58
+ id: item.id,
59
+ createdAt: item.createdAt,
60
+ userId: item.userId,
61
+ metadata: item.metadata,
62
+ dismissedAt,
63
+ });
64
+ }
65
+
66
+ /**
67
+ * Create a restored (non-dismissed) version of an existing item.
68
+ * Removes the dismissedAt property.
69
+ */
70
+ createRestored<TMetadata extends BaseMetadata = BaseMetadata>(
71
+ item: DismissibleItemDto<TMetadata>,
72
+ ): DismissibleItemDto<TMetadata> {
73
+ return this.create({
74
+ id: item.id,
75
+ createdAt: item.createdAt,
76
+ userId: item.userId,
77
+ metadata: item.metadata,
78
+ dismissedAt: undefined,
79
+ });
80
+ }
81
+ }
@@ -0,0 +1,8 @@
1
+ import { Module } from '@nestjs/common';
2
+ import { DismissibleItemFactory } from './dismissible-item.factory';
3
+
4
+ @Module({
5
+ providers: [DismissibleItemFactory],
6
+ exports: [DismissibleItemFactory],
7
+ })
8
+ export class DismissibleItemModule {}
@@ -0,0 +1,55 @@
1
+ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
2
+ import { IsString, IsDate, IsOptional, IsObject } from 'class-validator';
3
+ import { Type } from 'class-transformer';
4
+
5
+ /**
6
+ * Base metadata type for dismissible items.
7
+ * Can be extended with custom metadata fields.
8
+ */
9
+ export type BaseMetadata = Record<string, unknown>;
10
+
11
+ /**
12
+ * Represents a dismissible item in the system.
13
+ */
14
+ export class DismissibleItemDto<TMetadata extends BaseMetadata = BaseMetadata> {
15
+ @ApiProperty({
16
+ description: 'Unique identifier for the item',
17
+ example: 'welcome-banner-v2',
18
+ })
19
+ @IsString()
20
+ id!: string;
21
+
22
+ @ApiProperty({
23
+ description: 'User identifier who created the item',
24
+ example: 'user-123',
25
+ })
26
+ @IsString()
27
+ userId!: string;
28
+
29
+ @ApiProperty({
30
+ description: 'When the item was created',
31
+ example: '2024-01-15T10:30:00.000Z',
32
+ })
33
+ @IsDate()
34
+ @Type(() => Date)
35
+ createdAt!: Date;
36
+
37
+ @ApiPropertyOptional({
38
+ description: 'When the item was dismissed, if applicable',
39
+ example: '2024-01-15T12:00:00.000Z',
40
+ })
41
+ @IsOptional()
42
+ @IsDate()
43
+ @Type(() => Date)
44
+ dismissedAt?: Date;
45
+
46
+ @ApiPropertyOptional({
47
+ description: 'Optional metadata associated with the item',
48
+ example: { version: 2, category: 'promotional' },
49
+ type: 'object',
50
+ additionalProperties: true,
51
+ })
52
+ @IsOptional()
53
+ @IsObject()
54
+ metadata?: TMetadata;
55
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from './dismissible-item';
2
+ export * from './dismissible-item.factory';
3
+ export * from './dismissible-item.module';
package/tsconfig.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "files": [],
4
+ "include": [],
5
+ "references": [
6
+ {
7
+ "path": "./tsconfig.lib.json"
8
+ }
9
+ ],
10
+ "compilerOptions": {
11
+ "esModuleInterop": true
12
+ }
13
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "../../dist/out-tsc",
5
+ "declaration": true,
6
+ "module": "commonjs",
7
+ "types": ["node"],
8
+ "emitDecoratorMetadata": true,
9
+ "experimentalDecorators": true,
10
+ "target": "ES2021"
11
+ },
12
+ "exclude": ["node_modules", "**/*.spec.ts", "**/*.test.ts"],
13
+ "include": ["src/**/*.ts"]
14
+ }