mnemonica 1.0.1 → 1.0.7

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 (99) hide show
  1. package/.ai/AGENTS.md +1 -1
  2. package/.ai/ONBOARDING.md +3 -2
  3. package/.ai/PROTOTYPE-CHAIN.md +122 -0
  4. package/.ai/TACTICA-DEEP-DIVE.md +52 -49
  5. package/.ai/TACTICA-RULES.md +8 -8
  6. package/.ai/ask/AGENTS.md +15 -9
  7. package/.ai/rules-skill/contributing.md +1 -1
  8. package/.ai/rules-skill/define-patterns.md +8 -2
  9. package/.ai/rules-skill/ecosystem.md +4 -4
  10. package/.ai/rules-skill/instance-methods.md +48 -15
  11. package/.ai/rules-skill/lookup-typed.md +17 -19
  12. package/.ai/rules-skill/type-system.md +12 -2
  13. package/AGENTS.md +25 -15
  14. package/CONTRIBUTING.md +8 -2
  15. package/FOR_HUMANS.md +193 -218
  16. package/README.md +84 -47
  17. package/SKILL.md +4 -4
  18. package/build/api/errors/exceptionConstructor.js +20 -12
  19. package/build/api/errors/index.js +9 -5
  20. package/build/api/errors/throwModificationError.js +15 -9
  21. package/build/api/index.js +35 -2
  22. package/build/api/types/InstanceCreator.js +5 -2
  23. package/build/api/types/Mnemosyne.d.ts +6 -6
  24. package/build/api/types/Mnemosyne.js +43 -120
  25. package/build/api/types/Props.js +5 -3
  26. package/build/api/types/TypeProxy.js +13 -9
  27. package/build/api/types/compileNewModificatorFunctionBody.js +14 -8
  28. package/build/api/types/createInstanceModificator.js +1 -1
  29. package/build/api/types/index.js +56 -13
  30. package/build/api/utils/index.js +21 -12
  31. package/build/constants/index.js +11 -8
  32. package/build/descriptors/types/index.js +79 -26
  33. package/build/index.d.ts +9 -10
  34. package/build/index.js +65 -25
  35. package/build/types/index.d.ts +76 -32
  36. package/build/types/index.js +1 -1
  37. package/build/utils/clone.d.ts +1 -0
  38. package/build/utils/clone.js +11 -0
  39. package/build/utils/collectConstructors.js +5 -3
  40. package/build/utils/exception.d.ts +1 -0
  41. package/build/utils/exception.js +14 -0
  42. package/build/utils/extract.d.ts +2 -3
  43. package/build/utils/extract.js +3 -2
  44. package/build/utils/fork.d.ts +1 -0
  45. package/build/utils/fork.js +33 -0
  46. package/build/utils/index.d.ts +2 -3
  47. package/build/utils/index.js +21 -7
  48. package/build/utils/merge.d.ts +2 -1
  49. package/build/utils/merge.js +10 -6
  50. package/build/utils/parent.d.ts +4 -1
  51. package/build/utils/parent.js +7 -7
  52. package/build/utils/parse.d.ts +2 -12
  53. package/build/utils/parse.js +1 -1
  54. package/build/utils/pick.d.ts +4 -3
  55. package/build/utils/pick.js +4 -5
  56. package/build/utils/sibling.d.ts +1 -0
  57. package/build/utils/sibling.js +25 -0
  58. package/build/utils/toJSON.d.ts +1 -1
  59. package/build/utils/toJSON.js +3 -2
  60. package/docs/UTILS.md +184 -0
  61. package/docs/ai-learning-trajectory.md +9 -9
  62. package/docs/async-constructors.md +21 -1
  63. package/docs/empathy-in-ai.md +170 -0
  64. package/docs/hott-primer.md +47 -0
  65. package/docs/performance-vs-security.md +2 -2
  66. package/docs/prototype-chain.md +127 -0
  67. package/docs/purpose.md +42 -11
  68. package/docs/tactica-pattern.md +20 -20
  69. package/docs/theory-of-operations.md +224 -0
  70. package/docs/typed-lookup.md +30 -26
  71. package/module/index.js +0 -1
  72. package/package.json +13 -7
  73. package/src/api/errors/exceptionConstructor.ts +14 -9
  74. package/src/api/errors/index.ts +8 -4
  75. package/src/api/errors/throwModificationError.ts +10 -9
  76. package/src/api/types/InstanceCreator.ts +5 -8
  77. package/src/api/types/Mnemosyne.ts +72 -231
  78. package/src/api/types/Props.ts +4 -2
  79. package/src/api/types/TypeProxy.ts +7 -11
  80. package/src/api/types/compileNewModificatorFunctionBody.ts +13 -7
  81. package/src/api/types/createInstanceModificator.ts +13 -0
  82. package/src/api/types/index.ts +15 -8
  83. package/src/api/utils/index.ts +16 -14
  84. package/src/constants/index.ts +6 -9
  85. package/src/descriptors/types/index.ts +45 -25
  86. package/src/index.ts +60 -71
  87. package/src/types/index.ts +188 -70
  88. package/src/utils/clone.ts +11 -0
  89. package/src/utils/collectConstructors.ts +4 -2
  90. package/src/utils/exception.ts +16 -0
  91. package/src/utils/extract.ts +5 -2
  92. package/src/utils/fork.ts +57 -0
  93. package/src/utils/index.ts +32 -13
  94. package/src/utils/merge.ts +18 -6
  95. package/src/utils/parent.ts +16 -7
  96. package/src/utils/parse.ts +5 -4
  97. package/src/utils/pick.ts +10 -2
  98. package/src/utils/sibling.ts +33 -0
  99. package/src/utils/toJSON.ts +3 -2
@@ -2,33 +2,66 @@
2
2
  name: mnemonica-instance-methods
3
3
  description: |
4
4
  Instance methods and properties in mnemonica: extract, pick, parent, fork,
5
- clone, getProps, and the internal __self__ reference. Use when the user asks
6
- about instance introspection, prototype chain traversal, forking instances,
7
- or the internal props system.
5
+ clone, sibling, exception, getProps, and the internal __self__ reference.
6
+ Starting from v1.0.6 these convenience methods are no longer auto-injected;
7
+ use the standalone utils.* API or attach them to a constructor prototype before
8
+ calling define().
8
9
  metadata:
9
10
  tags: [mnemonica, instances, methods, introspection, prototype]
10
11
  ---
11
12
 
12
13
  # Instance Methods
13
14
 
14
- ## Available Methods
15
+ For the type-level details of the corresponding standalone utilities
16
+ (`utils.extract`, `utils.pick`, `utils.parent`, `utils.fork`, `utils.clone`,
17
+ `utils.sibling`, `utils.exception`), see [`docs/UTILS.md`](../../docs/UTILS.md).
15
18
 
16
- | Method | Description |
17
- |--------|-------------|
18
- | `extract()` | Returns all enumerable properties as a plain object |
19
- | `pick(...keys)` | Returns selected properties |
20
- | `parent()` | Returns the parent instance in the prototype chain |
21
- | `parent(path)` | Looks up parent by constructor name |
22
- | `fork(...args)` | Creates a new instance with same or different args |
23
- | `clone` | Alias for `fork()` with no arguments |
24
- | `exception(error, ...args)` | Creates an error with context |
25
- | `sibling(name)` | Finds a sibling type by name |
19
+ ## Standalone Utilities (Default)
20
+
21
+ Starting from v1.0.6 the convenience methods are **not** exposed on instances by
22
+ default. Import them from the `utils` export:
23
+
24
+ ```typescript
25
+ import { utils } from 'mnemonica';
26
+
27
+ utils.extract(instance);
28
+ utils.pick(instance, 'name', 'age');
29
+ utils.parent(instance, 'UserType');
30
+ utils.fork(instance)(newArgs);
31
+ utils.clone(instance);
32
+ utils.sibling(instance);
33
+ utils.exception(instance, new Error('oops'));
34
+ ```
35
+
36
+ All utilities infer their type parameter from the instance argument.
37
+
38
+ ## Legacy Instance-Method Style (Opt-In)
39
+
40
+ To restore the old `instance.extract()` style for a root constructor, attach the
41
+ methods to its prototype **before** passing it to `define()`. See
42
+ `test/instance-methods-helper.js` for the full reference pattern.
43
+
44
+ ```typescript
45
+ import { define, utils } from 'mnemonica';
46
+
47
+ function UserType(data) {
48
+ Object.assign(this, data);
49
+ }
50
+
51
+ Object.defineProperty(UserType.prototype, 'extract', {
52
+ get() { return () => utils.extract(this); }
53
+ });
54
+
55
+ const User = define('User', UserType);
56
+ const user = new User({ name: 'Ada' });
57
+ user.extract(); // works
58
+ ```
26
59
 
27
60
  ## Internal Props System
28
61
 
29
62
  Instance metadata is stored externally via `WeakMap` against the prototype object,
30
63
  not the instance itself. This keeps instance enumeration clean — internal props
31
- never show up in `for...in`, `Object.keys`, or `JSON.stringify`.
64
+ never show up in `for...in`, `Object.keys()`, or `JSON.stringify()`.
32
65
 
33
66
  ```typescript
34
67
  const props = getProps(instance);
@@ -1,47 +1,45 @@
1
1
  ---
2
2
  name: mnemonica-lookup-typed
3
3
  description: |
4
- Type-safe lookup with lookupTyped() and TypeRegistry augmentation.
5
- Use when the user asks about lookupTyped, TypeRegistry, tactica-generated types,
4
+ Type-safe lookup with lookup() and TypeRegistry augmentation.
5
+ Use when the user asks about lookup, TypeRegistry, tactica-generated types,
6
6
  declaration merging, or type-safe constructor retrieval in mnemonica.
7
7
  metadata:
8
8
  tags: [mnemonica, lookup, types, tactica, declaration-merging]
9
9
  ---
10
10
 
11
- # Type-Safe Lookup with lookupTyped()
11
+ # Type-Safe Lookup with `lookup()`
12
12
 
13
- ## lookup() vs lookupTyped()
13
+ ## `lookup()` with and without `TypeRegistry` augmentation
14
14
 
15
15
  ```typescript
16
- import { lookup, lookupTyped } from 'mnemonica';
16
+ import { lookup } from 'mnemonica';
17
17
 
18
- // Runtime only, returns unknown type
18
+ // Without augmentation: returns TypeClass | undefined
19
19
  const SomeType = lookup('SomeType');
20
20
  const instance = new SomeType({ ... }); // no type safety
21
21
 
22
- // Compile-time type safety via TypeRegistry augmentation
23
- const SomeType = lookupTyped('SomeType');
22
+ // With augmentation: returns the typed constructor
23
+ const SomeType = lookup('SomeType');
24
24
  const instance = new SomeType({ ... }); // TypeScript knows the signature
25
25
  ```
26
26
 
27
27
  ## TypeRegistry Pattern
28
28
 
29
- The default `TypeRegistry` uses `[key: string]: TypeConstructor<never>` to
30
- enforce augmentation via declaration merging. Without augmentation, any key
31
- lookup produces a compile-time error because `TypeConstructor<never>` is not
32
- assignable to concrete types.
29
+ The default `TypeRegistry` is an empty interface. Application code or
30
+ `@mnemonica/tactica` augments it through TypeScript declaration merging.
33
31
 
34
32
  ```typescript
35
33
  // In mnemonica core
36
34
  export interface TypeRegistry {
37
- [key: string]: TypeConstructor<never>;
35
+ // Intentionally empty. Augment via declaration merging.
38
36
  }
39
37
 
40
38
  // Generated by tactica in .tactica/registry.ts
41
39
  declare module 'mnemonica' {
42
40
  interface TypeRegistry {
43
- 'UserType': new (...args: unknown[]) => UserTypeInstance;
44
- 'Parent.SubType': new (...args: unknown[]) => SubTypeInstance;
41
+ 'UserType': TypeConstructor<UserTypeInstance>;
42
+ 'Parent.SubType': TypeConstructor<SubTypeInstance>;
45
43
  }
46
44
  }
47
45
  ```
@@ -52,9 +50,9 @@ The axis that actually matters is whether `TypeRegistry` has been augmented for
52
50
 
53
51
  | Feature | Unaugmented `TypeRegistry` | Augmented `TypeRegistry` |
54
52
  |---------|---------------------------|--------------------------|
55
- | Constructor retrieval | `lookup('Name')` returns `unknown` | `lookupTyped('Name')` returns typed constructor |
53
+ | Constructor retrieval | `lookup('Name')` returns `TypeClass \| undefined` | `lookup('Name')` returns typed constructor |
56
54
  | Type safety | Runtime only | Compile-time + runtime |
57
- | Registry default | `[key: string]: TypeConstructor<never>` | Per-key constructor signatures |
55
+ | Registry default | Empty interface | Per-key constructor signatures |
58
56
  | Instance properties | `any` / `unknown` | Fully typed |
59
57
 
60
58
  Two ways to augment: hand-written `.d.ts` (small projects, learning) or [`@mnemonica/tactica`](https://www.npmjs.com/package/@mnemonica/tactica) (auto-generated, recommended for non-trivial projects). See [`../../docs/typed-lookup.md`](../../docs/typed-lookup.md) for both paths side by side.
@@ -65,7 +63,7 @@ Two ways to augment: hand-written `.d.ts` (small projects, learning) or [`@mnemo
65
63
  ```typescript
66
64
  const UserType = lookup('UserType');
67
65
  const user = new UserType({ name: 'John' });
68
- // user is typed as unknown — no IntelliSense for properties
66
+ // user is typed as object — no IntelliSense for properties
69
67
  ```
70
68
 
71
69
  **After** (TypeRegistry augmented — either hand-written or tactica-generated; runtime is identical)
@@ -78,7 +76,7 @@ declare module 'mnemonica' {
78
76
  }
79
77
 
80
78
  // In application code
81
- const UserType = lookupTyped('UserType');
79
+ const UserType = lookup('UserType');
82
80
  const user = new UserType({ name: 'John' });
83
81
  // user is fully typed as UserTypeInstance
84
82
  ```
@@ -26,7 +26,8 @@ type UserData = { name: string; age: number; };
26
26
  // ❌ WRONG - Instance is NOT an interface!
27
27
  interface UserData { name: string; age: number; }
28
28
 
29
- // ✅ CORRECT - Constructor/Prototype contract uses INTERFACE
29
+ // ✅ CORRECT - Constructor/Prototype contract uses INTERFACE (when attaching
30
+ // legacy instance methods to the prototype before define())
30
31
  interface MnemonicaInstance {
31
32
  extract(): Record<string, unknown>;
32
33
  parent(): object | undefined;
@@ -78,7 +79,7 @@ from (prototype, arguments, config). The resulting constructor is simultaneously
78
79
 
79
80
  - a runtime value (callable, `new`-able, with `.prototype`)
80
81
  - a node in the type Trie (carries `.subtypes`, participates in `instanceof`)
81
- - a behavioral contract (the prototype chain provides `.extract()`, `.fork()`, etc.)
82
+ - a behavioral contract (the prototype chain provides the user's own properties; legacy instance methods must be attached explicitly if desired)
82
83
 
83
84
  Augmented to user-specific types via a `TypeRegistry` declaration-merge — hand-written or generated by tactica.
84
85
 
@@ -112,3 +113,12 @@ const AdminType = UserType.define('AdminType', function (this: AdminType) {
112
113
  this.role = 'admin';
113
114
  });
114
115
  ```
116
+
117
+ ## Generic Public Utilities
118
+
119
+ The standalone utilities in `utils` infer their type parameters from the instance
120
+ argument. Callers should never need an explicit `<T>` cast for ordinary operations.
121
+
122
+ The full reference — helper types (`Extracted`, `Merge`, `InstanceResult`,
123
+ `Parsed`, `SiblingAccessor`), every utility signature, and the type-level
124
+ explanation of `utils.merge` — lives in [`docs/UTILS.md`](../../docs/UTILS.md).
package/AGENTS.md CHANGED
@@ -53,7 +53,7 @@ Load the docs that match your change type. The wrong context produces broken cod
53
53
  | Involves async constructors | + [`.ai/rules-skill/async-constructors.md`](./.ai/rules-skill/async-constructors.md) + [`.ai/async_init.md`](./.ai/async_init.md) |
54
54
  | Involves TypeScript types | + [`.ai/rules-skill/type-system.md`](./.ai/rules-skill/type-system.md) |
55
55
  | Involves proxy internals | + [`.ai/rules-skill/proxy-architecture.md`](./.ai/rules-skill/proxy-architecture.md) |
56
- | Uses tactica / `lookupTyped` | + [`.ai/TACTICA-RULES.md`](./.ai/TACTICA-RULES.md) |
56
+ | Uses tactica / `lookup` | + [`.ai/TACTICA-RULES.md`](./.ai/TACTICA-RULES.md) |
57
57
  | Docs-only change | README section you're touching only |
58
58
 
59
59
  **This file + `.ai/ONBOARDING.md` are the always-required baseline for any `src/` edit.**
@@ -78,6 +78,8 @@ npm run watch # watch mode
78
78
 
79
79
  **Must run `npm run test:cov` before completing any task.**
80
80
 
81
+ **Documentation changes:** When modifying any `.md` file, `npm run lint:md` is mandatory. It checks for broken links and anchors. Run it and fix any reported issues before finishing.
82
+
81
83
  ## Code Style (Project-Specific)
82
84
 
83
85
  See [`.ai/rules-skill/code-style.md`](./.ai/rules-skill/code-style.md) for the full style reference. Key rules: tabs only, space before function parens, colons aligned in object literals, `strict: true`, **no `any`** (`no-explicit-any: error`).
@@ -90,22 +92,28 @@ The core API is `define(TypeName, constructHandler, config?)` in `src/index.ts`.
90
92
  - `.lookup()` - find types by path
91
93
  - `.registerHook()` - register lifecycle hooks
92
94
 
93
- ### The `lookupTyped()` Function
95
+ ### The `lookup()` Function
94
96
 
95
- For user-facing semantics, see [`README.md`](./README.md) and [`.ai/TACTICA-RULES.md`](./.ai/TACTICA-RULES.md). The contributor-relevant detail is the implementation pattern: `TypeRegistry` exposes a `[key: string]: never` index so that any lookup against an unaugmented registry is a compile-time error.
97
+ For user-facing semantics, see [`README.md`](./README.md) and [`.ai/TACTICA-RULES.md`](./.ai/TACTICA-RULES.md). The contributor-relevant detail is the implementation pattern: `TypeRegistry` starts empty, and `lookup()` uses overloads so augmented keys return the typed constructor while unaugmented keys fall back to `TypeClass | undefined`.
96
98
 
97
99
  ```typescript
98
100
  // In mnemonica core (src/index.ts)
99
101
  export interface TypeRegistry {
100
- [key: string]: never; // forces augmentation before keys resolve to real types
102
+ // Intentionally empty. Augment via declaration merging.
101
103
  }
102
104
 
103
- export const lookupTyped = function <const K extends keyof TypeRegistry>(
105
+ export function lookup<const K extends keyof TypeRegistry>(
106
+ this: unknown,
104
107
  TypeNestedPath: K
105
- ): TypeRegistry[K] {
106
- // Runtime delegates to lookup(); type safety is compile-time only.
107
- return types.lookup(TypeNestedPath as string) as TypeRegistry[K];
108
- };
108
+ ): TypeRegistry[K] | undefined;
109
+ export function lookup(
110
+ this: unknown,
111
+ TypeNestedPath: string
112
+ ): TypeClass | undefined {
113
+ // Runtime delegates to types.lookup(); type safety is compile-time only.
114
+ const types = checkThis(this) ? defaultTypes : this || defaultTypes;
115
+ return types.lookup(TypeNestedPath);
116
+ }
109
117
  ```
110
118
 
111
119
  Tactica generates the augmentation:
@@ -114,15 +122,15 @@ Tactica generates the augmentation:
114
122
  // In .tactica/registry.ts (generated)
115
123
  declare module 'mnemonica' {
116
124
  interface TypeRegistry {
117
- 'UserType': new (...args: unknown[]) => UserTypeInstance;
118
- 'Parent.SubType': new (...args: unknown[]) => SubTypeInstance;
125
+ 'UserType': TypeConstructor<UserTypeInstance>;
126
+ 'Parent.SubType': TypeConstructor<SubTypeInstance>;
119
127
  }
120
128
  }
121
129
  ```
122
130
 
123
- Runtime behavior is identical to `lookup()`; the only difference is the compile-time constraint on the key.
131
+ Runtime behavior is identical whether `TypeRegistry` is augmented or not; the only difference is the compile-time return type.
124
132
 
125
- > **Roadmap.** Nested `lookupTyped()` (a type-safe `.lookupTyped()` method
133
+ > **Roadmap.** Nested `lookup()` (a type-safe `.lookup()` method
126
134
  > on constructors that preserves the prototype chain) is designed but not
127
135
  > yet shipped.
128
136
 
@@ -172,9 +180,11 @@ For test passing confirmations (e.g., `npm run test:cov`), checking the end of t
172
180
 
173
181
  These configuration files define the project's strict standards. Any changes require user approval first.
174
182
 
175
- ## Return Statement Design Rule
183
+ ## Return Statement Design Rule (Non-negotiable)
184
+
185
+ **Every return expression must go through an intermediate variable/constant.** No exceptions. This is critical for debuggability with `npm run debug` and Chrome Dev Tools — when execution pauses on `return result`, you can hover your mouse over `result` and inspect the value. With `return SomeFn(arg)`, the value is gone before the debugger can show it.
176
186
 
177
- **Always use an intermediate variable before returning.** This is critical for debuggability with `npm run debug` and Chrome Dev Tools.
187
+ **This rule is enforced. If you write `return new Foo()` or `return fn()`, the PR will be rejected.**
178
188
 
179
189
  ### Prohibited patterns:
180
190
  ```typescript
package/CONTRIBUTING.md CHANGED
@@ -26,6 +26,12 @@ npm run test:jest:cov
26
26
  `src/` → `build/`. To run linters with auto-fix, use `npm run lint:fix`.
27
27
  To run linters in read-only mode (CI parity), use `npm run lint:check`.
28
28
 
29
+ ### Doc-only changes
30
+
31
+ If your change touches only Markdown files (no `src/`, `test/`, `test-jest/`,
32
+ `test-ts/`, or build config), you can **skip `npm run build` and the test
33
+ suites**. Run `npm run lint:md` to catch dead links and broken anchors instead.
34
+
29
35
  ## Test frameworks
30
36
 
31
37
  There are two suites and both must stay green:
@@ -45,9 +51,9 @@ behavior that the type system tolerates, and vice versa.
45
51
  Conventional commits are preferred:
46
52
 
47
53
  ```
48
- feat: add lookupTyped overload for nested registry
54
+ feat: add lookup overload for nested registry
49
55
  fix(InstanceCreator): preserve __args__ across async chain
50
- docs: clarify exposeInstanceMethods default
56
+ docs: clarify instance method opt-in pattern
51
57
  chore(ci): bump setup-node to v4
52
58
  ```
53
59