@directive-run/knowledge 1.15.0 → 1.17.0

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/core/naming.md CHANGED
@@ -2,81 +2,116 @@
2
2
 
3
3
  > Covers all `@directive-run/*` packages — terminology, parameter names, return-style rules.
4
4
 
5
- Directive naming rules that AI coding assistants must follow. These are non-negotiable project conventions.
5
+ Directive uses precise vocabulary by design: each concept has one canonical name across every package, every language adapter, and every doc. The names are non-negotiable for code Directive ships, AND every term has a cross-paradigm alias so a developer coming from Redux / Zustand / XState / Jotai / React Query can find the right Directive concept on the first search.
6
6
 
7
- ## Decision Tree: "What do I call this?"
7
+ This file is both the rules and the bridge. Search for the term you already know; you'll land on the Directive equivalent.
8
8
 
9
- ```
10
- Resolver parameter names?
11
- → (req, context) req = requirement, NEVER "request"
12
- → NEVER (req, ctx) context is NEVER abbreviated
9
+ ## Coming from another library? Start here
13
10
 
14
- Computed values?
15
- → "derivations" / derive NEVER "computed", "selectors", "getters"
16
- system.derive.myValue NEVER system.computed.myValue
11
+ | If you call it… | (in this library) | Directive calls it… | Why the name |
12
+ |---|---|---|---|
13
+ | **state**, **store**, **atom**, **signal**, **observable** | Redux, Zustand, Jotai, Preact Signals, MobX | **facts** | "Facts about the world" — the system observes them. Same value-bag role as state, with a proxy that auto-tracks reads. |
14
+ | **selector**, **computed**, **getter**, **memo**, **derived store** | Redux, Vue/MobX, Zustand getters, Memoize, Svelte | **derivations** / `derive` | Auto-tracked computed values. No dep arrays. Directive's name aligns with "derived from facts." |
15
+ | **action**, **dispatch**, **reducer**, **command**, **intent** | Redux, Redux Toolkit, CQRS | **events** + **resolvers** | Events are the user-facing trigger; resolvers fulfill them. Reducers are split into the two halves Directive cares about. |
16
+ | **thunk**, **saga**, **effect**, **middleware**, **listener** | Redux Toolkit, redux-saga, redux-observable, NgRx | **resolvers** | Async logic that mutates state in response to a requirement. Same role as a thunk; declared once, run by the runtime. |
17
+ | **rule**, **condition**, **guard**, **trigger**, **policy** | XState guards, Cerbos, OPA, business-rule engines | **constraints** | Declarative "when X is true, Y must happen." Constraints emit requirements; resolvers fulfill them. |
18
+ | **request**, **query**, **side-effect spec** | React Query / TanStack Query queries, Apollo queries | **requirements** | The object a constraint emits. The runtime dedupes them, dispatches to resolvers, tracks status. |
19
+ | **subscription**, **listener**, **reaction**, **watcher** | Zustand subscribe, MobX reactions, Redux subscriptions | **effects** | Side-effects that run when watched facts change. Cleaner than `subscribe()` callbacks because effects auto-track. |
20
+ | **slice**, **feature**, **domain**, **bounded context** | Redux Toolkit slices, NgRx feature modules | **modules** | Encapsulates facts + derivations + constraints + resolvers + events for one bounded slice of the system. |
21
+ | **store**, **container**, **context**, **app state** | Redux Store, Pinia, React Context, NgRx Store | **system** | The runtime that wires modules together, evaluates constraints, dispatches resolvers, and exposes observation. |
22
+ | **state machine**, **statechart**, **xstate machine** | XState | (closest: **module** + **constraints**) | Directive isn't a state machine — but if your XState `states` model business rules (not UI flow), constraints + facts express the same logic declaratively with no transition functions. |
23
+ | **cache**, **query cache**, **stale-while-revalidate** | TanStack Query, SWR, Apollo cache | (closest: `@directive-run/query` + **constraints**) | Use `@directive-run/query` for the cache; constraints decide when refetches fire. Directive doesn't replace TanStack Query — it composes with it. |
24
+ | **agent**, **chain**, **graph**, **workflow** | LangChain, LangGraph, OpenAI Assistants | **agent** + **orchestrator** | `@directive-run/ai` keeps the names — agents are agents, orchestrators are orchestrators — but they run under the same constraint engine as the rest of Directive. |
25
+ | **guardrail**, **filter**, **safety check**, **moderation** | OpenAI moderation API, NVIDIA NeMo Guardrails | **guardrails** | Same name, same role; declarative input/output/tool-call checks. |
17
26
 
18
- State values?
19
- → "facts" NEVER "state", "store", "atoms"
20
- → system.facts.count NEVER system.state.count
27
+ If your term isn't in the table, search the canonical name in this doc's [Terminology quick reference](#terminology-quick-reference) — both directions are listed.
21
28
 
22
- Conditional triggers?
23
- → "constraints" NEVER "rules", "conditions", "triggers"
24
- → when() + require() NEVER if/then, trigger/action
29
+ ## Canonical terms (non-negotiable for code Directive ships)
25
30
 
26
- Fulfillment logic?
27
- → "resolvers" NEVER "handlers", "actions", "reducers"
28
- resolve(req, context) NEVER handle(req, ctx)
29
- ```
31
+ Above is the bridge from your existing vocabulary. Below are the canonical names — the only ones that should appear in `@directive-run/*` source, tests, docs, and generated AI rules.
32
+
33
+ ### `facts` the value bag
34
+
35
+ Not state, not store, not atoms. **Facts.** Accessed at `system.facts.x`. Mutated through events and resolvers. Read through derivations, hooks, and `system.facts.$store`.
36
+
37
+ ### `derivations` — auto-tracked computed values
38
+
39
+ Declared via `derive: { name: (facts) => … }`. Read at `system.derive.name`. The hook is `useDerived` (not `useComputed`, not `useSelector` — Directive's `useSelector` is a different, broader primitive that takes a selector function).
40
+
41
+ ### `constraints` — when-then declarations
42
+
43
+ Declared via `constraints: { name: { when: …, require: … } }`. The `when` returns a boolean; the `require` returns a requirement.
44
+
45
+ ### `resolvers` — requirement fulfillment
46
+
47
+ Declared via `resolvers: { name: { requirement: …, resolve: async (req, context) => … } }`. Mutate `context.facts`; never return data from `resolve`.
48
+
49
+ ### `requirements` — what constraints emit
50
+
51
+ Tagged union `{ type: "FETCH_USER", … }`. Constraints emit them; the runtime dedupes by `type` + `key()`; resolvers fulfill them.
52
+
53
+ ### `effects` — side effects from watched changes
54
+
55
+ Declared via `effects: { name: { run: (facts, prev) => … } }`. Run on relevant fact changes. The auto-tracking dependency model means no `deps` array unless you specifically need a partial dep set.
56
+
57
+ ### `events` — typed mutation entry points
58
+
59
+ Declared via `events: { name: (facts, payload) => … }`. Called as `system.events.name(payload)`. The typed events accessor is the canonical way to dispatch — `system.dispatch({type, ...payload})` works but the typed accessor carries autocomplete.
30
60
 
31
- ## Parameter Naming
61
+ ### `module` — bounded slice
62
+
63
+ Created via `createModule(name, { schema, init, derive, effects, events, constraints, resolvers })`. Composes into systems.
64
+
65
+ ### `system` — the runtime
66
+
67
+ Created via `createSystem({ module })` or `createSystem({ modules: { x, y } })`. Hosts the reconciliation loop.
68
+
69
+ ## Parameter naming
32
70
 
33
71
  ### `req` = requirement (NOT request)
34
72
 
35
- The `req` parameter in resolvers and constraint `key()` functions is short for **requirement** -- the object emitted by a constraint's `require` property.
73
+ The `req` parameter in resolvers and constraint `key()` functions is short for **requirement** the object emitted by a constraint's `require` property.
36
74
 
37
75
  ```typescript
38
- // CORRECT req is a requirement
76
+ // CORRECT req is a requirement
39
77
  resolvers: {
40
78
  fetchUser: {
41
79
  requirement: "FETCH_USER",
42
80
  key: (req) => `fetch-${req.userId}`,
43
81
  resolve: async (req, context) => {
44
82
  // req.type === "FETCH_USER"
45
- // req.userId from the requirement payload
46
83
  const user = await fetchUser(req.userId);
47
84
  context.facts.user = user;
48
85
  },
49
86
  },
50
87
  },
51
88
 
52
- // WRONG never use "request" or "r"
89
+ // WRONG never "request" or "r"
53
90
  resolve: async (request, context) => { /* ... */ },
54
91
  resolve: async (r, context) => { /* ... */ },
55
92
  ```
56
93
 
57
- ### `context` is Never Abbreviated
94
+ ### `context` is never abbreviated
58
95
 
59
96
  ```typescript
60
97
  // CORRECT
61
98
  resolve: async (req, context) => {
62
99
  context.facts.status = "loaded";
63
- context.signal; // AbortSignal
64
- context.snapshot(); // facts snapshot
100
+ context.signal; // AbortSignal
101
+ context.snapshot(); // facts snapshot
65
102
  },
66
103
 
67
- // WRONG never abbreviate to ctx
104
+ // WRONG never abbreviate to ctx
68
105
  resolve: async (req, ctx) => { /* ... */ },
69
106
  ```
70
107
 
71
- ## Return Style
108
+ ## Return style
72
109
 
73
- ### Always Use Braces (for `if` blocks with `return`)
110
+ ### Braces for `if` blocks with `return` (NOT for arrow expressions)
74
111
 
75
112
  The brace rule applies to control-flow blocks — not to arrow-expression bodies.
76
113
 
77
- **Arrow expressions** (single-line derivations, predicates, computed
78
- requirements): the concise form is preferred. No braces, no explicit
79
- `return`.
114
+ **Arrow expressions** (single-line derivations, predicates, computed requirements): the concise form is preferred. No braces, no explicit `return`.
80
115
 
81
116
  ```typescript
82
117
  // CORRECT — single-line arrow expressions stay concise
@@ -93,25 +128,24 @@ constraints: {
93
128
  },
94
129
  ```
95
130
 
96
- **Control-flow statements** (`if`, `for`, `while`): braces required, even
97
- for single-line bodies. Single-line `if (x) return y` shapes are never used.
131
+ **Control-flow statements** (`if`, `for`, `while`): braces required, even for single-line bodies. Single-line `if (x) return y` shapes are never used.
98
132
 
99
133
  ```typescript
100
- // WRONG single-line if return
134
+ // WRONG single-line if return
101
135
  if (facts.user) return "ready";
102
136
 
103
- // CORRECT always wrap the body in braces
137
+ // CORRECT always wrap the body in braces
104
138
  if (facts.user) {
105
139
  return "ready";
106
140
  }
107
141
  ```
108
142
 
109
- ### Blank Line Before `return`
143
+ ### Blank line before `return`
110
144
 
111
145
  Add a blank line before `return` when there is code above it. Skip the blank line when `return` is the first statement in a block.
112
146
 
113
147
  ```typescript
114
- // CORRECT blank line before return when code precedes it
148
+ // CORRECT blank line before return when code precedes it
115
149
  function getStatus(facts) {
116
150
  const phase = facts.phase;
117
151
  const hasUser = facts.user !== null;
@@ -119,12 +153,12 @@ function getStatus(facts) {
119
153
  return phase === "ready" && hasUser;
120
154
  }
121
155
 
122
- // CORRECT no blank line when return is first statement
156
+ // CORRECT no blank line when return is first statement
123
157
  function isReady(facts) {
124
158
  return facts.phase === "ready";
125
159
  }
126
160
 
127
- // CORRECT blank line after brace-style return block
161
+ // CORRECT blank line after brace-style return block
128
162
  function process(facts) {
129
163
  if (!facts.ready) {
130
164
  return null;
@@ -134,26 +168,20 @@ function process(facts) {
134
168
 
135
169
  return result;
136
170
  }
137
-
138
- // WRONG – no blank line before return after code
139
- function getStatus(facts) {
140
- const phase = facts.phase;
141
- return phase === "ready"; // Missing blank line
142
- }
143
171
  ```
144
172
 
145
- ## Multi-Line Code Formatting
173
+ ## Multi-line code formatting
146
174
 
147
175
  Never put properties or statements on a single line inside braces. Always expand to one item per line with proper indentation. This applies everywhere: schema definitions, init functions, events, effects, requirement types, and any other object or block.
148
176
 
149
177
  ```typescript
150
- // WRONG properties crammed on one line
178
+ // WRONG properties crammed on one line
151
179
  schema: {
152
180
  facts: { phase: t.string(), count: t.number() },
153
181
  requirements: { FETCH_USER: { id: t.string() }, RESET: {} },
154
182
  },
155
183
 
156
- // CORRECT one property per line, always expanded
184
+ // CORRECT one property per line, always expanded
157
185
  schema: {
158
186
  facts: {
159
187
  phase: t.string(),
@@ -167,41 +195,31 @@ schema: {
167
195
  },
168
196
  },
169
197
 
170
- // WRONG statements crammed on one line
198
+ // WRONG statements crammed on one line
171
199
  init: (facts) => { facts.phase = "idle"; facts.count = 0; },
172
200
 
173
- // CORRECT one statement per line
201
+ // CORRECT one statement per line
174
202
  init: (facts) => {
175
203
  facts.phase = "idle";
176
204
  facts.count = 0;
177
205
  },
178
-
179
- // WRONG
180
- events: { reset: (facts) => { facts.count = 0; facts.phase = "idle"; } },
181
-
182
- // CORRECT
183
- events: {
184
- reset: (facts) => {
185
- facts.count = 0;
186
- facts.phase = "idle";
187
- },
188
- },
189
206
  ```
190
207
 
191
208
  Single-expression arrows (no braces) are fine on one line. Empty objects `{}` are fine inline.
209
+
192
210
  ```typescript
193
- // OK single expression, no braces
211
+ // OK single expression, no braces
194
212
  derive: {
195
213
  isReady: (facts) => facts.phase === "ready",
196
214
  },
197
215
 
198
- // OK empty object
216
+ // OK empty object
199
217
  RESET: {},
200
218
  ```
201
219
 
202
- ## Multi-Module Naming
220
+ ## Multi-module naming
203
221
 
204
- ### `facts.self.*` for Own Module
222
+ ### `facts.self.*` for own module
205
223
 
206
224
  In multi-module systems, constraints, effects, and derivations with `crossModuleDeps` receive namespaced facts. Own module facts are always at `facts.self.*`.
207
225
 
@@ -214,7 +232,7 @@ constraints: {
214
232
  },
215
233
  },
216
234
 
217
- // WRONG bare facts.* in multi-module context
235
+ // WRONG bare facts.* in multi-module context
218
236
  constraints: {
219
237
  loadWhenAuth: {
220
238
  when: (facts) => facts.isAuthenticated && !facts.loaded,
@@ -223,48 +241,48 @@ constraints: {
223
241
  },
224
242
  ```
225
243
 
226
- ### System-Level Access Uses Dot Notation
244
+ ### System-level access uses dot notation
227
245
 
228
246
  ```typescript
229
- // CORRECT dot notation through namespace proxy
247
+ // CORRECT dot notation through namespace proxy
230
248
  system.facts.auth.token;
231
249
  system.facts.cart.items;
232
250
  system.derive.auth.isLoggedIn;
233
251
  system.events.auth.login({ token: "..." });
234
252
 
235
- // WRONG bracket notation with internal separator
253
+ // WRONG bracket notation with internal separator
236
254
  system.facts["auth::token"];
237
255
  system.facts["auth_token"];
238
256
  ```
239
257
 
240
- ## Type Casting Rules
258
+ ## Type casting rules
241
259
 
242
- ### Never Cast When Reading
260
+ ### Never cast when reading
243
261
 
244
262
  The schema provides all types. Do not add `as` casts when reading facts or derivations from the system.
245
263
 
246
264
  ```typescript
247
- // CORRECT schema provides the type
265
+ // CORRECT schema provides the type
248
266
  const profile = system.facts.profile;
249
267
  const isReady = system.derive.isReady;
250
268
 
251
- // WRONG unnecessary cast
269
+ // WRONG unnecessary cast
252
270
  const profile = system.facts.profile as UserProfile;
253
271
  const isReady = system.derive.isReady as boolean;
254
272
  ```
255
273
 
256
- ### Cast Only in Schema Definition
274
+ ### Cast only in schema definition
257
275
 
258
- Type assertions are only valid in schema definition using the `{} as {}` pattern:
276
+ Type assertions are only valid in schema definition using the `{} as {}` pattern, or via the `t.*` builders (preferred):
259
277
 
260
278
  ```typescript
261
- // CORRECT cast in schema definition
279
+ // CORRECT cast in schema definition
262
280
  schema: {
263
281
  facts: {} as { profile: UserProfile; settings: AppSettings },
264
282
  derivations: {} as { displayName: string },
265
283
  },
266
284
 
267
- // OR use t.* builders (preferred)
285
+ // PREFERRED t.* builders
268
286
  schema: {
269
287
  facts: {
270
288
  profile: t.object<UserProfile>(),
@@ -276,17 +294,28 @@ schema: {
276
294
  },
277
295
  ```
278
296
 
279
- ## Terminology Quick Reference
297
+ ## Terminology quick reference
298
+
299
+ Two-way lookup. Search the term you know.
280
300
 
281
- | Directive Term | NEVER Use |
301
+ | Directive term | Cross-paradigm aliases (use these to search) |
282
302
  |---|---|
283
- | facts | state, store, atoms, signals |
284
- | derivations / derive | computed, selectors, getters, memos |
285
- | constraints | rules, conditions, triggers, guards |
286
- | resolvers | handlers, actions, reducers, sagas |
287
- | requirements | requests, commands, intents |
288
- | effects | watchers, subscriptions, reactions |
289
- | module | slice, feature, domain |
290
- | system | store, container, context |
291
- | `req` (parameter) | request, r, requirement (spelled out) |
292
- | `context` (parameter) | ctx, c, resolverContext |
303
+ | **facts** | state, store, atoms, signals, observables, model |
304
+ | **derivations** / `derive` | computed, selectors, getters, memos, derived stores, $: blocks (Svelte) |
305
+ | **constraints** | rules, conditions, triggers, guards, policies, invariants |
306
+ | **resolvers** | handlers, actions, reducers, sagas, thunks, effects (Redux), middleware, listeners |
307
+ | **requirements** | requests, commands, intents, queries (TanStack), tasks |
308
+ | **effects** | watchers, subscriptions, reactions, autoruns, listeners |
309
+ | **events** | actions, intents, commands, methods (Pinia), signals (NgRx) |
310
+ | **module** | slice, feature, domain, bounded context, NgRx feature module |
311
+ | **system** | store, container, context, app state, app shell |
312
+ | **`req`** (parameter) | request, r, requirement (spelled out) |
313
+ | **`context`** (parameter) | ctx, c, resolverContext |
314
+
315
+ The forbidden direction is also non-negotiable: in code Directive ships, the columns reverse. Do NOT use `state`, `store`, `selectors`, `computed`, `actions`, `reducers`, `subscriptions`, `slices`, `request`, or `ctx` in `@directive-run/*` source. The alias map is for retrieval — the canonical names are for code.
316
+
317
+ ## See also
318
+
319
+ - [`core-patterns.md`](./core-patterns.md) — the actual code shapes every naming rule here applies to
320
+ - [`multi-module.md`](./multi-module.md) — `facts.self.*` convention and dot-notation rules
321
+ - [`anti-patterns.md`](./anti-patterns.md) — naming-shaped mistakes (`request` for `req`, `ctx` for `context`, hallucinated TS schema types)
package/core/plugins.md CHANGED
@@ -354,3 +354,8 @@ persistencePlugin({
354
354
  exclude: ["authToken", "isLoading", "error"],
355
355
  })
356
356
  ```
357
+
358
+ ## See also
359
+
360
+ - [`system-api.md`](./system-api.md) — `plugins:` option on `createSystem` and the lifecycle hooks plugins observe
361
+ - [`error-boundaries.md`](./error-boundaries.md) — `createCircuitBreaker` and the resilience plugins that pair with error boundaries
@@ -368,3 +368,9 @@ const handleClick = useCallback(() => events.increment(), [events]);
368
368
  const events = useEvents(system);
369
369
  <button onClick={() => events.increment()}>+</button>
370
370
  ```
371
+
372
+ ## See also
373
+
374
+ - [`system-api.md`](./system-api.md) — the system instance the hooks here read from + the SSR/hydration story
375
+ - [`core-patterns.md`](./core-patterns.md) — the module shape every React example here assumes you've built
376
+ - [`anti-patterns.md`](./anti-patterns.md) — the hallucinated React hooks (`useEvent`, `useSystem`, `DirectiveProvider`) that AI assistants reach for
package/core/resolvers.md CHANGED
@@ -357,3 +357,11 @@ system.start();
357
357
  await system.settle();
358
358
  console.log(system.facts.data); // Resolved value
359
359
  ```
360
+
361
+ ## See also
362
+
363
+ - [`constraints.md`](./constraints.md) — the demand side of the loop; what emits the requirements this resolver fulfills
364
+ - [`core-patterns.md`](./core-patterns.md) — how resolvers fit into the module shape
365
+ - [`error-boundaries.md`](./error-boundaries.md) — recovery strategies when a resolver throws (retry, fallback, circuit breaker)
366
+ - [`testing.md`](./testing.md) — mock resolvers via `mocks.resolvers` and the programmatic `mockResolver(type)` helper
367
+ - [`anti-patterns.md`](./anti-patterns.md) — the resolver-shaped mistakes (returning data from `resolve`, abbreviating `context` to `ctx`)
@@ -265,3 +265,9 @@ const myModule = createModule("simple", {
265
265
  ```
266
266
 
267
267
  This gives full TypeScript inference but skips runtime validation. Use `t.*()` when you want dev-mode validation, transforms, or self-documenting schemas.
268
+
269
+ ## See also
270
+
271
+ - [`core-patterns.md`](./core-patterns.md) — where the schemas these builders define get attached (modules)
272
+ - [`system-api.md`](./system-api.md) — schema flows through `createSystem` and shows up on `system.facts.*`
273
+ - [`anti-patterns.md`](./anti-patterns.md) — hallucinated schema types (`t.map`, `t.set`, `t.date`, `t.promise`, `t.tuple`, `t.record`, `t.any`) that don't exist
@@ -453,3 +453,11 @@ system.start();
453
453
  system.stop();
454
454
  system.destroy();
455
455
  ```
456
+
457
+ ## See also
458
+
459
+ - [`core-patterns.md`](./core-patterns.md) — the modules that go into `createSystem({ module: ... })`
460
+ - [`multi-module.md`](./multi-module.md) — `createSystem({ modules: { ... } })` for namespaced systems
461
+ - [`history.md`](./history.md) — the `history:` config and the `system.history.*` time-travel surface
462
+ - [`plugins.md`](./plugins.md) — the `plugins:` config and the lifecycle hooks plugins observe
463
+ - [`react-adapter.md`](./react-adapter.md) — consuming a system from React components
package/core/testing.md CHANGED
@@ -404,3 +404,9 @@ resolve: (req, context) => { context.facts.x = 1; }
404
404
  | `system.assertFactSet(key, value?)` | instance method | Assert a fact value |
405
405
  | `system.assertFactChanges(key, n)` | instance method | Assert the change count for a fact |
406
406
  | `system.waitForIdle(maxWait?)` | instance method | Wait for in-flight resolvers (default 5000ms) |
407
+
408
+ ## See also
409
+
410
+ - [`resolvers.md`](./resolvers.md) — the resolver shape `mocks.resolvers` replaces and the retry/cancel semantics tests need to know about
411
+ - [`core-patterns.md`](./core-patterns.md) — the module shape `createTestSystem({ module })` is testing
412
+ - [`history.md`](./history.md) — recorded snapshots for replay-based testing patterns
package/dist/index.cjs CHANGED
@@ -1,12 +1,16 @@
1
- 'use strict';var fs=require('fs'),path=require('path'),url=require('url');var _documentCurrentScript=typeof document!=='undefined'?document.currentScript:null;var h=path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)))),g=path.resolve(h,"..");function s(n){let t=path.join(g,n);if(fs.existsSync(t))return t;let e=path.join(g,"..",n);return fs.existsSync(e)?e:t}var w=s("core"),A=s("ai"),y=s("examples"),R=s("api-skeleton.md"),r=null,i=null;function a(n,t){try{let e=fs.readdirSync(n).filter(o=>o.endsWith(".md")||o.endsWith(".ts"));for(let o of e){let m=o.replace(/\.(md|ts)$/,"");t.set(m,fs.readFileSync(path.join(n,o),"utf-8"));}}catch(e){if(e.code!=="ENOENT")throw e}}function u(){let n=new Map;a(w,n),a(A,n);try{n.set("api-skeleton",fs.readFileSync(R,"utf-8"));}catch{}return n}function f(){let n=new Map;return a(y,n),n}function _(n){return r||(r=u()),r.get(n)??""}function O(){return r||(r=u()),r}function S(n){return i||(i=f()),i.get(n)??""}function P(){return i||(i=f()),i}function T(n){return n.map(t=>_(t)).filter(Boolean).join(`
1
+ 'use strict';var fs=require('fs'),path=require('path'),url=require('url');var _documentCurrentScript=typeof document!=='undefined'?document.currentScript:null;var L=path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)))),P=path.resolve(L,".."),a=null;function k(){let t=[path.join(P,"core","anti-patterns.md"),path.join(P,"..","core","anti-patterns.md")];for(let e of t)if(fs.existsSync(e))return e;return t[0]??""}function z(t){return t.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").replace(/-{2,}/g,"-")}function K(t){let e=t.toLowerCase();return /schema|builder/.test(e)?"schema":/constraint|cross-?module|require/.test(e)?"constraint":/resolver|settle|start/.test(e)?"resolver":/deriv|passthrough/.test(e)?"derivation":/effect/.test(e)?"effect":/usedirective|react|hook/.test(e)?"react":/import|bracket|deep import|name|context|abbreviat/.test(e)?"naming":("module")}function G(t){let e=t.toLowerCase();return /nonexistent|missing|no error|deep import|async logic/.test(e)?"error":"warning"}function U(t){let e=t.split(`
2
+ `),r=[],n=null,o=/^##\s+(\d+)\.\s+(.+?)\s*$/;for(let s of e){let i=o.exec(s);if(i?.[1]&&i?.[2]){n&&r.push(n),n={number:Number(i[1]),title:i[2],body:""};continue}if(n){if(/^##\s+\D/.test(s)){r.push(n),n=null;break}n.body+=`${s}
3
+ `;}}return n&&r.push(n),r}function $(t){let e=t.match(/```typescript\n([\s\S]*?)```/),r=[];for(let p of t.split(`
4
+ `)){if(p.startsWith("```"))break;r.push(p);}let n=r.join(`
5
+ `).trim();if(!e?.[1])return {explanation:n};let o=e[1],s=o.match(/\/\/\s*WRONG[^\n]*\n([\s\S]*?)(?:\/\/\s*CORRECT|$)/),i=o.match(/\/\/\s*CORRECT[^\n]*\n([\s\S]*)/);return {explanation:n,badExample:s?.[1]?.trim()??void 0,goodExample:i?.[1]?.trim()??void 0}}function h(){if(a)return a;let t=k(),e;try{e=fs.readFileSync(t,"utf-8");}catch{return a=Object.freeze([]),a}let n=U(e).map(o=>{let{explanation:s,badExample:i,goodExample:p}=$(o.body);return {id:z(o.title),number:o.number,title:o.title,severity:G(o.title),category:K(o.title),explanation:s,badExample:i,goodExample:p}});return a=Object.freeze(n),a}function D(){return h()}function B(t){return h().find(e=>e.id===t)}function W(){a=null;}var V=path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)))),C=path.resolve(V,".."),R=["redux","zustand","xstate","mobx","jotai","recoil"],u=null;function Y(){let t=[path.join(C,"migration.json"),path.join(C,"..","migration.json")];for(let e of t)if(fs.existsSync(e))return e;return t[0]??""}function S(){if(u)return u;try{let t=fs.readFileSync(Y(),"utf-8"),e=JSON.parse(t);u=Object.freeze(e.sources??[]);}catch{u=Object.freeze([]);}return u}function Z(){return R}function tt(){return S()}function et(t){return S().find(e=>e.id===t)}function nt(){u=null;}var ct=path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)))),E=path.resolve(ct,".."),g=null;function lt(){let t=[path.join(E,"compositions.json"),path.join(E,"..","compositions.json")];for(let e of t)if(fs.existsSync(e))return e;return t[0]??""}function m(){if(g)return g;try{let t=fs.readFileSync(lt(),"utf-8"),e=JSON.parse(t);g=Object.freeze(e.edges??[]);}catch{g=Object.freeze([]);}return g}function ut(){return m()}function gt(t){return m().filter(e=>e.from===t)}function pt(t){return m().filter(e=>e.to===t)}function ft(){g=null;}var Pt=path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)))),v=path.resolve(Pt,"..");function f(t){let e=path.join(v,t);if(fs.existsSync(e))return e;let r=path.join(v,"..",t);return fs.existsSync(r)?r:e}var ht=f("core"),At=f("ai"),Ct=f("examples"),Rt=f("api-skeleton.md"),c=null,l=null;function y(t,e){try{let r=fs.readdirSync(t).filter(n=>n.endsWith(".md")||n.endsWith(".ts"));for(let n of r){let o=n.replace(/\.(md|ts)$/,"");e.set(o,fs.readFileSync(path.join(t,n),"utf-8"));}}catch(r){if(r.code!=="ENOENT")throw r}}function w(){let t=new Map;y(ht,t),y(At,t);try{t.set("api-skeleton",fs.readFileSync(Rt,"utf-8"));}catch{}return t}function _(){let t=new Map;return y(Ct,t),t}function St(t){return c||(c=w()),c.get(t)??""}function Gt(){return c||(c=w()),c}function Mt(t){return l||(l=_()),l.get(t)??""}function Ut(){return l||(l=_()),l}function $t(t){return t.map(e=>St(e)).filter(Boolean).join(`
2
6
 
3
7
  ---
4
8
 
5
- `)}function k(n){return n.map(t=>{let e=S(t);return e?`### Example: ${t}
9
+ `)}function Dt(t){return t.map(e=>{let r=Mt(e);return r?`### Example: ${e}
6
10
 
7
11
  \`\`\`typescript
8
- ${e}
12
+ ${r}
9
13
  \`\`\``:""}).filter(Boolean).join(`
10
14
 
11
- `)}function v(){r=null,i=null;}exports.clearCache=v;exports.getAllExamples=P;exports.getAllKnowledge=O;exports.getExample=S;exports.getExampleFiles=k;exports.getKnowledge=_;exports.getKnowledgeFiles=T;//# sourceMappingURL=index.cjs.map
15
+ `)}function Bt(){c=null,l=null;}exports.MIGRATION_SOURCES=R;exports.clearAntiPatternCache=W;exports.clearCache=Bt;exports.clearCompositionsCache=ft;exports.clearMigrationCache=nt;exports.getAllExamples=Ut;exports.getAllKnowledge=Gt;exports.getAntiPatternById=B;exports.getAntiPatterns=D;exports.getCompositions=ut;exports.getCompositionsFor=gt;exports.getExample=Mt;exports.getExampleFiles=Dt;exports.getKnowledge=St;exports.getKnowledgeFiles=$t;exports.getMigrationPattern=et;exports.getMigrationPatterns=tt;exports.getMigrationSources=Z;exports.getReverseCompositionsFor=pt;//# sourceMappingURL=index.cjs.map
12
16
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":["__dirname","dirname","fileURLToPath","PKG_ROOT","resolve","resolveAsset","name","fromDist","join","existsSync","fromSrc","CORE_DIR","AI_DIR","EXAMPLES_DIR","API_SKELETON_PATH","knowledgeCache","exampleCache","loadDir","dir","map","files","readdirSync","f","file","readFileSync","err","loadAllKnowledge","loadAllExamples","getKnowledge","getAllKnowledge","getExample","getAllExamples","getKnowledgeFiles","names","getExampleFiles","content","clearCache"],"mappings":"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","file":"index.cjs","sourcesContent":["import { existsSync, readFileSync, readdirSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n// Resolve package root: works both in src/ (dev) and dist/ (bundled)\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst PKG_ROOT = resolve(__dirname, \"..\");\n\n/**\n * Resolve a path relative to the package root.\n * Tries dist-relative first (bundled), then src-relative (dev).\n */\nfunction resolveAsset(name: string): string {\n const fromDist = join(PKG_ROOT, name);\n if (existsSync(fromDist)) {\n return fromDist;\n }\n\n const fromSrc = join(PKG_ROOT, \"..\", name);\n if (existsSync(fromSrc)) {\n return fromSrc;\n }\n\n return fromDist; // default, will just return empty maps\n}\n\nconst CORE_DIR = resolveAsset(\"core\");\nconst AI_DIR = resolveAsset(\"ai\");\nconst EXAMPLES_DIR = resolveAsset(\"examples\");\nconst API_SKELETON_PATH = resolveAsset(\"api-skeleton.md\");\n\nlet knowledgeCache: Map<string, string> | null = null;\nlet exampleCache: Map<string, string> | null = null;\n\nfunction loadDir(dir: string, map: Map<string, string>): void {\n try {\n const files = readdirSync(dir).filter(\n (f) => f.endsWith(\".md\") || f.endsWith(\".ts\"),\n );\n for (const file of files) {\n const name = file.replace(/\\.(md|ts)$/, \"\");\n map.set(name, readFileSync(join(dir, file), \"utf-8\"));\n }\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") {\n throw err;\n }\n // directory does not exist — expected during tests or incomplete installs\n }\n}\n\nfunction loadAllKnowledge(): Map<string, string> {\n const map = new Map<string, string>();\n loadDir(CORE_DIR, map);\n loadDir(AI_DIR, map);\n\n // Include api-skeleton\n try {\n map.set(\"api-skeleton\", readFileSync(API_SKELETON_PATH, \"utf-8\"));\n } catch {\n // may not exist yet\n }\n\n return map;\n}\n\nfunction loadAllExamples(): Map<string, string> {\n const map = new Map<string, string>();\n loadDir(EXAMPLES_DIR, map);\n\n return map;\n}\n\nexport function getKnowledge(name: string): string {\n if (!knowledgeCache) {\n knowledgeCache = loadAllKnowledge();\n }\n\n return knowledgeCache.get(name) ?? \"\";\n}\n\nexport function getAllKnowledge(): ReadonlyMap<string, string> {\n if (!knowledgeCache) {\n knowledgeCache = loadAllKnowledge();\n }\n\n return knowledgeCache;\n}\n\nexport function getExample(name: string): string {\n if (!exampleCache) {\n exampleCache = loadAllExamples();\n }\n\n return exampleCache.get(name) ?? \"\";\n}\n\nexport function getAllExamples(): ReadonlyMap<string, string> {\n if (!exampleCache) {\n exampleCache = loadAllExamples();\n }\n\n return exampleCache;\n}\n\nexport function getKnowledgeFiles(names: string[]): string {\n return names\n .map((name) => getKnowledge(name))\n .filter(Boolean)\n .join(\"\\n\\n---\\n\\n\");\n}\n\nexport function getExampleFiles(names: string[]): string {\n return names\n .map((name) => {\n const content = getExample(name);\n if (!content) {\n return \"\";\n }\n\n return `### Example: ${name}\\n\\n\\`\\`\\`typescript\\n${content}\\n\\`\\`\\``;\n })\n .filter(Boolean)\n .join(\"\\n\\n\");\n}\n\n/** Clear cached knowledge and examples. Useful for dev/watch mode. */\nexport function clearCache(): void {\n knowledgeCache = null;\n exampleCache = null;\n}\n"]}
1
+ {"version":3,"sources":["../src/parsers/anti-patterns.ts","../src/parsers/migration.ts","../src/parsers/compositions.ts","../src/index.ts"],"names":["__dirname","dirname","fileURLToPath","PKG_ROOT","resolve","cache","resolveSourcePath","candidates","join","c","existsSync","slugify","title","categorize","lower","severityFor","splitSections","md","lines","sections","current","heading","line","match","extractExamples","body","codeBlock","explanationLines","explanation","code","wrongMatch","correctMatch","loadAntiPatterns","sourcePath","readFileSync","out","section","badExample","goodExample","getAntiPatterns","getAntiPatternById","id","ap","clearAntiPatternCache","MIGRATION_SOURCES","load","raw","parsed","getMigrationSources","getMigrationPatterns","getMigrationPattern","source","p","clearMigrationCache","getCompositions","getCompositionsFor","packageName","getReverseCompositionsFor","clearCompositionsCache","resolveAsset","name","fromDist","fromSrc","CORE_DIR","AI_DIR","EXAMPLES_DIR","API_SKELETON_PATH","knowledgeCache","exampleCache","loadDir","dir","map","files","readdirSync","f","file","err","loadAllKnowledge","loadAllExamples","getKnowledge","getAllKnowledge","getExample","getAllExamples","getKnowledgeFiles","names","getExampleFiles","content","clearCache"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Parser for `packages/knowledge/core/anti-patterns.md` → structured\n * `AntiPattern[]` data. Lazy + cached so the cost is paid once per\n * process at first call.\n *\n * The source markdown is organized as numbered sections (`## N. Title`)\n * each containing a TypeScript code block with `// WRONG` and\n * `// CORRECT` examples. Some sections include prose between the\n * heading and the code block — captured as `explanation`.\n *\n * IDs are kebab-cased slugs derived from the heading text; stable\n * across releases as long as the heading isn't renamed.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst PKG_ROOT = resolve(__dirname, \"..\");\n\nexport type AntiPatternSeverity = \"error\" | \"warning\" | \"info\";\nexport type AntiPatternCategory =\n | \"module\"\n | \"schema\"\n | \"constraint\"\n | \"resolver\"\n | \"derivation\"\n | \"effect\"\n | \"naming\"\n | \"react\"\n | \"composition\";\n\nexport interface AntiPattern {\n /** Stable slug, derived from the heading text. */\n id: string;\n /** Section number in the source markdown (1..N). */\n number: number;\n /** Human-readable title (the heading text minus the number prefix). */\n title: string;\n /** Default severity. Heuristic; tunable per-rule later. */\n severity: AntiPatternSeverity;\n /** Heuristic category from title keywords. */\n category: AntiPatternCategory;\n /** Body prose between heading and the first code block (often empty). */\n explanation: string;\n /** The `// WRONG` example, if present. */\n badExample?: string;\n /** The `// CORRECT` example, if present. */\n goodExample?: string;\n}\n\nlet cache: ReadonlyArray<AntiPattern> | null = null;\n\nfunction resolveSourcePath(): string {\n const candidates = [\n join(PKG_ROOT, \"core\", \"anti-patterns.md\"),\n join(PKG_ROOT, \"..\", \"core\", \"anti-patterns.md\"),\n ];\n for (const c of candidates) {\n if (existsSync(c)) {\n return c;\n }\n }\n return candidates[0] ?? \"\";\n}\n\nfunction slugify(title: string): string {\n return title\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .replace(/-{2,}/g, \"-\");\n}\n\nfunction categorize(title: string): AntiPatternCategory {\n const lower = title.toLowerCase();\n if (/schema|builder/.test(lower)) {\n return \"schema\";\n }\n if (/constraint|cross-?module|require/.test(lower)) {\n return \"constraint\";\n }\n if (/resolver|settle|start/.test(lower)) {\n return \"resolver\";\n }\n if (/deriv|passthrough/.test(lower)) {\n return \"derivation\";\n }\n if (/effect/.test(lower)) {\n return \"effect\";\n }\n if (/usedirective|react|hook/.test(lower)) {\n return \"react\";\n }\n if (/import|bracket|deep import|name|context|abbreviat/.test(lower)) {\n return \"naming\";\n }\n if (/init|module|config/.test(lower)) {\n return \"module\";\n }\n return \"module\";\n}\n\nfunction severityFor(title: string): AntiPatternSeverity {\n const lower = title.toLowerCase();\n if (/nonexistent|missing|no error|deep import|async logic/.test(lower)) {\n return \"error\";\n }\n return \"warning\";\n}\n\ninterface Section {\n number: number;\n title: string;\n body: string;\n}\n\nfunction splitSections(md: string): Section[] {\n const lines = md.split(\"\\n\");\n const sections: Section[] = [];\n let current: Section | null = null;\n const heading = /^##\\s+(\\d+)\\.\\s+(.+?)\\s*$/;\n for (const line of lines) {\n const match = heading.exec(line);\n if (match?.[1] && match?.[2]) {\n if (current) {\n sections.push(current);\n }\n current = {\n number: Number(match[1]),\n title: match[2],\n body: \"\",\n };\n continue;\n }\n if (current) {\n // Stop at the first non-numbered ## section (e.g. \"Quick Reference Checklist\")\n if (/^##\\s+\\D/.test(line)) {\n sections.push(current);\n current = null;\n break;\n }\n current.body += `${line}\\n`;\n }\n }\n if (current) {\n sections.push(current);\n }\n return sections;\n}\n\nfunction extractExamples(body: string): {\n explanation: string;\n badExample?: string;\n goodExample?: string;\n} {\n const codeBlock = body.match(/```typescript\\n([\\s\\S]*?)```/);\n const explanationLines: string[] = [];\n for (const line of body.split(\"\\n\")) {\n if (line.startsWith(\"```\")) {\n break;\n }\n explanationLines.push(line);\n }\n const explanation = explanationLines.join(\"\\n\").trim();\n\n if (!codeBlock?.[1]) {\n return { explanation };\n }\n\n const code = codeBlock[1];\n // Split on the WRONG/CORRECT comment markers.\n const wrongMatch = code.match(\n /\\/\\/\\s*WRONG[^\\n]*\\n([\\s\\S]*?)(?:\\/\\/\\s*CORRECT|$)/,\n );\n const correctMatch = code.match(/\\/\\/\\s*CORRECT[^\\n]*\\n([\\s\\S]*)/);\n\n return {\n explanation,\n badExample: wrongMatch?.[1]?.trim() ?? undefined,\n goodExample: correctMatch?.[1]?.trim() ?? undefined,\n };\n}\n\nfunction loadAntiPatterns(): ReadonlyArray<AntiPattern> {\n if (cache) {\n return cache;\n }\n\n const sourcePath = resolveSourcePath();\n let md: string;\n try {\n md = readFileSync(sourcePath, \"utf-8\");\n } catch {\n cache = Object.freeze([]);\n return cache;\n }\n\n const sections = splitSections(md);\n const out: AntiPattern[] = sections.map((section) => {\n const { explanation, badExample, goodExample } = extractExamples(\n section.body,\n );\n return {\n id: slugify(section.title),\n number: section.number,\n title: section.title,\n severity: severityFor(section.title),\n category: categorize(section.title),\n explanation,\n badExample,\n goodExample,\n };\n });\n\n cache = Object.freeze(out);\n return cache;\n}\n\n/** Return every parsed anti-pattern, stable order, frozen. */\nexport function getAntiPatterns(): ReadonlyArray<AntiPattern> {\n return loadAntiPatterns();\n}\n\n/** Look up one anti-pattern by its slug id. */\nexport function getAntiPatternById(id: string): AntiPattern | undefined {\n return loadAntiPatterns().find((ap) => ap.id === id);\n}\n\n/** Clear the in-memory cache. Test / watch-mode helper. */\nexport function clearAntiPatternCache(): void {\n cache = null;\n}\n","/**\n * Loader for `packages/knowledge/migration.json` → structured\n * migration patterns. Lazy + cached.\n *\n * The JSON ships in the published tarball; consumers don't need to\n * parse markdown at runtime.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst PKG_ROOT = resolve(__dirname, \"..\");\n\n/** Supported source-library identifiers. */\nexport const MIGRATION_SOURCES = [\n \"redux\",\n \"zustand\",\n \"xstate\",\n \"mobx\",\n \"jotai\",\n \"recoil\",\n] as const;\nexport type MigrationSourceId = (typeof MIGRATION_SOURCES)[number];\n\nexport interface MigrationConceptRow {\n from: string;\n to: string;\n note: string;\n}\n\nexport interface MigrationPattern {\n id: MigrationSourceId;\n name: string;\n conceptMap: MigrationConceptRow[];\n steps: string[];\n before: string;\n after: string;\n}\n\ninterface MigrationFile {\n version: number;\n sources: MigrationPattern[];\n}\n\nlet cache: ReadonlyArray<MigrationPattern> | null = null;\n\nfunction resolveSourcePath(): string {\n const candidates = [\n join(PKG_ROOT, \"migration.json\"),\n join(PKG_ROOT, \"..\", \"migration.json\"),\n ];\n for (const c of candidates) {\n if (existsSync(c)) {\n return c;\n }\n }\n return candidates[0] ?? \"\";\n}\n\nfunction load(): ReadonlyArray<MigrationPattern> {\n if (cache) {\n return cache;\n }\n try {\n const raw = readFileSync(resolveSourcePath(), \"utf-8\");\n const parsed = JSON.parse(raw) as MigrationFile;\n cache = Object.freeze(parsed.sources ?? []);\n } catch {\n cache = Object.freeze([]);\n }\n return cache;\n}\n\n/** All supported source library IDs. */\nexport function getMigrationSources(): ReadonlyArray<MigrationSourceId> {\n return MIGRATION_SOURCES;\n}\n\n/** All migration patterns, in registry order. */\nexport function getMigrationPatterns(): ReadonlyArray<MigrationPattern> {\n return load();\n}\n\n/** One migration pattern by its source-library id. */\nexport function getMigrationPattern(\n source: string,\n): MigrationPattern | undefined {\n return load().find((p) => p.id === source);\n}\n\n/** Test / watch-mode helper. */\nexport function clearMigrationCache(): void {\n cache = null;\n}\n","/**\n * Loader for `packages/knowledge/compositions.json` → typed\n * composition graph. Lazy + cached.\n *\n * The JSON ships in the published tarball. Adjacency list rather\n * than per-package projections so consumers can answer both\n * \"what does X compose with?\" and \"what composes with X?\" without\n * duplicate data.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n// After bundling, all parsers live in dist/index.js — one level up\n// from dist reaches the package root. In src/ during dev, the file is\n// two levels up; the fallback in `resolveSourcePath` handles that.\nconst PKG_ROOT = resolve(__dirname, \"..\");\n\nexport interface CompositionEdge {\n from: string;\n to: string;\n reason: string;\n}\n\ninterface CompositionsFile {\n version: number;\n edges: CompositionEdge[];\n}\n\nlet cache: ReadonlyArray<CompositionEdge> | null = null;\n\nfunction resolveSourcePath(): string {\n const candidates = [\n join(PKG_ROOT, \"compositions.json\"),\n join(PKG_ROOT, \"..\", \"compositions.json\"),\n ];\n for (const c of candidates) {\n if (existsSync(c)) {\n return c;\n }\n }\n return candidates[0] ?? \"\";\n}\n\nfunction load(): ReadonlyArray<CompositionEdge> {\n if (cache) {\n return cache;\n }\n try {\n const raw = readFileSync(resolveSourcePath(), \"utf-8\");\n const parsed = JSON.parse(raw) as CompositionsFile;\n cache = Object.freeze(parsed.edges ?? []);\n } catch {\n cache = Object.freeze([]);\n }\n return cache;\n}\n\n/** All composition edges as a flat list. */\nexport function getCompositions(): ReadonlyArray<CompositionEdge> {\n return load();\n}\n\n/**\n * All edges originating at the given package. Returns an empty array\n * when the package is unknown or has no outgoing compositions.\n */\nexport function getCompositionsFor(\n packageName: string,\n): ReadonlyArray<CompositionEdge> {\n return load().filter((e) => e.from === packageName);\n}\n\n/**\n * All edges arriving at the given package — \"who composes WITH me?\"\n */\nexport function getReverseCompositionsFor(\n packageName: string,\n): ReadonlyArray<CompositionEdge> {\n return load().filter((e) => e.to === packageName);\n}\n\n/** Test / watch-mode helper. */\nexport function clearCompositionsCache(): void {\n cache = null;\n}\n","import { existsSync, readFileSync, readdirSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n// Resolve package root: works both in src/ (dev) and dist/ (bundled)\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst PKG_ROOT = resolve(__dirname, \"..\");\n\n/**\n * Resolve a path relative to the package root.\n * Tries dist-relative first (bundled), then src-relative (dev).\n */\nfunction resolveAsset(name: string): string {\n const fromDist = join(PKG_ROOT, name);\n if (existsSync(fromDist)) {\n return fromDist;\n }\n\n const fromSrc = join(PKG_ROOT, \"..\", name);\n if (existsSync(fromSrc)) {\n return fromSrc;\n }\n\n return fromDist; // default, will just return empty maps\n}\n\nconst CORE_DIR = resolveAsset(\"core\");\nconst AI_DIR = resolveAsset(\"ai\");\nconst EXAMPLES_DIR = resolveAsset(\"examples\");\nconst API_SKELETON_PATH = resolveAsset(\"api-skeleton.md\");\n\nlet knowledgeCache: Map<string, string> | null = null;\nlet exampleCache: Map<string, string> | null = null;\n\nfunction loadDir(dir: string, map: Map<string, string>): void {\n try {\n const files = readdirSync(dir).filter(\n (f) => f.endsWith(\".md\") || f.endsWith(\".ts\"),\n );\n for (const file of files) {\n const name = file.replace(/\\.(md|ts)$/, \"\");\n map.set(name, readFileSync(join(dir, file), \"utf-8\"));\n }\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") {\n throw err;\n }\n // directory does not exist — expected during tests or incomplete installs\n }\n}\n\nfunction loadAllKnowledge(): Map<string, string> {\n const map = new Map<string, string>();\n loadDir(CORE_DIR, map);\n loadDir(AI_DIR, map);\n\n // Include api-skeleton\n try {\n map.set(\"api-skeleton\", readFileSync(API_SKELETON_PATH, \"utf-8\"));\n } catch {\n // may not exist yet\n }\n\n return map;\n}\n\nfunction loadAllExamples(): Map<string, string> {\n const map = new Map<string, string>();\n loadDir(EXAMPLES_DIR, map);\n\n return map;\n}\n\nexport function getKnowledge(name: string): string {\n if (!knowledgeCache) {\n knowledgeCache = loadAllKnowledge();\n }\n\n return knowledgeCache.get(name) ?? \"\";\n}\n\nexport function getAllKnowledge(): ReadonlyMap<string, string> {\n if (!knowledgeCache) {\n knowledgeCache = loadAllKnowledge();\n }\n\n return knowledgeCache;\n}\n\nexport function getExample(name: string): string {\n if (!exampleCache) {\n exampleCache = loadAllExamples();\n }\n\n return exampleCache.get(name) ?? \"\";\n}\n\nexport function getAllExamples(): ReadonlyMap<string, string> {\n if (!exampleCache) {\n exampleCache = loadAllExamples();\n }\n\n return exampleCache;\n}\n\nexport function getKnowledgeFiles(names: string[]): string {\n return names\n .map((name) => getKnowledge(name))\n .filter(Boolean)\n .join(\"\\n\\n---\\n\\n\");\n}\n\nexport function getExampleFiles(names: string[]): string {\n return names\n .map((name) => {\n const content = getExample(name);\n if (!content) {\n return \"\";\n }\n\n return `### Example: ${name}\\n\\n\\`\\`\\`typescript\\n${content}\\n\\`\\`\\``;\n })\n .filter(Boolean)\n .join(\"\\n\\n\");\n}\n\n/** Clear cached knowledge and examples. Useful for dev/watch mode. */\nexport function clearCache(): void {\n knowledgeCache = null;\n exampleCache = null;\n}\n\n// ---------------------------------------------------------------------------\n// Structured data APIs (v1.16.0 — back the MCP `list_review_rules`,\n// `get_review_rule`, `list_migration_sources`, `get_migration_pattern`,\n// `get_composable_packages` tools).\n// ---------------------------------------------------------------------------\n\nexport {\n type AntiPattern,\n type AntiPatternCategory,\n type AntiPatternSeverity,\n clearAntiPatternCache,\n getAntiPatternById,\n getAntiPatterns,\n} from \"./parsers/anti-patterns.js\";\n\nexport {\n type MigrationConceptRow,\n type MigrationPattern,\n type MigrationSourceId,\n MIGRATION_SOURCES,\n clearMigrationCache,\n getMigrationPattern,\n getMigrationPatterns,\n getMigrationSources,\n} from \"./parsers/migration.js\";\n\nexport {\n type CompositionEdge,\n clearCompositionsCache,\n getCompositions,\n getCompositionsFor,\n getReverseCompositionsFor,\n} from \"./parsers/compositions.js\";\n"]}