@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,512 @@
1
+ // Example: dashboard-loader
2
+ // Source: examples/dashboard-loader/src/dashboard-loader.ts
3
+ // Pure module file — no DOM wiring
4
+
5
+ /**
6
+ * Dashboard Loader — Directive Module
7
+ *
8
+ * Demonstrates loading & error states with concurrent resource fetching,
9
+ * configurable delays/failure rates, retry with exponential backoff,
10
+ * and combined status derivations.
11
+ */
12
+
13
+ import { type ModuleSchema, createModule, t } from "@directive-run/core";
14
+ import {
15
+ type Permissions,
16
+ type Preferences,
17
+ type Profile,
18
+ fetchMockPermissions,
19
+ fetchMockPreferences,
20
+ fetchMockProfile,
21
+ } from "./mock-api.js";
22
+
23
+ // ============================================================================
24
+ // Types
25
+ // ============================================================================
26
+
27
+ export type ResourceStatus = "idle" | "loading" | "success" | "error";
28
+
29
+ export interface ResourceState<T> {
30
+ data: T | null;
31
+ status: ResourceStatus;
32
+ error: string | null;
33
+ attempts: number;
34
+ startedAt: number | null;
35
+ completedAt: number | null;
36
+ }
37
+
38
+ export interface EventLogEntry {
39
+ timestamp: number;
40
+ event: string;
41
+ resource: string;
42
+ detail: string;
43
+ }
44
+
45
+ function makeIdleResource<T>(): ResourceState<T> {
46
+ return {
47
+ data: null,
48
+ status: "idle",
49
+ error: null,
50
+ attempts: 0,
51
+ startedAt: null,
52
+ completedAt: null,
53
+ };
54
+ }
55
+
56
+ // ============================================================================
57
+ // Schema
58
+ // ============================================================================
59
+
60
+ export const dashboardLoaderSchema = {
61
+ facts: {
62
+ userId: t.string(),
63
+ profile: t.object<ResourceState<Profile>>(),
64
+ preferences: t.object<ResourceState<Preferences>>(),
65
+ permissions: t.object<ResourceState<Permissions>>(),
66
+ profileDelay: t.number(),
67
+ preferencesDelay: t.number(),
68
+ permissionsDelay: t.number(),
69
+ profileFailRate: t.number(),
70
+ preferencesFailRate: t.number(),
71
+ permissionsFailRate: t.number(),
72
+ loadRequested: t.boolean(),
73
+ eventLog: t.object<EventLogEntry[]>(),
74
+ },
75
+ derivations: {
76
+ loadedCount: t.number(),
77
+ totalResources: t.number(),
78
+ allLoaded: t.boolean(),
79
+ anyError: t.boolean(),
80
+ anyLoading: t.boolean(),
81
+ combinedStatus: t.string(),
82
+ canStart: t.boolean(),
83
+ },
84
+ events: {
85
+ setUserId: { value: t.string() },
86
+ start: {},
87
+ retryResource: { resource: t.string() },
88
+ reloadAll: {},
89
+ setDelay: { resource: t.string(), value: t.number() },
90
+ setFailRate: { resource: t.string(), value: t.number() },
91
+ },
92
+ requirements: {
93
+ FETCH_PROFILE: { userId: t.string() },
94
+ FETCH_PREFERENCES: { userId: t.string() },
95
+ FETCH_PERMISSIONS: { userId: t.string() },
96
+ },
97
+ } satisfies ModuleSchema;
98
+
99
+ // ============================================================================
100
+ // Helpers
101
+ // ============================================================================
102
+
103
+ function addLogEntry(
104
+ facts: any,
105
+ event: string,
106
+ resource: string,
107
+ detail: string,
108
+ ): void {
109
+ const log = [...(facts.eventLog as EventLogEntry[])];
110
+ log.push({ timestamp: Date.now(), event, resource, detail });
111
+ facts.eventLog = log;
112
+ }
113
+
114
+ // ============================================================================
115
+ // Module
116
+ // ============================================================================
117
+
118
+ export const dashboardLoaderModule = createModule("dashboard-loader", {
119
+ schema: dashboardLoaderSchema,
120
+
121
+ init: (facts) => {
122
+ facts.userId = "";
123
+ facts.profile = makeIdleResource<Profile>();
124
+ facts.preferences = makeIdleResource<Preferences>();
125
+ facts.permissions = makeIdleResource<Permissions>();
126
+ facts.profileDelay = 1000;
127
+ facts.preferencesDelay = 1500;
128
+ facts.permissionsDelay = 2000;
129
+ facts.profileFailRate = 0;
130
+ facts.preferencesFailRate = 0;
131
+ facts.permissionsFailRate = 0;
132
+ facts.loadRequested = false;
133
+ facts.eventLog = [];
134
+ },
135
+
136
+ // ============================================================================
137
+ // Derivations
138
+ // ============================================================================
139
+
140
+ derive: {
141
+ loadedCount: (facts) => {
142
+ const resources = [
143
+ facts.profile,
144
+ facts.preferences,
145
+ facts.permissions,
146
+ ] as ResourceState<unknown>[];
147
+
148
+ return resources.filter((r) => r.status === "success").length;
149
+ },
150
+
151
+ totalResources: () => 3,
152
+
153
+ allLoaded: (facts) => {
154
+ const resources = [
155
+ facts.profile,
156
+ facts.preferences,
157
+ facts.permissions,
158
+ ] as ResourceState<unknown>[];
159
+
160
+ return resources.every((r) => r.status === "success");
161
+ },
162
+
163
+ anyError: (facts) => {
164
+ const resources = [
165
+ facts.profile,
166
+ facts.preferences,
167
+ facts.permissions,
168
+ ] as ResourceState<unknown>[];
169
+
170
+ return resources.some((r) => r.status === "error");
171
+ },
172
+
173
+ anyLoading: (facts) => {
174
+ const resources = [
175
+ facts.profile,
176
+ facts.preferences,
177
+ facts.permissions,
178
+ ] as ResourceState<unknown>[];
179
+
180
+ return resources.some((r) => r.status === "loading");
181
+ },
182
+
183
+ combinedStatus: (facts, derive) => {
184
+ const loaded = derive.loadedCount as number;
185
+ const anyErr = derive.anyError as boolean;
186
+ const anyLoad = derive.anyLoading as boolean;
187
+ const allIdle = [
188
+ facts.profile,
189
+ facts.preferences,
190
+ facts.permissions,
191
+ ].every((r: any) => r.status === "idle");
192
+
193
+ if (allIdle) {
194
+ return "Not started";
195
+ }
196
+
197
+ const errCount = [
198
+ facts.profile,
199
+ facts.preferences,
200
+ facts.permissions,
201
+ ].filter((r: any) => r.status === "error").length;
202
+
203
+ if (anyLoad) {
204
+ return `Loading ${loaded} of 3...`;
205
+ }
206
+
207
+ if (anyErr && loaded > 0) {
208
+ return `${errCount} failed, ${loaded} loaded`;
209
+ }
210
+
211
+ if (anyErr) {
212
+ return `${errCount} failed`;
213
+ }
214
+
215
+ return "All loaded";
216
+ },
217
+
218
+ canStart: (facts) => {
219
+ const id = (facts.userId as string).trim();
220
+ const allIdle = [
221
+ facts.profile,
222
+ facts.preferences,
223
+ facts.permissions,
224
+ ].every((r: any) => r.status === "idle");
225
+
226
+ return id.length > 0 && allIdle;
227
+ },
228
+ },
229
+
230
+ // ============================================================================
231
+ // Events
232
+ // ============================================================================
233
+
234
+ events: {
235
+ setUserId: (facts, { value }) => {
236
+ facts.userId = value;
237
+ },
238
+
239
+ start: (facts) => {
240
+ const id = (facts.userId as string).trim();
241
+ if (id.length === 0) {
242
+ return;
243
+ }
244
+
245
+ // Reset all resources to idle so constraints re-fire
246
+ facts.profile = makeIdleResource<Profile>();
247
+ facts.preferences = makeIdleResource<Preferences>();
248
+ facts.permissions = makeIdleResource<Permissions>();
249
+ facts.loadRequested = true;
250
+ facts.eventLog = [];
251
+ },
252
+
253
+ retryResource: (facts, { resource }) => {
254
+ const res = (facts as any)[resource] as ResourceState<unknown>;
255
+ if (!res || res.status !== "error") {
256
+ return;
257
+ }
258
+
259
+ (facts as any)[resource] = {
260
+ ...res,
261
+ status: "idle",
262
+ error: null,
263
+ };
264
+ },
265
+
266
+ reloadAll: (facts) => {
267
+ facts.profile = makeIdleResource<Profile>();
268
+ facts.preferences = makeIdleResource<Preferences>();
269
+ facts.permissions = makeIdleResource<Permissions>();
270
+ facts.eventLog = [];
271
+ },
272
+
273
+ setDelay: (facts, { resource, value }) => {
274
+ const key = `${resource}Delay` as keyof typeof facts;
275
+ if (key in facts) {
276
+ (facts as any)[key] = value;
277
+ }
278
+ },
279
+
280
+ setFailRate: (facts, { resource, value }) => {
281
+ const key = `${resource}FailRate` as keyof typeof facts;
282
+ if (key in facts) {
283
+ (facts as any)[key] = value;
284
+ }
285
+ },
286
+ },
287
+
288
+ // ============================================================================
289
+ // Constraints
290
+ // ============================================================================
291
+
292
+ constraints: {
293
+ needsProfile: {
294
+ priority: 100,
295
+ when: (facts) => {
296
+ const id = (facts.userId as string).trim();
297
+ const profile = facts.profile as ResourceState<Profile>;
298
+
299
+ return (
300
+ (facts.loadRequested as boolean) &&
301
+ id !== "" &&
302
+ profile.status === "idle"
303
+ );
304
+ },
305
+ require: (facts) => ({
306
+ type: "FETCH_PROFILE",
307
+ userId: (facts.userId as string).trim(),
308
+ }),
309
+ },
310
+
311
+ needsPreferences: {
312
+ priority: 90,
313
+ when: (facts) => {
314
+ const id = (facts.userId as string).trim();
315
+ const prefs = facts.preferences as ResourceState<Preferences>;
316
+
317
+ return (
318
+ (facts.loadRequested as boolean) &&
319
+ id !== "" &&
320
+ prefs.status === "idle"
321
+ );
322
+ },
323
+ require: (facts) => ({
324
+ type: "FETCH_PREFERENCES",
325
+ userId: (facts.userId as string).trim(),
326
+ }),
327
+ },
328
+
329
+ needsPermissions: {
330
+ priority: 80,
331
+ when: (facts) => {
332
+ const id = (facts.userId as string).trim();
333
+ const perms = facts.permissions as ResourceState<Permissions>;
334
+
335
+ return (
336
+ (facts.loadRequested as boolean) &&
337
+ id !== "" &&
338
+ perms.status === "idle"
339
+ );
340
+ },
341
+ require: (facts) => ({
342
+ type: "FETCH_PERMISSIONS",
343
+ userId: (facts.userId as string).trim(),
344
+ }),
345
+ },
346
+ },
347
+
348
+ // ============================================================================
349
+ // Resolvers
350
+ // ============================================================================
351
+
352
+ resolvers: {
353
+ fetchProfile: {
354
+ requirement: "FETCH_PROFILE",
355
+ retry: { attempts: 3, backoff: "exponential" },
356
+ timeout: 10000,
357
+ resolve: async (req, context) => {
358
+ const prev = context.facts.profile as ResourceState<Profile>;
359
+ context.facts.profile = {
360
+ ...prev,
361
+ status: "loading",
362
+ attempts: prev.attempts + 1,
363
+ startedAt: prev.startedAt ?? Date.now(),
364
+ };
365
+ addLogEntry(
366
+ context.facts,
367
+ "loading",
368
+ "profile",
369
+ `Attempt ${prev.attempts + 1}`,
370
+ );
371
+
372
+ try {
373
+ const data = await fetchMockProfile(
374
+ req.userId,
375
+ context.facts.profileDelay as number,
376
+ context.facts.profileFailRate as number,
377
+ );
378
+ context.facts.profile = {
379
+ data,
380
+ status: "success",
381
+ error: null,
382
+ attempts: (context.facts.profile as ResourceState<Profile>)
383
+ .attempts,
384
+ startedAt: (context.facts.profile as ResourceState<Profile>)
385
+ .startedAt,
386
+ completedAt: Date.now(),
387
+ };
388
+ addLogEntry(context.facts, "success", "profile", data.name);
389
+ } catch (err) {
390
+ const msg = err instanceof Error ? err.message : "Unknown error";
391
+ context.facts.profile = {
392
+ ...(context.facts.profile as ResourceState<Profile>),
393
+ status: "error",
394
+ error: msg,
395
+ completedAt: Date.now(),
396
+ };
397
+ addLogEntry(context.facts, "error", "profile", msg);
398
+ throw err;
399
+ }
400
+ },
401
+ },
402
+
403
+ fetchPreferences: {
404
+ requirement: "FETCH_PREFERENCES",
405
+ retry: { attempts: 2, backoff: "exponential" },
406
+ resolve: async (req, context) => {
407
+ const prev = context.facts.preferences as ResourceState<Preferences>;
408
+ context.facts.preferences = {
409
+ ...prev,
410
+ status: "loading",
411
+ attempts: prev.attempts + 1,
412
+ startedAt: prev.startedAt ?? Date.now(),
413
+ };
414
+ addLogEntry(
415
+ context.facts,
416
+ "loading",
417
+ "preferences",
418
+ `Attempt ${prev.attempts + 1}`,
419
+ );
420
+
421
+ try {
422
+ const data = await fetchMockPreferences(
423
+ req.userId,
424
+ context.facts.preferencesDelay as number,
425
+ context.facts.preferencesFailRate as number,
426
+ );
427
+ context.facts.preferences = {
428
+ data,
429
+ status: "success",
430
+ error: null,
431
+ attempts: (context.facts.preferences as ResourceState<Preferences>)
432
+ .attempts,
433
+ startedAt: (context.facts.preferences as ResourceState<Preferences>)
434
+ .startedAt,
435
+ completedAt: Date.now(),
436
+ };
437
+ addLogEntry(
438
+ context.facts,
439
+ "success",
440
+ "preferences",
441
+ `${data.theme} / ${data.locale}`,
442
+ );
443
+ } catch (err) {
444
+ const msg = err instanceof Error ? err.message : "Unknown error";
445
+ context.facts.preferences = {
446
+ ...(context.facts.preferences as ResourceState<Preferences>),
447
+ status: "error",
448
+ error: msg,
449
+ completedAt: Date.now(),
450
+ };
451
+ addLogEntry(context.facts, "error", "preferences", msg);
452
+ throw err;
453
+ }
454
+ },
455
+ },
456
+
457
+ fetchPermissions: {
458
+ requirement: "FETCH_PERMISSIONS",
459
+ retry: { attempts: 3, backoff: "exponential" },
460
+ timeout: 15000,
461
+ resolve: async (req, context) => {
462
+ const prev = context.facts.permissions as ResourceState<Permissions>;
463
+ context.facts.permissions = {
464
+ ...prev,
465
+ status: "loading",
466
+ attempts: prev.attempts + 1,
467
+ startedAt: prev.startedAt ?? Date.now(),
468
+ };
469
+ addLogEntry(
470
+ context.facts,
471
+ "loading",
472
+ "permissions",
473
+ `Attempt ${prev.attempts + 1}`,
474
+ );
475
+
476
+ try {
477
+ const data = await fetchMockPermissions(
478
+ req.userId,
479
+ context.facts.permissionsDelay as number,
480
+ context.facts.permissionsFailRate as number,
481
+ );
482
+ context.facts.permissions = {
483
+ data,
484
+ status: "success",
485
+ error: null,
486
+ attempts: (context.facts.permissions as ResourceState<Permissions>)
487
+ .attempts,
488
+ startedAt: (context.facts.permissions as ResourceState<Permissions>)
489
+ .startedAt,
490
+ completedAt: Date.now(),
491
+ };
492
+ addLogEntry(
493
+ context.facts,
494
+ "success",
495
+ "permissions",
496
+ `${data.role} (${data.features.join(", ")})`,
497
+ );
498
+ } catch (err) {
499
+ const msg = err instanceof Error ? err.message : "Unknown error";
500
+ context.facts.permissions = {
501
+ ...(context.facts.permissions as ResourceState<Permissions>),
502
+ status: "error",
503
+ error: msg,
504
+ completedAt: Date.now(),
505
+ };
506
+ addLogEntry(context.facts, "error", "permissions", msg);
507
+ throw err;
508
+ }
509
+ },
510
+ },
511
+ },
512
+ });
@@ -0,0 +1,105 @@
1
+ // Example: debounce-constraints
2
+ // Source: examples/debounce-constraints/src/main.ts
3
+ // Extracted for AI rules — DOM wiring stripped
4
+
5
+ /**
6
+ * Debounce Constraints — DOM Rendering & System Wiring
7
+ *
8
+ * Creates the Directive system, subscribes to state changes,
9
+ * renders the search input, debounce progress bar, results list,
10
+ * stats, config sliders, and event timeline.
11
+ * A 100ms timer drives reactive debounce countdown.
12
+ */
13
+
14
+ import { createSystem } from "@directive-run/core";
15
+ import { devtoolsPlugin } from "@directive-run/core/plugins";
16
+ import {
17
+ type EventLogEntry,
18
+ type SearchResult,
19
+ debounceSearchModule,
20
+ debounceSearchSchema,
21
+ } from "./debounce-search.js";
22
+
23
+ // ============================================================================
24
+ // System
25
+ // ============================================================================
26
+
27
+ const system = createSystem({
28
+ module: debounceSearchModule,
29
+ debug: { runHistory: true },
30
+ plugins: [devtoolsPlugin({ name: "debounce-constraints" })],
31
+ });
32
+ system.start();
33
+
34
+ const allKeys = [
35
+ ...Object.keys(debounceSearchSchema.facts),
36
+ ...Object.keys(debounceSearchSchema.derivations),
37
+ ];
38
+
39
+ // ============================================================================
40
+ // DOM References
41
+ // ============================================================================
42
+
43
+ // Status bar
44
+
45
+ // Search form
46
+ "dc-search-input",
47
+
48
+ // Progress bar
49
+
50
+ // Query display
51
+
52
+ // Results
53
+
54
+ // Stats
55
+
56
+ // Config sliders
57
+ "dc-debounce-delay",
58
+ "dc-api-delay",
59
+ "dc-min-chars",
60
+
61
+ // Timeline
62
+
63
+ // ============================================================================
64
+ // Render
65
+ // ============================================================================
66
+
67
+
68
+ // ============================================================================
69
+ // Subscribe
70
+ // ============================================================================
71
+
72
+ system.subscribe(allKeys, render);
73
+
74
+ // Timer — tick every 100ms for smooth debounce progress bar
75
+ const tickInterval = setInterval(() => {
76
+ system.events.tick();
77
+ }, 100);
78
+
79
+ // ============================================================================
80
+ // Controls
81
+ // ============================================================================
82
+
83
+ // Search input — fire on every keystroke
84
+
85
+ // Clear
86
+
87
+ // Sliders
88
+
89
+
90
+ // ============================================================================
91
+ // Helpers
92
+ // ============================================================================
93
+
94
+ function escapeHtml(text: string): string {
95
+
96
+ return div.innerHTML;
97
+ }
98
+
99
+ // ============================================================================
100
+ // Initial Render
101
+ // ============================================================================
102
+
103
+ render();
104
+
105
+ // Signal to tests that the module script has fully initialized