@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,220 @@
1
+ // Example: feature-flags
2
+ // Source: examples/feature-flags/src/module.ts
3
+ // Pure module file — no DOM wiring
4
+
5
+ /**
6
+ * Feature Flags Directive Module (Example)
7
+ *
8
+ * Mirrors the real feature flag system running on directive.run.
9
+ * 8 flags with two interaction patterns:
10
+ *
11
+ * 1. Maintenance mode – disables chat, search, playground, and vote API
12
+ * 2. Onboarding toast → depends on brand switcher (constraint auto-enables)
13
+ */
14
+ import { createModule, t } from "@directive-run/core";
15
+
16
+ export const featureFlagsModule = createModule("feature-flags", {
17
+ schema: {
18
+ facts: {
19
+ // Individual feature toggles
20
+ chatEnabled: t.boolean(),
21
+ searchEnabled: t.boolean(),
22
+ playgroundEnabled: t.boolean(),
23
+ brandSwitcherEnabled: t.boolean(),
24
+ themeSelectorEnabled: t.boolean(),
25
+ onboardingToastEnabled: t.boolean(),
26
+ versionSelectorEnabled: t.boolean(),
27
+ voteApiEnabled: t.boolean(),
28
+
29
+ // Context
30
+ maintenanceMode: t.boolean(),
31
+ },
32
+ derivations: {
33
+ canUseChat: t.boolean(),
34
+ canUseSearch: t.boolean(),
35
+ canUsePlayground: t.boolean(),
36
+ canUseBrandSwitcher: t.boolean(),
37
+ canUseThemeSelector: t.boolean(),
38
+ canShowOnboardingToast: t.boolean(),
39
+ canUseVersionSelector: t.boolean(),
40
+ canUseVoteApi: t.boolean(),
41
+ enabledCount: t.number(),
42
+ allFeaturesEnabled: t.boolean(),
43
+ },
44
+ events: {
45
+ configure: {
46
+ chatEnabled: t.boolean(),
47
+ searchEnabled: t.boolean(),
48
+ playgroundEnabled: t.boolean(),
49
+ brandSwitcherEnabled: t.boolean(),
50
+ themeSelectorEnabled: t.boolean(),
51
+ onboardingToastEnabled: t.boolean(),
52
+ versionSelectorEnabled: t.boolean(),
53
+ voteApiEnabled: t.boolean(),
54
+ },
55
+ setMaintenanceMode: { enabled: t.boolean() },
56
+ toggleFlag: { flag: t.string(), enabled: t.boolean() },
57
+ resetAll: {},
58
+ },
59
+ requirements: {
60
+ ENABLE_BRAND_SWITCHER: {},
61
+ LOG_MAINTENANCE_WARNING: {},
62
+ },
63
+ },
64
+
65
+ init: (facts) => {
66
+ facts.chatEnabled = true;
67
+ facts.searchEnabled = true;
68
+ facts.playgroundEnabled = true;
69
+ facts.brandSwitcherEnabled = true;
70
+ facts.themeSelectorEnabled = true;
71
+ facts.onboardingToastEnabled = true;
72
+ facts.versionSelectorEnabled = true;
73
+ facts.voteApiEnabled = true;
74
+
75
+ facts.maintenanceMode = false;
76
+ },
77
+
78
+ derive: {
79
+ canUseChat: (facts) => facts.chatEnabled && !facts.maintenanceMode,
80
+ canUseSearch: (facts) => facts.searchEnabled && !facts.maintenanceMode,
81
+ canUsePlayground: (facts) =>
82
+ facts.playgroundEnabled && !facts.maintenanceMode,
83
+ canUseBrandSwitcher: (facts) => facts.brandSwitcherEnabled,
84
+ canUseThemeSelector: (facts) => facts.themeSelectorEnabled,
85
+ canShowOnboardingToast: (facts) =>
86
+ facts.onboardingToastEnabled && facts.brandSwitcherEnabled,
87
+ canUseVersionSelector: (facts) => facts.versionSelectorEnabled,
88
+ canUseVoteApi: (facts) => facts.voteApiEnabled && !facts.maintenanceMode,
89
+ enabledCount: (facts) => {
90
+ let count = 0;
91
+ if (facts.chatEnabled) count++;
92
+ if (facts.searchEnabled) count++;
93
+ if (facts.playgroundEnabled) count++;
94
+ if (facts.brandSwitcherEnabled) count++;
95
+ if (facts.themeSelectorEnabled) count++;
96
+ if (facts.onboardingToastEnabled) count++;
97
+ if (facts.versionSelectorEnabled) count++;
98
+ if (facts.voteApiEnabled) count++;
99
+
100
+ return count;
101
+ },
102
+ allFeaturesEnabled: (facts) =>
103
+ facts.chatEnabled &&
104
+ facts.searchEnabled &&
105
+ facts.playgroundEnabled &&
106
+ facts.brandSwitcherEnabled &&
107
+ facts.themeSelectorEnabled &&
108
+ facts.onboardingToastEnabled &&
109
+ facts.versionSelectorEnabled &&
110
+ facts.voteApiEnabled,
111
+ },
112
+
113
+ events: {
114
+ configure: (facts, payload) => {
115
+ facts.chatEnabled = payload.chatEnabled;
116
+ facts.searchEnabled = payload.searchEnabled;
117
+ facts.playgroundEnabled = payload.playgroundEnabled;
118
+ facts.brandSwitcherEnabled = payload.brandSwitcherEnabled;
119
+ facts.themeSelectorEnabled = payload.themeSelectorEnabled;
120
+ facts.onboardingToastEnabled = payload.onboardingToastEnabled;
121
+ facts.versionSelectorEnabled = payload.versionSelectorEnabled;
122
+ facts.voteApiEnabled = payload.voteApiEnabled;
123
+ },
124
+
125
+ setMaintenanceMode: (facts, { enabled }) => {
126
+ facts.maintenanceMode = enabled;
127
+ },
128
+
129
+ toggleFlag: (facts, { flag, enabled }) => {
130
+ const key = flag as keyof typeof facts;
131
+ if (key in facts && typeof facts[key] === "boolean") {
132
+ (facts as Record<string, boolean>)[key] = enabled;
133
+ }
134
+ },
135
+
136
+ resetAll: (facts) => {
137
+ facts.chatEnabled = true;
138
+ facts.searchEnabled = true;
139
+ facts.playgroundEnabled = true;
140
+ facts.brandSwitcherEnabled = true;
141
+ facts.themeSelectorEnabled = true;
142
+ facts.onboardingToastEnabled = true;
143
+ facts.versionSelectorEnabled = true;
144
+ facts.voteApiEnabled = true;
145
+ facts.maintenanceMode = false;
146
+ },
147
+ },
148
+
149
+ constraints: {
150
+ onboardingRequiresBrandSwitcher: {
151
+ when: (facts) =>
152
+ facts.onboardingToastEnabled && !facts.brandSwitcherEnabled,
153
+ require: { type: "ENABLE_BRAND_SWITCHER" },
154
+ },
155
+
156
+ maintenanceWarning: {
157
+ when: (facts) => facts.maintenanceMode,
158
+ require: { type: "LOG_MAINTENANCE_WARNING" },
159
+ },
160
+ },
161
+
162
+ resolvers: {
163
+ enableBrandSwitcher: {
164
+ requirement: "ENABLE_BRAND_SWITCHER",
165
+ resolve: async (req, context) => {
166
+ context.facts.brandSwitcherEnabled = true;
167
+ },
168
+ },
169
+
170
+ logMaintenanceWarning: {
171
+ requirement: "LOG_MAINTENANCE_WARNING",
172
+ resolve: async (req, context) => {
173
+ console.warn(
174
+ "[feature-flags] Maintenance mode is active. Chat, search, playground, and vote API are disabled.",
175
+ );
176
+ },
177
+ },
178
+ },
179
+
180
+ effects: {
181
+ logChanges: {
182
+ deps: [
183
+ "chatEnabled",
184
+ "searchEnabled",
185
+ "playgroundEnabled",
186
+ "brandSwitcherEnabled",
187
+ "themeSelectorEnabled",
188
+ "onboardingToastEnabled",
189
+ "versionSelectorEnabled",
190
+ "voteApiEnabled",
191
+ "maintenanceMode",
192
+ ],
193
+ run: (facts, prev) => {
194
+ if (!prev) {
195
+ return;
196
+ }
197
+
198
+ const flags = [
199
+ "chatEnabled",
200
+ "searchEnabled",
201
+ "playgroundEnabled",
202
+ "brandSwitcherEnabled",
203
+ "themeSelectorEnabled",
204
+ "onboardingToastEnabled",
205
+ "versionSelectorEnabled",
206
+ "voteApiEnabled",
207
+ "maintenanceMode",
208
+ ] as const;
209
+
210
+ for (const flag of flags) {
211
+ if (facts[flag] !== prev[flag]) {
212
+ console.log(
213
+ `[feature-flags] ${flag}: ${prev[flag]} → ${facts[flag]}`,
214
+ );
215
+ }
216
+ }
217
+ },
218
+ },
219
+ },
220
+ });
@@ -0,0 +1,347 @@
1
+ // Example: form-wizard
2
+ // Source: examples/form-wizard/src/form-wizard.ts
3
+ // Pure module file — no DOM wiring
4
+
5
+ /**
6
+ * Form Wizard — Directive Modules
7
+ *
8
+ * Two-module system demonstrating multi-step form validation,
9
+ * constraint-driven step advancement, cross-module async email
10
+ * availability checking, and persistence of draft data.
11
+ *
12
+ * - wizard module: step navigation, field data, derivations for per-step
13
+ * validity, constraints to advance/submit, resolvers for step transitions.
14
+ * - validation module: cross-module email availability check using
15
+ * crossModuleDeps on the wizard schema.
16
+ */
17
+
18
+ import {
19
+ type ModuleSchema,
20
+ createModule,
21
+ createSystem,
22
+ t,
23
+ } from "@directive-run/core";
24
+ import { devtoolsPlugin, persistencePlugin } from "@directive-run/core/plugins";
25
+
26
+ // ============================================================================
27
+ // Types
28
+ // ============================================================================
29
+
30
+ export type PlanType = "free" | "pro" | "enterprise";
31
+
32
+ // ============================================================================
33
+ // Wizard Schema
34
+ // ============================================================================
35
+
36
+ export const wizardSchema = {
37
+ facts: {
38
+ currentStep: t.number(),
39
+ totalSteps: t.number(),
40
+ advanceRequested: t.boolean(),
41
+ email: t.string(),
42
+ password: t.string(),
43
+ name: t.string(),
44
+ company: t.string(),
45
+ plan: t.string<PlanType>(),
46
+ newsletter: t.boolean(),
47
+ submitted: t.boolean(),
48
+ },
49
+ derivations: {
50
+ step0Valid: t.boolean(),
51
+ step1Valid: t.boolean(),
52
+ step2Valid: t.boolean(),
53
+ currentStepValid: t.boolean(),
54
+ canAdvance: t.boolean(),
55
+ canGoBack: t.boolean(),
56
+ progress: t.number(),
57
+ isLastStep: t.boolean(),
58
+ },
59
+ events: {
60
+ requestAdvance: {},
61
+ goBack: {},
62
+ setField: { field: t.string(), value: t.object<unknown>() },
63
+ reset: {},
64
+ },
65
+ requirements: {
66
+ ADVANCE_STEP: {},
67
+ SUBMIT_FORM: {},
68
+ },
69
+ } satisfies ModuleSchema;
70
+
71
+ // ============================================================================
72
+ // Helpers
73
+ // ============================================================================
74
+
75
+ /** Inline step validity check for use in constraints (which only receive facts). */
76
+ function isStepValid(facts: Record<string, unknown>, step: number): boolean {
77
+ if (step === 0) {
78
+ return (
79
+ (facts.email as string).includes("@") &&
80
+ (facts.password as string).length >= 8
81
+ );
82
+ }
83
+ if (step === 1) {
84
+ return (facts.name as string).trim().length > 0;
85
+ }
86
+ if (step === 2) {
87
+ return (facts.plan as string) !== "";
88
+ }
89
+
90
+ return false;
91
+ }
92
+
93
+ // ============================================================================
94
+ // Wizard Module
95
+ // ============================================================================
96
+
97
+ export const wizardModule = createModule("wizard", {
98
+ schema: wizardSchema,
99
+
100
+ init: (facts) => {
101
+ facts.currentStep = 0;
102
+ facts.totalSteps = 3;
103
+ facts.advanceRequested = false;
104
+ facts.email = "";
105
+ facts.password = "";
106
+ facts.name = "";
107
+ facts.company = "";
108
+ facts.plan = "free";
109
+ facts.newsletter = false;
110
+ facts.submitted = false;
111
+ },
112
+
113
+ // ============================================================================
114
+ // Derivations
115
+ // ============================================================================
116
+
117
+ derive: {
118
+ step0Valid: (facts) => {
119
+ return facts.email.includes("@") && facts.password.length >= 8;
120
+ },
121
+
122
+ step1Valid: (facts) => {
123
+ return facts.name.trim().length > 0;
124
+ },
125
+
126
+ step2Valid: (facts) => {
127
+ return facts.plan !== "";
128
+ },
129
+
130
+ currentStepValid: (facts, derive) => {
131
+ if (facts.currentStep === 0) {
132
+ return derive.step0Valid;
133
+ }
134
+ if (facts.currentStep === 1) {
135
+ return derive.step1Valid;
136
+ }
137
+ if (facts.currentStep === 2) {
138
+ return derive.step2Valid;
139
+ }
140
+
141
+ return false;
142
+ },
143
+
144
+ canAdvance: (facts, derive) => {
145
+ return (
146
+ derive.currentStepValid && facts.currentStep < facts.totalSteps - 1
147
+ );
148
+ },
149
+
150
+ canGoBack: (facts) => {
151
+ return facts.currentStep > 0;
152
+ },
153
+
154
+ progress: (facts) => {
155
+ return Math.round(((facts.currentStep + 1) / facts.totalSteps) * 100);
156
+ },
157
+
158
+ isLastStep: (facts) => {
159
+ return facts.currentStep === facts.totalSteps - 1;
160
+ },
161
+ },
162
+
163
+ // ============================================================================
164
+ // Events
165
+ // ============================================================================
166
+
167
+ events: {
168
+ requestAdvance: (facts) => {
169
+ facts.advanceRequested = true;
170
+ },
171
+
172
+ goBack: (facts) => {
173
+ if (facts.currentStep > 0) {
174
+ facts.currentStep = facts.currentStep - 1;
175
+ }
176
+ },
177
+
178
+ setField: (facts, { field, value }) => {
179
+ (facts as Record<string, unknown>)[field] = value;
180
+ },
181
+
182
+ reset: (facts) => {
183
+ facts.currentStep = 0;
184
+ facts.advanceRequested = false;
185
+ facts.email = "";
186
+ facts.password = "";
187
+ facts.name = "";
188
+ facts.company = "";
189
+ facts.plan = "free";
190
+ facts.newsletter = false;
191
+ facts.submitted = false;
192
+ },
193
+ },
194
+
195
+ // ============================================================================
196
+ // Constraints
197
+ // ============================================================================
198
+
199
+ constraints: {
200
+ submit: {
201
+ priority: 60,
202
+ when: (facts) => {
203
+ const isLastStep = facts.currentStep === facts.totalSteps - 1;
204
+ const stepValid = isStepValid(facts, facts.currentStep);
205
+
206
+ return facts.advanceRequested && isLastStep && stepValid;
207
+ },
208
+ require: { type: "SUBMIT_FORM" },
209
+ },
210
+
211
+ advance: {
212
+ priority: 50,
213
+ when: (facts) => {
214
+ const isLastStep = facts.currentStep === facts.totalSteps - 1;
215
+ const stepValid = isStepValid(facts, facts.currentStep);
216
+
217
+ return facts.advanceRequested && !isLastStep && stepValid;
218
+ },
219
+ require: { type: "ADVANCE_STEP" },
220
+ },
221
+ },
222
+
223
+ // ============================================================================
224
+ // Resolvers
225
+ // ============================================================================
226
+
227
+ resolvers: {
228
+ advanceStep: {
229
+ requirement: "ADVANCE_STEP",
230
+ resolve: async (req, context) => {
231
+ context.facts.currentStep = context.facts.currentStep + 1;
232
+ context.facts.advanceRequested = false;
233
+ },
234
+ },
235
+
236
+ submitForm: {
237
+ requirement: "SUBMIT_FORM",
238
+ timeout: 10000,
239
+ resolve: async (req, context) => {
240
+ // Simulate API submission
241
+ await new Promise((resolve) => setTimeout(resolve, 800));
242
+ context.facts.submitted = true;
243
+ context.facts.advanceRequested = false;
244
+ },
245
+ },
246
+ },
247
+ });
248
+
249
+ // ============================================================================
250
+ // Validation Schema
251
+ // ============================================================================
252
+
253
+ export const validationSchema = {
254
+ facts: {
255
+ emailAvailable: t.boolean(),
256
+ checkingEmail: t.boolean(),
257
+ emailChecked: t.string(),
258
+ },
259
+ derivations: {},
260
+ events: {},
261
+ requirements: {
262
+ CHECK_EMAIL: { email: t.string() },
263
+ },
264
+ } satisfies ModuleSchema;
265
+
266
+ // ============================================================================
267
+ // Validation Module
268
+ // ============================================================================
269
+
270
+ export const validationModule = createModule("validation", {
271
+ schema: validationSchema,
272
+
273
+ crossModuleDeps: { wizard: wizardSchema },
274
+
275
+ init: (facts) => {
276
+ facts.emailAvailable = true;
277
+ facts.checkingEmail = false;
278
+ facts.emailChecked = "";
279
+ },
280
+
281
+ // ============================================================================
282
+ // Constraints
283
+ // ============================================================================
284
+
285
+ constraints: {
286
+ checkEmail: {
287
+ when: (facts) => {
288
+ const email = facts.wizard.email;
289
+ const checked = facts.self.emailChecked;
290
+
291
+ return email.includes("@") && email !== checked;
292
+ },
293
+ require: (facts) => ({
294
+ type: "CHECK_EMAIL",
295
+ email: facts.wizard.email,
296
+ }),
297
+ },
298
+ },
299
+
300
+ // ============================================================================
301
+ // Resolvers
302
+ // ============================================================================
303
+
304
+ resolvers: {
305
+ checkEmail: {
306
+ requirement: "CHECK_EMAIL",
307
+ resolve: async (req, context) => {
308
+ context.facts.checkingEmail = true;
309
+
310
+ try {
311
+ // Simulate API availability check
312
+ await new Promise((resolve) => setTimeout(resolve, 500));
313
+ context.facts.emailAvailable = req.email !== "taken@test.com";
314
+ context.facts.emailChecked = req.email;
315
+ } finally {
316
+ context.facts.checkingEmail = false;
317
+ }
318
+ },
319
+ },
320
+ },
321
+ });
322
+
323
+ // ============================================================================
324
+ // System
325
+ // ============================================================================
326
+
327
+ export const system = createSystem({
328
+ modules: {
329
+ wizard: wizardModule,
330
+ validation: validationModule,
331
+ },
332
+ debug: { runHistory: true },
333
+ plugins: [
334
+ devtoolsPlugin({ name: "form-wizard" }),
335
+ persistencePlugin({
336
+ storage: localStorage,
337
+ key: "form-wizard-draft",
338
+ include: [
339
+ "wizard::email",
340
+ "wizard::name",
341
+ "wizard::company",
342
+ "wizard::plan",
343
+ "wizard::currentStep",
344
+ ],
345
+ }),
346
+ ],
347
+ });