@epsilon-asi/foundation 0.0.1-testing → 0.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 (60) hide show
  1. package/build/decorators/environment/index.d.ts +164 -0
  2. package/build/decorators/environment/index.js +223 -0
  3. package/build/decorators/factory/examples/authorization.d.ts +48 -0
  4. package/build/decorators/factory/examples/authorization.js +113 -0
  5. package/build/decorators/factory/examples/runExamples.js +4 -0
  6. package/build/decorators/factory/examples/validation.d.ts +49 -0
  7. package/build/decorators/factory/examples/validation.js +109 -0
  8. package/build/decorators/factory/type-tests/decoratorRegistry.type-test.d.ts +1 -0
  9. package/build/decorators/factory/type-tests/decoratorRegistry.type-test.js +458 -0
  10. package/build/decorators/requests/mapping.d.ts +234 -0
  11. package/build/decorators/requests/mapping.js +460 -0
  12. package/build/decorators/validation/ValidatableClass.d.ts +0 -0
  13. package/build/decorators/validation/ValidatableClass.js +1 -0
  14. package/build/decorators/validation/validatable.d.ts +2 -0
  15. package/build/decorators/validation/validatable.js +8 -0
  16. package/build/index.d.ts +2 -5
  17. package/build/index.js +16 -10
  18. package/package.json +19 -3
  19. package/.npm/_logs/2026-05-02T22_04_53_277Z-debug-0.log +0 -22
  20. package/.npm/_logs/2026-05-02T22_04_53_601Z-debug-0.log +0 -18
  21. package/.npm/_logs/2026-05-02T22_04_58_522Z-debug-0.log +0 -18
  22. package/.npm/_logs/2026-05-02T22_05_00_812Z-debug-0.log +0 -19
  23. package/.npm/_logs/2026-05-02T22_05_00_961Z-debug-0.log +0 -14
  24. package/build/package.json +0 -49
  25. package/coverage/clover.xml +0 -220
  26. package/coverage/coverage-final.json +0 -6
  27. package/coverage/lcov-report/base.css +0 -224
  28. package/coverage/lcov-report/block-navigation.js +0 -87
  29. package/coverage/lcov-report/clone.ts.html +0 -352
  30. package/coverage/lcov-report/decoratorRegistry.ts.html +0 -1183
  31. package/coverage/lcov-report/favicon.png +0 -0
  32. package/coverage/lcov-report/index.html +0 -176
  33. package/coverage/lcov-report/index.ts.html +0 -145
  34. package/coverage/lcov-report/prettify.css +0 -1
  35. package/coverage/lcov-report/prettify.js +0 -2
  36. package/coverage/lcov-report/snapshot.ts.html +0 -370
  37. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  38. package/coverage/lcov-report/sorter.js +0 -210
  39. package/coverage/lcov-report/symbolMetadata.ts.html +0 -145
  40. package/coverage/lcov.info +0 -490
  41. package/src/decorators/factory/__tests__/cloneAndSnapshot.test.ts +0 -223
  42. package/src/decorators/factory/__tests__/decoratorRegistry.test.ts +0 -292
  43. package/src/decorators/factory/__tests__/decoratorRegistryEdges.test.ts +0 -262
  44. package/src/decorators/factory/clone.ts +0 -89
  45. package/src/decorators/factory/decoratorRegistry.ts +0 -366
  46. package/src/decorators/factory/examples/accessors.ts +0 -38
  47. package/src/decorators/factory/examples/crud.ts +0 -82
  48. package/src/decorators/factory/examples/orm.ts +0 -83
  49. package/src/decorators/factory/examples/routing.ts +0 -70
  50. package/src/decorators/factory/examples/runExamples.ts +0 -22
  51. package/src/decorators/factory/examples/serialization.ts +0 -47
  52. package/src/decorators/factory/index.ts +0 -20
  53. package/src/decorators/factory/snapshot.ts +0 -95
  54. package/src/decorators/factory/symbolMetadata.ts +0 -20
  55. package/src/decorators/factory/type-tests/decoratorRegistry.type-test.ts +0 -302
  56. package/src/decorators/factory/types.ts +0 -259
  57. package/src/decorators/validation/validatable.ts +0 -63
  58. package/src/index.ts +0 -9
  59. package/tsconfig.json +0 -19
  60. package/tsconfig.type-tests.json +0 -8
@@ -1,262 +0,0 @@
1
- import { createDecoratorRegistry } from '../index';
2
- import { ensureSymbolMetadata, symbolMetadata } from '../symbolMetadata';
3
-
4
- type EdgeSchema = {
5
- class: {
6
- entity: { metadata: { table: string } };
7
- tag: { metadata: undefined };
8
- };
9
- property: {
10
- column: {
11
- metadata: { type: 'uuid' | 'varchar'; primary?: boolean };
12
- target: string;
13
- };
14
- serializable: {
15
- metadata: undefined;
16
- target: unknown;
17
- };
18
- };
19
- method: {
20
- route: {
21
- metadata: { method: 'GET' | 'POST'; path: string };
22
- target: (...args: any[]) => Promise<unknown>;
23
- };
24
- };
25
- accessor: {
26
- cached: {
27
- metadata: { ttlMs?: number };
28
- target: string;
29
- };
30
- };
31
- };
32
-
33
- type EdgeContract = {
34
- id: string;
35
- email: string;
36
- displayName: string;
37
- get(id: string): Promise<{ id: string }>;
38
- };
39
-
40
- function fakeMetadataContext(
41
- kind: 'class' | 'field' | 'method' | 'accessor' | 'getter' | 'setter',
42
- overrides: Partial<
43
- ClassDecoratorContext | ClassFieldDecoratorContext | ClassMethodDecoratorContext | ClassAccessorDecoratorContext
44
- > = {},
45
- ) {
46
- return {
47
- kind,
48
- name: kind === 'class' ? 'FakeClass' : 'member',
49
- static: false,
50
- private: false,
51
- metadata: {},
52
- addInitializer: jest.fn(),
53
- access: {},
54
- ...overrides,
55
- } as any;
56
- }
57
-
58
- describe('modern decorator registry edge coverage', () => {
59
- it('uses default options and returns empty snapshots for objects without constructors', () => {
60
- const registry = createDecoratorRegistry<EdgeSchema>();
61
- const targetWithoutConstructor = Object.create(null);
62
-
63
- expect(registry.namespace).toBe('default');
64
- expect(registry.allowPrivateMembers).toBe(false);
65
- expect(registry.read(targetWithoutConstructor).all).toHaveLength(0);
66
- });
67
-
68
- it('supports unscoped class decorators and no-metadata class decorators', () => {
69
- const registry = createDecoratorRegistry<EdgeSchema>({ namespace: 'unscoped' });
70
- const Entity = registry.class('entity');
71
- const Tag = registry.class('tag') as unknown as () => ClassDecorator;
72
-
73
- @Entity<EdgeContract>({ table: 'users' })
74
- @Tag()
75
- class UserController implements EdgeContract {
76
- id = 'user-1';
77
- email = 'ray@example.com';
78
- displayName = 'Ray';
79
-
80
- async get(id: string): Promise<{ id: string }> {
81
- return { id };
82
- }
83
- }
84
-
85
- const snapshot = registry.read(UserController);
86
- expect(snapshot.classRecords()).toHaveLength(2);
87
- expect(snapshot.firstClass('entity')?.metadata).toEqual({ table: 'users' });
88
- expect(snapshot.firstClass('tag')?.metadata).toBeUndefined();
89
- expect(snapshot.classes.map((record) => record.id)).toEqual([
90
- 'unscoped:class:tag:0',
91
- 'unscoped:class:entity:1',
92
- ]);
93
- });
94
-
95
- it('supports direct and .on keyed forms for property, method, and accessor decorators', () => {
96
- const registry = createDecoratorRegistry<EdgeSchema>({ namespace: 'keyed-forms' });
97
- const scoped = registry.for<EdgeContract>();
98
- const Column = scoped.property('column');
99
- const Serializable = scoped.property('serializable');
100
- const Route = scoped.method('route');
101
-
102
- class UserController implements EdgeContract {
103
- @Column('id', { type: 'uuid', primary: true })
104
- id = 'user-1';
105
-
106
- @Serializable.on('email')
107
- @Column.on('email', { type: 'varchar' })
108
- email = 'ray@example.com';
109
-
110
- displayName = 'Ray';
111
-
112
- @Route.on('get', { method: 'GET', path: '/users/:id' })
113
- async get(id: string): Promise<{ id: string }> {
114
- return { id };
115
- }
116
- }
117
-
118
- const snapshot = registry.read(UserController);
119
- expect(snapshot.propertyMap('column').get('id')?.[0].metadata).toEqual({
120
- type: 'uuid',
121
- primary: true,
122
- });
123
- expect(snapshot.propertyMap('serializable').get('email')?.[0].metadata).toBeUndefined();
124
- expect(snapshot.methodMap('route').get('get')?.[0].metadata).toEqual({
125
- method: 'GET',
126
- path: '/users/:id',
127
- });
128
- });
129
-
130
- it('records getter and setter decorators as accessor metadata', () => {
131
- const registry = createDecoratorRegistry<EdgeSchema>({ namespace: 'getter-setter' });
132
- const Cached = registry.for<EdgeContract>().accessor('cached');
133
-
134
- class UserView {
135
- private name = 'Ray';
136
-
137
- @Cached({ ttlMs: 10 })
138
- get displayName(): string {
139
- return this.name;
140
- }
141
-
142
- @Cached({ ttlMs: 20 })
143
- set displayName(value: string) {
144
- this.name = value;
145
- }
146
- }
147
-
148
- const records = registry.read(UserView).accessorMap('cached').get('displayName');
149
- expect(records).toHaveLength(2);
150
- expect(records?.map((record) => record.metadata)).toEqual([{ ttlMs: 10 }, { ttlMs: 20 }]);
151
- });
152
-
153
- it('throws for invalid direct keys and invalid .on arguments', () => {
154
- const registry = createDecoratorRegistry<EdgeSchema>({ namespace: 'bad-keys' });
155
- const Column = registry.for<EdgeContract>().property('column') as unknown as {
156
- (...args: unknown[]): unknown;
157
- on(...args: unknown[]): unknown;
158
- };
159
-
160
- expect(() => Column(123, { type: 'varchar' })).toThrow(/expected a string or symbol/);
161
- expect(() => Column.on()).toThrow(/requires a member key/);
162
- expect(() => Column.on({ not: 'a-key' }, { type: 'varchar' })).toThrow(
163
- /expected a string or symbol/,
164
- );
165
- });
166
-
167
- it('throws when decorators are manually applied to the wrong context kind', () => {
168
- const registry = createDecoratorRegistry<EdgeSchema>({ namespace: 'wrong-kind' });
169
- const scoped = registry.for<EdgeContract>();
170
- const Entity = scoped.class('entity');
171
- const Column = scoped.property('column');
172
- const Route = scoped.method('route');
173
- const Cached = scoped.accessor('cached');
174
-
175
- expect(() =>
176
- (Entity({ table: 'users' }) as any)(class Fake {}, fakeMetadataContext('method')),
177
- ).toThrow(/can only be used on class targets/);
178
-
179
- expect(() =>
180
- (Column({ type: 'varchar' }) as any)(undefined, fakeMetadataContext('method')),
181
- ).toThrow(/can only be used on property targets/);
182
-
183
- expect(() =>
184
- (Route({ method: 'GET', path: '/x' }) as any)(async () => undefined, fakeMetadataContext('field')),
185
- ).toThrow(/can only be used on method targets/);
186
-
187
- expect(() =>
188
- (Cached({ ttlMs: 1 }) as any)(undefined, fakeMetadataContext('field')),
189
- ).toThrow(/can only be used on accessor targets/);
190
- });
191
-
192
- it('throws when a keyed decorator is applied to a different member at runtime', () => {
193
- const registry = createDecoratorRegistry<EdgeSchema>({ namespace: 'runtime-key-mismatch' });
194
- const Column = registry.for<EdgeContract>().property('column') as unknown as (...args: unknown[]) => any;
195
- const decorator = Column('email', { type: 'varchar' });
196
-
197
- expect(() =>
198
- decorator(undefined, fakeMetadataContext('field', { name: 'id' })),
199
- ).toThrow(/keyed for/);
200
- });
201
-
202
- it('allows manually decorated private members when configured', () => {
203
- const registry = createDecoratorRegistry<EdgeSchema>({
204
- namespace: 'manual-private',
205
- allowPrivateMembers: true,
206
- });
207
- const Column = registry.for<EdgeContract>().property('column');
208
- const metadataObject = {};
209
- const decorator = Column({ type: 'varchar' });
210
-
211
- (decorator as any)(undefined, fakeMetadataContext('field', {
212
- name: '#secret',
213
- private: true,
214
- metadata: metadataObject,
215
- }));
216
-
217
- const Manual = class {};
218
- Object.defineProperty(Manual, symbolMetadata, {
219
- configurable: true,
220
- value: metadataObject,
221
- });
222
-
223
- const record = registry.read(Manual).propertyRecords('column')[0];
224
- expect(record.private).toBe(true);
225
- expect(record.memberName).toBe('#secret');
226
- });
227
-
228
- it('accepts manually applied getter and setter contexts for accessor decorators', () => {
229
- const registry = createDecoratorRegistry<EdgeSchema>({ namespace: 'manual-accessor-kinds' });
230
- const Cached = registry.for<EdgeContract>().accessor('cached');
231
- const metadataObject = {};
232
-
233
- (Cached({ ttlMs: 1 }) as any)(() => 'Ray', fakeMetadataContext('getter', {
234
- name: 'displayName',
235
- metadata: metadataObject,
236
- }));
237
- (Cached({ ttlMs: 2 }) as any)((_value: string) => undefined, fakeMetadataContext('setter', {
238
- name: 'displayName',
239
- metadata: metadataObject,
240
- }));
241
-
242
- const Manual = class {};
243
- Object.defineProperty(Manual, symbolMetadata, {
244
- configurable: true,
245
- value: metadataObject,
246
- });
247
-
248
- expect(registry.read(Manual).accessorRecords('cached').map((record) => record.metadata)).toEqual([
249
- { ttlMs: 1 },
250
- { ttlMs: 2 },
251
- ]);
252
- });
253
-
254
- it('reuses Symbol.metadata once the polyfill has been installed', () => {
255
- const first = ensureSymbolMetadata();
256
- const second = ensureSymbolMetadata();
257
-
258
- expect(first).toBe(second);
259
- expect(first).toBe(symbolMetadata);
260
- expect((Symbol as SymbolConstructor & { metadata?: symbol }).metadata).toBe(symbolMetadata);
261
- });
262
- });
@@ -1,89 +0,0 @@
1
- function isObjectLike(value: unknown): value is object {
2
- return (typeof value === 'object' && value !== null) || typeof value === 'function';
3
- }
4
-
5
- export function cloneDeep<T>(value: T, seen = new WeakMap<object, unknown>()): T {
6
- if (!isObjectLike(value)) return value;
7
-
8
- if (typeof value === 'function') return value;
9
-
10
- const existing = seen.get(value);
11
- if (existing !== undefined) return existing as T;
12
-
13
- if (value instanceof Date) {
14
- return new Date(value.getTime()) as T;
15
- }
16
-
17
- if (value instanceof RegExp) {
18
- return new RegExp(value.source, value.flags) as T;
19
- }
20
-
21
- if (value instanceof Map) {
22
- const cloned = new Map<unknown, unknown>();
23
- seen.set(value, cloned);
24
- for (const [key, mapValue] of value.entries()) {
25
- cloned.set(cloneDeep(key, seen), cloneDeep(mapValue, seen));
26
- }
27
- return cloned as T;
28
- }
29
-
30
- if (value instanceof Set) {
31
- const cloned = new Set<unknown>();
32
- seen.set(value, cloned);
33
- for (const setValue of value.values()) {
34
- cloned.add(cloneDeep(setValue, seen));
35
- }
36
- return cloned as T;
37
- }
38
-
39
- if (Array.isArray(value)) {
40
- const cloned: unknown[] = [];
41
- seen.set(value, cloned);
42
- for (const item of value) cloned.push(cloneDeep(item, seen));
43
- return cloned as T;
44
- }
45
-
46
- const cloned = Object.create(Object.getPrototypeOf(value)) as Record<PropertyKey, unknown>;
47
- seen.set(value, cloned);
48
-
49
- for (const key of Reflect.ownKeys(value)) {
50
- const descriptor = Object.getOwnPropertyDescriptor(value, key);
51
- if (descriptor === undefined) continue;
52
-
53
- if ('value' in descriptor) {
54
- descriptor.value = cloneDeep(descriptor.value, seen);
55
- }
56
-
57
- Object.defineProperty(cloned, key, descriptor);
58
- }
59
-
60
- return cloned as T;
61
- }
62
-
63
- export function deepFreeze<T>(value: T, seen = new WeakSet<object>()): T {
64
- if (!isObjectLike(value)) return value;
65
- if (seen.has(value)) return value;
66
-
67
- seen.add(value);
68
-
69
- if (value instanceof Map) {
70
- for (const [key, mapValue] of value.entries()) {
71
- deepFreeze(key, seen);
72
- deepFreeze(mapValue, seen);
73
- }
74
- } else if (value instanceof Set) {
75
- for (const setValue of value.values()) {
76
- deepFreeze(setValue, seen);
77
- }
78
- } else {
79
- for (const key of Reflect.ownKeys(value)) {
80
- deepFreeze((value as Record<PropertyKey, unknown>)[key], seen);
81
- }
82
- }
83
-
84
- return Object.freeze(value);
85
- }
86
-
87
- export function immutableClone<T>(value: T): T {
88
- return deepFreeze(cloneDeep(value));
89
- }