@rikalabs/effect-react 0.0.1

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 (167) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +179 -0
  3. package/dist/actions/http.d.ts +18 -0
  4. package/dist/actions/index.d.ts +4 -0
  5. package/dist/actions/react.d.ts +7 -0
  6. package/dist/actions/service.d.ts +18 -0
  7. package/dist/actions/types.d.ts +33 -0
  8. package/dist/boundary/codecs.d.ts +40 -0
  9. package/dist/boundary/errors.d.ts +22 -0
  10. package/dist/boundary/index.d.ts +2 -0
  11. package/dist/chunk-2GIUCKL2.js +16 -0
  12. package/dist/chunk-2GIUCKL2.js.map +1 -0
  13. package/dist/chunk-2TG7YEVD.js +11 -0
  14. package/dist/chunk-2TG7YEVD.js.map +1 -0
  15. package/dist/chunk-6FI4ROTW.js +152 -0
  16. package/dist/chunk-6FI4ROTW.js.map +1 -0
  17. package/dist/chunk-C5JI7D7W.js +213 -0
  18. package/dist/chunk-C5JI7D7W.js.map +1 -0
  19. package/dist/chunk-EEYASTXR.js +99 -0
  20. package/dist/chunk-EEYASTXR.js.map +1 -0
  21. package/dist/chunk-H7MOLKTU.js +301 -0
  22. package/dist/chunk-H7MOLKTU.js.map +1 -0
  23. package/dist/chunk-IVIYY6S5.js +77 -0
  24. package/dist/chunk-IVIYY6S5.js.map +1 -0
  25. package/dist/chunk-JKN75OYC.js +87 -0
  26. package/dist/chunk-JKN75OYC.js.map +1 -0
  27. package/dist/chunk-M2CJG6T7.js +24 -0
  28. package/dist/chunk-M2CJG6T7.js.map +1 -0
  29. package/dist/chunk-MDGEGQZB.js +206 -0
  30. package/dist/chunk-MDGEGQZB.js.map +1 -0
  31. package/dist/chunk-NI2GNZ7S.js +78 -0
  32. package/dist/chunk-NI2GNZ7S.js.map +1 -0
  33. package/dist/chunk-O7XTA7H3.js +423 -0
  34. package/dist/chunk-O7XTA7H3.js.map +1 -0
  35. package/dist/chunk-S67FHWAR.js +88 -0
  36. package/dist/chunk-S67FHWAR.js.map +1 -0
  37. package/dist/chunk-SKC3HMF3.js +17 -0
  38. package/dist/chunk-SKC3HMF3.js.map +1 -0
  39. package/dist/chunk-TUJZ6XJY.js +127 -0
  40. package/dist/chunk-TUJZ6XJY.js.map +1 -0
  41. package/dist/chunk-WPV3WFMS.js +38 -0
  42. package/dist/chunk-WPV3WFMS.js.map +1 -0
  43. package/dist/chunk-XIBEKS5A.js +301 -0
  44. package/dist/chunk-XIBEKS5A.js.map +1 -0
  45. package/dist/chunk-YG22YP5K.js +68 -0
  46. package/dist/chunk-YG22YP5K.js.map +1 -0
  47. package/dist/chunk-ZMZQBREU.js +262 -0
  48. package/dist/chunk-ZMZQBREU.js.map +1 -0
  49. package/dist/client/index.cjs +191 -0
  50. package/dist/client/index.cjs.map +1 -0
  51. package/dist/client/index.d.ts +8 -0
  52. package/dist/client/index.js +14 -0
  53. package/dist/client/index.js.map +1 -0
  54. package/dist/config/index.cjs +63 -0
  55. package/dist/config/index.cjs.map +1 -0
  56. package/dist/config/index.d.ts +32 -0
  57. package/dist/config/index.js +9 -0
  58. package/dist/config/index.js.map +1 -0
  59. package/dist/data/index.d.ts +3 -0
  60. package/dist/data/react.d.ts +10 -0
  61. package/dist/data/service.d.ts +20 -0
  62. package/dist/data/types.d.ts +31 -0
  63. package/dist/devtools/events.d.ts +37 -0
  64. package/dist/devtools/index.cjs +149 -0
  65. package/dist/devtools/index.cjs.map +1 -0
  66. package/dist/devtools/index.d.ts +2 -0
  67. package/dist/devtools/index.js +18 -0
  68. package/dist/devtools/index.js.map +1 -0
  69. package/dist/devtools/react.d.ts +8 -0
  70. package/dist/form/index.cjs +301 -0
  71. package/dist/form/index.cjs.map +1 -0
  72. package/dist/form/index.d.ts +3 -0
  73. package/dist/form/index.js +14 -0
  74. package/dist/form/index.js.map +1 -0
  75. package/dist/form/react.d.ts +9 -0
  76. package/dist/form/service.d.ts +3 -0
  77. package/dist/form/types.d.ts +41 -0
  78. package/dist/framework/app.d.ts +21 -0
  79. package/dist/framework/cache.d.ts +10 -0
  80. package/dist/framework/contracts.d.ts +32 -0
  81. package/dist/framework/index.cjs +1006 -0
  82. package/dist/framework/index.cjs.map +1 -0
  83. package/dist/framework/index.d.ts +4 -0
  84. package/dist/framework/index.js +35 -0
  85. package/dist/framework/index.js.map +1 -0
  86. package/dist/framework/manifest.d.ts +12 -0
  87. package/dist/framework/vite.d.ts +13 -0
  88. package/dist/framework-vite/index.cjs +163 -0
  89. package/dist/framework-vite/index.cjs.map +1 -0
  90. package/dist/framework-vite/index.d.ts +1 -0
  91. package/dist/framework-vite/index.js +125 -0
  92. package/dist/framework-vite/index.js.map +1 -0
  93. package/dist/grid/grid.d.ts +8 -0
  94. package/dist/grid/index.cjs +238 -0
  95. package/dist/grid/index.cjs.map +1 -0
  96. package/dist/grid/index.d.ts +2 -0
  97. package/dist/grid/index.js +19 -0
  98. package/dist/grid/index.js.map +1 -0
  99. package/dist/grid/types.d.ts +35 -0
  100. package/dist/index.cjs +2512 -0
  101. package/dist/index.cjs.map +1 -0
  102. package/dist/index.d.ts +13 -0
  103. package/dist/index.js +207 -0
  104. package/dist/index.js.map +1 -0
  105. package/dist/kernel/app.d.ts +26 -0
  106. package/dist/kernel/index.d.ts +3 -0
  107. package/dist/kernel/runtime.d.ts +5 -0
  108. package/dist/kernel/telemetry.d.ts +37 -0
  109. package/dist/navigation/index.d.ts +4 -0
  110. package/dist/navigation/matcher.d.ts +13 -0
  111. package/dist/navigation/react.d.ts +12 -0
  112. package/dist/navigation/service.d.ts +23 -0
  113. package/dist/navigation/types.d.ts +65 -0
  114. package/dist/query/index.cjs +361 -0
  115. package/dist/query/index.cjs.map +1 -0
  116. package/dist/query/index.d.ts +3 -0
  117. package/dist/query/index.js +30 -0
  118. package/dist/query/index.js.map +1 -0
  119. package/dist/query/react.d.ts +27 -0
  120. package/dist/query/service.d.ts +10 -0
  121. package/dist/query/types.d.ts +5 -0
  122. package/dist/react/index.d.ts +1 -0
  123. package/dist/react/provider.d.ts +10 -0
  124. package/dist/realtime/channel.d.ts +15 -0
  125. package/dist/realtime/index.cjs +117 -0
  126. package/dist/realtime/index.cjs.map +1 -0
  127. package/dist/realtime/index.d.ts +2 -0
  128. package/dist/realtime/index.js +15 -0
  129. package/dist/realtime/index.js.map +1 -0
  130. package/dist/realtime/presence.d.ts +22 -0
  131. package/dist/render/hydration.d.ts +24 -0
  132. package/dist/render/index.d.ts +2 -0
  133. package/dist/render/ssr.d.ts +13 -0
  134. package/dist/router/helpers.d.ts +26 -0
  135. package/dist/router/index.cjs +236 -0
  136. package/dist/router/index.cjs.map +1 -0
  137. package/dist/router/index.d.ts +4 -0
  138. package/dist/router/index.js +40 -0
  139. package/dist/router/index.js.map +1 -0
  140. package/dist/router/react.d.ts +5 -0
  141. package/dist/router/service.d.ts +5 -0
  142. package/dist/router/types.d.ts +1 -0
  143. package/dist/server/index.cjs +174 -0
  144. package/dist/server/index.cjs.map +1 -0
  145. package/dist/server/index.d.ts +16 -0
  146. package/dist/server/index.js +12 -0
  147. package/dist/server/index.js.map +1 -0
  148. package/dist/state/index.cjs +128 -0
  149. package/dist/state/index.cjs.map +1 -0
  150. package/dist/state/index.d.ts +2 -0
  151. package/dist/state/index.js +36 -0
  152. package/dist/state/index.js.map +1 -0
  153. package/dist/state/react.d.ts +3 -0
  154. package/dist/state/service.d.ts +28 -0
  155. package/dist/testing/index.cjs +970 -0
  156. package/dist/testing/index.cjs.map +1 -0
  157. package/dist/testing/index.d.ts +2 -0
  158. package/dist/testing/index.js +13 -0
  159. package/dist/testing/index.js.map +1 -0
  160. package/dist/virtual/index.cjs +160 -0
  161. package/dist/virtual/index.cjs.map +1 -0
  162. package/dist/virtual/index.d.ts +2 -0
  163. package/dist/virtual/index.js +21 -0
  164. package/dist/virtual/index.js.map +1 -0
  165. package/dist/virtual/types.d.ts +25 -0
  166. package/dist/virtual/virtual.d.ts +9 -0
  167. package/package.json +156 -0
@@ -0,0 +1,970 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/testing/index.ts
21
+ var testing_exports = {};
22
+ __export(testing_exports, {
23
+ createTestApp: () => createTestApp
24
+ });
25
+ module.exports = __toCommonJS(testing_exports);
26
+
27
+ // src/actions/service.ts
28
+ var import_effect3 = require("effect");
29
+
30
+ // src/boundary/codecs.ts
31
+ var import_effect = require("effect");
32
+
33
+ // src/boundary/errors.ts
34
+ var BoundaryDecodeError = class extends Error {
35
+ constructor(source, messageText, causeValue) {
36
+ super(`Boundary decode failed at ${source}: ${messageText}`);
37
+ this.source = source;
38
+ this.messageText = messageText;
39
+ this.causeValue = causeValue;
40
+ this.name = "BoundaryDecodeError";
41
+ }
42
+ _tag = "BoundaryDecodeError";
43
+ };
44
+ var BoundaryTransportError = class extends Error {
45
+ constructor(source, messageText, causeValue) {
46
+ super(`Boundary transport failed at ${source}: ${messageText}`);
47
+ this.source = source;
48
+ this.messageText = messageText;
49
+ this.causeValue = causeValue;
50
+ this.name = "BoundaryTransportError";
51
+ }
52
+ _tag = "BoundaryTransportError";
53
+ };
54
+ var BoundaryProtocolError = class extends Error {
55
+ constructor(source, messageText) {
56
+ super(`Boundary protocol violation at ${source}: ${messageText}`);
57
+ this.source = source;
58
+ this.messageText = messageText;
59
+ this.name = "BoundaryProtocolError";
60
+ }
61
+ _tag = "BoundaryProtocolError";
62
+ };
63
+
64
+ // src/boundary/codecs.ts
65
+ var Boundary = class extends import_effect.Context.Tag("EffectReact/Boundary")() {
66
+ };
67
+ var parseErrorMessage = (error) => import_effect.Cause.pretty(import_effect.Cause.fail(error)).trim();
68
+ var makeBoundaryService = () => ({
69
+ decodeUnknown: ({ source, schema, value }) => import_effect.Schema.decodeUnknown(schema)(value).pipe(
70
+ import_effect.Effect.mapError(
71
+ (error) => new BoundaryDecodeError(source, parseErrorMessage(error), error)
72
+ )
73
+ ),
74
+ decodeTextJson: ({ source, schema, text }) => import_effect.Effect.gen(function* () {
75
+ const payload = yield* import_effect.Effect.try({
76
+ try: () => JSON.parse(text),
77
+ catch: (cause) => new BoundaryTransportError(source, "failed to parse JSON payload", cause)
78
+ });
79
+ return yield* import_effect.Schema.decodeUnknown(schema)(payload).pipe(
80
+ import_effect.Effect.mapError(
81
+ (error) => new BoundaryDecodeError(source, parseErrorMessage(error), error)
82
+ )
83
+ );
84
+ }),
85
+ encode: ({ source, schema, value }) => import_effect.Schema.encode(schema)(value).pipe(
86
+ import_effect.Effect.mapError((error) => new BoundaryProtocolError(source, parseErrorMessage(error)))
87
+ )
88
+ });
89
+ var BoundaryLive = import_effect.Layer.succeed(Boundary, makeBoundaryService());
90
+
91
+ // src/kernel/telemetry.ts
92
+ var import_effect2 = require("effect");
93
+ var Telemetry = class extends import_effect2.Context.Tag("EffectReact/Telemetry")() {
94
+ };
95
+ var TelemetryLive = import_effect2.Layer.effect(
96
+ Telemetry,
97
+ import_effect2.Effect.map(import_effect2.PubSub.unbounded(), (pubsub) => ({
98
+ emit: (event) => import_effect2.PubSub.publish(pubsub, event).pipe(import_effect2.Effect.asVoid),
99
+ stream: import_effect2.Stream.fromPubSub(pubsub)
100
+ }))
101
+ );
102
+
103
+ // src/actions/service.ts
104
+ var toFailureDetail = (error) => {
105
+ if (error instanceof Error) {
106
+ return `${error.name}: ${error.message}`;
107
+ }
108
+ return String(error);
109
+ };
110
+ var Actions = class extends import_effect3.Context.Tag("EffectReact/Actions")() {
111
+ };
112
+ var makeActionsLayer = (options) => import_effect3.Layer.effect(
113
+ Actions,
114
+ import_effect3.Effect.gen(function* () {
115
+ const boundary = yield* Boundary;
116
+ const telemetry = yield* Telemetry;
117
+ const actionMap = new Map(
118
+ options.actions.map((action) => [action.name, action])
119
+ );
120
+ const run = (definition, input) => import_effect3.Effect.gen(function* () {
121
+ yield* telemetry.emit({
122
+ _tag: "action",
123
+ phase: "start",
124
+ name: definition.name,
125
+ timestamp: Date.now()
126
+ });
127
+ const decodedInput = yield* boundary.decodeUnknown({
128
+ source: `action:${definition.name}:input`,
129
+ schema: definition.input,
130
+ value: input
131
+ });
132
+ const value = yield* definition.handler(decodedInput);
133
+ const decodedOutput = yield* boundary.decodeUnknown({
134
+ source: `action:${definition.name}:output`,
135
+ schema: definition.output,
136
+ value
137
+ });
138
+ yield* telemetry.emit({
139
+ _tag: "action",
140
+ phase: "success",
141
+ name: definition.name,
142
+ timestamp: Date.now()
143
+ });
144
+ return decodedOutput;
145
+ }).pipe(
146
+ import_effect3.Effect.tapError(
147
+ (error) => telemetry.emit({
148
+ _tag: "action",
149
+ phase: "failure",
150
+ name: definition.name,
151
+ timestamp: Date.now(),
152
+ detail: error
153
+ })
154
+ )
155
+ );
156
+ const dispatch = (name, input) => import_effect3.Effect.gen(function* () {
157
+ const definition = actionMap.get(name);
158
+ if (definition === void 0) {
159
+ return {
160
+ _tag: "defect",
161
+ message: `Unknown action: ${name}`
162
+ };
163
+ }
164
+ const decodedInput = yield* boundary.decodeUnknown({
165
+ source: `action:${name}:dispatch-input`,
166
+ schema: definition.input,
167
+ value: input
168
+ });
169
+ const handler = definition.handler;
170
+ const result = yield* import_effect3.Effect.exit(handler(decodedInput));
171
+ if (result._tag === "Success") {
172
+ const encoded = yield* boundary.encode({
173
+ source: `action:${name}:wire-success`,
174
+ schema: definition.output,
175
+ value: result.value
176
+ });
177
+ return {
178
+ _tag: "success",
179
+ value: encoded
180
+ };
181
+ }
182
+ const failure = import_effect3.Cause.failureOption(result.cause);
183
+ if (failure._tag === "Some") {
184
+ const value = failure.value;
185
+ if (value instanceof BoundaryDecodeError || value instanceof BoundaryProtocolError) {
186
+ return {
187
+ _tag: "defect",
188
+ message: toFailureDetail(value)
189
+ };
190
+ }
191
+ const encodedError = yield* boundary.encode({
192
+ source: `action:${name}:wire-failure`,
193
+ schema: definition.error,
194
+ value
195
+ }).pipe(
196
+ import_effect3.Effect.catchAll(() => import_effect3.Effect.succeed(value))
197
+ );
198
+ return {
199
+ _tag: "failure",
200
+ error: encodedError
201
+ };
202
+ }
203
+ return {
204
+ _tag: "defect",
205
+ message: import_effect3.Cause.pretty(result.cause)
206
+ };
207
+ }).pipe(
208
+ import_effect3.Effect.catchAll(
209
+ (error) => import_effect3.Effect.succeed({
210
+ _tag: "defect",
211
+ message: toFailureDetail(error)
212
+ })
213
+ )
214
+ );
215
+ return {
216
+ run,
217
+ dispatch
218
+ };
219
+ })
220
+ );
221
+
222
+ // src/actions/http.ts
223
+ var import_effect4 = require("effect");
224
+ var ActionRequestSchema = import_effect4.Schema.Struct({
225
+ name: import_effect4.Schema.String,
226
+ input: import_effect4.Schema.Unknown
227
+ });
228
+ var ActionWireResultSchema = import_effect4.Schema.Union(
229
+ import_effect4.Schema.Struct({ _tag: import_effect4.Schema.Literal("success"), value: import_effect4.Schema.Unknown }),
230
+ import_effect4.Schema.Struct({ _tag: import_effect4.Schema.Literal("failure"), error: import_effect4.Schema.Unknown }),
231
+ import_effect4.Schema.Struct({ _tag: import_effect4.Schema.Literal("defect"), message: import_effect4.Schema.String })
232
+ );
233
+ var ActionTransportError = class extends Error {
234
+ constructor(messageText, causeValue) {
235
+ super(messageText);
236
+ this.messageText = messageText;
237
+ this.causeValue = causeValue;
238
+ this.name = "ActionTransportError";
239
+ }
240
+ _tag = "ActionTransportError";
241
+ };
242
+ var jsonResponse = (status, body) => new Response(JSON.stringify(body), {
243
+ status,
244
+ headers: {
245
+ "content-type": "application/json"
246
+ }
247
+ });
248
+ var createActionHttpHandlerEffect = () => (request) => import_effect4.Effect.gen(function* () {
249
+ if (request.method.toUpperCase() !== "POST") {
250
+ return jsonResponse(405, { error: "Method Not Allowed" });
251
+ }
252
+ const rawBody = yield* import_effect4.Effect.tryPromise({
253
+ try: () => request.text(),
254
+ catch: (cause) => new ActionTransportError("Failed to read request body", cause)
255
+ });
256
+ const boundary = yield* Boundary;
257
+ const payload = yield* boundary.decodeTextJson({
258
+ source: "action:http:request",
259
+ schema: ActionRequestSchema,
260
+ text: rawBody
261
+ });
262
+ const actions = yield* Actions;
263
+ const result = yield* actions.dispatch(payload.name, payload.input);
264
+ return jsonResponse(200, result);
265
+ }).pipe(
266
+ import_effect4.Effect.catchAll(
267
+ (error) => import_effect4.Effect.succeed(
268
+ jsonResponse(500, {
269
+ error: error instanceof Error ? error.message : String(error)
270
+ })
271
+ )
272
+ )
273
+ );
274
+ var createActionHttpHandler = () => {
275
+ const handleEffect = createActionHttpHandlerEffect();
276
+ return (runtimeRun) => (request) => runtimeRun(handleEffect(request));
277
+ };
278
+
279
+ // src/kernel/app.ts
280
+ var import_effect8 = require("effect");
281
+
282
+ // src/data/types.ts
283
+ var QueryRuntimeError = class extends Error {
284
+ constructor(messageText) {
285
+ super(messageText);
286
+ this.messageText = messageText;
287
+ this.name = "QueryRuntimeError";
288
+ }
289
+ _tag = "QueryRuntimeError";
290
+ };
291
+
292
+ // src/data/service.ts
293
+ var import_effect5 = require("effect");
294
+ var defaultRuntimeOptions = {
295
+ capacity: 2048,
296
+ timeToLive: "5 minutes"
297
+ };
298
+ var stableStringify = (value) => {
299
+ if (value === null || value === void 0) {
300
+ return String(value);
301
+ }
302
+ if (typeof value !== "object") {
303
+ return JSON.stringify(value);
304
+ }
305
+ if (Array.isArray(value)) {
306
+ return `[${value.map(stableStringify).join(",")}]`;
307
+ }
308
+ const entries = Object.entries(value).sort(
309
+ ([a], [b]) => a.localeCompare(b)
310
+ );
311
+ return `{${entries.map(([key, nested]) => `${JSON.stringify(key)}:${stableStringify(nested)}`).join(",")}}`;
312
+ };
313
+ var initialSnapshot = (key) => ({
314
+ key,
315
+ phase: "initial",
316
+ data: void 0,
317
+ error: void 0,
318
+ updatedAt: null
319
+ });
320
+ var Data = class extends import_effect5.Context.Tag("EffectReact/Data")() {
321
+ };
322
+ var makeDataLayer = (options = {}) => {
323
+ const merged = {
324
+ ...defaultRuntimeOptions,
325
+ ...options
326
+ };
327
+ return import_effect5.Layer.effect(
328
+ Data,
329
+ import_effect5.Effect.gen(function* () {
330
+ const boundary = yield* Boundary;
331
+ const telemetry = yield* Telemetry;
332
+ const lookups = /* @__PURE__ */ new Map();
333
+ const snapshots = yield* import_effect5.SubscriptionRef.make(
334
+ /* @__PURE__ */ new Map()
335
+ );
336
+ const cache = yield* import_effect5.Cache.make({
337
+ capacity: merged.capacity,
338
+ timeToLive: merged.timeToLive,
339
+ lookup: (key) => import_effect5.Effect.suspend(() => {
340
+ const lookup = lookups.get(key);
341
+ if (lookup === void 0) {
342
+ return import_effect5.Effect.fail(new QueryRuntimeError(`No query executor registered for ${key}`));
343
+ }
344
+ return lookup;
345
+ })
346
+ });
347
+ const setSnapshot = (key, update) => import_effect5.SubscriptionRef.update(snapshots, (current) => {
348
+ const next = new Map(current);
349
+ const previous = next.get(key) ?? initialSnapshot(key);
350
+ next.set(key, update(previous));
351
+ return next;
352
+ }).pipe(import_effect5.Effect.asVoid);
353
+ const buildKey = (definition, input) => {
354
+ const base = definition.key ? definition.key(input) : input;
355
+ return `${definition.name}:${stableStringify(base)}`;
356
+ };
357
+ const fetch2 = (definition, input, runOptions) => import_effect5.Effect.gen(function* () {
358
+ const decodedInput = yield* boundary.decodeUnknown({
359
+ source: `query:${definition.name}:input`,
360
+ schema: definition.input,
361
+ value: input
362
+ });
363
+ const key = buildKey(definition, decodedInput);
364
+ yield* telemetry.emit({
365
+ _tag: "query",
366
+ phase: "start",
367
+ key,
368
+ timestamp: Date.now()
369
+ });
370
+ yield* setSnapshot(key, (previous) => ({
371
+ ...previous,
372
+ phase: "loading",
373
+ error: void 0
374
+ }));
375
+ lookups.set(
376
+ key,
377
+ definition.run(decodedInput).pipe(
378
+ import_effect5.Effect.flatMap(
379
+ (output) => boundary.decodeUnknown({
380
+ source: `query:${definition.name}:output`,
381
+ schema: definition.output,
382
+ value: output
383
+ })
384
+ )
385
+ )
386
+ );
387
+ if (runOptions?.forceRefresh === true) {
388
+ yield* cache.refresh(key).pipe(import_effect5.Effect.ignore);
389
+ }
390
+ const value = yield* cache.get(key).pipe(
391
+ import_effect5.Effect.mapError(
392
+ (error) => error
393
+ )
394
+ );
395
+ yield* setSnapshot(key, () => ({
396
+ key,
397
+ phase: "success",
398
+ data: value,
399
+ error: void 0,
400
+ updatedAt: Date.now()
401
+ }));
402
+ yield* telemetry.emit({
403
+ _tag: "query",
404
+ phase: "success",
405
+ key,
406
+ timestamp: Date.now()
407
+ });
408
+ return value;
409
+ }).pipe(
410
+ import_effect5.Effect.tapError(
411
+ (error) => import_effect5.Effect.gen(function* () {
412
+ const decodedInput = yield* boundary.decodeUnknown({
413
+ source: `query:${definition.name}:input`,
414
+ schema: definition.input,
415
+ value: input
416
+ });
417
+ const key = buildKey(definition, decodedInput);
418
+ yield* setSnapshot(key, (previous) => ({
419
+ ...previous,
420
+ phase: "failure",
421
+ error,
422
+ updatedAt: previous.updatedAt
423
+ }));
424
+ yield* telemetry.emit({
425
+ _tag: "query",
426
+ phase: "failure",
427
+ key,
428
+ timestamp: Date.now(),
429
+ detail: error
430
+ });
431
+ })
432
+ )
433
+ );
434
+ const prefetch = (definition, input) => fetch2(definition, input).pipe(import_effect5.Effect.asVoid);
435
+ const invalidate = (definition, input) => import_effect5.Effect.gen(function* () {
436
+ const key = `${definition.name}:${stableStringify(input)}`;
437
+ yield* cache.invalidate(key);
438
+ yield* import_effect5.SubscriptionRef.update(snapshots, (current) => {
439
+ const next = new Map(current);
440
+ next.delete(key);
441
+ return next;
442
+ }).pipe(import_effect5.Effect.asVoid);
443
+ yield* telemetry.emit({
444
+ _tag: "query",
445
+ phase: "invalidate",
446
+ key,
447
+ timestamp: Date.now()
448
+ });
449
+ });
450
+ const getSnapshot = (definition, input) => import_effect5.Effect.gen(function* () {
451
+ const key = `${definition.name}:${stableStringify(input)}`;
452
+ const current = yield* import_effect5.SubscriptionRef.get(snapshots);
453
+ const snapshot = current.get(key);
454
+ if (snapshot === void 0) {
455
+ return initialSnapshot(key);
456
+ }
457
+ return snapshot;
458
+ });
459
+ return {
460
+ fetch: fetch2,
461
+ prefetch,
462
+ invalidate,
463
+ getSnapshot,
464
+ getAllSnapshots: import_effect5.SubscriptionRef.get(snapshots),
465
+ hydrateSnapshots: (nextSnapshots) => import_effect5.SubscriptionRef.set(snapshots, new Map(nextSnapshots)),
466
+ snapshots: snapshots.changes
467
+ };
468
+ })
469
+ );
470
+ };
471
+
472
+ // src/navigation/types.ts
473
+ var NavigationRuntimeError = class extends Error {
474
+ constructor(messageText) {
475
+ super(messageText);
476
+ this.messageText = messageText;
477
+ this.name = "NavigationRuntimeError";
478
+ }
479
+ _tag = "NavigationRuntimeError";
480
+ };
481
+ var NavigationCancelledError = class extends Error {
482
+ constructor(pathname) {
483
+ super(`Navigation cancelled for ${pathname}`);
484
+ this.pathname = pathname;
485
+ this.name = "NavigationCancelledError";
486
+ }
487
+ _tag = "NavigationCancelledError";
488
+ };
489
+
490
+ // src/navigation/matcher.ts
491
+ var normalizePathname = (pathname) => {
492
+ if (pathname.length === 0) {
493
+ return "/";
494
+ }
495
+ const withSlash = pathname.startsWith("/") ? pathname : `/${pathname}`;
496
+ if (withSlash.length > 1 && withSlash.endsWith("/")) {
497
+ return withSlash.slice(0, -1);
498
+ }
499
+ return withSlash;
500
+ };
501
+ var scoreRoute = (path) => {
502
+ if (path === "/") {
503
+ return 10;
504
+ }
505
+ return path.split("/").filter((segment) => segment.length > 0).reduce((score, segment) => {
506
+ if (segment === "*") {
507
+ return score;
508
+ }
509
+ if (segment.startsWith(":")) {
510
+ return score + 2;
511
+ }
512
+ return score + 5;
513
+ }, 0);
514
+ };
515
+ var splitSegments = (path) => normalizePathname(path).split("/").filter((segment) => segment.length > 0);
516
+ var matchRoutePath = (routePath, pathname) => {
517
+ const routeSegments = splitSegments(routePath);
518
+ const pathSegments = splitSegments(pathname);
519
+ const params = {};
520
+ let i = 0;
521
+ let j = 0;
522
+ while (i < routeSegments.length && j < pathSegments.length) {
523
+ const routeSegment = routeSegments[i];
524
+ const pathSegment = pathSegments[j];
525
+ if (routeSegment === "*") {
526
+ return params;
527
+ }
528
+ if (routeSegment.startsWith(":")) {
529
+ params[routeSegment.slice(1)] = decodeURIComponent(pathSegment);
530
+ i += 1;
531
+ j += 1;
532
+ continue;
533
+ }
534
+ if (routeSegment !== pathSegment) {
535
+ return null;
536
+ }
537
+ i += 1;
538
+ j += 1;
539
+ }
540
+ if (i < routeSegments.length && routeSegments[i] === "*") {
541
+ return params;
542
+ }
543
+ if (i !== routeSegments.length || j !== pathSegments.length) {
544
+ return null;
545
+ }
546
+ return params;
547
+ };
548
+ var matchRoute = ({ routes, pathname, search }) => {
549
+ const normalized = normalizePathname(pathname);
550
+ const sorted = [...routes].sort((a, b) => scoreRoute(b.path) - scoreRoute(a.path));
551
+ for (const route of sorted) {
552
+ const params = matchRoutePath(route.path, normalized);
553
+ if (params !== null) {
554
+ return {
555
+ route,
556
+ pathname: normalized,
557
+ params,
558
+ search
559
+ };
560
+ }
561
+ }
562
+ return null;
563
+ };
564
+ var buildHref = (pathname, searchText) => searchText.length > 0 ? `${normalizePathname(pathname)}${searchText}` : normalizePathname(pathname);
565
+ var normalizeSearchText = (searchText) => {
566
+ if (searchText.length === 0 || searchText === "?") {
567
+ return "";
568
+ }
569
+ return searchText.startsWith("?") ? searchText : `?${searchText}`;
570
+ };
571
+ var parseHref = (href) => {
572
+ const [pathPart, ...searchParts] = href.split("?");
573
+ return {
574
+ pathname: normalizePathname(pathPart ?? "/"),
575
+ searchText: normalizeSearchText(searchParts.length === 0 ? "" : searchParts.join("?"))
576
+ };
577
+ };
578
+
579
+ // src/navigation/service.ts
580
+ var import_effect6 = require("effect");
581
+ var describeUnknown = (value) => {
582
+ if (value instanceof Error) {
583
+ return `${value.name}: ${value.message}`;
584
+ }
585
+ if (typeof value === "string") {
586
+ return value;
587
+ }
588
+ try {
589
+ return JSON.stringify(value);
590
+ } catch {
591
+ return String(value);
592
+ }
593
+ };
594
+ var initialSnapshot2 = {
595
+ pathname: "/",
596
+ searchText: "",
597
+ href: "/",
598
+ status: "idle",
599
+ match: null,
600
+ loaders: {},
601
+ error: void 0
602
+ };
603
+ var toLoadersByName = (loaders) => new Map(loaders.map((loader) => [loader.name, loader]));
604
+ var planLoaderBatches = (loaders) => {
605
+ const byName = toLoadersByName(loaders);
606
+ const depthByName = /* @__PURE__ */ new Map();
607
+ const visiting = /* @__PURE__ */ new Set();
608
+ const resolveDepth = (name) => {
609
+ const cached = depthByName.get(name);
610
+ if (cached !== void 0) {
611
+ return import_effect6.Effect.succeed(cached);
612
+ }
613
+ if (visiting.has(name)) {
614
+ return import_effect6.Effect.fail(new NavigationRuntimeError(`Cyclic loader dependency detected at ${name}`));
615
+ }
616
+ const loader = byName.get(name);
617
+ if (loader === void 0) {
618
+ return import_effect6.Effect.fail(new NavigationRuntimeError(`Loader dependency '${name}' is not registered`));
619
+ }
620
+ visiting.add(name);
621
+ const dependencies = loader.dependsOn ?? [];
622
+ return import_effect6.Effect.forEach(dependencies, resolveDepth).pipe(
623
+ import_effect6.Effect.map((depths) => {
624
+ const depth = depths.length === 0 ? 0 : Math.max(...depths) + 1;
625
+ depthByName.set(name, depth);
626
+ visiting.delete(name);
627
+ return depth;
628
+ }),
629
+ import_effect6.Effect.catchAll((error) => {
630
+ visiting.delete(name);
631
+ return import_effect6.Effect.fail(error);
632
+ })
633
+ );
634
+ };
635
+ return import_effect6.Effect.gen(function* () {
636
+ const entries = yield* import_effect6.Effect.forEach(
637
+ loaders,
638
+ (loader) => import_effect6.Effect.map(resolveDepth(loader.name), (depth) => [depth, loader])
639
+ );
640
+ const grouped = /* @__PURE__ */ new Map();
641
+ for (const [depth, loader] of entries) {
642
+ const existing = grouped.get(depth);
643
+ if (existing === void 0) {
644
+ grouped.set(depth, [loader]);
645
+ } else {
646
+ existing.push(loader);
647
+ }
648
+ }
649
+ const depths = Array.from(grouped.keys()).sort((a, b) => a - b);
650
+ return depths.map((depth) => grouped.get(depth) ?? []);
651
+ });
652
+ };
653
+ var Navigation = class extends import_effect6.Context.Tag("EffectReact/Navigation")() {
654
+ };
655
+ var makeNavigationLayer = (options) => {
656
+ const loaders = options.loaders ?? [];
657
+ const initial = parseHref(options.initialHref ?? "/");
658
+ return import_effect6.Layer.effect(
659
+ Navigation,
660
+ import_effect6.Effect.gen(function* () {
661
+ const boundary = yield* Boundary;
662
+ const telemetry = yield* Telemetry;
663
+ const snapshotsRef = yield* import_effect6.SubscriptionRef.make({
664
+ ...initialSnapshot2,
665
+ pathname: initial.pathname,
666
+ searchText: initial.searchText,
667
+ href: buildHref(initial.pathname, initial.searchText)
668
+ });
669
+ const activeFiberRef = yield* import_effect6.Ref.make(import_effect6.Option.none());
670
+ const runLoaders = (snapshot) => import_effect6.Effect.gen(function* () {
671
+ if (snapshot.match === null) {
672
+ return {};
673
+ }
674
+ const routeLoaders = loaders.filter((loader) => loader.routeId === snapshot.match.route.id);
675
+ if (routeLoaders.length === 0) {
676
+ return {};
677
+ }
678
+ const batches = yield* planLoaderBatches(routeLoaders);
679
+ const results = {};
680
+ const states = {};
681
+ for (const loader of routeLoaders) {
682
+ states[loader.name] = { _tag: "pending" };
683
+ }
684
+ yield* import_effect6.SubscriptionRef.update(snapshotsRef, (current) => ({
685
+ ...current,
686
+ loaders: {
687
+ ...current.loaders,
688
+ ...states
689
+ }
690
+ }));
691
+ for (const batch of batches) {
692
+ const exits = yield* import_effect6.Effect.all(
693
+ batch.map((loader) => {
694
+ const base = loader.run({
695
+ route: snapshot.match.route,
696
+ pathname: snapshot.pathname,
697
+ searchText: snapshot.searchText,
698
+ params: snapshot.match.params,
699
+ search: snapshot.match.search,
700
+ dependencyResults: results
701
+ });
702
+ const withRetry = loader.retry ? import_effect6.Effect.retry(base, loader.retry) : base;
703
+ return import_effect6.Effect.exit(withRetry).pipe(import_effect6.Effect.map((exit) => [loader, exit]));
704
+ }),
705
+ {
706
+ concurrency: "unbounded"
707
+ }
708
+ );
709
+ for (const [loader, exit] of exits) {
710
+ if (exit._tag === "Success") {
711
+ results[loader.name] = exit.value;
712
+ states[loader.name] = {
713
+ _tag: "success",
714
+ value: exit.value
715
+ };
716
+ continue;
717
+ }
718
+ const failure = import_effect6.Cause.failureOption(exit.cause);
719
+ states[loader.name] = {
720
+ _tag: "failure",
721
+ error: failure._tag === "Some" ? failure.value : import_effect6.Cause.pretty(exit.cause)
722
+ };
723
+ yield* import_effect6.SubscriptionRef.update(snapshotsRef, (current) => ({
724
+ ...current,
725
+ status: "failure",
726
+ loaders: {
727
+ ...current.loaders,
728
+ ...states
729
+ },
730
+ error: states[loader.name]
731
+ }));
732
+ return yield* import_effect6.Effect.fail(
733
+ new NavigationRuntimeError(`Loader '${loader.name}' failed for route '${snapshot.match.route.id}'`)
734
+ );
735
+ }
736
+ yield* import_effect6.SubscriptionRef.update(snapshotsRef, (current) => ({
737
+ ...current,
738
+ loaders: {
739
+ ...current.loaders,
740
+ ...states
741
+ }
742
+ }));
743
+ }
744
+ return states;
745
+ });
746
+ const performNavigation = (href) => import_effect6.Effect.gen(function* () {
747
+ const { pathname, searchText } = parseHref(href);
748
+ const searchParams = new URLSearchParams(searchText);
749
+ const candidate = options.routes.find((route) => matchRoute({
750
+ routes: [route],
751
+ pathname,
752
+ search: {}
753
+ }) !== null);
754
+ const decodedSearch = candidate?.search === void 0 ? import_effect6.Effect.succeed({}) : boundary.decodeUnknown({
755
+ source: `route:${candidate.id}:search`,
756
+ schema: candidate.search,
757
+ value: Object.fromEntries(searchParams.entries())
758
+ });
759
+ const search = yield* decodedSearch;
760
+ const matched = matchRoute({
761
+ routes: options.routes,
762
+ pathname,
763
+ search
764
+ });
765
+ if (matched === null) {
766
+ return yield* import_effect6.Effect.fail(
767
+ new NavigationRuntimeError(`No route matched pathname '${pathname}'`)
768
+ );
769
+ }
770
+ yield* telemetry.emit({
771
+ _tag: "navigation",
772
+ phase: "start",
773
+ pathname,
774
+ routeId: matched.route.id,
775
+ timestamp: Date.now()
776
+ });
777
+ const loadingSnapshot = {
778
+ pathname,
779
+ searchText: normalizeSearchText(searchText),
780
+ href: buildHref(pathname, normalizeSearchText(searchText)),
781
+ status: "loading",
782
+ match: matched,
783
+ loaders: {},
784
+ error: void 0
785
+ };
786
+ yield* import_effect6.SubscriptionRef.set(snapshotsRef, loadingSnapshot);
787
+ const loaderStates = yield* runLoaders(loadingSnapshot);
788
+ const completed = {
789
+ ...loadingSnapshot,
790
+ status: "success",
791
+ loaders: loaderStates,
792
+ error: void 0
793
+ };
794
+ yield* import_effect6.SubscriptionRef.set(snapshotsRef, completed);
795
+ yield* telemetry.emit({
796
+ _tag: "navigation",
797
+ phase: "success",
798
+ pathname,
799
+ routeId: matched.route.id,
800
+ timestamp: Date.now()
801
+ });
802
+ return completed;
803
+ });
804
+ const navigate = (href) => import_effect6.Effect.gen(function* () {
805
+ const previous = yield* import_effect6.Ref.getAndSet(activeFiberRef, import_effect6.Option.none());
806
+ if (import_effect6.Option.isSome(previous)) {
807
+ yield* import_effect6.Fiber.interrupt(previous.value);
808
+ yield* telemetry.emit({
809
+ _tag: "navigation",
810
+ phase: "cancel",
811
+ pathname: href,
812
+ timestamp: Date.now()
813
+ });
814
+ }
815
+ const fiber = yield* import_effect6.Effect.fork(performNavigation(href));
816
+ yield* import_effect6.Ref.set(activeFiberRef, import_effect6.Option.some(fiber));
817
+ const exit = yield* import_effect6.Effect.exit(import_effect6.Fiber.join(fiber));
818
+ const current = yield* import_effect6.Ref.get(activeFiberRef);
819
+ if (import_effect6.Option.isSome(current) && current.value === fiber) {
820
+ yield* import_effect6.Ref.set(activeFiberRef, import_effect6.Option.none());
821
+ }
822
+ if (exit._tag === "Success") {
823
+ return exit.value;
824
+ }
825
+ if (import_effect6.Cause.isInterruptedOnly(exit.cause)) {
826
+ return yield* import_effect6.Effect.fail(new NavigationCancelledError(href));
827
+ }
828
+ const failure = import_effect6.Cause.failureOption(exit.cause);
829
+ if (failure._tag === "Some") {
830
+ return yield* import_effect6.Effect.fail(
831
+ failure.value instanceof NavigationRuntimeError ? failure.value : new NavigationRuntimeError(describeUnknown(failure.value))
832
+ );
833
+ }
834
+ return yield* import_effect6.Effect.fail(new NavigationRuntimeError(import_effect6.Cause.pretty(exit.cause)));
835
+ }).pipe(
836
+ import_effect6.Effect.tapError(
837
+ (error) => telemetry.emit({
838
+ _tag: "navigation",
839
+ phase: "failure",
840
+ pathname: href,
841
+ timestamp: Date.now(),
842
+ detail: error
843
+ })
844
+ )
845
+ );
846
+ const revalidate = () => import_effect6.Effect.gen(function* () {
847
+ const snapshot = yield* import_effect6.SubscriptionRef.get(snapshotsRef);
848
+ return yield* navigate(snapshot.href);
849
+ });
850
+ return {
851
+ navigate,
852
+ revalidate,
853
+ getSnapshot: import_effect6.SubscriptionRef.get(snapshotsRef),
854
+ hydrateSnapshot: (snapshot) => import_effect6.SubscriptionRef.set(snapshotsRef, snapshot),
855
+ snapshots: snapshotsRef.changes
856
+ };
857
+ })
858
+ );
859
+ };
860
+
861
+ // src/kernel/runtime.ts
862
+ var import_effect7 = require("effect");
863
+ var createManagedRuntime = (layer) => import_effect7.ManagedRuntime.make(layer);
864
+
865
+ // src/kernel/app.ts
866
+ var createAppLayer = (options) => {
867
+ const boundaryLayer = BoundaryLive;
868
+ const telemetryLayer = TelemetryLive;
869
+ const dataLayer = makeDataLayer(options.data).pipe(
870
+ import_effect8.Layer.provide([boundaryLayer, telemetryLayer])
871
+ );
872
+ const actionsLayer = makeActionsLayer({
873
+ actions: options.actions ?? []
874
+ }).pipe(import_effect8.Layer.provide([boundaryLayer, telemetryLayer]));
875
+ const navigationLayer = makeNavigationLayer({
876
+ routes: options.routes,
877
+ ...options.loaders !== void 0 ? { loaders: options.loaders } : {},
878
+ ...options.initialHref !== void 0 ? { initialHref: options.initialHref } : {}
879
+ }).pipe(import_effect8.Layer.provide([boundaryLayer, telemetryLayer]));
880
+ return import_effect8.Layer.mergeAll(boundaryLayer, telemetryLayer, dataLayer, actionsLayer, navigationLayer);
881
+ };
882
+ var createAppRuntime = (options) => createManagedRuntime(createAppLayer(options));
883
+
884
+ // src/framework/manifest.ts
885
+ var routesFromManifest = (manifest) => manifest.pages.map((page) => page.route);
886
+ var loadersFromManifest = (manifest) => {
887
+ const pageLoaders = manifest.pages.map((page) => page.loader).filter((loader) => loader !== void 0);
888
+ const layoutLoaders = (manifest.layouts ?? []).map((layout) => layout.loader).filter((loader) => loader !== void 0);
889
+ return [...pageLoaders, ...layoutLoaders];
890
+ };
891
+
892
+ // src/config/index.ts
893
+ var defaultConfig = {
894
+ appDir: "app",
895
+ adapters: ["node", "bun"],
896
+ ssr: {
897
+ streaming: true
898
+ },
899
+ cache: {
900
+ defaultPolicy: "no-store",
901
+ routeSegmentDefaults: "explicit"
902
+ },
903
+ strict: {
904
+ boundarySchemas: true,
905
+ typedErrors: true
906
+ }
907
+ };
908
+ var resolveConfig = (config = {}) => ({
909
+ appDir: config.appDir ?? defaultConfig.appDir,
910
+ adapters: config.adapters ?? defaultConfig.adapters,
911
+ ssr: {
912
+ streaming: config.ssr?.streaming ?? defaultConfig.ssr.streaming
913
+ },
914
+ cache: {
915
+ defaultPolicy: config.cache?.defaultPolicy ?? defaultConfig.cache.defaultPolicy,
916
+ routeSegmentDefaults: config.cache?.routeSegmentDefaults ?? defaultConfig.cache.routeSegmentDefaults
917
+ },
918
+ strict: {
919
+ boundarySchemas: config.strict?.boundarySchemas ?? defaultConfig.strict.boundarySchemas,
920
+ typedErrors: config.strict?.typedErrors ?? defaultConfig.strict.typedErrors
921
+ }
922
+ });
923
+
924
+ // src/framework/app.ts
925
+ var createPageMatcher = (pages) => {
926
+ const routes = pages.map((page) => page.route);
927
+ return (href) => {
928
+ const parsed = parseHref(href);
929
+ const matched = matchRoute({
930
+ routes,
931
+ pathname: parsed.pathname,
932
+ search: {}
933
+ });
934
+ if (matched === null) {
935
+ return void 0;
936
+ }
937
+ return pages.find((page) => page.route.id === matched.route.id);
938
+ };
939
+ };
940
+ var createApp = (options) => {
941
+ const manifest = options.manifest;
942
+ const routes = routesFromManifest(manifest);
943
+ const loaders = loadersFromManifest(manifest);
944
+ const actions = manifest.actions ?? [];
945
+ const runtime = createAppRuntime({
946
+ routes,
947
+ actions,
948
+ loaders,
949
+ ...options.initialHref !== void 0 ? { initialHref: options.initialHref } : {}
950
+ });
951
+ const actionHandlerFactory = createActionHttpHandler();
952
+ const handleActionRequest = actionHandlerFactory((effect) => runtime.runPromise(effect));
953
+ return {
954
+ manifest,
955
+ config: resolveConfig(options.config),
956
+ runtime,
957
+ actions,
958
+ matchPage: createPageMatcher(manifest.pages),
959
+ handleActionRequest,
960
+ dispose: () => runtime.dispose()
961
+ };
962
+ };
963
+
964
+ // src/testing/index.ts
965
+ var createTestApp = (options) => createApp(options);
966
+ // Annotate the CommonJS export names for ESM import in node:
967
+ 0 && (module.exports = {
968
+ createTestApp
969
+ });
970
+ //# sourceMappingURL=index.cjs.map