mnemonica 1.0.6 → 1.0.8

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/src/index.ts CHANGED
@@ -14,7 +14,6 @@ import type {
14
14
  TypeClass,
15
15
  TypeAbsorber,
16
16
  MnemonicaModule,
17
- TypeConstructor,
18
17
  InstanceResult,
19
18
  Merge
20
19
  } from './types';
@@ -26,9 +25,37 @@ export const {
26
25
  } = TypesUtils;
27
26
 
28
27
  export type {
29
- IDEF, TypeConstructor, _Internal_TC_, Proto, ProtoFlat,
28
+ IDEF, TypeConstructor, TypeConstructorBase,
29
+ InstanceOfTypeRegistry, LiteralKeysOf, ParentPath, PathOfInstance,
30
+ AllParentPrefixes, ParentPathOfInstance,
31
+ _Internal_TC_, Proto, ProtoFlat,
30
32
  hooksOpts, hook, hooksTypes, TypesCollection
31
33
  } from './types';
34
+
35
+ /**
36
+ * Type-safe registry for mnemonica types.
37
+ *
38
+ * This interface starts empty. Application code or `@mnemonica/tactica`
39
+ * augments it through TypeScript module declaration merging:
40
+ *
41
+ * ```ts
42
+ * declare module 'mnemonica' {
43
+ * interface TypeRegistry {
44
+ * 'MyType': TypeConstructor<MyTypeInstance>;
45
+ * 'MyType.SubType': TypeConstructor<SubTypeInstance>;
46
+ * }
47
+ * }
48
+ * ```
49
+ *
50
+ * Once augmented, `lookup('MyType')` and `utils.parent(instance, 'MyType')`
51
+ * resolve keys against this registry. Unaugmented keys fall back to the broad
52
+ * `TypeClass | undefined` return type.
53
+ */
54
+ export interface TypeRegistry {
55
+ // Intentionally empty. Augment this interface via declaration merging
56
+ // (hand-written or generated by @mnemonica/tactica) to enable typed
57
+ // utilities like lookup() and parent().
58
+ }
32
59
  export {
33
60
  getProps, setProps
34
61
  } from './api/types/Props';
@@ -78,7 +105,12 @@ export const define = function <
78
105
  return defineResult;
79
106
  } as TypeAbsorber;
80
107
 
81
- export const lookup = function (
108
+ export function lookup<const K extends keyof TypeRegistry>(
109
+ this: unknown,
110
+ TypeNestedPath: K
111
+ ): TypeRegistry[K];
112
+ export function lookup(this: unknown, TypeNestedPath: string): TypeClass | undefined;
113
+ export function lookup(
82
114
  this: unknown,
83
115
  TypeNestedPath: string
84
116
  ): TypeClass | undefined {
@@ -86,58 +118,8 @@ export const lookup = function (
86
118
  // Type assertion needed because TypesCollectionProxy is a Proxy
87
119
  const lookupResult = (types as { lookup: (path: string) => TypeClass | undefined }).lookup(TypeNestedPath);
88
120
  return lookupResult;
89
- };
90
-
91
- /**
92
- * Default TypeRegistry interface - augment this in your project via
93
- * TypeScript declaration merging. The augmentation can be hand-written
94
- * in a .d.ts file (small projects) or generated by @mnemonica/tactica.
95
- *
96
- * Uses `TypeConstructor<never>` as the index value type to enforce
97
- * augmentation: without it, any key lookup resolves to a useless type
98
- * because `TypeConstructor<never>` is not assignable to concrete types.
99
- *
100
- * Example (in a hand-written .d.ts, or generated by tactica in
101
- * .tactica/registry.ts):
102
- * declare module 'mnemonica' {
103
- * interface TypeRegistry {
104
- * 'MyType': new (...args: unknown[]) => MyTypeInstance;
105
- * }
106
- * }
107
- *
108
- * See docs/typed-lookup.md for both paths side by side.
109
- */
110
- export interface TypeRegistry {
111
- [key: string]: TypeConstructor<never>;
112
121
  }
113
122
 
114
- /**
115
- * Type-safe lookup function. Requires a TypeRegistry augmentation —
116
- * hand-written in a .d.ts (small projects) or generated by
117
- * @mnemonica/tactica (auto-synced from your define() calls).
118
- *
119
- * At runtime this delegates to lookup(). The type safety is
120
- * compile-time only: TypeScript resolves the key against the augmented
121
- * TypeRegistry and returns the constructor's type signature.
122
- *
123
- * Usage (once TypeRegistry is augmented):
124
- * const SomeType = lookupTyped('SomeType');
125
- * const instance = new SomeType({ ... }); // Full type safety!
126
- *
127
- * See docs/typed-lookup.md for both augmentation paths side by side.
128
- */
129
- export const lookupTyped = function <
130
- const K extends keyof TypeRegistry
131
- >(
132
- this: unknown,
133
- TypeNestedPath: K
134
- ): TypeRegistry[K] {
135
- const types = checkThis(this) ? defaultTypes : this || defaultTypes;
136
- const lookupResult = (types as { lookup: (path: string) => unknown })
137
- .lookup(TypeNestedPath as string) as TypeRegistry[K];
138
- return lookupResult;
139
- };
140
-
141
123
 
142
124
  const $run = function <E extends object, T extends object, S extends Proto<E, T>>(
143
125
  entity: E,
@@ -1,6 +1,8 @@
1
1
  'use strict';
2
2
  /* eslint no-unused-vars: "off" */
3
3
 
4
+ import type { TypeRegistry } from '../index';
5
+
4
6
  // Props type for getProps/setProps
5
7
  export type PropsType = Record<string, unknown>;
6
8
 
@@ -78,6 +80,79 @@ export interface _Internal_TC_<ConstructorInstance extends object> {
78
80
  */
79
81
  export type TypeConstructor<ConstructorInstance extends object> = _Internal_TC_<ConstructorInstance>;
80
82
 
83
+ /**
84
+ * Minimal constructor shape used for the TypeRegistry index signature.
85
+ * It accepts any mnemonica-compatible constructor, but deliberately returns
86
+ * `object` so unaugmented registry lookups are not useful without an explicit
87
+ * per-key constructor type.
88
+ */
89
+ export interface TypeConstructorBase {
90
+ new (...args: unknown[]): object;
91
+ }
92
+
93
+ /**
94
+ * Instance type produced by a TypeRegistry constructor.
95
+ */
96
+ export type InstanceOfTypeRegistry<K extends keyof TypeRegistry> =
97
+ TypeRegistry[K] extends new (...args: unknown[]) => infer R ? R : never;
98
+
99
+
100
+ /**
101
+ * Extract only the literal string keys of a type, filtering out generic
102
+ * index signatures. This lets us iterate over augmented TypeRegistry keys
103
+ * without picking up the `[key: string]` signature.
104
+ */
105
+ export type LiteralKeysOf<T> = keyof T extends infer K
106
+ ? K extends string
107
+ ? string extends K ? never : K
108
+ : never
109
+ : never;
110
+
111
+ /**
112
+ * Given a dotted TypeRegistry key, extract the parent path.
113
+ * `ParentPath<'A.B.C'>` → `'A.B'`.
114
+ */
115
+ export type ParentPath<K extends string> =
116
+ K extends `${infer P}.${string}` ? P : never;
117
+
118
+ /**
119
+ * Given a dotted TypeRegistry key, return the union of all ancestor prefixes.
120
+ * `AllParentPrefixes<'A.B.C'>` → `'A' | 'A.B'`.
121
+ */
122
+ export type AllParentPrefixes<K extends string> =
123
+ K extends `${infer P}.${string}` ? P | AllParentPrefixes<P> : never;
124
+
125
+ /**
126
+ * Given a parent path, return the union of TypeRegistry keys that are direct
127
+ * or indirect children of that path.
128
+ */
129
+ export type ChildKeysOf<P extends string> = {
130
+ [K in keyof TypeRegistry]: K extends `${P}.${string}` ? K : never
131
+ }[keyof TypeRegistry];
132
+
133
+ /**
134
+ * Given an instance type, find the TypeRegistry key(s) whose constructor
135
+ * returns that instance type. This inverts the registry at the type level
136
+ * so utilities like `parent()` can derive an instance's path from its
137
+ * type alone.
138
+ */
139
+ export type PathOfInstance<T extends object> = {
140
+ [K in LiteralKeysOf<TypeRegistry>]: TypeRegistry[K] extends new (...args: unknown[]) => infer R
141
+ ? T extends R ? K : never
142
+ : never
143
+ }[LiteralKeysOf<TypeRegistry>];
144
+
145
+ /**
146
+ * Given an instance type, return the union of TypeRegistry paths that are
147
+ * valid parent lookups for that instance. Root types produce `never` because
148
+ * they have no parent path.
149
+ */
150
+ export type ParentPathOfInstance<T extends object> = {
151
+ [K in LiteralKeysOf<TypeRegistry>]: TypeRegistry[K] extends new (...args: unknown[]) => infer R
152
+ ? T extends R ? AllParentPrefixes<K> : never
153
+ : never
154
+ }[LiteralKeysOf<TypeRegistry>];
155
+
81
156
  // Hook types
82
157
  export type hooksTypes = 'preCreation' | 'postCreation' | 'creationError';
83
158
 
@@ -288,6 +363,9 @@ export interface MnemonicaInstance<T extends object = object> {
288
363
  pick<K extends keyof T>(...keys: (K | K[])[]): { [P in K]: T[P] } & {};
289
364
  pick(...keys: string[]): Record<string, unknown>;
290
365
  parent(): object | undefined;
366
+ parent<K extends ParentPathOfInstance<this> & string>(
367
+ constructorLookupPath: K
368
+ ): InstanceOfTypeRegistry<K> | undefined;
291
369
  parent(constructorLookupPath: string): object | undefined;
292
370
  readonly clone: this;
293
371
  fork(...forkArgs: unknown[]): this;
@@ -507,7 +585,12 @@ export interface UtilsCollection {
507
585
  ...args: unknown[]
508
586
  ): InstanceResult<Merge<B, A>>;
509
587
  parse<T extends object>(self: T): Parsed<T>;
510
- parent<T extends object>(instance: T, path?: string): object | undefined;
588
+ parent<T extends object>(instance: T): object | undefined;
589
+ parent<T extends object, K extends ParentPathOfInstance<T> & string>(
590
+ instance: T,
591
+ path: K
592
+ ): InstanceOfTypeRegistry<K> | undefined;
593
+ parent<T extends object>(instance: T, path: string): object | undefined;
511
594
  toJSON<T extends object>(instance: T): string;
512
595
  [key: string]: CallableFunction;
513
596
  }
@@ -516,7 +599,10 @@ export interface UtilsCollection {
516
599
  export interface MnemonicaModule {
517
600
  // Core functions
518
601
  define: TypeAbsorber;
519
- lookup: (TypeNestedPath: string) => TypeClass | undefined;
602
+ lookup: {
603
+ (TypeNestedPath: string): TypeClass | undefined;
604
+ <const K extends keyof TypeRegistry>(TypeNestedPath: K): TypeRegistry[K];
605
+ };
520
606
  apply: ApplyFunction;
521
607
  call: CallFunction;
522
608
  bind: BindFunction;
@@ -1,5 +1,10 @@
1
1
  'use strict';
2
2
 
3
+ import type {
4
+ InstanceOfTypeRegistry,
5
+ ParentPathOfInstance
6
+ } from '../types';
7
+
3
8
  import { ErrorsTypes } from '../descriptors/errors';
4
9
  const { WRONG_INSTANCE_INVOCATION } = ErrorsTypes;
5
10
 
@@ -10,7 +15,13 @@ import {
10
15
  // seek for firts parent instance
11
16
  // of instance prototype chain
12
17
  // with constructors of path
13
- export const parent = <T extends object>( instance: T, path?: string ): object | undefined => {
18
+ export function parent <T extends object> (instance: T): object | undefined;
19
+ export function parent <T extends object, K extends ParentPathOfInstance<T> & string> (
20
+ instance: T,
21
+ path: K
22
+ ): InstanceOfTypeRegistry<K> | undefined;
23
+ export function parent (instance: object, path: string): object | undefined;
24
+ export function parent <T extends object> (instance: T, path?: string): object | undefined {
14
25
 
15
26
  // at this situation this check is enough
16
27
  if ( instance !== Object( instance ) ) {
@@ -37,4 +48,4 @@ export const parent = <T extends object>( instance: T, path?: string ): object |
37
48
  p : parent( p as object, path );
38
49
  return result;
39
50
 
40
- };
51
+ }