@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,385 @@
1
+ // Example: ab-testing
2
+ // Source: examples/ab-testing/src/main.ts
3
+ // Extracted for AI rules — DOM wiring stripped
4
+
5
+ /**
6
+ * A/B Testing Engine — DOM Rendering & System Wiring
7
+ *
8
+ * Creates the Directive system, subscribes to state changes,
9
+ * renders experiment cards and event timeline.
10
+ */
11
+
12
+ import {
13
+ type ModuleSchema,
14
+ createModule,
15
+ createSystem,
16
+ t,
17
+ } from "@directive-run/core";
18
+ import { devtoolsPlugin } from "@directive-run/core/plugins";
19
+
20
+ // ============================================================================
21
+ // Types
22
+ // ============================================================================
23
+
24
+ interface Variant {
25
+ id: string;
26
+ weight: number;
27
+ label: string;
28
+ }
29
+
30
+ interface Experiment {
31
+ id: string;
32
+ name: string;
33
+ variants: Variant[];
34
+ active: boolean;
35
+ }
36
+
37
+ interface TimelineEntry {
38
+ time: number;
39
+ event: string;
40
+ detail: string;
41
+ type: string;
42
+ }
43
+
44
+ // ============================================================================
45
+ // Deterministic Hash
46
+ // ============================================================================
47
+
48
+ function hashCode(str: string): number {
49
+ let hash = 0;
50
+ for (let i = 0; i < str.length; i++) {
51
+ const char = str.charCodeAt(i);
52
+ hash = ((hash << 5) - hash + char) | 0;
53
+ }
54
+
55
+ return Math.abs(hash);
56
+ }
57
+
58
+ function pickVariant(
59
+ userId: string,
60
+ experimentId: string,
61
+ variants: Variant[],
62
+ ): string {
63
+ const hash = hashCode(`${userId}:${experimentId}`);
64
+ const totalWeight = variants.reduce((sum, v) => sum + v.weight, 0);
65
+ let roll = hash % totalWeight;
66
+
67
+ for (const variant of variants) {
68
+ roll -= variant.weight;
69
+ if (roll < 0) {
70
+ return variant.id;
71
+ }
72
+ }
73
+
74
+ return variants[variants.length - 1].id;
75
+ }
76
+
77
+ // ============================================================================
78
+ // Timeline
79
+ // ============================================================================
80
+
81
+ const timeline: TimelineEntry[] = [];
82
+
83
+ function log(type: "event" | "constraint" | "resolver", msg: string) {
84
+ console.log(`[AB] [${type}] ${msg}`);
85
+
86
+ // Classify for timeline
87
+ let event = "";
88
+ let detail = msg;
89
+ let tlType = "register";
90
+
91
+ if (msg.startsWith("Registered")) {
92
+ event = "registered";
93
+ detail = msg.replace("Registered experiment: ", "");
94
+ tlType = "register";
95
+ } else if (msg.startsWith("Assigned") || msg.includes("→")) {
96
+ event = "assigned";
97
+ detail = msg;
98
+ tlType = "assign";
99
+ } else if (msg.includes("Exposure tracked")) {
100
+ event = "exposure";
101
+ detail = msg.replace("Exposure tracked: ", "");
102
+ tlType = "exposure";
103
+ } else if (msg.includes("Manual assignment")) {
104
+ event = "manual";
105
+ detail = msg.replace("Manual assignment: ", "");
106
+ tlType = "assign";
107
+ } else if (
108
+ msg.includes("Paused") ||
109
+ msg.includes("Resumed") ||
110
+ msg.includes("Reset")
111
+ ) {
112
+ event = msg.toLowerCase().split(" ")[0];
113
+ detail = msg;
114
+ tlType = "control";
115
+ } else {
116
+ event = type;
117
+ detail = msg;
118
+ tlType = "register";
119
+ }
120
+
121
+ timeline.unshift({ time: Date.now(), event, detail, type: tlType });
122
+ }
123
+
124
+ // ============================================================================
125
+ // Schema
126
+ // ============================================================================
127
+
128
+ const schema = {
129
+ facts: {
130
+ experiments: t.object<Experiment[]>(),
131
+ assignments: t.object<Record<string, string>>(),
132
+ exposures: t.object<Record<string, number>>(),
133
+ userId: t.string(),
134
+ paused: t.boolean(),
135
+ },
136
+ derivations: {
137
+ activeExperiments: t.object<Experiment[]>(),
138
+ assignedCount: t.number(),
139
+ exposedCount: t.number(),
140
+ },
141
+ events: {
142
+ registerExperiment: {
143
+ id: t.string(),
144
+ name: t.string(),
145
+ variants: t.object<Variant[]>(),
146
+ },
147
+ assignVariant: { experimentId: t.string(), variantId: t.string() },
148
+ recordExposure: { experimentId: t.string() },
149
+ pauseAll: {},
150
+ resumeAll: {},
151
+ reset: {},
152
+ },
153
+ requirements: {
154
+ ASSIGN_VARIANT: { experimentId: t.string() },
155
+ TRACK_EXPOSURE: { experimentId: t.string(), variantId: t.string() },
156
+ },
157
+ } satisfies ModuleSchema;
158
+
159
+ // ============================================================================
160
+ // Module
161
+ // ============================================================================
162
+
163
+ const abTesting = createModule("ab-testing", {
164
+ schema,
165
+
166
+ init: (facts) => {
167
+ facts.experiments = [];
168
+ facts.assignments = {};
169
+ facts.exposures = {};
170
+ facts.userId = `user-${hashCode(String(Date.now())).toString(36)}`;
171
+ facts.paused = false;
172
+ },
173
+
174
+ derive: {
175
+ activeExperiments: (facts) =>
176
+ (facts.experiments as Experiment[]).filter(
177
+ (e) => e.active && !facts.paused,
178
+ ),
179
+ assignedCount: (facts) => Object.keys(facts.assignments).length,
180
+ exposedCount: (facts) => Object.keys(facts.exposures).length,
181
+ },
182
+
183
+ events: {
184
+ registerExperiment: (facts, { id, name, variants }) => {
185
+ const experiments = facts.experiments as Experiment[];
186
+ if (!experiments.find((e: Experiment) => e.id === id)) {
187
+ facts.experiments = [
188
+ ...experiments,
189
+ { id, name, variants, active: true },
190
+ ];
191
+ }
192
+ },
193
+ assignVariant: (facts, { experimentId, variantId }) => {
194
+ facts.assignments = { ...facts.assignments, [experimentId]: variantId };
195
+ },
196
+ recordExposure: (facts, { experimentId }) => {
197
+ facts.exposures = { ...facts.exposures, [experimentId]: Date.now() };
198
+ },
199
+ pauseAll: (facts) => {
200
+ facts.paused = true;
201
+ },
202
+ resumeAll: (facts) => {
203
+ facts.paused = false;
204
+ },
205
+ reset: (facts) => {
206
+ facts.assignments = {};
207
+ facts.exposures = {};
208
+ facts.paused = false;
209
+ },
210
+ },
211
+
212
+ constraints: {
213
+ needsAssignment: {
214
+ priority: 100,
215
+ when: (facts) => {
216
+ if (facts.paused) {
217
+ return false;
218
+ }
219
+ const experiments = facts.experiments as Experiment[];
220
+ const assignments = facts.assignments as Record<string, string>;
221
+
222
+ return experiments.some(
223
+ (e: Experiment) => e.active && !assignments[e.id],
224
+ );
225
+ },
226
+ require: (facts) => {
227
+ const experiments = facts.experiments as Experiment[];
228
+ const assignments = facts.assignments as Record<string, string>;
229
+ const unassigned = experiments.find(
230
+ (e: Experiment) => e.active && !assignments[e.id],
231
+ );
232
+
233
+ return { type: "ASSIGN_VARIANT", experimentId: unassigned!.id };
234
+ },
235
+ },
236
+
237
+ needsExposure: {
238
+ priority: 50,
239
+ when: (facts) => {
240
+ if (facts.paused) {
241
+ return false;
242
+ }
243
+ const assignments = facts.assignments as Record<string, string>;
244
+ const exposures = facts.exposures as Record<string, number>;
245
+
246
+ return Object.keys(assignments).some((id) => !exposures[id]);
247
+ },
248
+ require: (facts) => {
249
+ const assignments = facts.assignments as Record<string, string>;
250
+ const exposures = facts.exposures as Record<string, number>;
251
+ const experimentId = Object.keys(assignments).find(
252
+ (id) => !exposures[id],
253
+ )!;
254
+
255
+ return {
256
+ type: "TRACK_EXPOSURE",
257
+ experimentId,
258
+ variantId: assignments[experimentId],
259
+ };
260
+ },
261
+ },
262
+ },
263
+
264
+ resolvers: {
265
+ assignVariant: {
266
+ requirement: "ASSIGN_VARIANT",
267
+ resolve: async (req, context) => {
268
+ const experiments = context.facts.experiments as Experiment[];
269
+ const experiment = experiments.find(
270
+ (e: Experiment) => e.id === req.experimentId,
271
+ );
272
+ if (!experiment) {
273
+ return;
274
+ }
275
+
276
+ const variantId = pickVariant(
277
+ context.facts.userId,
278
+ req.experimentId,
279
+ experiment.variants,
280
+ );
281
+
282
+ context.facts.assignments = {
283
+ ...context.facts.assignments,
284
+ [req.experimentId]: variantId,
285
+ };
286
+ log("resolver", `Assigned ${req.experimentId} → ${variantId}`);
287
+ },
288
+ },
289
+
290
+ trackExposure: {
291
+ requirement: "TRACK_EXPOSURE",
292
+ resolve: async (req, context) => {
293
+ const now = Date.now();
294
+ context.facts.exposures = {
295
+ ...context.facts.exposures,
296
+ [req.experimentId]: now,
297
+ };
298
+ log(
299
+ "resolver",
300
+ `Exposure tracked: ${req.experimentId} (variant: ${req.variantId}) at ${new Date(now).toLocaleTimeString()}`,
301
+ );
302
+ },
303
+ },
304
+ },
305
+ });
306
+
307
+ // ============================================================================
308
+ // System
309
+ // ============================================================================
310
+
311
+ const system = createSystem({
312
+ module: abTesting,
313
+ debug: { runHistory: true },
314
+ plugins: [devtoolsPlugin({ name: "ab-testing" })],
315
+ });
316
+ system.start();
317
+
318
+ // ============================================================================
319
+ // DOM References
320
+ // ============================================================================
321
+
322
+ // Stats
323
+
324
+ // Timeline
325
+
326
+ // ============================================================================
327
+ // Render
328
+ // ============================================================================
329
+
330
+ function escapeHtml(text: string): string {
331
+
332
+ return div.innerHTML;
333
+ }
334
+
335
+
336
+ // ============================================================================
337
+ // Subscribe
338
+ // ============================================================================
339
+
340
+ system.subscribe(
341
+ [
342
+ "experiments",
343
+ "assignments",
344
+ "exposures",
345
+ "userId",
346
+ "paused",
347
+ "activeExperiments",
348
+ "assignedCount",
349
+ "exposedCount",
350
+ ],
351
+ render,
352
+ );
353
+
354
+ // Button handlers
355
+
356
+
357
+ // ============================================================================
358
+ // Register sample experiments
359
+ // ============================================================================
360
+
361
+ system.events.registerExperiment({
362
+ id: "theme-icons",
363
+ name: "Theme Icons",
364
+ variants: [
365
+ { id: "custom-svg", weight: 50, label: "Custom SVG" },
366
+ { id: "phosphor", weight: 50, label: "Phosphor" },
367
+ ],
368
+ });
369
+ log("event", "Registered experiment: theme-icons");
370
+
371
+ system.events.registerExperiment({
372
+ id: "cta-color",
373
+ name: "CTA Button Color",
374
+ variants: [
375
+ { id: "brand", weight: 50, label: "Brand" },
376
+ { id: "green", weight: 30, label: "Green" },
377
+ { id: "orange", weight: 20, label: "Orange" },
378
+ ],
379
+ });
380
+ log("event", "Registered experiment: cta-color");
381
+
382
+ // Initial render
383
+ render();
384
+
385
+ // Signal to tests that initialization is complete