@schorts/shared-kernel 1.0.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.
Files changed (88) hide show
  1. package/.idx/airules.md +186 -0
  2. package/.idx/dev.nix +54 -0
  3. package/.vscode/settings.json +7 -0
  4. package/CHANGELOG +0 -0
  5. package/README.md +98 -0
  6. package/__tests__/auth/auth-provider.test.ts +45 -0
  7. package/__tests__/auth/require-auth.decorator.test.ts +88 -0
  8. package/__tests__/criteria/criteria.test.ts +159 -0
  9. package/__tests__/criteria/direction.test.ts +11 -0
  10. package/__tests__/criteria/filter-criterion.test.ts +15 -0
  11. package/__tests__/criteria/operator.test.ts +22 -0
  12. package/__tests__/criteria/order.test.ts +14 -0
  13. package/__tests__/domain-events/domain-event-primitives.test.ts +34 -0
  14. package/__tests__/domain-events/domain-event.test.ts +50 -0
  15. package/__tests__/entities/entity.test.ts +114 -0
  16. package/__tests__/formatters/pascal-camel-to-snake.test.ts +19 -0
  17. package/__tests__/http/fetch-http-provider.test.ts +155 -0
  18. package/__tests__/http/http-provider.test.ts +55 -0
  19. package/__tests__/json-api/json-api-connector.test.ts +78 -0
  20. package/__tests__/json-api/json-api-list.test.ts +24 -0
  21. package/__tests__/json-api/json-api-single.test.ts +24 -0
  22. package/__tests__/json-api/url-criteria-builder.test.ts +74 -0
  23. package/__tests__/messages/message.test.ts +16 -0
  24. package/__tests__/models/base-model.test.ts +10 -0
  25. package/__tests__/state-manager/state-manager.test.ts +101 -0
  26. package/__tests__/utils/url/url-with-params-builder.test.ts +39 -0
  27. package/__tests__/value-objects/coordinates-value.test.ts +68 -0
  28. package/__tests__/value-objects/email-value.test.ts +43 -0
  29. package/__tests__/value-objects/enum-value.test.ts +51 -0
  30. package/__tests__/value-objects/integer-value.test.ts +115 -0
  31. package/__tests__/value-objects/phone-value.test.ts +82 -0
  32. package/__tests__/value-objects/slug-value.test.ts +43 -0
  33. package/__tests__/value-objects/string-value.test.ts +121 -0
  34. package/__tests__/value-objects/uuid-value.test.ts +67 -0
  35. package/__tests__/value-objects/value-object.test.ts +25 -0
  36. package/jest.config.js +25 -0
  37. package/package.json +289 -0
  38. package/src/auth/auth-provider.ts +10 -0
  39. package/src/auth/exceptions/index.ts +1 -0
  40. package/src/auth/exceptions/not-authenticated.ts +1 -0
  41. package/src/auth/index.ts +3 -0
  42. package/src/auth/require-auth.decorator.ts +41 -0
  43. package/src/criteria/criteria.ts +51 -0
  44. package/src/criteria/direction.ts +1 -0
  45. package/src/criteria/exceptions/index.ts +2 -0
  46. package/src/criteria/exceptions/limit-not-valid.ts +1 -0
  47. package/src/criteria/exceptions/offset-not-valid.ts +1 -0
  48. package/src/criteria/filter-criterion.ts +6 -0
  49. package/src/criteria/index.ts +7 -0
  50. package/src/criteria/operator.ts +12 -0
  51. package/src/criteria/order.ts +4 -0
  52. package/src/domain-events/domain-event-primitives.ts +7 -0
  53. package/src/domain-events/domain-event.ts +15 -0
  54. package/src/domain-events/index.ts +2 -0
  55. package/src/entities/entity.ts +22 -0
  56. package/src/entities/index.ts +1 -0
  57. package/src/formatters/index.ts +1 -0
  58. package/src/formatters/pascal-camel-to-snake.ts +8 -0
  59. package/src/http/exceptions/http-exception.ts +8 -0
  60. package/src/http/exceptions/index.ts +1 -0
  61. package/src/http/fetch-http-provider.ts +120 -0
  62. package/src/http/http-provider.ts +7 -0
  63. package/src/http/index.ts +4 -0
  64. package/src/json-api/index.ts +4 -0
  65. package/src/json-api/json-api-connector.ts +56 -0
  66. package/src/json-api/json-api-list.ts +13 -0
  67. package/src/json-api/json-api-single.ts +13 -0
  68. package/src/json-api/url-criteria-builder.ts +49 -0
  69. package/src/messages/index.ts +1 -0
  70. package/src/messages/message.ts +3 -0
  71. package/src/models/base-model.ts +3 -0
  72. package/src/models/index.ts +1 -0
  73. package/src/state-manager/index.ts +1 -0
  74. package/src/state-manager/state-manager.ts +28 -0
  75. package/src/utils/index.ts +1 -0
  76. package/src/utils/url/index.ts +1 -0
  77. package/src/utils/url/url-with-params-builder.ts +19 -0
  78. package/src/value-objects/coordinates-value.ts +50 -0
  79. package/src/value-objects/email-value.ts +25 -0
  80. package/src/value-objects/enum-value.ts +25 -0
  81. package/src/value-objects/index.ts +10 -0
  82. package/src/value-objects/integer-value.ts +29 -0
  83. package/src/value-objects/phone-value.ts +53 -0
  84. package/src/value-objects/slug-value.ts +25 -0
  85. package/src/value-objects/string-value.ts +27 -0
  86. package/src/value-objects/uuid-value.ts +34 -0
  87. package/src/value-objects/value-object.ts +7 -0
  88. package/tsconfig.json +46 -0
@@ -0,0 +1,186 @@
1
+ # Gemini AI Rules for Firebase Studio Nix Projects
2
+
3
+ ## 1. Persona & Expertise
4
+
5
+ You are an expert in configuring development environments within Firebase Studio. You are proficient in using the `dev.nix` file to define reproducible, declarative, and isolated development environments. You have experience with the Nix language in the context of Firebase Studio, including packaging, managing dependencies, and configuring services.
6
+
7
+ ## 2. Project Context
8
+
9
+ This project is a Nix-based environment for Firebase Studio, defined by a `.idx/dev.nix` file. The primary goal is to ensure a reproducible and consistent development environment. The project leverages the power of Nix to manage dependencies, tools, and services in a declarative manner. **Note:** This is not a Nix Flake-based environment.
10
+
11
+ ## 3. `dev.nix` Configuration
12
+
13
+ The `.idx/dev.nix` file is the single source of truth for the development environment. Here are some of the most common configuration options:
14
+
15
+ ### `channel`
16
+ The `nixpkgs` channel determines which package versions are available.
17
+
18
+ ```nix
19
+ { pkgs, ... }: {
20
+ channel = "stable-24.05"; # or "unstable"
21
+ }
22
+ ```
23
+
24
+ ### `packages`
25
+ A list of packages to install from the specified channel. You can search for packages on the [NixOS package search](https://search.nixos.org/packages).
26
+
27
+ ```nix
28
+ { pkgs, ... }: {
29
+ packages = [
30
+ pkgs.nodejs_20
31
+ pkgs.go
32
+ ];
33
+ }
34
+ ```
35
+
36
+ ### `env`
37
+ A set of environment variables to define within the workspace.
38
+
39
+ ```nix
40
+ { pkgs, ... }: {
41
+ env = {
42
+ API_KEY = "your-secret-key";
43
+ };
44
+ }
45
+ ```
46
+
47
+ ### `idx.extensions`
48
+ A list of VS Code extensions to install from the [Open VSX Registry](https://open-vsx.org/).
49
+
50
+ ```nix
51
+ { pkgs, ... }: {
52
+ idx = {
53
+ extensions = [
54
+ "vscodevim.vim"
55
+ "golang.go"
56
+ ];
57
+ };
58
+ }
59
+ ```
60
+
61
+ ### `idx.workspace`
62
+ Workspace lifecycle hooks.
63
+
64
+ - **`onCreate`:** Runs when a workspace is first created.
65
+ - **`onStart`:** Runs every time the workspace is (re)started.
66
+
67
+ ```nix
68
+ { pkgs, ... }: {
69
+ idx = {
70
+ workspace = {
71
+ onCreate = {
72
+ npm-install = "npm install";
73
+ };
74
+ onStart = {
75
+ start-server = "npm run dev";
76
+ };
77
+ };
78
+ };
79
+ }
80
+ ```
81
+
82
+ ### `idx.previews`
83
+ Configure a web preview for your application. The `$PORT` variable is dynamically assigned.
84
+
85
+ ```nix
86
+ { pkgs, ... }: {
87
+ idx = {
88
+ previews = {
89
+ enable = true;
90
+ previews = {
91
+ web = {
92
+ command = ["npm" "run" "dev" "--" "--port" "$PORT"];
93
+ manager = "web";
94
+ };
95
+ };
96
+ };
97
+ };
98
+ }
99
+ ```
100
+
101
+ ## 4. Example Setups for Common Frameworks
102
+
103
+ Here are some examples of how to configure your `dev.nix` for common languages and frameworks.
104
+
105
+ ### Node.js Web Server
106
+ This example sets up a Node.js environment, installs dependencies, and runs a development server with a web preview.
107
+
108
+ ```nix
109
+ { pkgs, ... }: {
110
+ packages = [ pkgs.nodejs_20 ];
111
+ idx = {
112
+ extensions = [ "dbaeumer.vscode-eslint" ];
113
+ workspace = {
114
+ onCreate = {
115
+ npm-install = "npm install";
116
+ };
117
+ onStart = {
118
+ dev-server = "npm run dev";
119
+ };
120
+ };
121
+ previews = {
122
+ enable = true;
123
+ previews = {
124
+ web = {
125
+ command = ["npm" "run" "dev" "--" "--port" "$PORT"];
126
+ manager = "web";
127
+ };
128
+ };
129
+ };
130
+ };
131
+ }
132
+ ```
133
+
134
+ ### Python with Flask
135
+ This example sets up a Python environment for a Flask web server. Remember to create a `requirements.txt` file with `Flask` in it.
136
+
137
+ ```nix
138
+ { pkgs, ... }: {
139
+ packages = [ pkgs.python3 pkgs.pip ];
140
+ idx = {
141
+ extensions = [ "ms-python.python" ];
142
+ workspace = {
143
+ onCreate = {
144
+ pip-install = "pip install -r requirements.txt";
145
+ };
146
+ };
147
+ previews = {
148
+ enable = true;
149
+ previews = {
150
+ web = {
151
+ command = ["flask" "run" "--port" "$PORT"];
152
+ manager = "web";
153
+ };
154
+ };
155
+ };
156
+ };
157
+ }
158
+ ```
159
+
160
+ ### Go CLI
161
+ This example sets up a Go environment for building a command-line interface.
162
+
163
+ ```nix
164
+ { pkgs, ... }: {
165
+ packages = [ pkgs.go ];
166
+ idx = {
167
+ extensions = [ "golang.go" ];
168
+ workspace = {
169
+ onCreate = {
170
+ go-mod = "go mod tidy";
171
+ };
172
+ onStart = {
173
+ run-app = "go run .";
174
+ };
175
+ };
176
+ };
177
+ }
178
+ ```
179
+
180
+ ## 5. Interaction Guidelines
181
+
182
+ - Assume the user is familiar with general software development concepts but may be new to Nix and Firebase Studio.
183
+ - When generating Nix code, provide comments to explain the purpose of different sections.
184
+ - Explain the benefits of using `dev.nix` for reproducibility and dependency management.
185
+ - If a request is ambiguous, ask for clarification on the desired tools, libraries, and versions to be included in the environment.
186
+ - When suggesting changes to `dev.nix`, explain the impact of the changes on the development environment and remind the user to reload the environment.
package/.idx/dev.nix ADDED
@@ -0,0 +1,54 @@
1
+ # To learn more about how to use Nix to configure your environment
2
+ # see: https://developers.google.com/idx/guides/customize-idx-env
3
+ { pkgs, ... }: {
4
+ # Which nixpkgs channel to use.
5
+ channel = "stable-24.05"; # or "unstable"
6
+ # Use https://search.nixos.org/packages to find packages
7
+ packages = [
8
+ # pkgs.go
9
+ # pkgs.python311
10
+ # pkgs.python311Packages.pip
11
+ pkgs.nodejs_20
12
+ # pkgs.nodePackages.nodemon
13
+ ];
14
+ # Sets environment variables in the workspace
15
+ env = {};
16
+ idx = {
17
+ # Search for the extensions you want on https://open-vsx.org/ and use "publisher.id"
18
+ extensions = [
19
+ # "vscodevim.vim"
20
+ "google.gemini-cli-vscode-ide-companion"
21
+ ];
22
+ # Enable previews
23
+ previews = {
24
+ enable = true;
25
+ previews = {
26
+ # web = {
27
+ # # Example: run "npm run dev" with PORT set to IDX's defined port for previews,
28
+ # # and show it in IDX's web preview panel
29
+ # command = ["npm" "run" "dev"];
30
+ # manager = "web";
31
+ # env = {
32
+ # # Environment variables to set for your server
33
+ # PORT = "$PORT";
34
+ # };
35
+ # };
36
+ };
37
+ };
38
+ # Workspace lifecycle hooks
39
+ workspace = {
40
+ # Runs when a workspace is first created
41
+ onCreate = {
42
+ # Example: install JS dependencies from NPM
43
+ # npm-install = "npm install";
44
+ # Open editors for the following files by default, if they exist:
45
+ default.openFiles = [ ".idx/dev.nix" "README.md" ];
46
+ };
47
+ # Runs when the workspace is (re)started
48
+ onStart = {
49
+ # Example: start a background task to watch and re-build backend code
50
+ # watch-backend = "npm run watch-backend";
51
+ };
52
+ };
53
+ };
54
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "editor.tabSize": 2,
3
+ "files.exclude": {
4
+ "**/node_modules": true,
5
+ "**/dist": true
6
+ }
7
+ }
package/CHANGELOG ADDED
File without changes
package/README.md ADDED
@@ -0,0 +1,98 @@
1
+ # 📦 Shared Kernel
2
+
3
+ A modular, type-safe foundation for building expressive, maintainable applications. This package provides core abstractions for domain modeling, HTTP integration, authentication, state management, and more — designed to be framework-agnostic and highly extensible.
4
+
5
+ ## 🚀 Installation
6
+
7
+ ```bash
8
+ npm install @schorts/shared-kernel --save
9
+ ```
10
+
11
+ ## 🧱 Modules
12
+
13
+ ### 🔐 Auth
14
+
15
+ - **AuthProvider:** Abstract interface for authentication strategies.
16
+ - **RequireAuthDecorator:** Method-level decorator for enforcing authentication.
17
+
18
+ ### 📊 Criteria
19
+
20
+ - **Criteria:** Fluent query builder for filtering, sorting, and pagination.
21
+
22
+ ### 📣 Domain Events
23
+
24
+ - **DomainEvent:** Base class for domain-driven event dispatching.
25
+
26
+ ### 🧬 Entities
27
+
28
+ - **Entity:** Base class for identity-based domain entities.
29
+
30
+ ### 🧹 Formatters
31
+
32
+ - **PascalCamelToSnake:** Utility for converting PascalCase or camelCase to snake_case.
33
+
34
+ ### 🌐 HTTP
35
+
36
+ - **HTTPProvider:** Abstract interface for HTTP transport.
37
+ - **FetchHTTPProvider:** Concrete implementation using fetch.
38
+
39
+ ### 🔗 JSON:API
40
+
41
+ - **JSONAPIConnector:** Connector for interacting with JSON:API-compliant endpoints.
42
+
43
+ ### 🧩 Models
44
+
45
+ - **BaseModel:** Base class for serializable, type-safe models.
46
+
47
+ ### 🧠 State Manager
48
+
49
+ - **StateManager:** Abstract reactive state manager with listener support.
50
+
51
+ ### 🧪 Value Objects
52
+
53
+ - **CoordinatesValue**
54
+ - **EmailValue**
55
+ - **EnumValue**
56
+ - **IntegerValue**
57
+ - **PhoneValue**
58
+ - **SlugValue**
59
+ - **StringValue**
60
+ - **UUIDValue**
61
+
62
+ Each value object enforces domain constraints and immutability, ensuring correctness at the boundary of your system.
63
+
64
+ ## 🧠 Philosophy
65
+
66
+ This kernel is built around:
67
+
68
+ - **Type safety:** Every abstraction is strongly typed and composable.
69
+ - **Domain expressiveness:** Value objects, entities, and events encode business logic directly.
70
+ - **Extensibility:** Plug in your own HTTP, auth, or state strategies.
71
+ - **Framework independence:** Use it with Node.js, frontend apps, or serverless functions.
72
+
73
+ ## 📦 Example Usage
74
+
75
+ ```ts
76
+ import { Criteria, JSONAPIConnector, FetchHTTPProvider } from "@schorts/shared-kernel";
77
+
78
+ const criteria = new Criteria()
79
+ .where("status", "EQUAL", "active")
80
+ .orderBy("created_at", "DESC")
81
+ .limitResults(10);
82
+
83
+ const connector = new JSONAPIConnector(new FetchHTTPProvider());
84
+
85
+ const users = await connector.findMany(new URL("/users", base), criteria);
86
+ ```
87
+
88
+ ## 🧪 Testing
89
+
90
+ Each module is fully covered with unit tests. To run them:
91
+
92
+ ```bash
93
+ npm run test
94
+ ```
95
+
96
+ ## 📜 License
97
+
98
+ LGPL
@@ -0,0 +1,45 @@
1
+ import { expectTypeOf } from "expect-type";
2
+
3
+ import { AuthProvider } from "../../src/auth";
4
+ import { Entity } from "../../src/entities";
5
+
6
+ type Model = {
7
+ id: string;
8
+ name: string;
9
+ };
10
+
11
+ class TestUser extends Entity<Model> {
12
+ toPrimitives(): Model {
13
+ return {
14
+ id: this.id.toString(),
15
+ name: "test",
16
+ };
17
+ }
18
+ }
19
+
20
+ describe('AuthProvider', () => {
21
+ it('should declare an "authenticate" method', () => {
22
+ expectTypeOf<AuthProvider<TestUser>['authenticate']>().toBeFunction();
23
+ expectTypeOf<AuthProvider<TestUser>['authenticate']>().returns.toEqualTypeOf<Promise<void>>();
24
+ });
25
+
26
+ it('should declare a "logout" method', () => {
27
+ expectTypeOf<AuthProvider<TestUser>['logout']>().toBeFunction();
28
+ expectTypeOf<AuthProvider<TestUser >['logout']>().returns.toEqualTypeOf<Promise<void>>();
29
+ });
30
+
31
+ it('should declare a "isAuthenticated" method', () => {
32
+ expectTypeOf<AuthProvider<TestUser>['isAuthenticated']>().toBeFunction();
33
+ expectTypeOf<AuthProvider<TestUser>['isAuthenticated']>().returns.toEqualTypeOf<Promise<boolean>>();
34
+ });
35
+
36
+ it('should declare a "currentUser" method', () => {
37
+ expectTypeOf<AuthProvider<TestUser>['currentUser']>().toBeFunction();
38
+ expectTypeOf<AuthProvider<TestUser>['currentUser']>().returns.toEqualTypeOf<Promise<TestUser | null>>();
39
+ });
40
+
41
+ it('should declare a "onAuthChange" method', () => {
42
+ expectTypeOf<AuthProvider<TestUser>['onAuthChange']>().toBeFunction();
43
+ expectTypeOf<AuthProvider<TestUser>['onAuthChange']>().returns.toEqualTypeOf<() => void>();
44
+ });
45
+ });
@@ -0,0 +1,88 @@
1
+ import { RequireAuth, NotAuthenticated } from '../../src/auth';
2
+
3
+ class FakeAuthProvider {
4
+ constructor(private authenticated: boolean) {}
5
+ async isAuthenticated() {
6
+ return this.authenticated;
7
+ }
8
+ }
9
+
10
+ class TestService {
11
+ authProvider: FakeAuthProvider;
12
+
13
+ constructor(authenticated: boolean) {
14
+ this.authProvider = new FakeAuthProvider(authenticated);
15
+ }
16
+
17
+ async securedMethod(): Promise<string> {
18
+ return 'should not reach';
19
+ }
20
+ }
21
+
22
+ describe('RequireAuth decorator (manual application)', () => {
23
+ it('should throw NotAuthenticated when not authenticated and no onFail', async () => {
24
+ const service = new TestService(false);
25
+ const originalMethod = service.securedMethod;
26
+ const decorated = RequireAuth()(originalMethod, {
27
+ kind: 'method',
28
+ name: 'securedMethod',
29
+ access: { has: () => true, get: () => originalMethod },
30
+ static: false,
31
+ private: false,
32
+ addInitializer: () => {},
33
+ metadata: {
34
+ get: () => undefined,
35
+ set: () => {},
36
+ },
37
+ });
38
+ const wrappedMethod = typeof decorated === 'function' ? decorated : decorated?.value;
39
+ service.securedMethod = wrappedMethod.bind(service);
40
+
41
+ await expect(service.securedMethod()).rejects.toThrow(NotAuthenticated);
42
+ });
43
+
44
+ it('should return fallback value when not authenticated and onFail is provided', async () => {
45
+ const service = new TestService(false);
46
+ const fallback = () => 'fallback';
47
+ const originalMethod = service.securedMethod;
48
+ const decorated = RequireAuth(fallback)(originalMethod, {
49
+ kind: 'method',
50
+ name: 'securedMethod',
51
+ access: { has: () => true, get: () => originalMethod },
52
+ static: false,
53
+ private: false,
54
+ addInitializer: () => {},
55
+ metadata: {
56
+ get: () => undefined,
57
+ set: () => {},
58
+ },
59
+ });
60
+ const wrappedMethod = typeof decorated === 'function' ? decorated : decorated?.value;
61
+ service.securedMethod = wrappedMethod.bind(service);
62
+ const result = await service.securedMethod();
63
+
64
+ expect(result).toBe('fallback');
65
+ });
66
+
67
+ it('should call original method when authenticated', async () => {
68
+ const service = new TestService(true);
69
+ const originalMethod = service.securedMethod;
70
+ const decorated = RequireAuth()(originalMethod, {
71
+ kind: 'method',
72
+ name: 'securedMethod',
73
+ access: { has: () => true, get: () => originalMethod },
74
+ static: false,
75
+ private: false,
76
+ addInitializer: () => {},
77
+ metadata: {
78
+ get: () => undefined,
79
+ set: () => {},
80
+ },
81
+ });
82
+ const wrappedMethod = typeof decorated === 'function' ? decorated : decorated?.value;
83
+ service.securedMethod = wrappedMethod.bind(service);
84
+ const result = await service.securedMethod();
85
+
86
+ expect(result).toBe('should not reach');
87
+ });
88
+ });
@@ -0,0 +1,159 @@
1
+ import { expectTypeOf } from "expect-type";
2
+
3
+ import {
4
+ Criteria,
5
+ FilterCriterion,
6
+ Order,
7
+ Operator,
8
+ Direction,
9
+ PaginationNotValid,
10
+ OffsetNotValid,
11
+ LimitNotValid,
12
+ } from "../../src/criteria";
13
+
14
+ describe('Criteria', () => {
15
+ describe('filters', () => {
16
+ it('should be defined', () => {
17
+ expectTypeOf<Criteria>().toHaveProperty("filters");
18
+ });
19
+
20
+ it('should be of type Record<string, FilterCriterion>', () => {
21
+ expectTypeOf<Criteria["filters"]>().toEqualTypeOf<Record<string, FilterCriterion>>();
22
+ });
23
+
24
+ it('should be empty', () => {
25
+ const criteria = new Criteria();
26
+ const filters = criteria.filters;
27
+ const filterKeys = Object.keys(filters);
28
+
29
+ expect(filterKeys).toHaveLength(0);
30
+ });
31
+ });
32
+
33
+ describe('orders', () => {
34
+ it('should be defined', () => {
35
+ expectTypeOf<Criteria>().toHaveProperty("orders");
36
+ });
37
+
38
+ it('should be of type Array<Order>', () => {
39
+ expectTypeOf<Criteria["orders"]>().toEqualTypeOf<Array<Order>>();
40
+ });
41
+
42
+ it('should be empty', () => {
43
+ const criteria = new Criteria();
44
+ const orders = criteria.orders;
45
+
46
+ expect(orders).toHaveLength(0);
47
+ });
48
+ });
49
+
50
+ it('should have a "limit" property of type number or undefined', () => {
51
+ expectTypeOf<Criteria>().toHaveProperty("limit");
52
+ expectTypeOf<Criteria["limit"]>().toEqualTypeOf<number | undefined>();
53
+ });
54
+
55
+ it('should have a "offset" property of type number or undefined', () => {
56
+ expectTypeOf<Criteria>().toHaveProperty("offset");
57
+ expectTypeOf<Criteria["offset"]>().toEqualTypeOf<number | undefined>();
58
+ });
59
+
60
+ describe('#where', () => {
61
+ it('should receive field, operator and value', () => {
62
+ expectTypeOf<Criteria["where"]>().parameters.toEqualTypeOf<[string, Operator, any]>();
63
+ });
64
+
65
+ it('should return the same instance', () => {
66
+ const criteria = new Criteria();
67
+ const result = criteria.where('field', 'EQUAL', 'value');
68
+
69
+ expect(result).toEqual(criteria);
70
+ });
71
+
72
+ it('should add the filter to the "filters" property', () => {
73
+ const criteria = new Criteria();
74
+ const result = criteria.where('field', 'EQUAL', 'value');
75
+ const filters = result.filters;
76
+ const expectedFilters = { field: { value: "value", operator: "EQUAL" } };
77
+
78
+ expect(filters["field"]).toBeDefined();
79
+ expect(filters).toEqual(expectedFilters);
80
+ });
81
+ });
82
+
83
+ describe('#orderBy', () => {
84
+ it('should receive field and direction', () => {
85
+ expectTypeOf<Criteria["orderBy"]>().parameters.toEqualTypeOf<[string, Direction?]>();
86
+ });
87
+
88
+ it('should return the same instance', () => {
89
+ const criteria = new Criteria();
90
+ const result = criteria.orderBy('field', 'DESC');
91
+
92
+ expect(result).toEqual(criteria);
93
+ });
94
+
95
+ it('should add the order to the "orders" property', () => {
96
+ const criteria = new Criteria();
97
+ const result = criteria.orderBy("field", "DESC");
98
+ const orders = result.orders;
99
+ const expectedOrders = [{ field: "field", direction: "DESC" }];
100
+
101
+ expect(orders).toHaveLength(1);
102
+ expect(orders).toEqual(expectedOrders);
103
+ });
104
+ });
105
+
106
+ describe('#limitResults', () => {
107
+ it('should receive a number', () => {
108
+ expectTypeOf<Criteria["limitResults"]>().parameters.toEqualTypeOf<[number]>();
109
+ });
110
+
111
+ it('should return the same instance', () => {
112
+ const criteria = new Criteria();
113
+ const result = criteria.limitResults(1);
114
+
115
+ expect(result).toEqual(criteria);
116
+ });
117
+
118
+ it('should assign the value to the "limit" property', () => {
119
+ const criteria = new Criteria();
120
+ const limit = 1;
121
+ const result = criteria.limitResults(limit);
122
+
123
+ expect(result.limit).toEqual(limit);
124
+ });
125
+
126
+ it('should throw an LimitNotValid exception if value is less than 1', () => {
127
+ const criteria = new Criteria();
128
+
129
+ expect(() => criteria.limitResults(0)).toThrow(LimitNotValid);
130
+ });
131
+ });
132
+
133
+ describe('#offsetResults', () => {
134
+ it('should receive a number', () => {
135
+ expectTypeOf<Criteria["offsetResults"]>().parameters.toEqualTypeOf<[number]>();
136
+ });
137
+
138
+ it('should return the same instance', () => {
139
+ const criteria = new Criteria();
140
+ const result = criteria.offsetResults(1);
141
+
142
+ expect(result).toEqual(criteria);
143
+ });
144
+
145
+ it('should assign the value to the "offset" property', () => {
146
+ const criteria = new Criteria();
147
+ const offset = 1;
148
+ const result = criteria.offsetResults(offset);
149
+
150
+ expect(result.offset).toEqual(offset);
151
+ });
152
+
153
+ it('should throw an OffsetNotValid exception if value is less than 1', () => {
154
+ const criteria = new Criteria();
155
+
156
+ expect(() => criteria.offsetResults(0)).toThrow(OffsetNotValid);
157
+ });
158
+ });
159
+ });
@@ -0,0 +1,11 @@
1
+ import { expectTypeOf } from "expect-type";
2
+
3
+ import { Direction } from "../../src/criteria";
4
+
5
+ type ExpectedDirection = "ASC" | "DESC";
6
+
7
+ describe('Direction', () => {
8
+ it('should match the expected type', () => {
9
+ expectTypeOf<Direction>().toEqualTypeOf<ExpectedDirection>();
10
+ });
11
+ });
@@ -0,0 +1,15 @@
1
+ import { expectTypeOf } from "expect-type";
2
+
3
+ import { FilterCriterion, Operator } from "../../src/criteria";
4
+
5
+ type ExpectedFilterCriterion = {
6
+ value: any;
7
+ operator: Operator;
8
+ }
9
+
10
+ describe('FilterCriterion', () => {
11
+ it('should match the expected type', () => {
12
+ expectTypeOf<FilterCriterion>().toEqualTypeOf<ExpectedFilterCriterion>();
13
+ });
14
+ });
15
+
@@ -0,0 +1,22 @@
1
+ import { expectTypeOf } from "expect-type";
2
+
3
+ import { Operator } from "../../src/criteria";
4
+
5
+ type ExpectedOperator =
6
+ | "EQUAL"
7
+ | "GREATER_THAN"
8
+ | "LESS_THAN"
9
+ | "GREATER_THAN_OR_EQUAL"
10
+ | "LESS_THAN_OR_EQUAL"
11
+ | "NOT_EQUAL"
12
+ | "IN"
13
+ | "NOT_IN"
14
+ | "LIKE"
15
+ | "BETWEEN"
16
+ | "GEO_RADIUS";
17
+
18
+ describe('Operator', () => {
19
+ it('should match the expected type', () => {
20
+ expectTypeOf<Operator>().toEqualTypeOf<ExpectedOperator>();
21
+ });
22
+ });