@directive-run/knowledge 0.2.0 → 0.4.2

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 (54) hide show
  1. package/README.md +3 -3
  2. package/ai/ai-adapters.md +7 -7
  3. package/ai/ai-agents-streaming.md +8 -8
  4. package/ai/ai-budget-resilience.md +5 -5
  5. package/ai/ai-communication.md +1 -1
  6. package/ai/ai-guardrails-memory.md +7 -7
  7. package/ai/ai-mcp-rag.md +5 -5
  8. package/ai/ai-multi-agent.md +14 -14
  9. package/ai/ai-orchestrator.md +8 -8
  10. package/ai/ai-security.md +2 -2
  11. package/ai/ai-tasks.md +9 -9
  12. package/ai/ai-testing-evals.md +2 -2
  13. package/core/anti-patterns.md +39 -39
  14. package/core/constraints.md +15 -15
  15. package/core/core-patterns.md +9 -9
  16. package/core/error-boundaries.md +7 -7
  17. package/core/multi-module.md +16 -16
  18. package/core/naming.md +21 -21
  19. package/core/plugins.md +14 -14
  20. package/core/react-adapter.md +13 -13
  21. package/core/resolvers.md +14 -14
  22. package/core/schema-types.md +22 -22
  23. package/core/system-api.md +16 -16
  24. package/core/testing.md +5 -5
  25. package/core/time-travel.md +20 -20
  26. package/dist/index.cjs +6 -105
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.js +7 -97
  29. package/dist/index.js.map +1 -1
  30. package/examples/ab-testing.ts +18 -90
  31. package/examples/ai-checkpoint.ts +68 -87
  32. package/examples/ai-guardrails.ts +20 -70
  33. package/examples/auth-flow.ts +2 -2
  34. package/examples/batch-resolver.ts +19 -59
  35. package/examples/contact-form.ts +220 -69
  36. package/examples/counter.ts +77 -95
  37. package/examples/dashboard-loader.ts +37 -55
  38. package/examples/debounce-constraints.ts +0 -2
  39. package/examples/dynamic-modules.ts +17 -20
  40. package/examples/error-boundaries.ts +30 -81
  41. package/examples/newsletter.ts +22 -49
  42. package/examples/notifications.ts +24 -23
  43. package/examples/optimistic-updates.ts +36 -41
  44. package/examples/pagination.ts +2 -2
  45. package/examples/permissions.ts +22 -32
  46. package/examples/provider-routing.ts +26 -83
  47. package/examples/shopping-cart.ts +8 -8
  48. package/examples/sudoku.ts +55 -62
  49. package/examples/theme-locale.ts +4 -7
  50. package/examples/time-machine.ts +12 -90
  51. package/examples/topic-guard.ts +30 -38
  52. package/examples/url-sync.ts +8 -8
  53. package/examples/websocket.ts +5 -5
  54. package/package.json +3 -3
@@ -1,9 +1,9 @@
1
1
  // Example: time-machine
2
- // Source: examples/time-machine/src/main.ts
3
- // Extracted for AI rules — DOM wiring stripped
2
+ // Source: examples/time-machine/src/module.ts
3
+ // Pure module fileno DOM wiring
4
4
 
5
5
  /**
6
- * Time Machine — Time-Travel Debugging
6
+ * Time Machine — Directive Module
7
7
  *
8
8
  * Drawing canvas where each stroke is a fact mutation. Full time-travel:
9
9
  * undo/redo, export/import JSON, replay animation, changesets, snapshot slider.
@@ -21,7 +21,7 @@ import { devtoolsPlugin } from "@directive-run/core/plugins";
21
21
  // Types
22
22
  // ============================================================================
23
23
 
24
- interface Stroke {
24
+ export interface Stroke {
25
25
  id: string;
26
26
  x: number;
27
27
  y: number;
@@ -29,7 +29,7 @@ interface Stroke {
29
29
  size: number;
30
30
  }
31
31
 
32
- interface TimelineEntry {
32
+ export interface TimelineEntry {
33
33
  time: number;
34
34
  event: string;
35
35
  detail: string;
@@ -45,16 +45,16 @@ interface TimelineEntry {
45
45
  }
46
46
 
47
47
  // ============================================================================
48
- // Timeline
48
+ // Timeline (external mutable array, same pattern as fraud-analysis)
49
49
  // ============================================================================
50
50
 
51
- const timeline: TimelineEntry[] = [];
51
+ export const timeline: TimelineEntry[] = [];
52
52
 
53
- function addTimeline(
53
+ export function addTimeline(
54
54
  event: string,
55
55
  detail: string,
56
56
  type: TimelineEntry["type"],
57
- ) {
57
+ ): void {
58
58
  timeline.unshift({ time: Date.now(), event, detail, type });
59
59
  if (timeline.length > 50) {
60
60
  timeline.length = 50;
@@ -65,9 +65,9 @@ function addTimeline(
65
65
  // Schema
66
66
  // ============================================================================
67
67
 
68
- const schema = {
68
+ export const schema = {
69
69
  facts: {
70
- strokes: t.object<Stroke[]>(),
70
+ strokes: t.array<Stroke>(),
71
71
  currentColor: t.string(),
72
72
  brushSize: t.number(),
73
73
  changesetActive: t.boolean(),
@@ -140,86 +140,8 @@ const canvasModule = createModule("canvas", {
140
140
  // System with Time-Travel
141
141
  // ============================================================================
142
142
 
143
- const system = createSystem({
143
+ export const system = createSystem({
144
144
  module: canvasModule,
145
145
  debug: { timeTravel: true, maxSnapshots: 200, runHistory: true },
146
146
  plugins: [devtoolsPlugin({ name: "time-machine" })],
147
147
  });
148
- system.start();
149
-
150
- const tt = system.debug!;
151
-
152
- // ============================================================================
153
- // DOM References
154
- // ============================================================================
155
-
156
- const ctx = canvasEl.getContext("2d")!;
157
- "tm-brush-size",
158
- "tm-snapshot-slider",
159
- "tm-export-area",
160
- "tm-begin-changeset",
161
- "tm-end-changeset",
162
-
163
- // Timeline
164
-
165
- // ============================================================================
166
- // Canvas Rendering
167
- // ============================================================================
168
-
169
- function drawCanvas(): void {
170
- ctx.fillStyle = "#0f172a";
171
- ctx.fillRect(0, 0, canvasEl.width, canvasEl.height);
172
-
173
- const strokes = system.facts.strokes as Stroke[];
174
- for (const stroke of strokes) {
175
- ctx.beginPath();
176
- ctx.arc(stroke.x, stroke.y, stroke.size / 2, 0, Math.PI * 2);
177
- ctx.fillStyle = stroke.color;
178
- ctx.fill();
179
- }
180
- }
181
-
182
- // ============================================================================
183
- // Render
184
- // ============================================================================
185
-
186
- function escapeHtml(text: string): string {
187
-
188
- return div.innerHTML;
189
- }
190
-
191
-
192
- // ============================================================================
193
- // Subscribe
194
- // ============================================================================
195
-
196
- const allKeys = [...Object.keys(schema.facts)];
197
- system.subscribe(allKeys, render);
198
-
199
- // ============================================================================
200
- // Canvas Interaction (pointer events for mouse + touch)
201
- // ============================================================================
202
-
203
- let isDrawing = false;
204
-
205
- function canvasCoords(e: PointerEvent): { x: number; y: number } {
206
- const scaleX = canvasEl.width / rect.width;
207
- const scaleY = canvasEl.height / rect.height;
208
-
209
- return {
210
- x: Math.round((e.clientX - rect.left) * scaleX),
211
- y: Math.round((e.clientY - rect.top) * scaleY),
212
- };
213
- }
214
-
215
-
216
- // ============================================================================
217
- // Controls
218
- // ============================================================================
219
-
220
-
221
- // ============================================================================
222
- // Initial Render
223
- // ============================================================================
224
-
225
- render();
@@ -42,11 +42,11 @@ export interface GuardrailLogEntry {
42
42
  export const topicGuardSchema = {
43
43
  facts: {
44
44
  input: t.string(),
45
- messages: t.object<ChatMessage[]>(),
45
+ messages: t.array<ChatMessage>(),
46
46
  isProcessing: t.boolean(),
47
47
  lastGuardrailResult: t.object<GuardrailResult | null>(),
48
- guardrailLog: t.object<GuardrailLogEntry[]>(),
49
- allowedTopics: t.object<string[]>(),
48
+ guardrailLog: t.array<GuardrailLogEntry>(),
49
+ allowedTopics: t.array<string>(),
50
50
  },
51
51
  derivations: {
52
52
  messageCount: t.number(),
@@ -93,42 +93,36 @@ export const topicGuardModule = createModule("topic-guard", {
93
93
 
94
94
  derive: {
95
95
  messageCount: (facts) => {
96
- return (facts.messages as ChatMessage[]).filter((m) => m.role === "user")
97
- .length;
96
+ return facts.messages.filter((m) => m.role === "user").length;
98
97
  },
99
98
 
100
99
  blockedCount: (facts) => {
101
- return (facts.messages as ChatMessage[]).filter(
102
- (m) => m.role === "user" && m.blocked,
103
- ).length;
100
+ return facts.messages.filter((m) => m.role === "user" && m.blocked)
101
+ .length;
104
102
  },
105
103
 
106
104
  allowedCount: (facts) => {
107
- return (facts.messages as ChatMessage[]).filter(
108
- (m) => m.role === "user" && !m.blocked,
109
- ).length;
105
+ return facts.messages.filter((m) => m.role === "user" && !m.blocked)
106
+ .length;
110
107
  },
111
108
 
112
109
  blockRate: (facts, derive) => {
113
- const total = derive.messageCount as number;
110
+ const total = derive.messageCount;
114
111
  if (total === 0) {
115
112
  return "0%";
116
113
  }
117
- const blocked = derive.blockedCount as number;
114
+ const blocked = derive.blockedCount;
118
115
  const rate = Math.round((blocked / total) * 100);
119
116
 
120
117
  return `${rate}%`;
121
118
  },
122
119
 
123
120
  canSend: (facts) => {
124
- return (
125
- (facts.input as string).trim().length > 0 &&
126
- !(facts.isProcessing as boolean)
127
- );
121
+ return facts.input.trim().length > 0 && !facts.isProcessing;
128
122
  },
129
123
 
130
124
  lastMessageBlocked: (facts) => {
131
- const msgs = facts.messages as ChatMessage[];
125
+ const msgs = facts.messages;
132
126
  if (msgs.length === 0) {
133
127
  return false;
134
128
  }
@@ -143,13 +137,13 @@ export const topicGuardModule = createModule("topic-guard", {
143
137
 
144
138
  events: {
145
139
  send: (facts) => {
146
- const text = (facts.input as string).trim();
140
+ const text = facts.input.trim();
147
141
  if (text.length === 0 || facts.isProcessing) {
148
142
  return;
149
143
  }
150
144
 
151
145
  // Add user message
152
- const messages = [...(facts.messages as ChatMessage[])];
146
+ const messages = [...facts.messages];
153
147
  messages.push({ role: "user", text, blocked: false });
154
148
  facts.messages = messages;
155
149
 
@@ -163,10 +157,7 @@ export const topicGuardModule = createModule("topic-guard", {
163
157
  return;
164
158
  }
165
159
 
166
- const classifierResult = checkTopicClassifier(
167
- text,
168
- facts.allowedTopics as string[],
169
- );
160
+ const classifierResult = checkTopicClassifier(text, facts.allowedTopics);
170
161
  facts.lastGuardrailResult = classifierResult;
171
162
  facts.isProcessing = true;
172
163
  facts.input = "";
@@ -184,7 +175,7 @@ export const topicGuardModule = createModule("topic-guard", {
184
175
  },
185
176
 
186
177
  toggleTopic: (facts, { topic }) => {
187
- const topics = [...(facts.allowedTopics as string[])];
178
+ const topics = [...facts.allowedTopics];
188
179
  const idx = topics.indexOf(topic);
189
180
  if (idx >= 0) {
190
181
  topics.splice(idx, 1);
@@ -203,12 +194,12 @@ export const topicGuardModule = createModule("topic-guard", {
203
194
  offTopicDetected: {
204
195
  priority: 100,
205
196
  when: (facts) => {
206
- const result = facts.lastGuardrailResult as GuardrailResult | null;
207
-
208
- return result?.blocked === true && (facts.isProcessing as boolean);
197
+ return (
198
+ facts.lastGuardrailResult?.blocked === true && facts.isProcessing
199
+ );
209
200
  },
210
201
  require: (facts) => {
211
- const result = facts.lastGuardrailResult as GuardrailResult;
202
+ const result = facts.lastGuardrailResult!;
212
203
 
213
204
  return {
214
205
  type: "BLOCK_MESSAGE",
@@ -221,9 +212,9 @@ export const topicGuardModule = createModule("topic-guard", {
221
212
  onTopicConfirmed: {
222
213
  priority: 90,
223
214
  when: (facts) => {
224
- const result = facts.lastGuardrailResult as GuardrailResult | null;
225
-
226
- return result?.blocked === false && (facts.isProcessing as boolean);
215
+ return (
216
+ facts.lastGuardrailResult?.blocked === false && facts.isProcessing
217
+ );
227
218
  },
228
219
  require: () => ({
229
220
  type: "ALLOW_MESSAGE",
@@ -239,7 +230,7 @@ export const topicGuardModule = createModule("topic-guard", {
239
230
  blockMessage: {
240
231
  requirement: "BLOCK_MESSAGE",
241
232
  resolve: async (req, context) => {
242
- const messages = [...(context.facts.messages as ChatMessage[])];
233
+ const messages = [...context.facts.messages];
243
234
  // Mark the last user message as blocked
244
235
  const lastUserIdx = messages.length - 1;
245
236
  if (lastUserIdx >= 0) {
@@ -264,7 +255,7 @@ export const topicGuardModule = createModule("topic-guard", {
264
255
  allowMessage: {
265
256
  requirement: "ALLOW_MESSAGE",
266
257
  resolve: async (_req, context) => {
267
- const messages = [...(context.facts.messages as ChatMessage[])];
258
+ const messages = [...context.facts.messages];
268
259
  const lastUserMsg = messages.filter((m) => m.role === "user").pop();
269
260
  const responseText = getMockAgentResponse(lastUserMsg?.text ?? "");
270
261
  messages.push({
@@ -286,14 +277,15 @@ export const topicGuardModule = createModule("topic-guard", {
286
277
  logGuardrailResult: {
287
278
  deps: ["lastGuardrailResult"],
288
279
  run: (facts) => {
289
- const result = facts.lastGuardrailResult as GuardrailResult | null;
280
+ const result = facts.lastGuardrailResult;
290
281
  if (!result) {
291
282
  return;
292
283
  }
293
284
 
294
- const msgs = facts.messages as ChatMessage[];
295
- const lastUserMsg = [...msgs].reverse().find((m) => m.role === "user");
296
- const log = [...(facts.guardrailLog as GuardrailLogEntry[])];
285
+ const lastUserMsg = [...facts.messages]
286
+ .reverse()
287
+ .find((m) => m.role === "user");
288
+ const log = [...facts.guardrailLog];
297
289
  log.push({
298
290
  timestamp: Date.now(),
299
291
  input: lastUserMsg?.text ?? "",
@@ -82,7 +82,7 @@ export const urlModule = createModule("url", {
82
82
  const params = readUrlParams();
83
83
  facts.search = params.search;
84
84
  facts.category = params.category;
85
- facts.sortBy = params.sortBy;
85
+ facts.sortBy = params.sortBy as "newest" | "price-asc" | "price-desc";
86
86
  facts.page = params.page;
87
87
  facts.syncingFromUrl = false;
88
88
  },
@@ -103,7 +103,7 @@ export const urlModule = createModule("url", {
103
103
  },
104
104
 
105
105
  setSortBy: (facts, { value }) => {
106
- facts.sortBy = value;
106
+ facts.sortBy = value as "newest" | "price-asc" | "price-desc";
107
107
  facts.page = 1;
108
108
  },
109
109
 
@@ -115,7 +115,7 @@ export const urlModule = createModule("url", {
115
115
  facts.syncingFromUrl = true;
116
116
  facts.search = search;
117
117
  facts.category = category;
118
- facts.sortBy = sortBy;
118
+ facts.sortBy = sortBy as "newest" | "price-asc" | "price-desc";
119
119
  facts.page = page;
120
120
  },
121
121
 
@@ -160,15 +160,15 @@ export const urlModule = createModule("url", {
160
160
  const params = new URLSearchParams();
161
161
 
162
162
  if (facts.search !== "") {
163
- params.set("q", facts.search as string);
163
+ params.set("q", facts.search);
164
164
  }
165
165
  if (facts.category !== "" && facts.category !== "all") {
166
- params.set("cat", facts.category as string);
166
+ params.set("cat", facts.category);
167
167
  }
168
168
  if (facts.sortBy !== "newest") {
169
- params.set("sort", facts.sortBy as string);
169
+ params.set("sort", facts.sortBy);
170
170
  }
171
- if ((facts.page as number) > 1) {
171
+ if (facts.page > 1) {
172
172
  params.set("page", String(facts.page));
173
173
  }
174
174
 
@@ -191,7 +191,7 @@ export const urlModule = createModule("url", {
191
191
 
192
192
  export const productsSchema = {
193
193
  facts: {
194
- items: t.object<Product[]>(),
194
+ items: t.array<Product>(),
195
195
  totalItems: t.number(),
196
196
  isLoading: t.boolean(),
197
197
  itemsPerPage: t.number(),
@@ -49,7 +49,7 @@ export const websocketSchema = {
49
49
  url: t.string(),
50
50
  status: t.string<WsStatus>(),
51
51
  connectRequested: t.boolean(),
52
- messages: t.object<WsMessage[]>(),
52
+ messages: t.array<WsMessage>(),
53
53
  retryCount: t.number(),
54
54
  maxRetries: t.number(),
55
55
  messageToSend: t.string(),
@@ -58,7 +58,7 @@ export const websocketSchema = {
58
58
  messageRate: t.number(),
59
59
  connectFailRate: t.number(),
60
60
  reconnectFailRate: t.number(),
61
- eventLog: t.object<EventLogEntry[]>(),
61
+ eventLog: t.array<EventLogEntry>(),
62
62
  },
63
63
  derivations: {
64
64
  isConnected: t.boolean(),
@@ -100,7 +100,7 @@ export const websocketSchema = {
100
100
  // ============================================================================
101
101
 
102
102
  function addLogEntry(facts: any, event: string, detail: string): void {
103
- const log = [...(facts.eventLog as EventLogEntry[])];
103
+ const log = [...facts.eventLog];
104
104
  log.push({ timestamp: Date.now(), event, detail });
105
105
  // Cap at 100
106
106
  if (log.length > 100) {
@@ -312,7 +312,7 @@ export const websocketModule = createModule("websocket", {
312
312
  return;
313
313
  }
314
314
 
315
- const messages = [...(context.facts.messages as WsMessage[])];
315
+ const messages = [...context.facts.messages];
316
316
  messages.push(msg);
317
317
  // Cap at 50
318
318
  if (messages.length > 50) {
@@ -369,7 +369,7 @@ export const websocketModule = createModule("websocket", {
369
369
  requirement: "RECONNECT",
370
370
  timeout: 60000,
371
371
  resolve: async (req, context) => {
372
- const retryCount = context.facts.retryCount as number;
372
+ const retryCount = context.facts.retryCount;
373
373
  context.facts.status = "reconnecting";
374
374
  context.facts.reconnectTargetTime = Date.now() + req.delay;
375
375
  addLogEntry(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@directive-run/knowledge",
3
- "version": "0.2.0",
3
+ "version": "0.4.2",
4
4
  "description": "Knowledge files, examples, and validation for Directive — the constraint-driven TypeScript runtime.",
5
5
  "license": "MIT",
6
6
  "author": "Jason Comes",
@@ -50,8 +50,8 @@
50
50
  "tsx": "^4.19.2",
51
51
  "typescript": "^5.7.2",
52
52
  "vitest": "^3.0.0",
53
- "@directive-run/core": "0.4.1",
54
- "@directive-run/ai": "0.4.1"
53
+ "@directive-run/core": "0.4.2",
54
+ "@directive-run/ai": "0.4.2"
55
55
  },
56
56
  "scripts": {
57
57
  "build": "tsx scripts/generate-api-skeleton.ts && tsx scripts/extract-examples.ts && tsup",