@directive-run/core 0.1.1 → 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.
package/README.md CHANGED
@@ -1,14 +1,26 @@
1
1
  # @directive-run/core
2
2
 
3
+ [![npm](https://img.shields.io/npm/v/@directive-run/core?color=%236366f1)](https://www.npmjs.com/package/@directive-run/core)
4
+ [![downloads](https://img.shields.io/npm/dm/@directive-run/core)](https://www.npmjs.com/package/@directive-run/core)
5
+ [![bundle size](https://img.shields.io/bundlephobia/minzip/@directive-run/core)](https://bundlephobia.com/package/@directive-run/core)
6
+ [![CI](https://img.shields.io/github/actions/workflow/status/directive-run/directive/ci.yml?branch=main&label=CI)](https://github.com/directive-run/directive/actions)
7
+ [![license](https://img.shields.io/npm/l/@directive-run/core)](https://github.com/directive-run/directive/blob/main/LICENSE)
8
+
3
9
  Constraint-driven runtime for TypeScript. Declare requirements, let the runtime resolve them.
4
10
 
11
+ - **Auto-tracking derivations** – computed values that track their own dependencies, no manual dep arrays
12
+ - **Typed constraint/resolver cycle** – constraints declare what must be true, resolvers make it true
13
+ - **Events** – typed event handlers with payloads for imperative actions
14
+ - **Plugin architecture** – logging, devtools, persistence, and custom lifecycle hooks
15
+ - **Framework adapters** – first-class bindings for React, Vue, Svelte, Solid, and Lit
16
+
5
17
  ## Install
6
18
 
7
19
  ```bash
8
20
  npm install @directive-run/core
9
21
  ```
10
22
 
11
- ## Usage
23
+ ## Quick Start
12
24
 
13
25
  ```typescript
14
26
  import { createModule, createSystem, t } from "@directive-run/core";
@@ -17,19 +29,25 @@ const counter = createModule("counter", {
17
29
  schema: {
18
30
  facts: { count: t.number() },
19
31
  derivations: { doubled: t.number() },
20
- events: { increment: {} },
32
+ events: { increment: {}, reset: {} },
21
33
  requirements: {},
22
34
  },
35
+
23
36
  init: (facts) => {
24
37
  facts.count = 0;
25
38
  },
39
+
26
40
  derive: {
27
41
  doubled: (facts) => facts.count * 2,
28
42
  },
29
- on: {
43
+
44
+ events: {
30
45
  increment: (facts) => {
31
46
  facts.count += 1;
32
47
  },
48
+ reset: (facts) => {
49
+ facts.count = 0;
50
+ },
33
51
  },
34
52
  });
35
53
 
@@ -37,21 +55,147 @@ const system = createSystem({ module: counter });
37
55
  system.start();
38
56
 
39
57
  system.events.increment();
40
- console.log(system.facts.count); // 1
58
+ console.log(system.facts.count); // 1
41
59
  console.log(system.read("doubled")); // 2
42
60
  ```
43
61
 
62
+ ## Derived State
63
+
64
+ Derivations auto-track which facts they read. No dependency arrays, no manual subscriptions. Derivations can depend on other derivations for composition:
65
+
66
+ ```typescript
67
+ const app = createModule("app", {
68
+ schema: {
69
+ facts: { items: t.array<string>(), filter: t.string() },
70
+ derivations: {
71
+ filtered: t.array<string>(),
72
+ count: t.number(),
73
+ summary: t.string(),
74
+ },
75
+ events: {},
76
+ requirements: {},
77
+ },
78
+
79
+ init: (facts) => {
80
+ facts.items = ["apple", "banana", "avocado"];
81
+ facts.filter = "a";
82
+ },
83
+
84
+ derive: {
85
+ filtered: (facts) => facts.items.filter((i) => i.startsWith(facts.filter)),
86
+ count: (_facts, derive) => derive.filtered.length,
87
+ summary: (_facts, derive) => `${derive.count} items match`,
88
+ },
89
+ });
90
+ ```
91
+
92
+ ## Constraints and Resolvers
93
+
94
+ The constraint/resolver cycle is the core of Directive. Constraints declare _what_ must be true. Resolvers declare _how_ to make it true. The runtime connects them automatically.
95
+
96
+ ```typescript
97
+ import { createModule, createSystem, t } from "@directive-run/core";
98
+
99
+ const userModule = createModule("user", {
100
+ schema: {
101
+ facts: {
102
+ userId: t.string().nullable(),
103
+ profile: t.object<{ name: string }>().nullable(),
104
+ },
105
+ derivations: {},
106
+ events: { login: { userId: t.string() } },
107
+ requirements: { FETCH_PROFILE: { userId: t.string() } },
108
+ },
109
+
110
+ init: (facts) => {
111
+ facts.userId = null;
112
+ facts.profile = null;
113
+ },
114
+
115
+ events: {
116
+ login: (facts, payload) => {
117
+ facts.userId = payload.userId;
118
+ },
119
+ },
120
+
121
+ constraints: {
122
+ needsProfile: {
123
+ when: (facts) => facts.userId !== null && facts.profile === null,
124
+ require: (facts) => ({ type: "FETCH_PROFILE", userId: facts.userId! }),
125
+ },
126
+ },
127
+
128
+ resolvers: {
129
+ fetchProfile: {
130
+ requirement: "FETCH_PROFILE",
131
+ retry: { attempts: 3, backoff: "exponential" },
132
+ resolve: async (req, context) => {
133
+ const res = await fetch(`/api/users/${req.userId}`);
134
+ context.facts.profile = await res.json();
135
+ },
136
+ },
137
+ },
138
+ });
139
+
140
+ const system = createSystem({ module: userModule });
141
+ system.start();
142
+
143
+ // Dispatching login sets userId, which triggers the constraint,
144
+ // which emits the requirement, which the resolver fulfills automatically.
145
+ system.events.login({ userId: "u-123" });
146
+ ```
147
+
148
+ ## Events
149
+
150
+ Events provide typed imperative actions with payloads. Define them in your schema and handle them with `events`:
151
+
152
+ ```typescript
153
+ events: {
154
+ addItem: (facts, payload: { name: string; price: number }) => {
155
+ facts.items = [...facts.items, { name: payload.name, price: payload.price }];
156
+ },
157
+ removeItem: (facts, payload: { id: string }) => {
158
+ facts.items = facts.items.filter((i) => i.id !== payload.id);
159
+ },
160
+ },
161
+
162
+ // Typed and autocompleted:
163
+ system.events.addItem({ name: "Widget", price: 9.99 });
164
+ ```
165
+
166
+ ## Framework Adapters
167
+
168
+ | Package | Framework | Reactivity Model |
169
+ |---------|-----------|-----------------|
170
+ | [`@directive-run/react`](https://www.npmjs.com/package/@directive-run/react) | React 18+ | `useSyncExternalStore` hooks |
171
+ | [`@directive-run/vue`](https://www.npmjs.com/package/@directive-run/vue) | Vue 3+ | `Ref` / `ShallowRef` composables |
172
+ | [`@directive-run/svelte`](https://www.npmjs.com/package/@directive-run/svelte) | Svelte 4+ | `Readable` stores |
173
+ | [`@directive-run/solid`](https://www.npmjs.com/package/@directive-run/solid) | Solid.js 1+ | `Accessor` signals |
174
+ | [`@directive-run/lit`](https://www.npmjs.com/package/@directive-run/lit) | Lit 3+ | `ReactiveController` classes |
175
+
44
176
  ## Subpath Exports
45
177
 
46
178
  | Import | Purpose |
47
179
  |--------|---------|
48
- | `@directive-run/core` | Core runtime, modules, systems |
49
- | `@directive-run/core/plugins` | Logging, devtools, persistence plugins |
50
- | `@directive-run/core/testing` | Mock resolvers, fake timers, assertions |
180
+ | `@directive-run/core` | Core runtime &ndash; modules, systems, schema types |
181
+ | `@directive-run/core/plugins` | Logging, devtools, persistence, observability, circuit breaker |
182
+ | `@directive-run/core/testing` | Mock resolvers, fake timers, assertion helpers |
51
183
  | `@directive-run/core/migration` | Redux/Zustand/XState migration helpers |
184
+ | `@directive-run/core/worker` | Web Worker support |
185
+
186
+ ## Why Directive?
187
+
188
+ - **Declarative over imperative** &ndash; describe what your system needs, not how to wire it up. Constraints and resolvers replace manual data-fetching orchestration.
189
+ - **Auto-tracking over manual subscriptions** &ndash; derivations detect their own dependencies at runtime. No selector functions, no dependency arrays, no stale closures.
190
+ - **Constraint-driven over event-driven** &ndash; instead of chaining events to coordinate async work, declare constraints that the runtime satisfies automatically with retry, batching, and error boundaries.
191
+ - **Framework-agnostic core** &ndash; one state layer, five framework adapters. Move between React, Vue, Svelte, Solid, and Lit without rewriting your state logic.
192
+
193
+ ## Documentation
194
+
195
+ - [Getting Started](https://directive.run/docs/getting-started)
196
+ - [API Reference](https://directive.run/docs/api)
197
+ - [GitHub](https://github.com/directive-run/directive)
52
198
 
53
199
  ## License
54
200
 
55
201
  MIT
56
-
57
- [Full documentation](https://directive.run/docs)
@@ -1,4 +1,4 @@
1
- import { R as Requirement, P as Plugin, F as Facts, S as Schema, Y as TimeTravelAPI, aE as SystemInspection, aH as TimeTravelState } from './plugins-CcwEXXMS.cjs';
1
+ import { R as Requirement, P as Plugin, F as Facts, S as Schema, Y as TimeTravelAPI, aE as SystemInspection, aH as TimeTravelState } from './plugins-KKRG7lDP.cjs';
2
2
  export { s as shallowEqual } from './utils-4JrY5fk9.cjs';
3
3
 
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { R as Requirement, P as Plugin, F as Facts, S as Schema, Y as TimeTravelAPI, aE as SystemInspection, aH as TimeTravelState } from './plugins-CcwEXXMS.js';
1
+ import { R as Requirement, P as Plugin, F as Facts, S as Schema, Y as TimeTravelAPI, aE as SystemInspection, aH as TimeTravelState } from './plugins-KKRG7lDP.js';
2
2
  export { s as shallowEqual } from './utils-4JrY5fk9.js';
3
3
 
4
4
  /**