@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,260 @@
1
+ // Example: pagination
2
+ // Source: examples/pagination/src/pagination.ts
3
+ // Pure module file — no DOM wiring
4
+
5
+ /**
6
+ * Pagination & Infinite Scroll — Directive Modules
7
+ *
8
+ * Two modules: `filters` owns search/sort/category,
9
+ * `list` owns items and pagination state with crossModuleDeps.
10
+ *
11
+ * Constraints:
12
+ * - loadMore: appends next page when scrollNearBottom
13
+ * - filterChanged: resets and re-fetches when filters change
14
+ *
15
+ * Effects:
16
+ * - observeScroll: IntersectionObserver on sentinel element
17
+ */
18
+
19
+ import {
20
+ type ModuleSchema,
21
+ createModule,
22
+ createSystem,
23
+ t,
24
+ } from "@directive-run/core";
25
+ import { devtoolsPlugin, loggingPlugin } from "@directive-run/core/plugins";
26
+ import { type ListItem, fetchPage } from "./mock-api.js";
27
+
28
+ // ============================================================================
29
+ // Filters Module
30
+ // ============================================================================
31
+
32
+ export const filtersSchema = {
33
+ facts: {
34
+ search: t.string(),
35
+ sortBy: t.string<"newest" | "oldest" | "title">(),
36
+ category: t.string(),
37
+ },
38
+ events: {
39
+ setSearch: { value: t.string() },
40
+ setSortBy: { value: t.string() },
41
+ setCategory: { value: t.string() },
42
+ },
43
+ } satisfies ModuleSchema;
44
+
45
+ export const filtersModule = createModule("filters", {
46
+ schema: filtersSchema,
47
+
48
+ init: (facts) => {
49
+ facts.search = "";
50
+ facts.sortBy = "newest";
51
+ facts.category = "all";
52
+ },
53
+
54
+ events: {
55
+ setSearch: (facts, { value }) => {
56
+ facts.search = value;
57
+ },
58
+ setSortBy: (facts, { value }) => {
59
+ facts.sortBy = value;
60
+ },
61
+ setCategory: (facts, { value }) => {
62
+ facts.category = value;
63
+ },
64
+ },
65
+ });
66
+
67
+ // ============================================================================
68
+ // List Module
69
+ // ============================================================================
70
+
71
+ export const listSchema = {
72
+ facts: {
73
+ items: t.object<ListItem[]>(),
74
+ cursor: t.string(),
75
+ hasMore: t.boolean(),
76
+ isLoadingMore: t.boolean(),
77
+ scrollNearBottom: t.boolean(),
78
+ lastFilterHash: t.string(),
79
+ },
80
+ derivations: {
81
+ totalLoaded: t.number(),
82
+ isEmpty: t.boolean(),
83
+ },
84
+ events: {
85
+ setScrollNearBottom: { value: t.boolean() },
86
+ },
87
+ requirements: {
88
+ LOAD_PAGE: {
89
+ cursor: t.string(),
90
+ search: t.string(),
91
+ sortBy: t.string(),
92
+ category: t.string(),
93
+ },
94
+ RESET_AND_LOAD: {
95
+ search: t.string(),
96
+ sortBy: t.string(),
97
+ category: t.string(),
98
+ },
99
+ },
100
+ } satisfies ModuleSchema;
101
+
102
+ export const listModule = createModule("list", {
103
+ schema: listSchema,
104
+
105
+ crossModuleDeps: { filters: filtersSchema },
106
+
107
+ init: (facts) => {
108
+ facts.items = [];
109
+ facts.cursor = "";
110
+ facts.hasMore = true;
111
+ facts.isLoadingMore = false;
112
+ facts.scrollNearBottom = false;
113
+ facts.lastFilterHash = "";
114
+ },
115
+
116
+ // ============================================================================
117
+ // Derivations
118
+ // ============================================================================
119
+
120
+ derive: {
121
+ totalLoaded: (facts) => facts.self.items.length,
122
+ isEmpty: (facts) => facts.self.items.length === 0 && !facts.self.hasMore,
123
+ },
124
+
125
+ // ============================================================================
126
+ // Events
127
+ // ============================================================================
128
+
129
+ events: {
130
+ setScrollNearBottom: (facts, { value }) => {
131
+ facts.scrollNearBottom = value;
132
+ },
133
+ },
134
+
135
+ // ============================================================================
136
+ // Constraints
137
+ // ============================================================================
138
+
139
+ constraints: {
140
+ loadMore: {
141
+ when: (facts) => {
142
+ return (
143
+ facts.self.hasMore &&
144
+ !facts.self.isLoadingMore &&
145
+ facts.self.scrollNearBottom
146
+ );
147
+ },
148
+ require: (facts) => ({
149
+ type: "LOAD_PAGE",
150
+ cursor: facts.self.cursor,
151
+ search: facts.filters.search,
152
+ sortBy: facts.filters.sortBy,
153
+ category: facts.filters.category,
154
+ }),
155
+ },
156
+
157
+ filterChanged: {
158
+ when: (facts) => {
159
+ const hash = `${facts.filters.search}|${facts.filters.sortBy}|${facts.filters.category}`;
160
+
161
+ return hash !== facts.self.lastFilterHash;
162
+ },
163
+ require: (facts) => ({
164
+ type: "RESET_AND_LOAD",
165
+ search: facts.filters.search,
166
+ sortBy: facts.filters.sortBy,
167
+ category: facts.filters.category,
168
+ }),
169
+ },
170
+ },
171
+
172
+ // ============================================================================
173
+ // Resolvers
174
+ // ============================================================================
175
+
176
+ resolvers: {
177
+ loadPage: {
178
+ requirement: "LOAD_PAGE",
179
+ resolve: async (req, context) => {
180
+ context.facts.isLoadingMore = true;
181
+
182
+ try {
183
+ const data = await fetchPage(req.cursor, 20, {
184
+ search: req.search,
185
+ sortBy: req.sortBy,
186
+ category: req.category,
187
+ });
188
+
189
+ context.facts.items = [...context.facts.items, ...data.items];
190
+ context.facts.cursor = data.nextCursor;
191
+ context.facts.hasMore = data.hasMore;
192
+ } finally {
193
+ context.facts.isLoadingMore = false;
194
+ }
195
+ },
196
+ },
197
+
198
+ resetAndLoad: {
199
+ requirement: "RESET_AND_LOAD",
200
+ resolve: async (req, context) => {
201
+ const hash = `${req.search}|${req.sortBy}|${req.category}`;
202
+
203
+ context.facts.items = [];
204
+ context.facts.cursor = "";
205
+ context.facts.hasMore = true;
206
+ context.facts.isLoadingMore = true;
207
+ context.facts.lastFilterHash = hash;
208
+
209
+ try {
210
+ const data = await fetchPage("", 20, {
211
+ search: req.search,
212
+ sortBy: req.sortBy,
213
+ category: req.category,
214
+ });
215
+
216
+ context.facts.items = data.items;
217
+ context.facts.cursor = data.nextCursor;
218
+ context.facts.hasMore = data.hasMore;
219
+ } finally {
220
+ context.facts.isLoadingMore = false;
221
+ }
222
+ },
223
+ },
224
+ },
225
+
226
+ // ============================================================================
227
+ // Effects
228
+ // ============================================================================
229
+
230
+ effects: {
231
+ observeScroll: {
232
+ run: (facts) => {
233
+ const sentinel = document.getElementById("pg-scroll-sentinel");
234
+ if (!sentinel) {
235
+ return;
236
+ }
237
+
238
+ const observer = new IntersectionObserver(
239
+ ([entry]) => {
240
+ facts.self.scrollNearBottom = entry.isIntersecting;
241
+ },
242
+ { rootMargin: "200px" },
243
+ );
244
+ observer.observe(sentinel);
245
+
246
+ return () => observer.disconnect();
247
+ },
248
+ },
249
+ },
250
+ });
251
+
252
+ // ============================================================================
253
+ // System
254
+ // ============================================================================
255
+
256
+ export const system = createSystem({
257
+ modules: { filters: filtersModule, list: listModule },
258
+ debug: { runHistory: true },
259
+ plugins: [loggingPlugin(), devtoolsPlugin({ name: "pagination" })],
260
+ });
@@ -0,0 +1,337 @@
1
+ // Example: permissions
2
+ // Source: examples/permissions/src/permissions.ts
3
+ // Pure module file — no DOM wiring
4
+
5
+ /**
6
+ * Role-Based Permissions — Directive Modules
7
+ *
8
+ * Three modules demonstrate cross-module constraint resolution:
9
+ * - auth: manages login state (role, userName, token)
10
+ * - permissions: loads permissions based on auth role, derives capability flags
11
+ * - content: manages articles with permission-gated actions
12
+ *
13
+ * The system uses `crossModuleDeps` so constraints in one module
14
+ * can react to derivations/facts from another module.
15
+ */
16
+
17
+ import {
18
+ type ModuleSchema,
19
+ createModule,
20
+ createSystem,
21
+ t,
22
+ } from "@directive-run/core";
23
+ import { devtoolsPlugin } from "@directive-run/core/plugins";
24
+ import {
25
+ type Article,
26
+ deleteArticle as apiDeleteArticle,
27
+ fetchArticles as apiFetchArticles,
28
+ fetchPermissions as apiFetchPermissions,
29
+ publishArticle as apiPublishArticle,
30
+ } from "./mock-api.js";
31
+
32
+ // ============================================================================
33
+ // Preset Users
34
+ // ============================================================================
35
+
36
+ const presetUsers: Record<
37
+ string,
38
+ { userName: string; role: string; token: string }
39
+ > = {
40
+ alice: { userName: "Alice", role: "admin", token: "tok-alice-admin" },
41
+ bob: { userName: "Bob", role: "editor", token: "tok-bob-editor" },
42
+ carol: { userName: "Carol", role: "viewer", token: "tok-carol-viewer" },
43
+ };
44
+
45
+ // ============================================================================
46
+ // Auth Module
47
+ // ============================================================================
48
+
49
+ export const authSchema = {
50
+ facts: {
51
+ role: t.string(),
52
+ userName: t.string(),
53
+ token: t.string(),
54
+ },
55
+ derivations: {
56
+ isAuthenticated: t.boolean(),
57
+ },
58
+ events: {
59
+ login: { userId: t.string() },
60
+ logout: {},
61
+ },
62
+ requirements: {},
63
+ } satisfies ModuleSchema;
64
+
65
+ export const authModule = createModule("auth", {
66
+ schema: authSchema,
67
+
68
+ init: (facts) => {
69
+ facts.role = "";
70
+ facts.userName = "";
71
+ facts.token = "";
72
+ },
73
+
74
+ derive: {
75
+ isAuthenticated: (facts) => facts.token !== "",
76
+ },
77
+
78
+ events: {
79
+ login: (facts, { userId }) => {
80
+ const preset = presetUsers[userId];
81
+ if (!preset) {
82
+ return;
83
+ }
84
+
85
+ facts.token = preset.token;
86
+ facts.userName = preset.userName;
87
+ facts.role = preset.role;
88
+ },
89
+
90
+ logout: (facts) => {
91
+ facts.token = "";
92
+ facts.userName = "";
93
+ facts.role = "";
94
+ },
95
+ },
96
+ });
97
+
98
+ // ============================================================================
99
+ // Permissions Module
100
+ // ============================================================================
101
+
102
+ export const permissionsSchema = {
103
+ facts: {
104
+ permissions: t.object<string[]>(),
105
+ loaded: t.boolean(),
106
+ },
107
+ derivations: {
108
+ canEdit: t.boolean(),
109
+ canPublish: t.boolean(),
110
+ canDelete: t.boolean(),
111
+ canManageUsers: t.boolean(),
112
+ canViewAnalytics: t.boolean(),
113
+ isAdmin: t.boolean(),
114
+ permissionCount: t.number(),
115
+ },
116
+ events: {
117
+ reset: {},
118
+ },
119
+ requirements: {
120
+ FETCH_PERMISSIONS: { role: t.string() },
121
+ },
122
+ } satisfies ModuleSchema;
123
+
124
+ export const permissionsModule = createModule("permissions", {
125
+ schema: permissionsSchema,
126
+
127
+ crossModuleDeps: { auth: authSchema },
128
+
129
+ init: (facts) => {
130
+ facts.permissions = [];
131
+ facts.loaded = false;
132
+ },
133
+
134
+ derive: {
135
+ canEdit: (facts) =>
136
+ (facts.self.permissions as string[]).includes("content.edit"),
137
+ canPublish: (facts) =>
138
+ (facts.self.permissions as string[]).includes("content.publish"),
139
+ canDelete: (facts) =>
140
+ (facts.self.permissions as string[]).includes("content.delete"),
141
+ canManageUsers: (facts) =>
142
+ (facts.self.permissions as string[]).includes("users.manage"),
143
+ canViewAnalytics: (facts) =>
144
+ (facts.self.permissions as string[]).includes("analytics.view"),
145
+ isAdmin: (_facts, derive) => derive.canManageUsers as boolean,
146
+ permissionCount: (facts) => (facts.self.permissions as string[]).length,
147
+ },
148
+
149
+ events: {
150
+ reset: (facts) => {
151
+ facts.permissions = [];
152
+ facts.loaded = false;
153
+ },
154
+ },
155
+
156
+ constraints: {
157
+ loadPermissions: {
158
+ when: (facts) => {
159
+ return (
160
+ (facts.auth.token as string) !== "" && !(facts.self.loaded as boolean)
161
+ );
162
+ },
163
+ require: (facts) => ({
164
+ type: "FETCH_PERMISSIONS",
165
+ role: facts.auth.role as string,
166
+ }),
167
+ },
168
+ },
169
+
170
+ resolvers: {
171
+ fetchPermissions: {
172
+ requirement: "FETCH_PERMISSIONS",
173
+ timeout: 5000,
174
+ resolve: async (req, context) => {
175
+ const perms = await apiFetchPermissions(req.role);
176
+ context.facts.permissions = perms;
177
+ context.facts.loaded = true;
178
+ },
179
+ },
180
+ },
181
+ });
182
+
183
+ // ============================================================================
184
+ // Content Module
185
+ // ============================================================================
186
+
187
+ export const contentSchema = {
188
+ facts: {
189
+ articles: t.object<Article[]>(),
190
+ loaded: t.boolean(),
191
+ publishRequested: t.string(),
192
+ deleteRequested: t.string(),
193
+ actionStatus: t.string(),
194
+ },
195
+ derivations: {},
196
+ events: {
197
+ requestPublish: { articleId: t.string() },
198
+ requestDelete: { articleId: t.string() },
199
+ clearAction: {},
200
+ },
201
+ requirements: {
202
+ LOAD_CONTENT: {},
203
+ PUBLISH_ARTICLE: { articleId: t.string() },
204
+ DELETE_ARTICLE: { articleId: t.string() },
205
+ },
206
+ } satisfies ModuleSchema;
207
+
208
+ export const contentModule = createModule("content", {
209
+ schema: contentSchema,
210
+
211
+ crossModuleDeps: { auth: authSchema, permissions: permissionsSchema },
212
+
213
+ init: (facts) => {
214
+ facts.articles = [];
215
+ facts.loaded = false;
216
+ facts.publishRequested = "";
217
+ facts.deleteRequested = "";
218
+ facts.actionStatus = "idle";
219
+ },
220
+
221
+ constraints: {
222
+ loadContent: {
223
+ when: (facts) => {
224
+ return (
225
+ (facts.auth.token as string) !== "" && !(facts.self.loaded as boolean)
226
+ );
227
+ },
228
+ require: { type: "LOAD_CONTENT" },
229
+ },
230
+
231
+ publishArticle: {
232
+ when: (facts) => {
233
+ return (
234
+ (facts.self.publishRequested as string) !== "" &&
235
+ (facts.permissions.permissions as string[]).includes(
236
+ "content.publish",
237
+ )
238
+ );
239
+ },
240
+ require: (facts) => ({
241
+ type: "PUBLISH_ARTICLE",
242
+ articleId: facts.self.publishRequested as string,
243
+ }),
244
+ },
245
+
246
+ deleteArticle: {
247
+ when: (facts) => {
248
+ return (
249
+ (facts.self.deleteRequested as string) !== "" &&
250
+ (facts.permissions.permissions as string[]).includes("content.delete")
251
+ );
252
+ },
253
+ require: (facts) => ({
254
+ type: "DELETE_ARTICLE",
255
+ articleId: facts.self.deleteRequested as string,
256
+ }),
257
+ },
258
+ },
259
+
260
+ resolvers: {
261
+ loadContent: {
262
+ requirement: "LOAD_CONTENT",
263
+ timeout: 5000,
264
+ resolve: async (_req, context) => {
265
+ const articles = await apiFetchArticles();
266
+ context.facts.articles = articles;
267
+ context.facts.loaded = true;
268
+ },
269
+ },
270
+
271
+ publishArticle: {
272
+ requirement: "PUBLISH_ARTICLE",
273
+ timeout: 5000,
274
+ resolve: async (req, context) => {
275
+ context.facts.actionStatus = "publishing";
276
+ await apiPublishArticle(req.articleId);
277
+
278
+ const articles = context.facts.articles as Article[];
279
+ context.facts.articles = articles.map((a) => {
280
+ if (a.id === req.articleId) {
281
+ return { ...a, status: "published" as const };
282
+ }
283
+
284
+ return a;
285
+ });
286
+ context.facts.publishRequested = "";
287
+ context.facts.actionStatus = "done";
288
+ },
289
+ },
290
+
291
+ deleteArticle: {
292
+ requirement: "DELETE_ARTICLE",
293
+ timeout: 5000,
294
+ resolve: async (req, context) => {
295
+ context.facts.actionStatus = "deleting";
296
+ await apiDeleteArticle(req.articleId);
297
+
298
+ const articles = context.facts.articles as Article[];
299
+ context.facts.articles = articles.filter((a) => a.id !== req.articleId);
300
+ context.facts.deleteRequested = "";
301
+ context.facts.actionStatus = "done";
302
+ },
303
+ },
304
+ },
305
+
306
+ events: {
307
+ requestPublish: (facts, { articleId }) => {
308
+ facts.publishRequested = articleId;
309
+ facts.actionStatus = "idle";
310
+ },
311
+
312
+ requestDelete: (facts, { articleId }) => {
313
+ facts.deleteRequested = articleId;
314
+ facts.actionStatus = "idle";
315
+ },
316
+
317
+ clearAction: (facts) => {
318
+ facts.publishRequested = "";
319
+ facts.deleteRequested = "";
320
+ facts.actionStatus = "idle";
321
+ },
322
+ },
323
+ });
324
+
325
+ // ============================================================================
326
+ // System
327
+ // ============================================================================
328
+
329
+ export const system = createSystem({
330
+ modules: {
331
+ auth: authModule,
332
+ permissions: permissionsModule,
333
+ content: contentModule,
334
+ },
335
+ debug: { runHistory: true },
336
+ plugins: [devtoolsPlugin({ name: "permissions" })],
337
+ });