@directive-run/knowledge 0.2.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.
Files changed (68) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +63 -0
  3. package/ai/ai-adapters.md +250 -0
  4. package/ai/ai-agents-streaming.md +269 -0
  5. package/ai/ai-budget-resilience.md +235 -0
  6. package/ai/ai-communication.md +281 -0
  7. package/ai/ai-debug-observability.md +243 -0
  8. package/ai/ai-guardrails-memory.md +332 -0
  9. package/ai/ai-mcp-rag.md +288 -0
  10. package/ai/ai-multi-agent.md +274 -0
  11. package/ai/ai-orchestrator.md +227 -0
  12. package/ai/ai-security.md +293 -0
  13. package/ai/ai-tasks.md +261 -0
  14. package/ai/ai-testing-evals.md +378 -0
  15. package/api-skeleton.md +5 -0
  16. package/core/anti-patterns.md +382 -0
  17. package/core/constraints.md +263 -0
  18. package/core/core-patterns.md +228 -0
  19. package/core/error-boundaries.md +322 -0
  20. package/core/multi-module.md +315 -0
  21. package/core/naming.md +283 -0
  22. package/core/plugins.md +344 -0
  23. package/core/react-adapter.md +262 -0
  24. package/core/resolvers.md +357 -0
  25. package/core/schema-types.md +262 -0
  26. package/core/system-api.md +271 -0
  27. package/core/testing.md +257 -0
  28. package/core/time-travel.md +238 -0
  29. package/dist/index.cjs +111 -0
  30. package/dist/index.cjs.map +1 -0
  31. package/dist/index.d.cts +10 -0
  32. package/dist/index.d.ts +10 -0
  33. package/dist/index.js +102 -0
  34. package/dist/index.js.map +1 -0
  35. package/examples/ab-testing.ts +385 -0
  36. package/examples/ai-checkpoint.ts +509 -0
  37. package/examples/ai-guardrails.ts +319 -0
  38. package/examples/ai-orchestrator.ts +589 -0
  39. package/examples/async-chains.ts +287 -0
  40. package/examples/auth-flow.ts +371 -0
  41. package/examples/batch-resolver.ts +341 -0
  42. package/examples/checkers.ts +589 -0
  43. package/examples/contact-form.ts +176 -0
  44. package/examples/counter.ts +393 -0
  45. package/examples/dashboard-loader.ts +512 -0
  46. package/examples/debounce-constraints.ts +105 -0
  47. package/examples/dynamic-modules.ts +293 -0
  48. package/examples/error-boundaries.ts +430 -0
  49. package/examples/feature-flags.ts +220 -0
  50. package/examples/form-wizard.ts +347 -0
  51. package/examples/fraud-analysis.ts +663 -0
  52. package/examples/goal-heist.ts +341 -0
  53. package/examples/multi-module.ts +57 -0
  54. package/examples/newsletter.ts +241 -0
  55. package/examples/notifications.ts +210 -0
  56. package/examples/optimistic-updates.ts +317 -0
  57. package/examples/pagination.ts +260 -0
  58. package/examples/permissions.ts +337 -0
  59. package/examples/provider-routing.ts +403 -0
  60. package/examples/server.ts +316 -0
  61. package/examples/shopping-cart.ts +422 -0
  62. package/examples/sudoku.ts +630 -0
  63. package/examples/theme-locale.ts +204 -0
  64. package/examples/time-machine.ts +225 -0
  65. package/examples/topic-guard.ts +306 -0
  66. package/examples/url-sync.ts +333 -0
  67. package/examples/websocket.ts +404 -0
  68. package/package.json +65 -0
@@ -0,0 +1,344 @@
1
+ # Plugins
2
+
3
+ Plugins extend Directive systems with cross-cutting functionality like logging, persistence, devtools, and resilience patterns.
4
+
5
+ ## Decision Tree: "Which plugin do I need?"
6
+
7
+ ```
8
+ What do you want?
9
+ ├── See state changes in console → loggingPlugin()
10
+ ├── Connect to browser DevTools → devtoolsPlugin()
11
+ ├── Persist state across reloads → persistencePlugin(config)
12
+ ├── Protect resolvers from cascading failures → createCircuitBreaker(config)
13
+ ├── Track metrics, traces, alerts → createObservability(config)
14
+ └── Custom cross-cutting behavior → Write a custom plugin
15
+ ```
16
+
17
+ ## Using Built-In Plugins
18
+
19
+ ```typescript
20
+ import { createSystem } from "@directive-run/core";
21
+ import {
22
+ devtoolsPlugin,
23
+ loggingPlugin,
24
+ persistencePlugin,
25
+ } from "@directive-run/core/plugins";
26
+
27
+ const system = createSystem({
28
+ module: myModule,
29
+ plugins: [
30
+ devtoolsPlugin(),
31
+ loggingPlugin({ verbose: true }),
32
+ persistencePlugin({
33
+ key: "my-app-state",
34
+ storage: localStorage,
35
+ }),
36
+ ],
37
+ });
38
+ ```
39
+
40
+ Plugins are applied in order. Place logging first to capture all events including those from other plugins.
41
+
42
+ ## loggingPlugin
43
+
44
+ Logs state changes, requirements, and resolutions to the console.
45
+
46
+ ```typescript
47
+ import { loggingPlugin } from "@directive-run/core/plugins";
48
+
49
+ // Default — logs facts changes and resolver start/complete
50
+ loggingPlugin()
51
+
52
+ // Verbose — logs everything including derivation recomputation and constraint evaluation
53
+ loggingPlugin({ verbose: true })
54
+
55
+ // Custom filter — only log specific events
56
+ loggingPlugin({
57
+ filter: (event) => {
58
+ // Only log resolver events
59
+ if (event.type === "resolution:start" || event.type === "resolution:complete") {
60
+ return true;
61
+ }
62
+
63
+ return false;
64
+ },
65
+ })
66
+ ```
67
+
68
+ ## devtoolsPlugin
69
+
70
+ Connects to the Directive DevTools browser extension for visual state inspection.
71
+
72
+ ```typescript
73
+ import { devtoolsPlugin } from "@directive-run/core/plugins";
74
+
75
+ // Default
76
+ devtoolsPlugin()
77
+
78
+ // With options
79
+ devtoolsPlugin({
80
+ name: "My App", // Name shown in DevTools
81
+ maxAge: 50, // Max actions to keep in history
82
+ })
83
+ ```
84
+
85
+ Only enable in development. The plugin is a no-op if the DevTools extension is not installed.
86
+
87
+ ## persistencePlugin
88
+
89
+ Persists fact state to storage. Restores on system creation.
90
+
91
+ ```typescript
92
+ import { persistencePlugin } from "@directive-run/core/plugins";
93
+
94
+ // localStorage (default)
95
+ persistencePlugin({
96
+ key: "my-app-state",
97
+ storage: localStorage,
98
+ })
99
+
100
+ // sessionStorage — cleared when tab closes
101
+ persistencePlugin({
102
+ key: "session-state",
103
+ storage: sessionStorage,
104
+ })
105
+
106
+ // Custom storage adapter
107
+ persistencePlugin({
108
+ key: "my-app",
109
+ storage: {
110
+ getItem: (key) => myCustomStore.get(key),
111
+ setItem: (key, value) => myCustomStore.set(key, value),
112
+ removeItem: (key) => myCustomStore.delete(key),
113
+ },
114
+ })
115
+
116
+ // Selective persistence — only persist certain facts
117
+ persistencePlugin({
118
+ key: "my-app",
119
+ storage: localStorage,
120
+ include: ["user", "preferences"], // Only these facts
121
+ // OR
122
+ exclude: ["tempData", "sessionId"], // Everything except these
123
+ })
124
+
125
+ // Versioning — handle schema changes
126
+ persistencePlugin({
127
+ key: "my-app",
128
+ storage: localStorage,
129
+ version: 2,
130
+ migrate: (oldState, oldVersion) => {
131
+ if (oldVersion === 1) {
132
+ return {
133
+ ...oldState,
134
+ newField: "default",
135
+ };
136
+ }
137
+
138
+ return oldState;
139
+ },
140
+ })
141
+ ```
142
+
143
+ ## createCircuitBreaker
144
+
145
+ Wraps resolvers with circuit breaker pattern for resilience.
146
+
147
+ ```typescript
148
+ import { createCircuitBreaker } from "@directive-run/core/plugins";
149
+
150
+ const system = createSystem({
151
+ module: myModule,
152
+ plugins: [
153
+ createCircuitBreaker({
154
+ // How many failures before opening the circuit
155
+ failureThreshold: 5,
156
+
157
+ // How long to wait before trying again (ms)
158
+ resetTimeout: 30000,
159
+
160
+ // Optional: only apply to specific resolver types
161
+ include: ["FETCH_DATA", "SYNC_REMOTE"],
162
+
163
+ // Optional: callback when circuit opens
164
+ onOpen: (resolverType) => {
165
+ console.warn(`Circuit opened for ${resolverType}`);
166
+ },
167
+
168
+ // Optional: callback when circuit closes
169
+ onClose: (resolverType) => {
170
+ console.log(`Circuit closed for ${resolverType}`);
171
+ },
172
+ }),
173
+ ],
174
+ });
175
+ ```
176
+
177
+ Circuit breaker states: **Closed** (normal) -> **Open** (failing, rejects immediately) -> **Half-Open** (testing one request).
178
+
179
+ ## createObservability
180
+
181
+ Metrics, tracing, and alerting for production systems.
182
+
183
+ ```typescript
184
+ import { createObservability } from "@directive-run/core/plugins";
185
+
186
+ const system = createSystem({
187
+ module: myModule,
188
+ plugins: [
189
+ createObservability({
190
+ metrics: {
191
+ enabled: true,
192
+ // Track resolver duration, constraint evaluation count, etc.
193
+ },
194
+ tracing: {
195
+ enabled: true,
196
+ // Trace resolver execution with spans
197
+ },
198
+ alerts: {
199
+ enabled: true,
200
+ onAlert: (alert) => {
201
+ // Send to monitoring service
202
+ sendToDatadog(alert);
203
+ },
204
+ },
205
+ }),
206
+ ],
207
+ });
208
+ ```
209
+
210
+ ## Plugin Lifecycle Hooks
211
+
212
+ Plugins hook into the system lifecycle. Use these to build custom plugins.
213
+
214
+ ```typescript
215
+ import type { DirectivePlugin } from "@directive-run/core";
216
+
217
+ const myPlugin: DirectivePlugin = {
218
+ name: "my-custom-plugin",
219
+
220
+ // System lifecycle
221
+ onInit: (system) => {
222
+ // Called when system is created
223
+ },
224
+ onStart: (system) => {
225
+ // Called when system.start() is invoked
226
+ },
227
+ onStop: (system) => {
228
+ // Called when system.stop() is invoked
229
+ },
230
+
231
+ // State tracking
232
+ onSnapshot: (snapshot) => {
233
+ // Called after every fact mutation
234
+ // snapshot.facts contains current state
235
+ // snapshot.changedKeys lists what changed
236
+ },
237
+
238
+ // Requirement pipeline
239
+ onRequirementEmitted: (requirement) => {
240
+ // Called when a constraint emits a requirement
241
+ // requirement.type, requirement.id, payload
242
+ },
243
+ onResolutionStart: (resolution) => {
244
+ // Called when a resolver begins executing
245
+ // resolution.resolverId, resolution.requirement
246
+ },
247
+ onResolutionComplete: (resolution) => {
248
+ // Called when a resolver finishes (success or failure)
249
+ // resolution.resolverId, resolution.duration, resolution.error?
250
+ },
251
+
252
+ // Error handling
253
+ onError: (error, context) => {
254
+ // Called on any error in the system
255
+ // context.source: "resolver" | "constraint" | "effect" | "derivation"
256
+ },
257
+ };
258
+
259
+ const system = createSystem({
260
+ module: myModule,
261
+ plugins: [myPlugin],
262
+ });
263
+ ```
264
+
265
+ ## Common Mistakes
266
+
267
+ ### Enabling devtools in production
268
+
269
+ ```typescript
270
+ // WRONG — devtools overhead in production
271
+ const system = createSystem({
272
+ module: myModule,
273
+ plugins: [devtoolsPlugin()],
274
+ });
275
+
276
+ // CORRECT — conditional on environment
277
+ const plugins = [];
278
+ if (process.env.NODE_ENV === "development") {
279
+ plugins.push(devtoolsPlugin());
280
+ plugins.push(loggingPlugin({ verbose: true }));
281
+ }
282
+
283
+ const system = createSystem({
284
+ module: myModule,
285
+ plugins,
286
+ });
287
+ ```
288
+
289
+ ### Persistence without versioning
290
+
291
+ ```typescript
292
+ // WRONG — schema changes break existing users
293
+ persistencePlugin({
294
+ key: "app-state",
295
+ storage: localStorage,
296
+ })
297
+
298
+ // CORRECT — version and migrate
299
+ persistencePlugin({
300
+ key: "app-state",
301
+ storage: localStorage,
302
+ version: 1,
303
+ migrate: (old, version) => old,
304
+ })
305
+ ```
306
+
307
+ ### Plugin order matters
308
+
309
+ ```typescript
310
+ // WRONG — logging misses events from persistence restore
311
+ const system = createSystem({
312
+ module: myModule,
313
+ plugins: [
314
+ persistencePlugin({ key: "app", storage: localStorage }),
315
+ loggingPlugin(), // Misses restore events
316
+ ],
317
+ });
318
+
319
+ // CORRECT — logging first to capture everything
320
+ const system = createSystem({
321
+ module: myModule,
322
+ plugins: [
323
+ loggingPlugin(),
324
+ persistencePlugin({ key: "app", storage: localStorage }),
325
+ ],
326
+ });
327
+ ```
328
+
329
+ ### Persisting sensitive or transient data
330
+
331
+ ```typescript
332
+ // WRONG — persists auth tokens and loading state
333
+ persistencePlugin({
334
+ key: "app",
335
+ storage: localStorage,
336
+ })
337
+
338
+ // CORRECT — exclude sensitive and transient facts
339
+ persistencePlugin({
340
+ key: "app",
341
+ storage: localStorage,
342
+ exclude: ["authToken", "isLoading", "error"],
343
+ })
344
+ ```
@@ -0,0 +1,262 @@
1
+ # React Adapter
2
+
3
+ The React adapter connects Directive systems to React components. Import from `@directive-run/react`.
4
+
5
+ ## Decision Tree: "How do I use Directive in React?"
6
+
7
+ ```
8
+ What are you building?
9
+ ├── Read system state in a component → useSelector(system, selector)
10
+ ├── Dispatch events from a component → useEvent(system)
11
+ ├── Create a system scoped to a component → useSystem(config)
12
+ ├── Share a system across components → DirectiveProvider + useDirectiveContext()
13
+ └── Global system shared by entire app → Create outside React, use useSelector
14
+ ```
15
+
16
+ ## Setup: System Outside React (Recommended)
17
+
18
+ Create the system outside of React. Components subscribe to it.
19
+
20
+ ```typescript
21
+ // system.ts — created once, imported anywhere
22
+ import { createSystem } from "@directive-run/core";
23
+ import { counterModule } from "./counter-module";
24
+
25
+ export const system = createSystem({ module: counterModule });
26
+ ```
27
+
28
+ ```typescript
29
+ // Counter.tsx
30
+ import { useSelector, useEvent } from "@directive-run/react";
31
+ import { system } from "./system";
32
+
33
+ function Counter() {
34
+ // Subscribe to derived state — re-renders only when value changes
35
+ const count = useSelector(system, (s) => s.facts.count);
36
+ const doubled = useSelector(system, (s) => s.derive.doubled);
37
+
38
+ // Get event dispatcher
39
+ const events = useEvent(system);
40
+
41
+ return (
42
+ <div>
43
+ <p>Count: {count}</p>
44
+ <p>Doubled: {doubled}</p>
45
+ <button onClick={() => events.increment()}>+1</button>
46
+ <button onClick={() => events.reset()}>Reset</button>
47
+ </div>
48
+ );
49
+ }
50
+ ```
51
+
52
+ ## useSelector
53
+
54
+ Subscribes to system state. Re-renders the component only when the selected value changes (shallow comparison).
55
+
56
+ ```typescript
57
+ import { useSelector } from "@directive-run/react";
58
+
59
+ function UserProfile() {
60
+ // Select a single fact
61
+ const name = useSelector(system, (s) => s.facts.userName);
62
+
63
+ // Select a derivation
64
+ const isAdmin = useSelector(system, (s) => s.derive.isAdmin);
65
+
66
+ // Select a computed value from multiple facts
67
+ const summary = useSelector(system, (s) => ({
68
+ name: s.facts.userName,
69
+ role: s.facts.role,
70
+ isAdmin: s.derive.isAdmin,
71
+ }));
72
+
73
+ return <div>{summary.name} ({summary.role})</div>;
74
+ }
75
+ ```
76
+
77
+ ### Multi-Module System
78
+
79
+ ```typescript
80
+ const system = createSystem({
81
+ modules: { auth: authModule, cart: cartModule },
82
+ });
83
+
84
+ function Header() {
85
+ const token = useSelector(system, (s) => s.facts.auth.token);
86
+ const itemCount = useSelector(system, (s) => s.derive.cart.itemCount);
87
+
88
+ return <header>Items: {itemCount}</header>;
89
+ }
90
+ ```
91
+
92
+ ## useEvent
93
+
94
+ Returns the system's event dispatcher. Stable reference (does not cause re-renders).
95
+
96
+ ```typescript
97
+ import { useEvent } from "@directive-run/react";
98
+
99
+ function LoginForm() {
100
+ const events = useEvent(system);
101
+
102
+ const handleSubmit = (email: string, password: string) => {
103
+ events.login({ email, password });
104
+ };
105
+
106
+ return <form onSubmit={() => handleSubmit("a@b.com", "pass")}>...</form>;
107
+ }
108
+
109
+ // Multi-module
110
+ function CartActions() {
111
+ const events = useEvent(system);
112
+
113
+ return (
114
+ <button onClick={() => events.cart.addItem({ productId: "p1" })}>
115
+ Add to Cart
116
+ </button>
117
+ );
118
+ }
119
+ ```
120
+
121
+ ## useSystem
122
+
123
+ Creates and manages a system's lifecycle within a React component. The system is created on mount and destroyed on unmount.
124
+
125
+ ```typescript
126
+ import { useSystem, useSelector, useEvent } from "@directive-run/react";
127
+
128
+ function GameBoard() {
129
+ // System created on mount, destroyed on unmount
130
+ const gameSystem = useSystem({
131
+ module: gameModule,
132
+ debug: { timeTravel: true },
133
+ });
134
+
135
+ const score = useSelector(gameSystem, (s) => s.facts.score);
136
+ const events = useEvent(gameSystem);
137
+
138
+ return (
139
+ <div>
140
+ <p>Score: {score}</p>
141
+ <button onClick={() => events.move({ direction: "up" })}>Up</button>
142
+ </div>
143
+ );
144
+ }
145
+ ```
146
+
147
+ Use `useSystem` when the system's lifecycle matches a component's lifecycle (e.g., a game board, a wizard, a modal form). For app-wide state, create the system outside React.
148
+
149
+ ## DirectiveProvider and useDirectiveContext
150
+
151
+ Share a system through React context.
152
+
153
+ ```typescript
154
+ import { DirectiveProvider, useDirectiveContext, useSelector, useEvent } from "@directive-run/react";
155
+
156
+ // Provide the system at the top of your tree
157
+ function App() {
158
+ return (
159
+ <DirectiveProvider system={system}>
160
+ <Dashboard />
161
+ </DirectiveProvider>
162
+ );
163
+ }
164
+
165
+ // Consume the system anywhere below
166
+ function Dashboard() {
167
+ const system = useDirectiveContext();
168
+ const stats = useSelector(system, (s) => s.derive.dashboardStats);
169
+
170
+ return <div>{stats.totalUsers} users</div>;
171
+ }
172
+ ```
173
+
174
+ ## CRITICAL: Hooks That DO NOT Exist
175
+
176
+ ```typescript
177
+ // WRONG — useDirective() does not exist. This is a common hallucination.
178
+ const { facts, derive, events } = useDirective(system);
179
+
180
+ // CORRECT — use useSelector for state, useEvent for actions
181
+ const count = useSelector(system, (s) => s.facts.count);
182
+ const events = useEvent(system);
183
+ ```
184
+
185
+ ## Common Mistakes
186
+
187
+ ### Creating the system inside a component without useSystem
188
+
189
+ ```typescript
190
+ // WRONG — creates a new system on every render
191
+ function Counter() {
192
+ const system = createSystem({ module: counterModule }); // New system each render!
193
+ const count = useSelector(system, (s) => s.facts.count);
194
+
195
+ return <div>{count}</div>;
196
+ }
197
+
198
+ // CORRECT — create outside the component
199
+ const system = createSystem({ module: counterModule });
200
+
201
+ function Counter() {
202
+ const count = useSelector(system, (s) => s.facts.count);
203
+
204
+ return <div>{count}</div>;
205
+ }
206
+
207
+ // ALSO CORRECT — useSystem manages lifecycle
208
+ function Counter() {
209
+ const system = useSystem({ module: counterModule });
210
+ const count = useSelector(system, (s) => s.facts.count);
211
+
212
+ return <div>{count}</div>;
213
+ }
214
+ ```
215
+
216
+ ### Selecting too much state (causes unnecessary re-renders)
217
+
218
+ ```typescript
219
+ // WRONG — re-renders on ANY fact change
220
+ const allFacts = useSelector(system, (s) => s.facts);
221
+
222
+ // CORRECT — select only what you need
223
+ const name = useSelector(system, (s) => s.facts.userName);
224
+ const count = useSelector(system, (s) => s.facts.count);
225
+ ```
226
+
227
+ ### Mutating facts directly in event handlers
228
+
229
+ ```typescript
230
+ // WRONG — bypass the event system
231
+ function Counter() {
232
+ const count = useSelector(system, (s) => s.facts.count);
233
+
234
+ return (
235
+ <button onClick={() => { system.facts.count += 1; }}>
236
+ {count}
237
+ </button>
238
+ );
239
+ }
240
+
241
+ // CORRECT — use events for intent-driven mutations
242
+ function Counter() {
243
+ const count = useSelector(system, (s) => s.facts.count);
244
+ const events = useEvent(system);
245
+
246
+ return (
247
+ <button onClick={() => events.increment()}>
248
+ {count}
249
+ </button>
250
+ );
251
+ }
252
+ ```
253
+
254
+ ### Casting values from useSelector
255
+
256
+ ```typescript
257
+ // WRONG — unnecessary type casting
258
+ const profile = useSelector(system, (s) => s.facts.profile as UserProfile);
259
+
260
+ // CORRECT — types are inferred from the module schema
261
+ const profile = useSelector(system, (s) => s.facts.profile);
262
+ ```