@plures/praxis 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 (263) hide show
  1. package/FRAMEWORK.md +420 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1310 -0
  4. package/dist/adapters/cli.d.ts +43 -0
  5. package/dist/adapters/cli.d.ts.map +1 -0
  6. package/dist/adapters/cli.js +126 -0
  7. package/dist/adapters/cli.js.map +1 -0
  8. package/dist/cli/commands/auth.d.ts +26 -0
  9. package/dist/cli/commands/auth.d.ts.map +1 -0
  10. package/dist/cli/commands/auth.js +233 -0
  11. package/dist/cli/commands/auth.js.map +1 -0
  12. package/dist/cli/commands/cloud.d.ts +27 -0
  13. package/dist/cli/commands/cloud.d.ts.map +1 -0
  14. package/dist/cli/commands/cloud.js +232 -0
  15. package/dist/cli/commands/cloud.js.map +1 -0
  16. package/dist/cli/commands/generate.d.ts +25 -0
  17. package/dist/cli/commands/generate.d.ts.map +1 -0
  18. package/dist/cli/commands/generate.js +168 -0
  19. package/dist/cli/commands/generate.js.map +1 -0
  20. package/dist/cli/index.d.ts +8 -0
  21. package/dist/cli/index.d.ts.map +1 -0
  22. package/dist/cli/index.js +179 -0
  23. package/dist/cli/index.js.map +1 -0
  24. package/dist/cloud/auth.d.ts +51 -0
  25. package/dist/cloud/auth.d.ts.map +1 -0
  26. package/dist/cloud/auth.js +194 -0
  27. package/dist/cloud/auth.js.map +1 -0
  28. package/dist/cloud/billing.d.ts +184 -0
  29. package/dist/cloud/billing.d.ts.map +1 -0
  30. package/dist/cloud/billing.js +179 -0
  31. package/dist/cloud/billing.js.map +1 -0
  32. package/dist/cloud/client.d.ts +39 -0
  33. package/dist/cloud/client.d.ts.map +1 -0
  34. package/dist/cloud/client.js +176 -0
  35. package/dist/cloud/client.js.map +1 -0
  36. package/dist/cloud/index.d.ts +44 -0
  37. package/dist/cloud/index.d.ts.map +1 -0
  38. package/dist/cloud/index.js +44 -0
  39. package/dist/cloud/index.js.map +1 -0
  40. package/dist/cloud/marketplace.d.ts +166 -0
  41. package/dist/cloud/marketplace.d.ts.map +1 -0
  42. package/dist/cloud/marketplace.js +159 -0
  43. package/dist/cloud/marketplace.js.map +1 -0
  44. package/dist/cloud/provisioning.d.ts +110 -0
  45. package/dist/cloud/provisioning.d.ts.map +1 -0
  46. package/dist/cloud/provisioning.js +148 -0
  47. package/dist/cloud/provisioning.js.map +1 -0
  48. package/dist/cloud/relay/endpoints.d.ts +62 -0
  49. package/dist/cloud/relay/endpoints.d.ts.map +1 -0
  50. package/dist/cloud/relay/endpoints.js +217 -0
  51. package/dist/cloud/relay/endpoints.js.map +1 -0
  52. package/dist/cloud/relay/health/index.d.ts +5 -0
  53. package/dist/cloud/relay/health/index.d.ts.map +1 -0
  54. package/dist/cloud/relay/health/index.js +9 -0
  55. package/dist/cloud/relay/health/index.js.map +1 -0
  56. package/dist/cloud/relay/stats/index.d.ts +5 -0
  57. package/dist/cloud/relay/stats/index.d.ts.map +1 -0
  58. package/dist/cloud/relay/stats/index.js +9 -0
  59. package/dist/cloud/relay/stats/index.js.map +1 -0
  60. package/dist/cloud/relay/sync/index.d.ts +5 -0
  61. package/dist/cloud/relay/sync/index.d.ts.map +1 -0
  62. package/dist/cloud/relay/sync/index.js +9 -0
  63. package/dist/cloud/relay/sync/index.js.map +1 -0
  64. package/dist/cloud/relay/usage/index.d.ts +5 -0
  65. package/dist/cloud/relay/usage/index.d.ts.map +1 -0
  66. package/dist/cloud/relay/usage/index.js +9 -0
  67. package/dist/cloud/relay/usage/index.js.map +1 -0
  68. package/dist/cloud/sponsors.d.ts +81 -0
  69. package/dist/cloud/sponsors.d.ts.map +1 -0
  70. package/dist/cloud/sponsors.js +130 -0
  71. package/dist/cloud/sponsors.js.map +1 -0
  72. package/dist/cloud/types.d.ts +169 -0
  73. package/dist/cloud/types.d.ts.map +1 -0
  74. package/dist/cloud/types.js +7 -0
  75. package/dist/cloud/types.js.map +1 -0
  76. package/dist/components/index.d.ts +43 -0
  77. package/dist/components/index.d.ts.map +1 -0
  78. package/dist/components/index.js +17 -0
  79. package/dist/components/index.js.map +1 -0
  80. package/dist/core/actors.d.ts +95 -0
  81. package/dist/core/actors.d.ts.map +1 -0
  82. package/dist/core/actors.js +158 -0
  83. package/dist/core/actors.js.map +1 -0
  84. package/dist/core/component/generator.d.ts +122 -0
  85. package/dist/core/component/generator.d.ts.map +1 -0
  86. package/dist/core/component/generator.js +307 -0
  87. package/dist/core/component/generator.js.map +1 -0
  88. package/dist/core/engine.d.ts +92 -0
  89. package/dist/core/engine.d.ts.map +1 -0
  90. package/dist/core/engine.js +199 -0
  91. package/dist/core/engine.js.map +1 -0
  92. package/dist/core/introspection.d.ts +141 -0
  93. package/dist/core/introspection.d.ts.map +1 -0
  94. package/dist/core/introspection.js +208 -0
  95. package/dist/core/introspection.js.map +1 -0
  96. package/dist/core/logic/generator.d.ts +76 -0
  97. package/dist/core/logic/generator.d.ts.map +1 -0
  98. package/dist/core/logic/generator.js +339 -0
  99. package/dist/core/logic/generator.js.map +1 -0
  100. package/dist/core/pluresdb/generator.d.ts +58 -0
  101. package/dist/core/pluresdb/generator.d.ts.map +1 -0
  102. package/dist/core/pluresdb/generator.js +162 -0
  103. package/dist/core/pluresdb/generator.js.map +1 -0
  104. package/dist/core/protocol.d.ts +121 -0
  105. package/dist/core/protocol.d.ts.map +1 -0
  106. package/dist/core/protocol.js +46 -0
  107. package/dist/core/protocol.js.map +1 -0
  108. package/dist/core/rules.d.ts +120 -0
  109. package/dist/core/rules.d.ts.map +1 -0
  110. package/dist/core/rules.js +81 -0
  111. package/dist/core/rules.js.map +1 -0
  112. package/dist/core/schema/loader.d.ts +47 -0
  113. package/dist/core/schema/loader.d.ts.map +1 -0
  114. package/dist/core/schema/loader.js +189 -0
  115. package/dist/core/schema/loader.js.map +1 -0
  116. package/dist/core/schema/normalize.d.ts +72 -0
  117. package/dist/core/schema/normalize.d.ts.map +1 -0
  118. package/dist/core/schema/normalize.js +190 -0
  119. package/dist/core/schema/normalize.js.map +1 -0
  120. package/dist/core/schema/types.d.ts +370 -0
  121. package/dist/core/schema/types.d.ts.map +1 -0
  122. package/dist/core/schema/types.js +161 -0
  123. package/dist/core/schema/types.js.map +1 -0
  124. package/dist/dsl/index.d.ts +152 -0
  125. package/dist/dsl/index.d.ts.map +1 -0
  126. package/dist/dsl/index.js +132 -0
  127. package/dist/dsl/index.js.map +1 -0
  128. package/dist/dsl.d.ts +124 -0
  129. package/dist/dsl.d.ts.map +1 -0
  130. package/dist/dsl.js +130 -0
  131. package/dist/dsl.js.map +1 -0
  132. package/dist/examples/advanced-todo/index.d.ts +55 -0
  133. package/dist/examples/advanced-todo/index.d.ts.map +1 -0
  134. package/dist/examples/advanced-todo/index.js +222 -0
  135. package/dist/examples/advanced-todo/index.js.map +1 -0
  136. package/dist/examples/auth-basic/index.d.ts +17 -0
  137. package/dist/examples/auth-basic/index.d.ts.map +1 -0
  138. package/dist/examples/auth-basic/index.js +122 -0
  139. package/dist/examples/auth-basic/index.js.map +1 -0
  140. package/dist/examples/cart/index.d.ts +19 -0
  141. package/dist/examples/cart/index.d.ts.map +1 -0
  142. package/dist/examples/cart/index.js +202 -0
  143. package/dist/examples/cart/index.js.map +1 -0
  144. package/dist/examples/hero-ecommerce/index.d.ts +39 -0
  145. package/dist/examples/hero-ecommerce/index.d.ts.map +1 -0
  146. package/dist/examples/hero-ecommerce/index.js +506 -0
  147. package/dist/examples/hero-ecommerce/index.js.map +1 -0
  148. package/dist/examples/svelte-counter/index.d.ts +31 -0
  149. package/dist/examples/svelte-counter/index.d.ts.map +1 -0
  150. package/dist/examples/svelte-counter/index.js +123 -0
  151. package/dist/examples/svelte-counter/index.js.map +1 -0
  152. package/dist/flows.d.ts +125 -0
  153. package/dist/flows.d.ts.map +1 -0
  154. package/dist/flows.js +160 -0
  155. package/dist/flows.js.map +1 -0
  156. package/dist/index.d.ts +67 -0
  157. package/dist/index.d.ts.map +1 -0
  158. package/dist/index.js +59 -0
  159. package/dist/index.js.map +1 -0
  160. package/dist/integrations/pluresdb.d.ts +56 -0
  161. package/dist/integrations/pluresdb.d.ts.map +1 -0
  162. package/dist/integrations/pluresdb.js +46 -0
  163. package/dist/integrations/pluresdb.js.map +1 -0
  164. package/dist/integrations/svelte.d.ts +306 -0
  165. package/dist/integrations/svelte.d.ts.map +1 -0
  166. package/dist/integrations/svelte.js +447 -0
  167. package/dist/integrations/svelte.js.map +1 -0
  168. package/dist/registry.d.ts +94 -0
  169. package/dist/registry.d.ts.map +1 -0
  170. package/dist/registry.js +181 -0
  171. package/dist/registry.js.map +1 -0
  172. package/dist/runtime/terminal-adapter.d.ts +105 -0
  173. package/dist/runtime/terminal-adapter.d.ts.map +1 -0
  174. package/dist/runtime/terminal-adapter.js +113 -0
  175. package/dist/runtime/terminal-adapter.js.map +1 -0
  176. package/dist/step.d.ts +34 -0
  177. package/dist/step.d.ts.map +1 -0
  178. package/dist/step.js +111 -0
  179. package/dist/step.js.map +1 -0
  180. package/dist/types.d.ts +63 -0
  181. package/dist/types.d.ts.map +1 -0
  182. package/dist/types.js +6 -0
  183. package/dist/types.js.map +1 -0
  184. package/docs/MONETIZATION.md +394 -0
  185. package/docs/TERMINAL_NODE.md +588 -0
  186. package/docs/guides/canvas.md +389 -0
  187. package/docs/guides/getting-started.md +347 -0
  188. package/docs/guides/history-state-pattern.md +618 -0
  189. package/docs/guides/orchestration.md +617 -0
  190. package/docs/guides/parallel-state-pattern.md +767 -0
  191. package/docs/guides/svelte-integration.md +691 -0
  192. package/package.json +96 -0
  193. package/src/__tests__/actors.test.ts +270 -0
  194. package/src/__tests__/billing.test.ts +175 -0
  195. package/src/__tests__/cloud.test.ts +247 -0
  196. package/src/__tests__/dsl.test.ts +154 -0
  197. package/src/__tests__/edge-cases.test.ts +475 -0
  198. package/src/__tests__/engine.test.ts +137 -0
  199. package/src/__tests__/generators.test.ts +270 -0
  200. package/src/__tests__/introspection.test.ts +321 -0
  201. package/src/__tests__/protocol.test.ts +40 -0
  202. package/src/__tests__/provisioning.test.ts +162 -0
  203. package/src/__tests__/schema.test.ts +241 -0
  204. package/src/__tests__/svelte-integration.test.ts +431 -0
  205. package/src/__tests__/terminal-node.test.ts +352 -0
  206. package/src/adapters/cli.ts +175 -0
  207. package/src/cli/commands/auth.ts +271 -0
  208. package/src/cli/commands/cloud.ts +281 -0
  209. package/src/cli/commands/generate.ts +225 -0
  210. package/src/cli/index.ts +190 -0
  211. package/src/cloud/README.md +383 -0
  212. package/src/cloud/auth.ts +245 -0
  213. package/src/cloud/billing.ts +336 -0
  214. package/src/cloud/client.ts +221 -0
  215. package/src/cloud/index.ts +121 -0
  216. package/src/cloud/marketplace.ts +303 -0
  217. package/src/cloud/provisioning.ts +254 -0
  218. package/src/cloud/relay/endpoints.ts +307 -0
  219. package/src/cloud/relay/health/function.json +17 -0
  220. package/src/cloud/relay/health/index.ts +10 -0
  221. package/src/cloud/relay/host.json +15 -0
  222. package/src/cloud/relay/local.settings.json +8 -0
  223. package/src/cloud/relay/stats/function.json +17 -0
  224. package/src/cloud/relay/stats/index.ts +10 -0
  225. package/src/cloud/relay/sync/function.json +17 -0
  226. package/src/cloud/relay/sync/index.ts +10 -0
  227. package/src/cloud/relay/usage/function.json +17 -0
  228. package/src/cloud/relay/usage/index.ts +10 -0
  229. package/src/cloud/sponsors.ts +213 -0
  230. package/src/cloud/types.ts +198 -0
  231. package/src/components/README.md +125 -0
  232. package/src/components/TerminalNode.svelte +457 -0
  233. package/src/components/index.ts +46 -0
  234. package/src/core/actors.ts +205 -0
  235. package/src/core/component/generator.ts +432 -0
  236. package/src/core/engine.ts +243 -0
  237. package/src/core/introspection.ts +329 -0
  238. package/src/core/logic/generator.ts +420 -0
  239. package/src/core/pluresdb/generator.ts +229 -0
  240. package/src/core/protocol.ts +132 -0
  241. package/src/core/rules.ts +167 -0
  242. package/src/core/schema/loader.ts +247 -0
  243. package/src/core/schema/normalize.ts +322 -0
  244. package/src/core/schema/types.ts +557 -0
  245. package/src/dsl/index.ts +218 -0
  246. package/src/dsl.ts +214 -0
  247. package/src/examples/advanced-todo/App.svelte +506 -0
  248. package/src/examples/advanced-todo/README.md +371 -0
  249. package/src/examples/advanced-todo/index.ts +309 -0
  250. package/src/examples/auth-basic/index.ts +163 -0
  251. package/src/examples/cart/index.ts +259 -0
  252. package/src/examples/hero-ecommerce/index.ts +657 -0
  253. package/src/examples/svelte-counter/index.ts +168 -0
  254. package/src/flows.ts +268 -0
  255. package/src/index.ts +154 -0
  256. package/src/integrations/pluresdb.ts +93 -0
  257. package/src/integrations/svelte.ts +617 -0
  258. package/src/registry.ts +223 -0
  259. package/src/runtime/terminal-adapter.ts +175 -0
  260. package/src/step.ts +151 -0
  261. package/src/types.ts +70 -0
  262. package/templates/basic-app/README.md +147 -0
  263. package/templates/fullstack-app/README.md +279 -0
@@ -0,0 +1,247 @@
1
+ /**
2
+ * Cloud Relay Tests
3
+ */
4
+
5
+ import { describe, it, expect, beforeEach, vi } from "vitest";
6
+ import { createCloudRelay } from "../cloud/client.js";
7
+ import type { CloudRelayConfig, HealthCheckResponse, UsageMetrics } from "../cloud/types.js";
8
+
9
+ describe("Cloud Relay Client", () => {
10
+ let config: CloudRelayConfig;
11
+
12
+ beforeEach(() => {
13
+ config = {
14
+ endpoint: "https://test.example.com",
15
+ appId: "test-app",
16
+ authToken: "test-token",
17
+ };
18
+
19
+ // Mock fetch globally
20
+ global.fetch = vi.fn();
21
+ });
22
+
23
+ it("should create a cloud relay client", () => {
24
+ const client = createCloudRelay(config);
25
+ expect(client).toBeDefined();
26
+ expect(client.getStatus).toBeDefined();
27
+ expect(client.connect).toBeDefined();
28
+ expect(client.disconnect).toBeDefined();
29
+ expect(client.sync).toBeDefined();
30
+ expect(client.getUsage).toBeDefined();
31
+ expect(client.getHealth).toBeDefined();
32
+ });
33
+
34
+ it("should return initial status as disconnected", () => {
35
+ const client = createCloudRelay(config);
36
+ const status = client.getStatus();
37
+ expect(status.connected).toBe(false);
38
+ expect(status.endpoint).toBe(config.endpoint);
39
+ expect(status.appId).toBe(config.appId);
40
+ });
41
+
42
+ it("should connect to relay successfully", async () => {
43
+ const mockHealthResponse: HealthCheckResponse = {
44
+ status: "healthy",
45
+ timestamp: Date.now(),
46
+ version: "0.1.0",
47
+ services: {
48
+ relay: true,
49
+ eventGrid: true,
50
+ storage: true,
51
+ auth: true,
52
+ },
53
+ };
54
+
55
+ (global.fetch as any).mockResolvedValueOnce({
56
+ ok: true,
57
+ json: async () => mockHealthResponse,
58
+ });
59
+
60
+ const client = createCloudRelay(config);
61
+ await client.connect();
62
+
63
+ const status = client.getStatus();
64
+ expect(status.connected).toBe(true);
65
+ expect(status.lastSync).toBeDefined();
66
+ });
67
+
68
+ it("should throw error when connecting to unavailable endpoint", async () => {
69
+ (global.fetch as any).mockResolvedValueOnce({
70
+ ok: false,
71
+ statusText: "Service Unavailable",
72
+ });
73
+
74
+ const client = createCloudRelay(config);
75
+ await expect(client.connect()).rejects.toThrow("Health check failed");
76
+ });
77
+
78
+ it("should sync facts and events", async () => {
79
+ const mockHealthResponse: HealthCheckResponse = {
80
+ status: "healthy",
81
+ timestamp: Date.now(),
82
+ version: "0.1.0",
83
+ services: {
84
+ relay: true,
85
+ eventGrid: true,
86
+ storage: true,
87
+ auth: true,
88
+ },
89
+ };
90
+
91
+ const mockSyncResponse = {
92
+ success: true,
93
+ clock: { "test-app": 1 },
94
+ timestamp: Date.now(),
95
+ };
96
+
97
+ (global.fetch as any)
98
+ .mockResolvedValueOnce({
99
+ ok: true,
100
+ json: async () => mockHealthResponse,
101
+ })
102
+ .mockResolvedValueOnce({
103
+ ok: true,
104
+ json: async () => mockSyncResponse,
105
+ });
106
+
107
+ const client = createCloudRelay(config);
108
+ await client.connect();
109
+
110
+ await client.sync({
111
+ type: "delta",
112
+ appId: config.appId,
113
+ clock: {},
114
+ facts: [{ tag: "TestFact", payload: { value: 42 } }],
115
+ events: [],
116
+ timestamp: Date.now(),
117
+ });
118
+
119
+ expect(global.fetch).toHaveBeenCalledTimes(2);
120
+ });
121
+
122
+ it("should get usage metrics", async () => {
123
+ const mockHealthResponse: HealthCheckResponse = {
124
+ status: "healthy",
125
+ timestamp: Date.now(),
126
+ version: "0.1.0",
127
+ services: {
128
+ relay: true,
129
+ eventGrid: true,
130
+ storage: true,
131
+ auth: true,
132
+ },
133
+ };
134
+
135
+ const mockUsageResponse: UsageMetrics = {
136
+ appId: config.appId,
137
+ syncCount: 10,
138
+ eventCount: 50,
139
+ factCount: 100,
140
+ storageBytes: 1024,
141
+ periodStart: Date.now() - 3600000,
142
+ periodEnd: Date.now(),
143
+ };
144
+
145
+ (global.fetch as any)
146
+ .mockResolvedValueOnce({
147
+ ok: true,
148
+ json: async () => mockHealthResponse,
149
+ })
150
+ .mockResolvedValueOnce({
151
+ ok: true,
152
+ json: async () => mockUsageResponse,
153
+ });
154
+
155
+ const client = createCloudRelay(config);
156
+ await client.connect();
157
+
158
+ const usage = await client.getUsage();
159
+ expect(usage.appId).toBe(config.appId);
160
+ expect(usage.syncCount).toBe(10);
161
+ expect(usage.eventCount).toBe(50);
162
+ });
163
+
164
+ it("should get health status", async () => {
165
+ const mockHealthResponse: HealthCheckResponse = {
166
+ status: "healthy",
167
+ timestamp: Date.now(),
168
+ version: "0.1.0",
169
+ services: {
170
+ relay: true,
171
+ eventGrid: true,
172
+ storage: true,
173
+ auth: true,
174
+ },
175
+ };
176
+
177
+ (global.fetch as any)
178
+ .mockResolvedValueOnce({
179
+ ok: true,
180
+ json: async () => mockHealthResponse,
181
+ })
182
+ .mockResolvedValueOnce({
183
+ ok: true,
184
+ json: async () => mockHealthResponse,
185
+ });
186
+
187
+ const client = createCloudRelay(config);
188
+ await client.connect();
189
+
190
+ const health = await client.getHealth();
191
+ expect(health.status).toBe("healthy");
192
+ expect(health.services.relay).toBe(true);
193
+ });
194
+
195
+ it("should disconnect and clear auto-sync timer", async () => {
196
+ const mockHealthResponse: HealthCheckResponse = {
197
+ status: "healthy",
198
+ timestamp: Date.now(),
199
+ version: "0.1.0",
200
+ services: {
201
+ relay: true,
202
+ eventGrid: true,
203
+ storage: true,
204
+ auth: true,
205
+ },
206
+ };
207
+
208
+ (global.fetch as any).mockResolvedValueOnce({
209
+ ok: true,
210
+ json: async () => mockHealthResponse,
211
+ });
212
+
213
+ config.autoSync = true;
214
+ config.syncInterval = 1000;
215
+
216
+ const client = createCloudRelay(config);
217
+ await client.connect();
218
+
219
+ const statusBefore = client.getStatus();
220
+ expect(statusBefore.connected).toBe(true);
221
+
222
+ await client.disconnect();
223
+
224
+ const statusAfter = client.getStatus();
225
+ expect(statusAfter.connected).toBe(false);
226
+ });
227
+
228
+ it("should throw error when syncing while disconnected", async () => {
229
+ const client = createCloudRelay(config);
230
+
231
+ await expect(
232
+ client.sync({
233
+ type: "delta",
234
+ appId: config.appId,
235
+ clock: {},
236
+ facts: [],
237
+ events: [],
238
+ timestamp: Date.now(),
239
+ })
240
+ ).rejects.toThrow("Not connected");
241
+ });
242
+
243
+ it("should throw error when getting usage while disconnected", async () => {
244
+ const client = createCloudRelay(config);
245
+ await expect(client.getUsage()).rejects.toThrow("Not connected");
246
+ });
247
+ });
@@ -0,0 +1,154 @@
1
+ /**
2
+ * DSL tests
3
+ */
4
+
5
+ import { describe, it, expect } from "vitest";
6
+ import {
7
+ defineFact,
8
+ defineEvent,
9
+ defineRule,
10
+ defineConstraint,
11
+ defineModule,
12
+ findEvent,
13
+ findFact,
14
+ } from "../dsl/index.js";
15
+
16
+ describe("DSL Helpers", () => {
17
+ describe("defineFact", () => {
18
+ it("should create a fact definition", () => {
19
+ const TestFact = defineFact<"TestFact", { value: number }>("TestFact");
20
+
21
+ expect(TestFact.tag).toBe("TestFact");
22
+
23
+ const fact = TestFact.create({ value: 42 });
24
+ expect(fact.tag).toBe("TestFact");
25
+ expect(fact.payload.value).toBe(42);
26
+ });
27
+
28
+ it("should provide type guard", () => {
29
+ const TestFact = defineFact<"TestFact", { value: number }>("TestFact");
30
+ const fact = TestFact.create({ value: 42 });
31
+
32
+ expect(TestFact.is(fact)).toBe(true);
33
+ expect(TestFact.is({ tag: "OtherFact", payload: {} })).toBe(false);
34
+ });
35
+ });
36
+
37
+ describe("defineEvent", () => {
38
+ it("should create an event definition", () => {
39
+ const TestEvent = defineEvent<"TEST", { action: string }>("TEST");
40
+
41
+ expect(TestEvent.tag).toBe("TEST");
42
+
43
+ const event = TestEvent.create({ action: "test" });
44
+ expect(event.tag).toBe("TEST");
45
+ expect(event.payload.action).toBe("test");
46
+ });
47
+
48
+ it("should provide type guard", () => {
49
+ const TestEvent = defineEvent<"TEST", { action: string }>("TEST");
50
+ const event = TestEvent.create({ action: "test" });
51
+
52
+ expect(TestEvent.is(event)).toBe(true);
53
+ expect(TestEvent.is({ tag: "OTHER", payload: {} })).toBe(false);
54
+ });
55
+ });
56
+
57
+ describe("defineRule", () => {
58
+ it("should create a rule descriptor", () => {
59
+ const rule = defineRule({
60
+ id: "test.rule",
61
+ description: "Test rule",
62
+ impl: () => [],
63
+ });
64
+
65
+ expect(rule.id).toBe("test.rule");
66
+ expect(rule.description).toBe("Test rule");
67
+ expect(typeof rule.impl).toBe("function");
68
+ });
69
+ });
70
+
71
+ describe("defineConstraint", () => {
72
+ it("should create a constraint descriptor", () => {
73
+ const constraint = defineConstraint({
74
+ id: "test.constraint",
75
+ description: "Test constraint",
76
+ impl: () => true,
77
+ });
78
+
79
+ expect(constraint.id).toBe("test.constraint");
80
+ expect(constraint.description).toBe("Test constraint");
81
+ expect(typeof constraint.impl).toBe("function");
82
+ });
83
+ });
84
+
85
+ describe("defineModule", () => {
86
+ it("should create a module", () => {
87
+ const rule = defineRule({
88
+ id: "test.rule",
89
+ description: "Test rule",
90
+ impl: () => [],
91
+ });
92
+
93
+ const constraint = defineConstraint({
94
+ id: "test.constraint",
95
+ description: "Test constraint",
96
+ impl: () => true,
97
+ });
98
+
99
+ const module = defineModule({
100
+ rules: [rule],
101
+ constraints: [constraint],
102
+ meta: { version: "1.0.0" },
103
+ });
104
+
105
+ expect(module.rules).toHaveLength(1);
106
+ expect(module.constraints).toHaveLength(1);
107
+ expect(module.meta?.version).toBe("1.0.0");
108
+ });
109
+ });
110
+
111
+ describe("findEvent", () => {
112
+ it("should find matching event", () => {
113
+ const TestEvent = defineEvent<"TEST", { value: number }>("TEST");
114
+ const events = [
115
+ { tag: "OTHER", payload: {} },
116
+ TestEvent.create({ value: 42 }),
117
+ ];
118
+
119
+ const found = findEvent(events, TestEvent);
120
+ expect(found).toBeDefined();
121
+ expect(found?.payload.value).toBe(42);
122
+ });
123
+
124
+ it("should return undefined if not found", () => {
125
+ const TestEvent = defineEvent<"TEST", { value: number }>("TEST");
126
+ const events = [{ tag: "OTHER", payload: {} }];
127
+
128
+ const found = findEvent(events, TestEvent);
129
+ expect(found).toBeUndefined();
130
+ });
131
+ });
132
+
133
+ describe("findFact", () => {
134
+ it("should find matching fact", () => {
135
+ const TestFact = defineFact<"TestFact", { value: number }>("TestFact");
136
+ const facts = [
137
+ { tag: "OtherFact", payload: {} },
138
+ TestFact.create({ value: 42 }),
139
+ ];
140
+
141
+ const found = findFact(facts, TestFact);
142
+ expect(found).toBeDefined();
143
+ expect(found?.payload.value).toBe(42);
144
+ });
145
+
146
+ it("should return undefined if not found", () => {
147
+ const TestFact = defineFact<"TestFact", { value: number }>("TestFact");
148
+ const facts = [{ tag: "OtherFact", payload: {} }];
149
+
150
+ const found = findFact(facts, TestFact);
151
+ expect(found).toBeUndefined();
152
+ });
153
+ });
154
+ });