@emkodev/emkore 1.0.3

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 (48) hide show
  1. package/CHANGELOG.md +269 -0
  2. package/DEVELOPER_GUIDE.md +227 -0
  3. package/LICENSE +21 -0
  4. package/README.md +126 -0
  5. package/bun.lock +22 -0
  6. package/example/README.md +200 -0
  7. package/example/create-user.interactor.ts +88 -0
  8. package/example/dto/user.dto.ts +34 -0
  9. package/example/entity/user.entity.ts +54 -0
  10. package/example/index.ts +18 -0
  11. package/example/interface/create-user.usecase.ts +93 -0
  12. package/example/interface/user.repository.ts +23 -0
  13. package/mod.ts +1 -0
  14. package/package.json +32 -0
  15. package/src/common/abstract.actor.ts +59 -0
  16. package/src/common/abstract.entity.ts +59 -0
  17. package/src/common/abstract.interceptor.ts +17 -0
  18. package/src/common/abstract.repository.ts +162 -0
  19. package/src/common/abstract.usecase.ts +113 -0
  20. package/src/common/config/config-registry.ts +190 -0
  21. package/src/common/config/config-section.ts +106 -0
  22. package/src/common/exception/authorization-exception.ts +28 -0
  23. package/src/common/exception/repository-exception.ts +46 -0
  24. package/src/common/interceptor/audit-log.interceptor.ts +181 -0
  25. package/src/common/interceptor/authorization.interceptor.ts +252 -0
  26. package/src/common/interceptor/performance.interceptor.ts +101 -0
  27. package/src/common/llm/api-definition.type.ts +185 -0
  28. package/src/common/pattern/unit-of-work.ts +78 -0
  29. package/src/common/platform/env.ts +38 -0
  30. package/src/common/registry/usecase-registry.ts +80 -0
  31. package/src/common/type/interceptor-context.type.ts +25 -0
  32. package/src/common/type/json-schema.type.ts +80 -0
  33. package/src/common/type/json.type.ts +5 -0
  34. package/src/common/type/lowercase.type.ts +48 -0
  35. package/src/common/type/metadata.type.ts +5 -0
  36. package/src/common/type/money.class.ts +384 -0
  37. package/src/common/type/permission.type.ts +43 -0
  38. package/src/common/validation/validation-result.ts +52 -0
  39. package/src/common/validation/validators.ts +441 -0
  40. package/src/index.ts +95 -0
  41. package/test/unit/abstract-actor.test.ts +608 -0
  42. package/test/unit/actor.test.ts +89 -0
  43. package/test/unit/api-definition.test.ts +628 -0
  44. package/test/unit/authorization.test.ts +101 -0
  45. package/test/unit/entity.test.ts +95 -0
  46. package/test/unit/money.test.ts +480 -0
  47. package/test/unit/validation.test.ts +138 -0
  48. package/tsconfig.json +18 -0
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Unit of Work Pattern
3
+ *
4
+ * Maintains a list of objects affected by a business transaction and coordinates
5
+ * writing out changes and resolving concurrency problems.
6
+ *
7
+ * This interface provides the minimal contract for transaction boundaries.
8
+ * Implementations can extend this with additional tracking capabilities:
9
+ *
10
+ * - Change tracking: registerNew(), registerDirty(), registerDeleted()
11
+ * - Identity map: registerClean() to prevent loading duplicates
12
+ * - Batching: Accumulate changes and apply them in a single operation
13
+ * - Optimistic locking: Track versions for concurrent modifications
14
+ *
15
+ * Examples of implementations:
16
+ * - SQL: Use database transactions (BEGIN/COMMIT/ROLLBACK)
17
+ * - REST APIs: Batch requests or implement compensating transactions
18
+ * - Document DBs: Use sessions or batch operations
19
+ * - Event Sourcing: Collect events and append to stream on commit
20
+ * - In-Memory: Track changes in collections, restore on rollback
21
+ *
22
+ * @see https://martinfowler.com/eaaCatalog/unitOfWork.html
23
+ */
24
+ export interface UnitOfWork {
25
+ readonly tenantId: string;
26
+
27
+ /**
28
+ * Commits all changes made within this unit of work.
29
+ * Should be atomic - either all changes succeed or none do.
30
+ */
31
+ commit(): Promise<void>;
32
+
33
+ /**
34
+ * Discards all changes made within this unit of work.
35
+ * Should restore state to before the unit of work began.
36
+ */
37
+ rollback(): Promise<void>;
38
+
39
+ // Optional methods that implementations might want to add:
40
+
41
+ /**
42
+ * Track a newly created entity for insertion
43
+ */
44
+ registerNew?(entity: unknown): void;
45
+
46
+ /**
47
+ * Track a modified entity for update
48
+ */
49
+ registerDirty?(entity: unknown): void;
50
+
51
+ /**
52
+ * Track an entity for deletion
53
+ */
54
+ registerDeleted?(entity: unknown): void;
55
+
56
+ /**
57
+ * Track a clean (unmodified) entity to prevent duplicate loads
58
+ */
59
+ registerClean?(entity: unknown): void;
60
+
61
+ /**
62
+ * Check if the unit of work has any pending changes
63
+ */
64
+ hasChanges?(): boolean;
65
+
66
+ /**
67
+ * Get a repository that participates in this unit of work
68
+ * @param type - Repository class constructor or identifier
69
+ */
70
+ getRepository?<T = unknown>(type: unknown): T;
71
+ }
72
+
73
+ export interface UnitOfWorkFactory {
74
+ transaction<T>(
75
+ tenantId: string,
76
+ body: (uow: UnitOfWork) => Promise<T>,
77
+ ): Promise<T>;
78
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Cross-platform environment variable access
3
+ * Works in Deno, Node.js, and browsers (with fallbacks)
4
+ */
5
+
6
+ // Type-safe access to global objects for cross-platform compatibility
7
+ declare const Deno: { env: { get(key: string): string | undefined } } | undefined;
8
+ declare const process: { env: Record<string, string | undefined> } | undefined;
9
+
10
+ /**
11
+ * Get environment variable value
12
+ * @param key Environment variable name
13
+ * @returns Value or undefined if not found
14
+ */
15
+ export function getEnv(key: string): string | undefined {
16
+ // Deno
17
+ if (typeof Deno !== "undefined" && Deno.env?.get) {
18
+ return Deno.env.get(key);
19
+ }
20
+
21
+ // Node.js
22
+ if (typeof process !== "undefined" && process?.env) {
23
+ return process.env[key];
24
+ }
25
+
26
+ // Browser/Worker fallback - no environment variables
27
+ return undefined;
28
+ }
29
+
30
+ /**
31
+ * Get environment variable with default value
32
+ * @param key Environment variable name
33
+ * @param defaultValue Default value if not found
34
+ * @returns Value or default
35
+ */
36
+ export function getEnvOrDefault(key: string, defaultValue: string): string {
37
+ return getEnv(key) ?? defaultValue;
38
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Use Case Registry System
3
+ *
4
+ * Provides types and utilities for auto-discovery and registration
5
+ * of use cases in dynamic routing systems.
6
+ */
7
+
8
+ import type { UnitOfWorkFactory } from "../pattern/unit-of-work.ts";
9
+ import type { Usecase } from "../abstract.usecase.ts";
10
+ import type { Permission } from "../type/permission.type.ts";
11
+ import type { ApiDefinition } from "../llm/api-definition.type.ts";
12
+
13
+ export interface UseCaseRegistryEntry<
14
+ TInput = unknown,
15
+ TOutput = unknown,
16
+ TUowFactory extends UnitOfWorkFactory = UnitOfWorkFactory,
17
+ > {
18
+ action: string;
19
+ resource: string;
20
+ createInteractor: () => Promise<Usecase<TInput, TOutput>>;
21
+ createUowFactory: () => Promise<TUowFactory>;
22
+ requiredPermissions: Permission[];
23
+ apiDefinition?: ApiDefinition;
24
+ }
25
+
26
+ export class UseCaseRegistryHelpers {
27
+ /**
28
+ * Get the name for a registry entry
29
+ */
30
+ static getName(entry: UseCaseRegistryEntry): string {
31
+ return `${entry.action}_${entry.resource}`;
32
+ }
33
+
34
+ /**
35
+ * Get the HTTP method for a registry entry
36
+ */
37
+ static getHttpMethod(
38
+ entry: UseCaseRegistryEntry,
39
+ ): "GET" | "POST" | "PUT" | "PATCH" | "DELETE" {
40
+ switch (entry.action) {
41
+ case "create":
42
+ return "POST";
43
+ case "retrieve":
44
+ case "list":
45
+ case "count":
46
+ return "GET";
47
+ case "update":
48
+ return "PATCH";
49
+ case "delete":
50
+ case "bulkDelete":
51
+ return "DELETE";
52
+ default:
53
+ return "POST";
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Get the API path for a registry entry
59
+ */
60
+ static getPath(entry: UseCaseRegistryEntry): string {
61
+ const basePath = `/api/v1/${entry.resource}`;
62
+
63
+ switch (entry.action) {
64
+ case "create":
65
+ return basePath;
66
+ case "retrieve":
67
+ case "update":
68
+ case "delete":
69
+ return `${basePath}/:id`;
70
+ case "list":
71
+ return basePath;
72
+ case "count":
73
+ return `${basePath}/count`;
74
+ case "bulkDelete":
75
+ return `${basePath}/bulk`;
76
+ default:
77
+ return `${basePath}/${entry.action}`;
78
+ }
79
+ }
80
+ }
@@ -0,0 +1,25 @@
1
+ import type { Actor } from "../abstract.actor.ts";
2
+ import type { UsecaseName } from "../abstract.usecase.ts";
3
+ import type { Permission } from "./permission.type.ts";
4
+ import type { Metadata } from "./metadata.type.ts";
5
+
6
+ export interface InterceptorContext {
7
+ readonly actor: Actor;
8
+ readonly usecaseName: UsecaseName;
9
+ readonly requiredPermissions: Permission[];
10
+ readonly metadata: Metadata;
11
+ }
12
+
13
+ export function createInterceptorContext(
14
+ actor: Actor,
15
+ usecaseName: UsecaseName,
16
+ requiredPermissions: Permission[],
17
+ metadata: Metadata = {},
18
+ ): InterceptorContext {
19
+ return {
20
+ actor,
21
+ usecaseName,
22
+ requiredPermissions,
23
+ metadata,
24
+ };
25
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * JSON Schema types for validation and API definitions.
3
+ * Based on JSON Schema Draft-07 specification.
4
+ */
5
+
6
+ export type JsonSchemaType =
7
+ | "string"
8
+ | "number"
9
+ | "boolean"
10
+ | "object"
11
+ | "array"
12
+ | "null"
13
+ | "integer";
14
+
15
+ export interface JsonSchema {
16
+ readonly type?: JsonSchemaType | JsonSchemaType[];
17
+ readonly properties?: Record<string, JsonSchema>;
18
+ readonly items?: JsonSchema;
19
+ readonly required?: string[];
20
+ readonly enum?: readonly unknown[];
21
+ readonly const?: unknown;
22
+ readonly minimum?: number;
23
+ readonly maximum?: number;
24
+ readonly minLength?: number;
25
+ readonly maxLength?: number;
26
+ readonly pattern?: string;
27
+ readonly format?: string;
28
+ readonly description?: string;
29
+ readonly default?: unknown;
30
+ readonly nullable?: boolean;
31
+ readonly anyOf?: JsonSchema[];
32
+ readonly allOf?: JsonSchema[];
33
+ readonly oneOf?: JsonSchema[];
34
+ readonly not?: JsonSchema;
35
+ readonly additionalProperties?: boolean | JsonSchema;
36
+ readonly minItems?: number;
37
+ readonly maxItems?: number;
38
+ readonly uniqueItems?: boolean;
39
+ readonly minProperties?: number;
40
+ readonly maxProperties?: number;
41
+ readonly multipleOf?: number;
42
+ readonly exclusiveMinimum?: boolean | number;
43
+ readonly exclusiveMaximum?: boolean | number;
44
+ }
45
+
46
+ export type JsonSchemaProperties = Record<string, JsonSchema>;
47
+
48
+ /**
49
+ * Mutable version of JsonSchema for building schemas dynamically.
50
+ */
51
+ export interface MutableJsonSchema {
52
+ type?: JsonSchemaType | JsonSchemaType[];
53
+ properties?: Record<string, MutableJsonSchema>;
54
+ items?: MutableJsonSchema | JsonSchema;
55
+ required?: string[] | boolean;
56
+ enum?: unknown[];
57
+ const?: unknown;
58
+ minimum?: number;
59
+ maximum?: number;
60
+ minLength?: number;
61
+ maxLength?: number;
62
+ pattern?: string;
63
+ format?: string;
64
+ description?: string;
65
+ default?: unknown;
66
+ nullable?: boolean;
67
+ anyOf?: JsonSchema[];
68
+ allOf?: JsonSchema[];
69
+ oneOf?: JsonSchema[];
70
+ not?: JsonSchema;
71
+ additionalProperties?: boolean | JsonSchema;
72
+ minItems?: number;
73
+ maxItems?: number;
74
+ uniqueItems?: boolean;
75
+ minProperties?: number;
76
+ maxProperties?: number;
77
+ multipleOf?: number;
78
+ exclusiveMinimum?: boolean | number;
79
+ exclusiveMaximum?: boolean | number;
80
+ }
@@ -0,0 +1,5 @@
1
+ export type JSONValue = string | number | boolean | null | JSON | JSONArray;
2
+ export interface JSON {
3
+ [key: string]: JSONValue;
4
+ }
5
+ export interface JSONArray extends Array<JSONValue> {}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Type-level utility to enforce lowercase string literals in const arrays.
3
+ *
4
+ * This prevents developers from accidentally using uppercase enum values,
5
+ * ensuring consistency with kebab-case naming conventions.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * // ✅ CORRECT - lowercase values
10
+ * const USER_STATUS = ["new", "requires-review"] as const satisfies LowercaseArray;
11
+ *
12
+ * // ❌ COMPILE ERROR - uppercase detected
13
+ * const USER_STATUS = ["NEW", "REQUIRES_REVIEW"] as const satisfies LowercaseArray;
14
+ *
15
+ * // ✅ CORRECT - with type extraction
16
+ * const USER_STATUS = ["new", "active", "archived"] as const satisfies LowercaseArray;
17
+ * type UserStatus = typeof USER_STATUS[number]; // "new" | "active" | "archived"
18
+ * ```
19
+ */
20
+
21
+ /**
22
+ * Ensures a string type contains only lowercase characters, numbers, and hyphens.
23
+ * Produces a compile-time error if uppercase letters are detected.
24
+ */
25
+ export type Lowercase<T extends string> = T extends
26
+ `${infer First}${infer Rest}`
27
+ ? First extends Uppercase<First>
28
+ ? First extends Lowercase<First> ? `${First}${Lowercase<Rest>}` // Numbers, hyphens, special chars
29
+ : never // Uppercase letter detected - ERROR
30
+ : `${First}${Lowercase<Rest>}` // Lowercase letter - OK
31
+ : T; // Empty string or base case
32
+
33
+ /**
34
+ * Enforces that all elements in a readonly array are lowercase strings.
35
+ * Use with `satisfies` keyword for compile-time validation.
36
+ */
37
+ export type LowercaseArray = readonly Lowercase<string>[];
38
+
39
+ /**
40
+ * Helper type to validate and extract union type from a lowercase const array.
41
+ *
42
+ * @example
43
+ * ```typescript
44
+ * const STATUSES = ["new", "in-progress", "done"] as const satisfies LowercaseArray;
45
+ * type Status = LowercaseArrayUnion<typeof STATUSES>; // "new" | "in-progress" | "done"
46
+ * ```
47
+ */
48
+ export type LowercaseArrayUnion<T extends LowercaseArray> = T[number];
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Metadata type for storing additional context information.
3
+ * Allows any JSON-serializable values including primitives and arrays.
4
+ */
5
+ export type Metadata = Record<string, unknown>;