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.
- package/.ai/AGENTS.md +1 -1
- package/.ai/ONBOARDING.md +3 -2
- package/.ai/PROTOTYPE-CHAIN.md +122 -0
- package/.ai/TACTICA-DEEP-DIVE.md +52 -49
- package/.ai/TACTICA-RULES.md +8 -8
- package/.ai/ask/AGENTS.md +15 -9
- package/.ai/rules-skill/contributing.md +1 -1
- package/.ai/rules-skill/define-patterns.md +8 -2
- package/.ai/rules-skill/ecosystem.md +4 -4
- package/.ai/rules-skill/instance-methods.md +48 -15
- package/.ai/rules-skill/lookup-typed.md +17 -19
- package/.ai/rules-skill/type-system.md +12 -2
- package/AGENTS.md +25 -15
- package/CONTRIBUTING.md +8 -2
- package/FOR_HUMANS.md +193 -218
- package/README.md +84 -47
- package/SKILL.md +4 -4
- package/build/api/errors/exceptionConstructor.js +20 -12
- package/build/api/errors/index.js +9 -5
- package/build/api/errors/throwModificationError.js +15 -9
- package/build/api/index.js +35 -2
- package/build/api/types/InstanceCreator.js +5 -2
- package/build/api/types/Mnemosyne.d.ts +6 -6
- package/build/api/types/Mnemosyne.js +43 -120
- package/build/api/types/Props.js +5 -3
- package/build/api/types/TypeProxy.js +13 -9
- package/build/api/types/compileNewModificatorFunctionBody.js +14 -8
- package/build/api/types/createInstanceModificator.js +1 -1
- package/build/api/types/index.js +56 -13
- package/build/api/utils/index.js +21 -12
- package/build/constants/index.js +11 -8
- package/build/descriptors/types/index.js +79 -26
- package/build/index.d.ts +9 -10
- package/build/index.js +65 -25
- package/build/types/index.d.ts +76 -32
- package/build/types/index.js +1 -1
- package/build/utils/clone.d.ts +1 -0
- package/build/utils/clone.js +11 -0
- package/build/utils/collectConstructors.js +5 -3
- package/build/utils/exception.d.ts +1 -0
- package/build/utils/exception.js +14 -0
- package/build/utils/extract.d.ts +2 -3
- package/build/utils/extract.js +3 -2
- package/build/utils/fork.d.ts +1 -0
- package/build/utils/fork.js +33 -0
- package/build/utils/index.d.ts +2 -3
- package/build/utils/index.js +21 -7
- package/build/utils/merge.d.ts +2 -1
- package/build/utils/merge.js +10 -6
- package/build/utils/parent.d.ts +4 -1
- package/build/utils/parent.js +7 -7
- package/build/utils/parse.d.ts +2 -12
- package/build/utils/parse.js +1 -1
- package/build/utils/pick.d.ts +4 -3
- package/build/utils/pick.js +4 -5
- package/build/utils/sibling.d.ts +1 -0
- package/build/utils/sibling.js +25 -0
- package/build/utils/toJSON.d.ts +1 -1
- package/build/utils/toJSON.js +3 -2
- package/docs/UTILS.md +184 -0
- package/docs/ai-learning-trajectory.md +9 -9
- package/docs/async-constructors.md +21 -1
- package/docs/empathy-in-ai.md +170 -0
- package/docs/hott-primer.md +47 -0
- package/docs/performance-vs-security.md +2 -2
- package/docs/prototype-chain.md +127 -0
- package/docs/purpose.md +42 -11
- package/docs/tactica-pattern.md +20 -20
- package/docs/theory-of-operations.md +224 -0
- package/docs/typed-lookup.md +30 -26
- package/module/index.js +0 -1
- package/package.json +13 -7
- package/src/api/errors/exceptionConstructor.ts +14 -9
- package/src/api/errors/index.ts +8 -4
- package/src/api/errors/throwModificationError.ts +10 -9
- package/src/api/types/InstanceCreator.ts +5 -8
- package/src/api/types/Mnemosyne.ts +72 -231
- package/src/api/types/Props.ts +4 -2
- package/src/api/types/TypeProxy.ts +7 -11
- package/src/api/types/compileNewModificatorFunctionBody.ts +13 -7
- package/src/api/types/createInstanceModificator.ts +13 -0
- package/src/api/types/index.ts +15 -8
- package/src/api/utils/index.ts +16 -14
- package/src/constants/index.ts +6 -9
- package/src/descriptors/types/index.ts +45 -25
- package/src/index.ts +60 -71
- package/src/types/index.ts +188 -70
- package/src/utils/clone.ts +11 -0
- package/src/utils/collectConstructors.ts +4 -2
- package/src/utils/exception.ts +16 -0
- package/src/utils/extract.ts +5 -2
- package/src/utils/fork.ts +57 -0
- package/src/utils/index.ts +32 -13
- package/src/utils/merge.ts +18 -6
- package/src/utils/parent.ts +16 -7
- package/src/utils/parse.ts +5 -4
- package/src/utils/pick.ts +10 -2
- package/src/utils/sibling.ts +33 -0
- 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.
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
|
5
|
-
Use when the user asks about
|
|
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
|
|
11
|
+
# Type-Safe Lookup with `lookup()`
|
|
12
12
|
|
|
13
|
-
## lookup()
|
|
13
|
+
## `lookup()` with and without `TypeRegistry` augmentation
|
|
14
14
|
|
|
15
15
|
```typescript
|
|
16
|
-
import { lookup
|
|
16
|
+
import { lookup } from 'mnemonica';
|
|
17
17
|
|
|
18
|
-
//
|
|
18
|
+
// Without augmentation: returns TypeClass | undefined
|
|
19
19
|
const SomeType = lookup('SomeType');
|
|
20
20
|
const instance = new SomeType({ ... }); // no type safety
|
|
21
21
|
|
|
22
|
-
//
|
|
23
|
-
const 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`
|
|
30
|
-
|
|
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
|
-
|
|
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':
|
|
44
|
-
'Parent.SubType':
|
|
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 `
|
|
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 |
|
|
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
|
|
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 =
|
|
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
|
|
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 / `
|
|
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 `
|
|
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`
|
|
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
|
-
|
|
102
|
+
// Intentionally empty. Augment via declaration merging.
|
|
101
103
|
}
|
|
102
104
|
|
|
103
|
-
export
|
|
105
|
+
export function lookup<const K extends keyof TypeRegistry>(
|
|
106
|
+
this: unknown,
|
|
104
107
|
TypeNestedPath: K
|
|
105
|
-
): TypeRegistry[K]
|
|
106
|
-
|
|
107
|
-
|
|
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':
|
|
118
|
-
'Parent.SubType':
|
|
125
|
+
'UserType': TypeConstructor<UserTypeInstance>;
|
|
126
|
+
'Parent.SubType': TypeConstructor<SubTypeInstance>;
|
|
119
127
|
}
|
|
120
128
|
}
|
|
121
129
|
```
|
|
122
130
|
|
|
123
|
-
Runtime behavior is identical
|
|
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 `
|
|
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
|
-
**
|
|
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
|
|
54
|
+
feat: add lookup overload for nested registry
|
|
49
55
|
fix(InstanceCreator): preserve __args__ across async chain
|
|
50
|
-
docs: clarify
|
|
56
|
+
docs: clarify instance method opt-in pattern
|
|
51
57
|
chore(ci): bump setup-node to v4
|
|
52
58
|
```
|
|
53
59
|
|