@directive-run/knowledge 1.1.1 → 1.3.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/react-adapter.md +32 -0
- package/core/system-api.md +45 -17
- package/core/testing.md +41 -0
- package/package.json +3 -3
package/core/react-adapter.md
CHANGED
|
@@ -182,6 +182,38 @@ const count = useSelector(system, (s) => s.facts.count);
|
|
|
182
182
|
const events = useEvent(system);
|
|
183
183
|
```
|
|
184
184
|
|
|
185
|
+
## createDirectiveContext
|
|
186
|
+
|
|
187
|
+
Eliminates prop-drilling by providing the system via React context:
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
import { createDirectiveContext } from "@directive-run/react";
|
|
191
|
+
|
|
192
|
+
const Counter = createDirectiveContext(counterSystem);
|
|
193
|
+
|
|
194
|
+
function App() {
|
|
195
|
+
return (
|
|
196
|
+
<Counter.Provider>
|
|
197
|
+
<Display />
|
|
198
|
+
</Counter.Provider>
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function Display() {
|
|
203
|
+
const count = Counter.useFact("count"); // No system arg needed
|
|
204
|
+
const doubled = Counter.useDerived("doubled");
|
|
205
|
+
const events = Counter.useEvents();
|
|
206
|
+
return <button onClick={() => events.increment()}>{count}</button>;
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Returns: `{ Provider, useSystem, useFact, useDerived, useEvents, useDispatch, useSelector, useWatch, useInspect, useExplain, useHistory }`.
|
|
211
|
+
|
|
212
|
+
Provider accepts `system` prop override for testing:
|
|
213
|
+
```tsx
|
|
214
|
+
<Counter.Provider system={testSystem}><ComponentUnderTest /></Counter.Provider>
|
|
215
|
+
```
|
|
216
|
+
|
|
185
217
|
## Common Mistakes
|
|
186
218
|
|
|
187
219
|
### Creating the system inside a component without useSystem
|
package/core/system-api.md
CHANGED
|
@@ -150,29 +150,35 @@ const total = system.read("cart.totalPrice");
|
|
|
150
150
|
```typescript
|
|
151
151
|
const inspection = system.inspect();
|
|
152
152
|
|
|
153
|
-
//
|
|
154
|
-
inspection.
|
|
155
|
-
// {
|
|
153
|
+
// Unmet requirements
|
|
154
|
+
inspection.unmet;
|
|
155
|
+
// [{ id: "req-1", requirement: { type: "FETCH_USER" }, fromConstraint: "needsUser" }]
|
|
156
156
|
|
|
157
|
-
//
|
|
158
|
-
inspection.
|
|
159
|
-
// {
|
|
157
|
+
// Inflight resolvers
|
|
158
|
+
inspection.inflight;
|
|
159
|
+
// [{ id: "req-2", resolverId: "fetchData", startedAt: 1709000000 }]
|
|
160
160
|
|
|
161
|
-
//
|
|
162
|
-
inspection.
|
|
163
|
-
// [{
|
|
161
|
+
// Facts with meta
|
|
162
|
+
inspection.facts;
|
|
163
|
+
// [{ key: "userId", meta: { label: "User ID" } }, { key: "count" }]
|
|
164
164
|
|
|
165
|
-
//
|
|
166
|
-
inspection.
|
|
167
|
-
// [{ id: "
|
|
165
|
+
// Constraints with state + meta
|
|
166
|
+
inspection.constraints;
|
|
167
|
+
// [{ id: "needsUser", active: true, disabled: false, priority: 0, hitCount: 3, meta: { label: "..." } }]
|
|
168
168
|
|
|
169
|
-
// Resolver
|
|
169
|
+
// Resolver definitions + meta
|
|
170
|
+
inspection.resolverDefs;
|
|
171
|
+
// [{ id: "fetchUser", requirement: "FETCH_USER", meta: { label: "..." } }]
|
|
172
|
+
|
|
173
|
+
// Resolver statuses (inflight only)
|
|
170
174
|
inspection.resolvers;
|
|
171
|
-
// {
|
|
175
|
+
// { "req-1": { state: "running" } }
|
|
172
176
|
|
|
173
|
-
//
|
|
174
|
-
inspection.
|
|
175
|
-
// [{ id: "
|
|
177
|
+
// Effects, derivations, modules — all with optional meta
|
|
178
|
+
inspection.effects; // [{ id: "log", meta: { ... } }]
|
|
179
|
+
inspection.derivations; // [{ id: "doubled", meta: { ... } }]
|
|
180
|
+
inspection.modules; // [{ id: "auth", meta: { ... } }]
|
|
181
|
+
inspection.events; // [{ name: "increment", meta: { ... } }]
|
|
176
182
|
|
|
177
183
|
// Unmet requirements (no matching resolver)
|
|
178
184
|
inspection.unmet;
|
|
@@ -221,6 +227,28 @@ system.meta.byTag("pii"); // MetaMatch[] — all PII-tagged fields
|
|
|
221
227
|
|
|
222
228
|
Meta is frozen at registration (Object.create(null) + Object.freeze). Zero hot-path cost. See [Definition Meta docs](https://directive.run/docs/advanced/meta).
|
|
223
229
|
|
|
230
|
+
## Observation Protocol
|
|
231
|
+
|
|
232
|
+
Typed event stream for all lifecycle events — enables browser extensions, third-party tools, and test assertions:
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
import type { ObservationEvent } from "@directive-run/core";
|
|
236
|
+
|
|
237
|
+
const unsub = system.observe((event: ObservationEvent) => {
|
|
238
|
+
if (event.type === "constraint.evaluate") console.log(event.id, event.active);
|
|
239
|
+
if (event.type === "resolver.complete") console.log(event.resolver, event.duration);
|
|
240
|
+
if (event.type === "fact.change") console.log(event.key, event.prev, "→", event.next);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// 18 event types: fact.change, constraint.evaluate/error, requirement.created/met/canceled,
|
|
244
|
+
// resolver.start/complete/error, effect.run/error, derivation.compute,
|
|
245
|
+
// reconcile.start/end, system.init/start/stop/destroy
|
|
246
|
+
|
|
247
|
+
unsub(); // Stop observing
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Zero overhead when no observers. Implemented as an internal plugin.
|
|
251
|
+
|
|
224
252
|
## Lifecycle
|
|
225
253
|
|
|
226
254
|
```typescript
|
package/core/testing.md
CHANGED
|
@@ -215,6 +215,47 @@ it("processes intermediate state", async () => {
|
|
|
215
215
|
});
|
|
216
216
|
```
|
|
217
217
|
|
|
218
|
+
## Coverage Tracking
|
|
219
|
+
|
|
220
|
+
Track which constraints, resolvers, effects, and derivations are exercised:
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
import { createCoverageTracker } from "@directive-run/core/testing";
|
|
224
|
+
|
|
225
|
+
const { run, report } = createCoverageTracker(system);
|
|
226
|
+
|
|
227
|
+
await run(async () => {
|
|
228
|
+
system.facts.userId = 123;
|
|
229
|
+
await system.settle();
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
const coverage = report();
|
|
233
|
+
// coverage.constraintCoverage — 0-1 (percentage hit)
|
|
234
|
+
// coverage.resolverCoverage — 0-1
|
|
235
|
+
// coverage.effectCoverage — 0-1
|
|
236
|
+
// coverage.derivationCoverage — 0-1
|
|
237
|
+
// coverage.constraintsMissed — Set<string> of IDs never triggered
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## Test Observer
|
|
241
|
+
|
|
242
|
+
Collect observation events for assertion-based testing:
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
import { createTestObserver } from "@directive-run/core/testing";
|
|
246
|
+
|
|
247
|
+
const observer = createTestObserver(system);
|
|
248
|
+
|
|
249
|
+
system.facts.count = 5;
|
|
250
|
+
await system.settle();
|
|
251
|
+
|
|
252
|
+
const evals = observer.ofType("constraint.evaluate");
|
|
253
|
+
expect(evals).toHaveLength(1);
|
|
254
|
+
|
|
255
|
+
observer.clear(); // Reset
|
|
256
|
+
observer.dispose(); // Stop observing
|
|
257
|
+
```
|
|
258
|
+
|
|
218
259
|
## Common Mistakes
|
|
219
260
|
|
|
220
261
|
### Testing real resolvers instead of mocking
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@directive-run/knowledge",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Knowledge files, examples, and validation for Directive — the constraint-driven TypeScript runtime.",
|
|
5
5
|
"license": "(MIT OR Apache-2.0)",
|
|
6
6
|
"author": "Jason Comes",
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
"tsx": "^4.19.2",
|
|
52
52
|
"typescript": "^5.7.2",
|
|
53
53
|
"vitest": "^3.0.0",
|
|
54
|
-
"@directive-run/
|
|
55
|
-
"@directive-run/
|
|
54
|
+
"@directive-run/ai": "1.3.0",
|
|
55
|
+
"@directive-run/core": "1.3.0"
|
|
56
56
|
},
|
|
57
57
|
"scripts": {
|
|
58
58
|
"build": "tsx scripts/generate-api-skeleton.ts && tsx scripts/generate-sitemap.ts && tsx scripts/extract-examples.ts && tsup",
|