@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.
- package/LICENSE +21 -0
- package/README.md +179 -0
- package/dist/actions/http.d.ts +18 -0
- package/dist/actions/index.d.ts +4 -0
- package/dist/actions/react.d.ts +7 -0
- package/dist/actions/service.d.ts +18 -0
- package/dist/actions/types.d.ts +33 -0
- package/dist/boundary/codecs.d.ts +40 -0
- package/dist/boundary/errors.d.ts +22 -0
- package/dist/boundary/index.d.ts +2 -0
- package/dist/chunk-2GIUCKL2.js +16 -0
- package/dist/chunk-2GIUCKL2.js.map +1 -0
- package/dist/chunk-2TG7YEVD.js +11 -0
- package/dist/chunk-2TG7YEVD.js.map +1 -0
- package/dist/chunk-6FI4ROTW.js +152 -0
- package/dist/chunk-6FI4ROTW.js.map +1 -0
- package/dist/chunk-C5JI7D7W.js +213 -0
- package/dist/chunk-C5JI7D7W.js.map +1 -0
- package/dist/chunk-EEYASTXR.js +99 -0
- package/dist/chunk-EEYASTXR.js.map +1 -0
- package/dist/chunk-H7MOLKTU.js +301 -0
- package/dist/chunk-H7MOLKTU.js.map +1 -0
- package/dist/chunk-IVIYY6S5.js +77 -0
- package/dist/chunk-IVIYY6S5.js.map +1 -0
- package/dist/chunk-JKN75OYC.js +87 -0
- package/dist/chunk-JKN75OYC.js.map +1 -0
- package/dist/chunk-M2CJG6T7.js +24 -0
- package/dist/chunk-M2CJG6T7.js.map +1 -0
- package/dist/chunk-MDGEGQZB.js +206 -0
- package/dist/chunk-MDGEGQZB.js.map +1 -0
- package/dist/chunk-NI2GNZ7S.js +78 -0
- package/dist/chunk-NI2GNZ7S.js.map +1 -0
- package/dist/chunk-O7XTA7H3.js +423 -0
- package/dist/chunk-O7XTA7H3.js.map +1 -0
- package/dist/chunk-S67FHWAR.js +88 -0
- package/dist/chunk-S67FHWAR.js.map +1 -0
- package/dist/chunk-SKC3HMF3.js +17 -0
- package/dist/chunk-SKC3HMF3.js.map +1 -0
- package/dist/chunk-TUJZ6XJY.js +127 -0
- package/dist/chunk-TUJZ6XJY.js.map +1 -0
- package/dist/chunk-WPV3WFMS.js +38 -0
- package/dist/chunk-WPV3WFMS.js.map +1 -0
- package/dist/chunk-XIBEKS5A.js +301 -0
- package/dist/chunk-XIBEKS5A.js.map +1 -0
- package/dist/chunk-YG22YP5K.js +68 -0
- package/dist/chunk-YG22YP5K.js.map +1 -0
- package/dist/chunk-ZMZQBREU.js +262 -0
- package/dist/chunk-ZMZQBREU.js.map +1 -0
- package/dist/client/index.cjs +191 -0
- package/dist/client/index.cjs.map +1 -0
- package/dist/client/index.d.ts +8 -0
- package/dist/client/index.js +14 -0
- package/dist/client/index.js.map +1 -0
- package/dist/config/index.cjs +63 -0
- package/dist/config/index.cjs.map +1 -0
- package/dist/config/index.d.ts +32 -0
- package/dist/config/index.js +9 -0
- package/dist/config/index.js.map +1 -0
- package/dist/data/index.d.ts +3 -0
- package/dist/data/react.d.ts +10 -0
- package/dist/data/service.d.ts +20 -0
- package/dist/data/types.d.ts +31 -0
- package/dist/devtools/events.d.ts +37 -0
- package/dist/devtools/index.cjs +149 -0
- package/dist/devtools/index.cjs.map +1 -0
- package/dist/devtools/index.d.ts +2 -0
- package/dist/devtools/index.js +18 -0
- package/dist/devtools/index.js.map +1 -0
- package/dist/devtools/react.d.ts +8 -0
- package/dist/form/index.cjs +301 -0
- package/dist/form/index.cjs.map +1 -0
- package/dist/form/index.d.ts +3 -0
- package/dist/form/index.js +14 -0
- package/dist/form/index.js.map +1 -0
- package/dist/form/react.d.ts +9 -0
- package/dist/form/service.d.ts +3 -0
- package/dist/form/types.d.ts +41 -0
- package/dist/framework/app.d.ts +21 -0
- package/dist/framework/cache.d.ts +10 -0
- package/dist/framework/contracts.d.ts +32 -0
- package/dist/framework/index.cjs +1006 -0
- package/dist/framework/index.cjs.map +1 -0
- package/dist/framework/index.d.ts +4 -0
- package/dist/framework/index.js +35 -0
- package/dist/framework/index.js.map +1 -0
- package/dist/framework/manifest.d.ts +12 -0
- package/dist/framework/vite.d.ts +13 -0
- package/dist/framework-vite/index.cjs +163 -0
- package/dist/framework-vite/index.cjs.map +1 -0
- package/dist/framework-vite/index.d.ts +1 -0
- package/dist/framework-vite/index.js +125 -0
- package/dist/framework-vite/index.js.map +1 -0
- package/dist/grid/grid.d.ts +8 -0
- package/dist/grid/index.cjs +238 -0
- package/dist/grid/index.cjs.map +1 -0
- package/dist/grid/index.d.ts +2 -0
- package/dist/grid/index.js +19 -0
- package/dist/grid/index.js.map +1 -0
- package/dist/grid/types.d.ts +35 -0
- package/dist/index.cjs +2512 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +207 -0
- package/dist/index.js.map +1 -0
- package/dist/kernel/app.d.ts +26 -0
- package/dist/kernel/index.d.ts +3 -0
- package/dist/kernel/runtime.d.ts +5 -0
- package/dist/kernel/telemetry.d.ts +37 -0
- package/dist/navigation/index.d.ts +4 -0
- package/dist/navigation/matcher.d.ts +13 -0
- package/dist/navigation/react.d.ts +12 -0
- package/dist/navigation/service.d.ts +23 -0
- package/dist/navigation/types.d.ts +65 -0
- package/dist/query/index.cjs +361 -0
- package/dist/query/index.cjs.map +1 -0
- package/dist/query/index.d.ts +3 -0
- package/dist/query/index.js +30 -0
- package/dist/query/index.js.map +1 -0
- package/dist/query/react.d.ts +27 -0
- package/dist/query/service.d.ts +10 -0
- package/dist/query/types.d.ts +5 -0
- package/dist/react/index.d.ts +1 -0
- package/dist/react/provider.d.ts +10 -0
- package/dist/realtime/channel.d.ts +15 -0
- package/dist/realtime/index.cjs +117 -0
- package/dist/realtime/index.cjs.map +1 -0
- package/dist/realtime/index.d.ts +2 -0
- package/dist/realtime/index.js +15 -0
- package/dist/realtime/index.js.map +1 -0
- package/dist/realtime/presence.d.ts +22 -0
- package/dist/render/hydration.d.ts +24 -0
- package/dist/render/index.d.ts +2 -0
- package/dist/render/ssr.d.ts +13 -0
- package/dist/router/helpers.d.ts +26 -0
- package/dist/router/index.cjs +236 -0
- package/dist/router/index.cjs.map +1 -0
- package/dist/router/index.d.ts +4 -0
- package/dist/router/index.js +40 -0
- package/dist/router/index.js.map +1 -0
- package/dist/router/react.d.ts +5 -0
- package/dist/router/service.d.ts +5 -0
- package/dist/router/types.d.ts +1 -0
- package/dist/server/index.cjs +174 -0
- package/dist/server/index.cjs.map +1 -0
- package/dist/server/index.d.ts +16 -0
- package/dist/server/index.js +12 -0
- package/dist/server/index.js.map +1 -0
- package/dist/state/index.cjs +128 -0
- package/dist/state/index.cjs.map +1 -0
- package/dist/state/index.d.ts +2 -0
- package/dist/state/index.js +36 -0
- package/dist/state/index.js.map +1 -0
- package/dist/state/react.d.ts +3 -0
- package/dist/state/service.d.ts +28 -0
- package/dist/testing/index.cjs +970 -0
- package/dist/testing/index.cjs.map +1 -0
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.js +13 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/virtual/index.cjs +160 -0
- package/dist/virtual/index.cjs.map +1 -0
- package/dist/virtual/index.d.ts +2 -0
- package/dist/virtual/index.js +21 -0
- package/dist/virtual/index.js.map +1 -0
- package/dist/virtual/types.d.ts +25 -0
- package/dist/virtual/virtual.d.ts +9 -0
- package/package.json +156 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Boundary
|
|
3
|
+
} from "./chunk-YG22YP5K.js";
|
|
4
|
+
import {
|
|
5
|
+
Telemetry
|
|
6
|
+
} from "./chunk-SKC3HMF3.js";
|
|
7
|
+
|
|
8
|
+
// src/data/types.ts
|
|
9
|
+
var defineQuery = (definition) => definition;
|
|
10
|
+
var QueryRuntimeError = class extends Error {
|
|
11
|
+
constructor(messageText) {
|
|
12
|
+
super(messageText);
|
|
13
|
+
this.messageText = messageText;
|
|
14
|
+
this.name = "QueryRuntimeError";
|
|
15
|
+
}
|
|
16
|
+
_tag = "QueryRuntimeError";
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// src/data/service.ts
|
|
20
|
+
import {
|
|
21
|
+
Cache,
|
|
22
|
+
Context,
|
|
23
|
+
Effect,
|
|
24
|
+
Layer,
|
|
25
|
+
SubscriptionRef
|
|
26
|
+
} from "effect";
|
|
27
|
+
var defaultRuntimeOptions = {
|
|
28
|
+
capacity: 2048,
|
|
29
|
+
timeToLive: "5 minutes"
|
|
30
|
+
};
|
|
31
|
+
var stableStringify = (value) => {
|
|
32
|
+
if (value === null || value === void 0) {
|
|
33
|
+
return String(value);
|
|
34
|
+
}
|
|
35
|
+
if (typeof value !== "object") {
|
|
36
|
+
return JSON.stringify(value);
|
|
37
|
+
}
|
|
38
|
+
if (Array.isArray(value)) {
|
|
39
|
+
return `[${value.map(stableStringify).join(",")}]`;
|
|
40
|
+
}
|
|
41
|
+
const entries = Object.entries(value).sort(
|
|
42
|
+
([a], [b]) => a.localeCompare(b)
|
|
43
|
+
);
|
|
44
|
+
return `{${entries.map(([key, nested]) => `${JSON.stringify(key)}:${stableStringify(nested)}`).join(",")}}`;
|
|
45
|
+
};
|
|
46
|
+
var initialSnapshot = (key) => ({
|
|
47
|
+
key,
|
|
48
|
+
phase: "initial",
|
|
49
|
+
data: void 0,
|
|
50
|
+
error: void 0,
|
|
51
|
+
updatedAt: null
|
|
52
|
+
});
|
|
53
|
+
var Data = class extends Context.Tag("EffectReact/Data")() {
|
|
54
|
+
};
|
|
55
|
+
var makeDataLayer = (options = {}) => {
|
|
56
|
+
const merged = {
|
|
57
|
+
...defaultRuntimeOptions,
|
|
58
|
+
...options
|
|
59
|
+
};
|
|
60
|
+
return Layer.effect(
|
|
61
|
+
Data,
|
|
62
|
+
Effect.gen(function* () {
|
|
63
|
+
const boundary = yield* Boundary;
|
|
64
|
+
const telemetry = yield* Telemetry;
|
|
65
|
+
const lookups = /* @__PURE__ */ new Map();
|
|
66
|
+
const snapshots = yield* SubscriptionRef.make(
|
|
67
|
+
/* @__PURE__ */ new Map()
|
|
68
|
+
);
|
|
69
|
+
const cache = yield* Cache.make({
|
|
70
|
+
capacity: merged.capacity,
|
|
71
|
+
timeToLive: merged.timeToLive,
|
|
72
|
+
lookup: (key) => Effect.suspend(() => {
|
|
73
|
+
const lookup = lookups.get(key);
|
|
74
|
+
if (lookup === void 0) {
|
|
75
|
+
return Effect.fail(new QueryRuntimeError(`No query executor registered for ${key}`));
|
|
76
|
+
}
|
|
77
|
+
return lookup;
|
|
78
|
+
})
|
|
79
|
+
});
|
|
80
|
+
const setSnapshot = (key, update) => SubscriptionRef.update(snapshots, (current) => {
|
|
81
|
+
const next = new Map(current);
|
|
82
|
+
const previous = next.get(key) ?? initialSnapshot(key);
|
|
83
|
+
next.set(key, update(previous));
|
|
84
|
+
return next;
|
|
85
|
+
}).pipe(Effect.asVoid);
|
|
86
|
+
const buildKey = (definition, input) => {
|
|
87
|
+
const base = definition.key ? definition.key(input) : input;
|
|
88
|
+
return `${definition.name}:${stableStringify(base)}`;
|
|
89
|
+
};
|
|
90
|
+
const fetch = (definition, input, runOptions) => Effect.gen(function* () {
|
|
91
|
+
const decodedInput = yield* boundary.decodeUnknown({
|
|
92
|
+
source: `query:${definition.name}:input`,
|
|
93
|
+
schema: definition.input,
|
|
94
|
+
value: input
|
|
95
|
+
});
|
|
96
|
+
const key = buildKey(definition, decodedInput);
|
|
97
|
+
yield* telemetry.emit({
|
|
98
|
+
_tag: "query",
|
|
99
|
+
phase: "start",
|
|
100
|
+
key,
|
|
101
|
+
timestamp: Date.now()
|
|
102
|
+
});
|
|
103
|
+
yield* setSnapshot(key, (previous) => ({
|
|
104
|
+
...previous,
|
|
105
|
+
phase: "loading",
|
|
106
|
+
error: void 0
|
|
107
|
+
}));
|
|
108
|
+
lookups.set(
|
|
109
|
+
key,
|
|
110
|
+
definition.run(decodedInput).pipe(
|
|
111
|
+
Effect.flatMap(
|
|
112
|
+
(output) => boundary.decodeUnknown({
|
|
113
|
+
source: `query:${definition.name}:output`,
|
|
114
|
+
schema: definition.output,
|
|
115
|
+
value: output
|
|
116
|
+
})
|
|
117
|
+
)
|
|
118
|
+
)
|
|
119
|
+
);
|
|
120
|
+
if (runOptions?.forceRefresh === true) {
|
|
121
|
+
yield* cache.refresh(key).pipe(Effect.ignore);
|
|
122
|
+
}
|
|
123
|
+
const value = yield* cache.get(key).pipe(
|
|
124
|
+
Effect.mapError(
|
|
125
|
+
(error) => error
|
|
126
|
+
)
|
|
127
|
+
);
|
|
128
|
+
yield* setSnapshot(key, () => ({
|
|
129
|
+
key,
|
|
130
|
+
phase: "success",
|
|
131
|
+
data: value,
|
|
132
|
+
error: void 0,
|
|
133
|
+
updatedAt: Date.now()
|
|
134
|
+
}));
|
|
135
|
+
yield* telemetry.emit({
|
|
136
|
+
_tag: "query",
|
|
137
|
+
phase: "success",
|
|
138
|
+
key,
|
|
139
|
+
timestamp: Date.now()
|
|
140
|
+
});
|
|
141
|
+
return value;
|
|
142
|
+
}).pipe(
|
|
143
|
+
Effect.tapError(
|
|
144
|
+
(error) => Effect.gen(function* () {
|
|
145
|
+
const decodedInput = yield* boundary.decodeUnknown({
|
|
146
|
+
source: `query:${definition.name}:input`,
|
|
147
|
+
schema: definition.input,
|
|
148
|
+
value: input
|
|
149
|
+
});
|
|
150
|
+
const key = buildKey(definition, decodedInput);
|
|
151
|
+
yield* setSnapshot(key, (previous) => ({
|
|
152
|
+
...previous,
|
|
153
|
+
phase: "failure",
|
|
154
|
+
error,
|
|
155
|
+
updatedAt: previous.updatedAt
|
|
156
|
+
}));
|
|
157
|
+
yield* telemetry.emit({
|
|
158
|
+
_tag: "query",
|
|
159
|
+
phase: "failure",
|
|
160
|
+
key,
|
|
161
|
+
timestamp: Date.now(),
|
|
162
|
+
detail: error
|
|
163
|
+
});
|
|
164
|
+
})
|
|
165
|
+
)
|
|
166
|
+
);
|
|
167
|
+
const prefetch = (definition, input) => fetch(definition, input).pipe(Effect.asVoid);
|
|
168
|
+
const invalidate = (definition, input) => Effect.gen(function* () {
|
|
169
|
+
const key = `${definition.name}:${stableStringify(input)}`;
|
|
170
|
+
yield* cache.invalidate(key);
|
|
171
|
+
yield* SubscriptionRef.update(snapshots, (current) => {
|
|
172
|
+
const next = new Map(current);
|
|
173
|
+
next.delete(key);
|
|
174
|
+
return next;
|
|
175
|
+
}).pipe(Effect.asVoid);
|
|
176
|
+
yield* telemetry.emit({
|
|
177
|
+
_tag: "query",
|
|
178
|
+
phase: "invalidate",
|
|
179
|
+
key,
|
|
180
|
+
timestamp: Date.now()
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
const getSnapshot = (definition, input) => Effect.gen(function* () {
|
|
184
|
+
const key = `${definition.name}:${stableStringify(input)}`;
|
|
185
|
+
const current = yield* SubscriptionRef.get(snapshots);
|
|
186
|
+
const snapshot = current.get(key);
|
|
187
|
+
if (snapshot === void 0) {
|
|
188
|
+
return initialSnapshot(key);
|
|
189
|
+
}
|
|
190
|
+
return snapshot;
|
|
191
|
+
});
|
|
192
|
+
return {
|
|
193
|
+
fetch,
|
|
194
|
+
prefetch,
|
|
195
|
+
invalidate,
|
|
196
|
+
getSnapshot,
|
|
197
|
+
getAllSnapshots: SubscriptionRef.get(snapshots),
|
|
198
|
+
hydrateSnapshots: (nextSnapshots) => SubscriptionRef.set(snapshots, new Map(nextSnapshots)),
|
|
199
|
+
snapshots: snapshots.changes
|
|
200
|
+
};
|
|
201
|
+
})
|
|
202
|
+
);
|
|
203
|
+
};
|
|
204
|
+
var fetchQuery = (definition, input, options) => Effect.flatMap(Data, (service) => service.fetch(definition, input, options));
|
|
205
|
+
|
|
206
|
+
export {
|
|
207
|
+
defineQuery,
|
|
208
|
+
QueryRuntimeError,
|
|
209
|
+
Data,
|
|
210
|
+
makeDataLayer,
|
|
211
|
+
fetchQuery
|
|
212
|
+
};
|
|
213
|
+
//# sourceMappingURL=chunk-C5JI7D7W.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/data/types.ts","../src/data/service.ts"],"sourcesContent":["import type { Duration, Effect, Schema } from \"effect\";\nimport type { BoundaryDecodeError } from \"../boundary\";\nimport type { BoundaryProtocolError } from \"../boundary/errors\";\n\nexport interface QueryDefinition<\n Name extends string,\n Input,\n Output,\n E,\n InputEncoded = Input,\n OutputEncoded = Output,\n> {\n readonly name: Name;\n readonly input: Schema.Schema<Input, InputEncoded, never>;\n readonly output: Schema.Schema<Output, OutputEncoded, never>;\n readonly run: (input: Input) => Effect.Effect<Output, E, never>;\n readonly key?: (input: Input) => unknown;\n}\n\nexport const defineQuery = <\n Name extends string,\n Input,\n Output,\n E,\n InputEncoded = Input,\n OutputEncoded = Output,\n>(\n definition: QueryDefinition<Name, Input, Output, E, InputEncoded, OutputEncoded>,\n): QueryDefinition<Name, Input, Output, E, InputEncoded, OutputEncoded> => definition;\n\nexport type QueryPhase = \"initial\" | \"loading\" | \"success\" | \"failure\";\n\nexport interface QuerySnapshot<Output, E> {\n readonly key: string;\n readonly phase: QueryPhase;\n readonly data: Output | undefined;\n readonly error: E | BoundaryDecodeError | BoundaryProtocolError | QueryRuntimeError | undefined;\n readonly updatedAt: number | null | undefined;\n}\n\nexport interface QueryRunOptions {\n readonly forceRefresh?: boolean;\n}\n\nexport interface QueryRuntimeOptions {\n readonly capacity?: number;\n readonly timeToLive?: Duration.DurationInput;\n}\n\nexport class QueryRuntimeError extends Error {\n readonly _tag = \"QueryRuntimeError\" as const;\n\n constructor(readonly messageText: string) {\n super(messageText);\n this.name = \"QueryRuntimeError\";\n }\n}\n","import {\n Cache,\n Context,\n Effect,\n Layer,\n SubscriptionRef,\n type Stream,\n} from \"effect\";\nimport { Boundary, type BoundaryDecodeError } from \"../boundary\";\nimport type { BoundaryProtocolError } from \"../boundary/errors\";\nimport { Telemetry } from \"../kernel/telemetry\";\nimport {\n type QueryDefinition,\n type QueryRunOptions,\n type QueryRuntimeOptions,\n type QuerySnapshot,\n QueryRuntimeError,\n} from \"./types\";\n\nconst defaultRuntimeOptions: Required<QueryRuntimeOptions> = {\n capacity: 2048,\n timeToLive: \"5 minutes\",\n};\n\nconst stableStringify = (value: unknown): string => {\n if (value === null || value === undefined) {\n return String(value);\n }\n\n if (typeof value !== \"object\") {\n return JSON.stringify(value);\n }\n\n if (Array.isArray(value)) {\n return `[${value.map(stableStringify).join(\",\")}]`;\n }\n\n const entries = Object.entries(value as Record<string, unknown>).sort(([a], [b]) =>\n a.localeCompare(b),\n );\n return `{${entries.map(([key, nested]) => `${JSON.stringify(key)}:${stableStringify(nested)}`).join(\",\")}}`;\n};\n\nconst initialSnapshot = <Output, E>(key: string): QuerySnapshot<Output, E> => ({\n key,\n phase: \"initial\",\n data: undefined,\n error: undefined,\n updatedAt: null,\n});\n\nexport interface DataService {\n readonly fetch: <Name extends string, Input, Output, E>(\n definition: QueryDefinition<Name, Input, Output, E>,\n input: unknown,\n options?: QueryRunOptions,\n ) => Effect.Effect<Output, E | BoundaryDecodeError | BoundaryProtocolError | QueryRuntimeError, never>;\n readonly prefetch: <Name extends string, Input, Output, E>(\n definition: QueryDefinition<Name, Input, Output, E>,\n input: unknown,\n ) => Effect.Effect<void, E | BoundaryDecodeError | BoundaryProtocolError | QueryRuntimeError, never>;\n readonly invalidate: <Name extends string, Input, Output, E>(\n definition: QueryDefinition<Name, Input, Output, E>,\n input: unknown,\n ) => Effect.Effect<void, QueryRuntimeError, never>;\n readonly getSnapshot: <Name extends string, Input, Output, E>(\n definition: QueryDefinition<Name, Input, Output, E>,\n input: unknown,\n ) => Effect.Effect<QuerySnapshot<Output, E>, never, never>;\n readonly getAllSnapshots: Effect.Effect<ReadonlyMap<string, QuerySnapshot<unknown, unknown>>, never, never>;\n readonly hydrateSnapshots: (\n snapshots: ReadonlyMap<string, QuerySnapshot<unknown, unknown>>,\n ) => Effect.Effect<void, never, never>;\n readonly snapshots: Stream.Stream<ReadonlyMap<string, QuerySnapshot<unknown, unknown>>>;\n}\n\nexport class Data extends Context.Tag(\"EffectReact/Data\")<Data, DataService>() {}\n\nexport const makeDataLayer = (\n options: QueryRuntimeOptions = {},\n): Layer.Layer<Data, never, Boundary | Telemetry> => {\n const merged = {\n ...defaultRuntimeOptions,\n ...options,\n };\n\n return Layer.effect(\n Data,\n Effect.gen(function* () {\n const boundary = yield* Boundary;\n const telemetry = yield* Telemetry;\n const lookups = new Map<string, Effect.Effect<unknown, unknown, never>>();\n const snapshots = yield* SubscriptionRef.make(\n new Map<string, QuerySnapshot<unknown, unknown>>() as ReadonlyMap<\n string,\n QuerySnapshot<unknown, unknown>\n >,\n );\n\n const cache = yield* Cache.make<string, unknown, unknown>({\n capacity: merged.capacity,\n timeToLive: merged.timeToLive,\n lookup: (key) =>\n Effect.suspend(() => {\n const lookup = lookups.get(key);\n if (lookup === undefined) {\n return Effect.fail(new QueryRuntimeError(`No query executor registered for ${key}`));\n }\n return lookup;\n }),\n });\n\n const setSnapshot = <Output, E>(\n key: string,\n update: (previous: QuerySnapshot<Output, E>) => QuerySnapshot<Output, E>,\n ): Effect.Effect<void> =>\n SubscriptionRef.update(snapshots, (current) => {\n const next = new Map(current);\n const previous =\n (next.get(key) as QuerySnapshot<Output, E> | undefined) ?? initialSnapshot<Output, E>(key);\n next.set(key, update(previous));\n return next;\n }).pipe(Effect.asVoid);\n\n const buildKey = <Name extends string, Input, Output, E>(\n definition: QueryDefinition<Name, Input, Output, E>,\n input: Input,\n ): string => {\n const base = definition.key ? definition.key(input) : input;\n return `${definition.name}:${stableStringify(base)}`;\n };\n\n const fetch = <Name extends string, Input, Output, E>(\n definition: QueryDefinition<Name, Input, Output, E>,\n input: unknown,\n runOptions?: QueryRunOptions,\n ): Effect.Effect<\n Output,\n E | BoundaryDecodeError | BoundaryProtocolError | QueryRuntimeError,\n never\n > =>\n Effect.gen(function* () {\n const decodedInput = yield* boundary.decodeUnknown({\n source: `query:${definition.name}:input`,\n schema: definition.input,\n value: input,\n });\n\n const key = buildKey(definition, decodedInput);\n yield* telemetry.emit({\n _tag: \"query\",\n phase: \"start\",\n key,\n timestamp: Date.now(),\n });\n\n yield* setSnapshot(key, (previous) => ({\n ...previous,\n phase: \"loading\",\n error: undefined,\n }));\n\n lookups.set(\n key,\n definition\n .run(decodedInput)\n .pipe(\n Effect.flatMap((output) =>\n boundary.decodeUnknown({\n source: `query:${definition.name}:output`,\n schema: definition.output,\n value: output,\n }),\n ),\n ) as Effect.Effect<unknown, unknown, never>,\n );\n\n if (runOptions?.forceRefresh === true) {\n yield* cache.refresh(key).pipe(Effect.ignore);\n }\n\n const value = yield* cache.get(key).pipe(\n Effect.mapError(\n (error) =>\n error as E | BoundaryDecodeError | BoundaryProtocolError | QueryRuntimeError,\n ),\n );\n\n yield* setSnapshot(key, () => ({\n key,\n phase: \"success\",\n data: value,\n error: undefined,\n updatedAt: Date.now(),\n }));\n\n yield* telemetry.emit({\n _tag: \"query\",\n phase: \"success\",\n key,\n timestamp: Date.now(),\n });\n\n return value as Output;\n }).pipe(\n Effect.tapError((error) =>\n Effect.gen(function* () {\n const decodedInput = yield* boundary.decodeUnknown({\n source: `query:${definition.name}:input`,\n schema: definition.input,\n value: input,\n });\n const key = buildKey(definition, decodedInput);\n yield* setSnapshot(key, (previous) => ({\n ...previous,\n phase: \"failure\",\n error,\n updatedAt: previous.updatedAt,\n }));\n yield* telemetry.emit({\n _tag: \"query\",\n phase: \"failure\",\n key,\n timestamp: Date.now(),\n detail: error,\n });\n }),\n ),\n );\n\n const prefetch: DataService[\"prefetch\"] = (definition, input) =>\n fetch(definition, input).pipe(Effect.asVoid);\n\n const invalidate: DataService[\"invalidate\"] = (definition, input) =>\n Effect.gen(function* () {\n const key = `${definition.name}:${stableStringify(input)}`;\n yield* cache.invalidate(key);\n yield* SubscriptionRef.update(snapshots, (current) => {\n const next = new Map(current);\n next.delete(key);\n return next;\n }).pipe(Effect.asVoid);\n yield* telemetry.emit({\n _tag: \"query\",\n phase: \"invalidate\",\n key,\n timestamp: Date.now(),\n });\n });\n\n const getSnapshot = <Name extends string, Input, Output, E>(\n definition: QueryDefinition<Name, Input, Output, E>,\n input: unknown,\n ): Effect.Effect<QuerySnapshot<Output, E>, never, never> =>\n Effect.gen(function* () {\n const key = `${definition.name}:${stableStringify(input)}`;\n const current = yield* SubscriptionRef.get(snapshots);\n const snapshot = current.get(key);\n if (snapshot === undefined) {\n return initialSnapshot<Output, E>(key);\n }\n return snapshot as QuerySnapshot<Output, E>;\n });\n\n return {\n fetch,\n prefetch,\n invalidate,\n getSnapshot,\n getAllSnapshots: SubscriptionRef.get(snapshots),\n hydrateSnapshots: (nextSnapshots) => SubscriptionRef.set(snapshots, new Map(nextSnapshots)),\n snapshots: snapshots.changes,\n } satisfies DataService;\n }),\n );\n};\n\nexport const fetchQuery = <Name extends string, Input, Output, E>(\n definition: QueryDefinition<Name, Input, Output, E>,\n input: unknown,\n options?: QueryRunOptions,\n): Effect.Effect<Output, E | BoundaryDecodeError | BoundaryProtocolError | QueryRuntimeError, Data> =>\n Effect.flatMap(Data, (service) => service.fetch(definition, input, options));\n"],"mappings":";;;;;;;;AAmBO,IAAM,cAAc,CAQzB,eACyE;AAqBpE,IAAM,oBAAN,cAAgC,MAAM;AAAA,EAG3C,YAAqB,aAAqB;AACxC,UAAM,WAAW;AADE;AAEnB,SAAK,OAAO;AAAA,EACd;AAAA,EALS,OAAO;AAMlB;;;ACxDA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAYP,IAAM,wBAAuD;AAAA,EAC3D,UAAU;AAAA,EACV,YAAY;AACd;AAEA,IAAM,kBAAkB,CAAC,UAA2B;AAClD,MAAI,UAAU,QAAQ,UAAU,QAAW;AACzC,WAAO,OAAO,KAAK;AAAA,EACrB;AAEA,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO,KAAK,UAAU,KAAK;AAAA,EAC7B;AAEA,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,WAAO,IAAI,MAAM,IAAI,eAAe,EAAE,KAAK,GAAG,CAAC;AAAA,EACjD;AAEA,QAAM,UAAU,OAAO,QAAQ,KAAgC,EAAE;AAAA,IAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAC5E,EAAE,cAAc,CAAC;AAAA,EACnB;AACA,SAAO,IAAI,QAAQ,IAAI,CAAC,CAAC,KAAK,MAAM,MAAM,GAAG,KAAK,UAAU,GAAG,CAAC,IAAI,gBAAgB,MAAM,CAAC,EAAE,EAAE,KAAK,GAAG,CAAC;AAC1G;AAEA,IAAM,kBAAkB,CAAY,SAA2C;AAAA,EAC7E;AAAA,EACA,OAAO;AAAA,EACP,MAAM;AAAA,EACN,OAAO;AAAA,EACP,WAAW;AACb;AA2BO,IAAM,OAAN,cAAmB,QAAQ,IAAI,kBAAkB,EAAqB,EAAE;AAAC;AAEzE,IAAM,gBAAgB,CAC3B,UAA+B,CAAC,MACmB;AACnD,QAAM,SAAS;AAAA,IACb,GAAG;AAAA,IACH,GAAG;AAAA,EACL;AAEA,SAAO,MAAM;AAAA,IACX;AAAA,IACA,OAAO,IAAI,aAAa;AACtB,YAAM,WAAW,OAAO;AACxB,YAAM,YAAY,OAAO;AACzB,YAAM,UAAU,oBAAI,IAAoD;AACxE,YAAM,YAAY,OAAO,gBAAgB;AAAA,QACvC,oBAAI,IAA6C;AAAA,MAInD;AAEA,YAAM,QAAQ,OAAO,MAAM,KAA+B;AAAA,QACxD,UAAU,OAAO;AAAA,QACjB,YAAY,OAAO;AAAA,QACnB,QAAQ,CAAC,QACP,OAAO,QAAQ,MAAM;AACnB,gBAAM,SAAS,QAAQ,IAAI,GAAG;AAC9B,cAAI,WAAW,QAAW;AACxB,mBAAO,OAAO,KAAK,IAAI,kBAAkB,oCAAoC,GAAG,EAAE,CAAC;AAAA,UACrF;AACA,iBAAO;AAAA,QACT,CAAC;AAAA,MACL,CAAC;AAED,YAAM,cAAc,CAClB,KACA,WAEA,gBAAgB,OAAO,WAAW,CAAC,YAAY;AAC7C,cAAM,OAAO,IAAI,IAAI,OAAO;AAC5B,cAAM,WACH,KAAK,IAAI,GAAG,KAA8C,gBAA2B,GAAG;AAC3F,aAAK,IAAI,KAAK,OAAO,QAAQ,CAAC;AAC9B,eAAO;AAAA,MACT,CAAC,EAAE,KAAK,OAAO,MAAM;AAEvB,YAAM,WAAW,CACf,YACA,UACW;AACX,cAAM,OAAO,WAAW,MAAM,WAAW,IAAI,KAAK,IAAI;AACtD,eAAO,GAAG,WAAW,IAAI,IAAI,gBAAgB,IAAI,CAAC;AAAA,MACpD;AAEA,YAAM,QAAQ,CACZ,YACA,OACA,eAMA,OAAO,IAAI,aAAa;AACtB,cAAM,eAAe,OAAO,SAAS,cAAc;AAAA,UACjD,QAAQ,SAAS,WAAW,IAAI;AAAA,UAChC,QAAQ,WAAW;AAAA,UACnB,OAAO;AAAA,QACT,CAAC;AAED,cAAM,MAAM,SAAS,YAAY,YAAY;AAC7C,eAAO,UAAU,KAAK;AAAA,UACpB,MAAM;AAAA,UACN,OAAO;AAAA,UACP;AAAA,UACA,WAAW,KAAK,IAAI;AAAA,QACtB,CAAC;AAED,eAAO,YAAY,KAAK,CAAC,cAAc;AAAA,UACrC,GAAG;AAAA,UACH,OAAO;AAAA,UACP,OAAO;AAAA,QACT,EAAE;AAEF,gBAAQ;AAAA,UACN;AAAA,UACA,WACG,IAAI,YAAY,EAChB;AAAA,YACC,OAAO;AAAA,cAAQ,CAAC,WACd,SAAS,cAAc;AAAA,gBACrB,QAAQ,SAAS,WAAW,IAAI;AAAA,gBAChC,QAAQ,WAAW;AAAA,gBACnB,OAAO;AAAA,cACT,CAAC;AAAA,YACH;AAAA,UACF;AAAA,QACJ;AAEA,YAAI,YAAY,iBAAiB,MAAM;AACrC,iBAAO,MAAM,QAAQ,GAAG,EAAE,KAAK,OAAO,MAAM;AAAA,QAC9C;AAEA,cAAM,QAAQ,OAAO,MAAM,IAAI,GAAG,EAAE;AAAA,UAClC,OAAO;AAAA,YACL,CAAC,UACC;AAAA,UACJ;AAAA,QACF;AAEA,eAAO,YAAY,KAAK,OAAO;AAAA,UAC7B;AAAA,UACA,OAAO;AAAA,UACP,MAAM;AAAA,UACN,OAAO;AAAA,UACP,WAAW,KAAK,IAAI;AAAA,QACtB,EAAE;AAEF,eAAO,UAAU,KAAK;AAAA,UACpB,MAAM;AAAA,UACN,OAAO;AAAA,UACP;AAAA,UACA,WAAW,KAAK,IAAI;AAAA,QACtB,CAAC;AAED,eAAO;AAAA,MACT,CAAC,EAAE;AAAA,QACD,OAAO;AAAA,UAAS,CAAC,UACf,OAAO,IAAI,aAAa;AACtB,kBAAM,eAAe,OAAO,SAAS,cAAc;AAAA,cACjD,QAAQ,SAAS,WAAW,IAAI;AAAA,cAChC,QAAQ,WAAW;AAAA,cACnB,OAAO;AAAA,YACT,CAAC;AACD,kBAAM,MAAM,SAAS,YAAY,YAAY;AAC7C,mBAAO,YAAY,KAAK,CAAC,cAAc;AAAA,cACrC,GAAG;AAAA,cACH,OAAO;AAAA,cACP;AAAA,cACA,WAAW,SAAS;AAAA,YACtB,EAAE;AACF,mBAAO,UAAU,KAAK;AAAA,cACpB,MAAM;AAAA,cACN,OAAO;AAAA,cACP;AAAA,cACA,WAAW,KAAK,IAAI;AAAA,cACpB,QAAQ;AAAA,YACV,CAAC;AAAA,UACH,CAAC;AAAA,QACH;AAAA,MACF;AAEF,YAAM,WAAoC,CAAC,YAAY,UACrD,MAAM,YAAY,KAAK,EAAE,KAAK,OAAO,MAAM;AAE7C,YAAM,aAAwC,CAAC,YAAY,UACzD,OAAO,IAAI,aAAa;AACtB,cAAM,MAAM,GAAG,WAAW,IAAI,IAAI,gBAAgB,KAAK,CAAC;AACxD,eAAO,MAAM,WAAW,GAAG;AAC3B,eAAO,gBAAgB,OAAO,WAAW,CAAC,YAAY;AACpD,gBAAM,OAAO,IAAI,IAAI,OAAO;AAC5B,eAAK,OAAO,GAAG;AACf,iBAAO;AAAA,QACT,CAAC,EAAE,KAAK,OAAO,MAAM;AACrB,eAAO,UAAU,KAAK;AAAA,UACpB,MAAM;AAAA,UACN,OAAO;AAAA,UACP;AAAA,UACA,WAAW,KAAK,IAAI;AAAA,QACtB,CAAC;AAAA,MACH,CAAC;AAEH,YAAM,cAAc,CAClB,YACA,UAEA,OAAO,IAAI,aAAa;AACtB,cAAM,MAAM,GAAG,WAAW,IAAI,IAAI,gBAAgB,KAAK,CAAC;AACxD,cAAM,UAAU,OAAO,gBAAgB,IAAI,SAAS;AACpD,cAAM,WAAW,QAAQ,IAAI,GAAG;AAChC,YAAI,aAAa,QAAW;AAC1B,iBAAO,gBAA2B,GAAG;AAAA,QACvC;AACA,eAAO;AAAA,MACT,CAAC;AAEH,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,iBAAiB,gBAAgB,IAAI,SAAS;AAAA,QAC9C,kBAAkB,CAAC,kBAAkB,gBAAgB,IAAI,WAAW,IAAI,IAAI,aAAa,CAAC;AAAA,QAC1F,WAAW,UAAU;AAAA,MACvB;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAEO,IAAM,aAAa,CACxB,YACA,OACA,YAEA,OAAO,QAAQ,MAAM,CAAC,YAAY,QAAQ,MAAM,YAAY,OAAO,OAAO,CAAC;","names":[]}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createHydrationScript,
|
|
3
|
+
dehydrateAppState
|
|
4
|
+
} from "./chunk-IVIYY6S5.js";
|
|
5
|
+
import {
|
|
6
|
+
navigateTo
|
|
7
|
+
} from "./chunk-O7XTA7H3.js";
|
|
8
|
+
|
|
9
|
+
// src/server/index.ts
|
|
10
|
+
import { Effect as Effect2 } from "effect";
|
|
11
|
+
import { createElement } from "react";
|
|
12
|
+
|
|
13
|
+
// src/render/ssr.ts
|
|
14
|
+
import { Effect } from "effect";
|
|
15
|
+
import * as ReactDOMServer from "react-dom/server";
|
|
16
|
+
var htmlResponse = (options) => {
|
|
17
|
+
const headers = new Headers(options.headers);
|
|
18
|
+
if (!headers.has("content-type")) {
|
|
19
|
+
headers.set("content-type", "text/html; charset=utf-8");
|
|
20
|
+
}
|
|
21
|
+
return new Response(options.html, {
|
|
22
|
+
status: options.status,
|
|
23
|
+
headers
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
var injectScript = (html, script) => {
|
|
27
|
+
const tag = `<script>${script}</script>`;
|
|
28
|
+
const bodyClose = html.lastIndexOf("</body>");
|
|
29
|
+
if (bodyClose === -1) {
|
|
30
|
+
return `${html}${tag}`;
|
|
31
|
+
}
|
|
32
|
+
return `${html.slice(0, bodyClose)}${tag}${html.slice(bodyClose)}`;
|
|
33
|
+
};
|
|
34
|
+
var toError = (cause) => cause instanceof Error ? cause : new Error(String(cause));
|
|
35
|
+
var createSsrHandler = (options) => (request) => {
|
|
36
|
+
const program = Effect.gen(function* () {
|
|
37
|
+
const element = yield* options.render(request);
|
|
38
|
+
const html = ReactDOMServer.renderToString(element);
|
|
39
|
+
const state = yield* dehydrateAppState();
|
|
40
|
+
const script = createHydrationScript(state, options.hydrationGlobalName);
|
|
41
|
+
return htmlResponse({
|
|
42
|
+
html: injectScript(html, script),
|
|
43
|
+
status: options.status ?? 200,
|
|
44
|
+
...options.headers !== void 0 ? { headers: options.headers } : {}
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
return options.runtime.runPromise(program).catch((cause) => {
|
|
48
|
+
const squashed = toError(cause);
|
|
49
|
+
if (options.onError !== void 0) {
|
|
50
|
+
return options.onError(squashed);
|
|
51
|
+
}
|
|
52
|
+
return new Response(`SSR render failed: ${squashed.message}`, {
|
|
53
|
+
status: 500,
|
|
54
|
+
headers: {
|
|
55
|
+
"content-type": "text/plain; charset=utf-8"
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// src/server/index.ts
|
|
62
|
+
var defaultNotFoundElement = () => createElement("main", void 0, "Not Found");
|
|
63
|
+
var createRequestHandler = (options) => {
|
|
64
|
+
const actionPath = options.actionPath ?? "/_actions";
|
|
65
|
+
const ssrHandler = createSsrHandler({
|
|
66
|
+
runtime: options.app.runtime,
|
|
67
|
+
...options.hydrationGlobalName !== void 0 ? { hydrationGlobalName: options.hydrationGlobalName } : {},
|
|
68
|
+
...options.onError !== void 0 ? { onError: options.onError } : {},
|
|
69
|
+
render: (request) => Effect2.gen(function* () {
|
|
70
|
+
const url = new URL(request.url);
|
|
71
|
+
const href = `${url.pathname}${url.search}`;
|
|
72
|
+
const page = options.app.matchPage(href);
|
|
73
|
+
if (page === void 0) {
|
|
74
|
+
return defaultNotFoundElement();
|
|
75
|
+
}
|
|
76
|
+
yield* navigateTo(href);
|
|
77
|
+
if (options.render !== void 0) {
|
|
78
|
+
return yield* options.render({ request, page });
|
|
79
|
+
}
|
|
80
|
+
return createElement(page.component);
|
|
81
|
+
}).pipe(
|
|
82
|
+
Effect2.mapError(
|
|
83
|
+
(error) => error instanceof Error ? error : new Error(String(error))
|
|
84
|
+
)
|
|
85
|
+
)
|
|
86
|
+
});
|
|
87
|
+
return (request) => {
|
|
88
|
+
const url = new URL(request.url);
|
|
89
|
+
if (request.method.toUpperCase() === "POST" && url.pathname === actionPath) {
|
|
90
|
+
return options.app.handleActionRequest(request);
|
|
91
|
+
}
|
|
92
|
+
return ssrHandler(request);
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export {
|
|
97
|
+
createRequestHandler
|
|
98
|
+
};
|
|
99
|
+
//# sourceMappingURL=chunk-EEYASTXR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server/index.ts","../src/render/ssr.ts"],"sourcesContent":["import { Effect } from \"effect\";\nimport { createElement, type ReactElement } from \"react\";\nimport type { AppServices } from \"../kernel/app\";\nimport { navigateTo } from \"../navigation\";\nimport { createSsrHandler } from \"../render\";\nimport type { AnyPageDefinition } from \"../framework/contracts\";\nimport type { EffectReactApp } from \"../framework/app\";\n\nexport interface CreateRequestHandlerOptions {\n readonly app: EffectReactApp;\n readonly render?: (options: {\n readonly request: Request;\n readonly page: AnyPageDefinition;\n }) => Effect.Effect<ReactElement, unknown, AppServices>;\n readonly actionPath?: string;\n readonly hydrationGlobalName?: string;\n readonly onError?: (error: Error) => Response;\n}\n\nconst defaultNotFoundElement = (): ReactElement =>\n createElement(\"main\", undefined, \"Not Found\");\n\nexport const createRequestHandler = (\n options: CreateRequestHandlerOptions,\n): ((request: Request) => Promise<Response>) => {\n const actionPath = options.actionPath ?? \"/_actions\";\n\n const ssrHandler = createSsrHandler({\n runtime: options.app.runtime,\n ...(options.hydrationGlobalName !== undefined\n ? { hydrationGlobalName: options.hydrationGlobalName }\n : {}),\n ...(options.onError !== undefined ? { onError: options.onError } : {}),\n render: (request) =>\n Effect.gen(function* () {\n const url = new URL(request.url);\n const href = `${url.pathname}${url.search}`;\n const page = options.app.matchPage(href);\n\n if (page === undefined) {\n return defaultNotFoundElement();\n }\n\n yield* navigateTo(href);\n\n if (options.render !== undefined) {\n return yield* options.render({ request, page });\n }\n\n return createElement(page.component);\n }).pipe(\n Effect.mapError((error) =>\n error instanceof Error ? error : new Error(String(error)),\n ),\n ),\n });\n\n return (request) => {\n const url = new URL(request.url);\n if (request.method.toUpperCase() === \"POST\" && url.pathname === actionPath) {\n return options.app.handleActionRequest(request);\n }\n\n return ssrHandler(request);\n };\n};\n","import { Effect } from \"effect\";\nimport type { ReactElement } from \"react\";\nimport * as ReactDOMServer from \"react-dom/server\";\nimport type { AppServices } from \"../kernel/app\";\nimport type { AppManagedRuntime } from \"../kernel/runtime\";\nimport { createHydrationScript, dehydrateAppState, type HydrationState } from \"./hydration\";\n\nexport interface CreateSsrHandlerOptions<E> {\n readonly runtime: AppManagedRuntime<AppServices>;\n readonly render: (request: Request) => Effect.Effect<ReactElement, E, AppServices>;\n readonly status?: number;\n readonly headers?: HeadersInit;\n readonly hydrationGlobalName?: string;\n readonly onError?: (error: E | Error) => Response;\n}\n\nconst htmlResponse = (options: {\n readonly html: string;\n readonly status: number;\n readonly headers?: HeadersInit;\n}): Response => {\n const headers = new Headers(options.headers);\n if (!headers.has(\"content-type\")) {\n headers.set(\"content-type\", \"text/html; charset=utf-8\");\n }\n return new Response(options.html, {\n status: options.status,\n headers,\n });\n};\n\nconst injectScript = (html: string, script: string): string => {\n const tag = `<script>${script}</script>`;\n const bodyClose = html.lastIndexOf(\"</body>\");\n if (bodyClose === -1) {\n return `${html}${tag}`;\n }\n return `${html.slice(0, bodyClose)}${tag}${html.slice(bodyClose)}`;\n};\n\nconst toError = (cause: unknown): Error =>\n cause instanceof Error ? cause : new Error(String(cause));\n\nexport const createSsrHandler = <E>(options: CreateSsrHandlerOptions<E>) =>\n (request: Request): Promise<Response> => {\n const program = Effect.gen(function* () {\n const element = yield* options.render(request);\n const html = ReactDOMServer.renderToString(element);\n\n const state: HydrationState = yield* dehydrateAppState();\n const script = createHydrationScript(state, options.hydrationGlobalName);\n\n return htmlResponse({\n html: injectScript(html, script),\n status: options.status ?? 200,\n ...(options.headers !== undefined ? { headers: options.headers } : {}),\n });\n });\n\n return options.runtime\n .runPromise(program)\n .catch((cause: unknown) => {\n const squashed = toError(cause);\n if (options.onError !== undefined) {\n return options.onError(squashed as E | Error);\n }\n return new Response(`SSR render failed: ${squashed.message}`, {\n status: 500,\n headers: {\n \"content-type\": \"text/plain; charset=utf-8\",\n },\n });\n });\n };\n"],"mappings":";;;;;;;;;AAAA,SAAS,UAAAA,eAAc;AACvB,SAAS,qBAAwC;;;ACDjD,SAAS,cAAc;AAEvB,YAAY,oBAAoB;AAchC,IAAM,eAAe,CAAC,YAIN;AACd,QAAM,UAAU,IAAI,QAAQ,QAAQ,OAAO;AAC3C,MAAI,CAAC,QAAQ,IAAI,cAAc,GAAG;AAChC,YAAQ,IAAI,gBAAgB,0BAA0B;AAAA,EACxD;AACA,SAAO,IAAI,SAAS,QAAQ,MAAM;AAAA,IAChC,QAAQ,QAAQ;AAAA,IAChB;AAAA,EACF,CAAC;AACH;AAEA,IAAM,eAAe,CAAC,MAAc,WAA2B;AAC7D,QAAM,MAAM,WAAW,MAAM;AAC7B,QAAM,YAAY,KAAK,YAAY,SAAS;AAC5C,MAAI,cAAc,IAAI;AACpB,WAAO,GAAG,IAAI,GAAG,GAAG;AAAA,EACtB;AACA,SAAO,GAAG,KAAK,MAAM,GAAG,SAAS,CAAC,GAAG,GAAG,GAAG,KAAK,MAAM,SAAS,CAAC;AAClE;AAEA,IAAM,UAAU,CAAC,UACf,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAEnD,IAAM,mBAAmB,CAAI,YAClC,CAAC,YAAwC;AACvC,QAAM,UAAU,OAAO,IAAI,aAAa;AACtC,UAAM,UAAU,OAAO,QAAQ,OAAO,OAAO;AAC7C,UAAM,OAAsB,8BAAe,OAAO;AAElD,UAAM,QAAwB,OAAO,kBAAkB;AACvD,UAAM,SAAS,sBAAsB,OAAO,QAAQ,mBAAmB;AAEvE,WAAO,aAAa;AAAA,MAClB,MAAM,aAAa,MAAM,MAAM;AAAA,MAC/B,QAAQ,QAAQ,UAAU;AAAA,MAC1B,GAAI,QAAQ,YAAY,SAAY,EAAE,SAAS,QAAQ,QAAQ,IAAI,CAAC;AAAA,IACtE,CAAC;AAAA,EACH,CAAC;AAED,SAAO,QAAQ,QACZ,WAAW,OAAO,EAClB,MAAM,CAAC,UAAmB;AACzB,UAAM,WAAW,QAAQ,KAAK;AAC9B,QAAI,QAAQ,YAAY,QAAW;AACjC,aAAO,QAAQ,QAAQ,QAAqB;AAAA,IAC9C;AACA,WAAO,IAAI,SAAS,sBAAsB,SAAS,OAAO,IAAI;AAAA,MAC5D,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,MAClB;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACL;;;ADtDF,IAAM,yBAAyB,MAC7B,cAAc,QAAQ,QAAW,WAAW;AAEvC,IAAM,uBAAuB,CAClC,YAC8C;AAC9C,QAAM,aAAa,QAAQ,cAAc;AAEzC,QAAM,aAAa,iBAAiB;AAAA,IAClC,SAAS,QAAQ,IAAI;AAAA,IACrB,GAAI,QAAQ,wBAAwB,SAChC,EAAE,qBAAqB,QAAQ,oBAAoB,IACnD,CAAC;AAAA,IACL,GAAI,QAAQ,YAAY,SAAY,EAAE,SAAS,QAAQ,QAAQ,IAAI,CAAC;AAAA,IACpE,QAAQ,CAAC,YACPC,QAAO,IAAI,aAAa;AACtB,YAAM,MAAM,IAAI,IAAI,QAAQ,GAAG;AAC/B,YAAM,OAAO,GAAG,IAAI,QAAQ,GAAG,IAAI,MAAM;AACzC,YAAM,OAAO,QAAQ,IAAI,UAAU,IAAI;AAEvC,UAAI,SAAS,QAAW;AACtB,eAAO,uBAAuB;AAAA,MAChC;AAEA,aAAO,WAAW,IAAI;AAEtB,UAAI,QAAQ,WAAW,QAAW;AAChC,eAAO,OAAO,QAAQ,OAAO,EAAE,SAAS,KAAK,CAAC;AAAA,MAChD;AAEA,aAAO,cAAc,KAAK,SAAS;AAAA,IACrC,CAAC,EAAE;AAAA,MACDA,QAAO;AAAA,QAAS,CAAC,UACf,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAAA,MAC1D;AAAA,IACF;AAAA,EACJ,CAAC;AAED,SAAO,CAAC,YAAY;AAClB,UAAM,MAAM,IAAI,IAAI,QAAQ,GAAG;AAC/B,QAAI,QAAQ,OAAO,YAAY,MAAM,UAAU,IAAI,aAAa,YAAY;AAC1E,aAAO,QAAQ,IAAI,oBAAoB,OAAO;AAAA,IAChD;AAEA,WAAO,WAAW,OAAO;AAAA,EAC3B;AACF;","names":["Effect","Effect"]}
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useEffectRuntime
|
|
3
|
+
} from "./chunk-2GIUCKL2.js";
|
|
4
|
+
import {
|
|
5
|
+
Data,
|
|
6
|
+
fetchQuery
|
|
7
|
+
} from "./chunk-C5JI7D7W.js";
|
|
8
|
+
|
|
9
|
+
// src/query/service.ts
|
|
10
|
+
import { Effect as Effect2 } from "effect";
|
|
11
|
+
|
|
12
|
+
// src/data/react.tsx
|
|
13
|
+
import { Effect, Fiber, Stream } from "effect";
|
|
14
|
+
import { useCallback, useEffect, useMemo, useSyncExternalStore } from "react";
|
|
15
|
+
var useDataService = () => {
|
|
16
|
+
const runtime = useEffectRuntime();
|
|
17
|
+
return useMemo(() => runtime.runSync(Data), [runtime]);
|
|
18
|
+
};
|
|
19
|
+
var useQuery = (definition, input, options = {}) => {
|
|
20
|
+
const runtime = useEffectRuntime();
|
|
21
|
+
const data = useDataService();
|
|
22
|
+
const subscribe = useCallback(
|
|
23
|
+
(listener) => {
|
|
24
|
+
const fiber = runtime.runFork(
|
|
25
|
+
Stream.runForEach(data.snapshots, () => Effect.sync(listener))
|
|
26
|
+
);
|
|
27
|
+
return () => {
|
|
28
|
+
runtime.runFork(Fiber.interrupt(fiber));
|
|
29
|
+
};
|
|
30
|
+
},
|
|
31
|
+
[data, runtime]
|
|
32
|
+
);
|
|
33
|
+
const getSnapshot = useCallback(
|
|
34
|
+
() => runtime.runSync(data.getSnapshot(definition, input)),
|
|
35
|
+
[data, definition, input, runtime]
|
|
36
|
+
);
|
|
37
|
+
const snapshot = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (options.enabled === false) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
runtime.runFork(data.prefetch(definition, input));
|
|
43
|
+
}, [data, definition, input, options.enabled, runtime]);
|
|
44
|
+
const refetch = useCallback(
|
|
45
|
+
() => runtime.runPromise(data.fetch(definition, input, { ...options.run, forceRefresh: true })),
|
|
46
|
+
[data, definition, input, options.run, runtime]
|
|
47
|
+
);
|
|
48
|
+
const invalidate2 = useCallback(
|
|
49
|
+
() => runtime.runPromise(data.invalidate(definition, input)),
|
|
50
|
+
[data, definition, input, runtime]
|
|
51
|
+
);
|
|
52
|
+
return {
|
|
53
|
+
...snapshot,
|
|
54
|
+
refetch,
|
|
55
|
+
invalidate: invalidate2
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
var useSuspenseQuery = (definition, input) => {
|
|
59
|
+
const result = useQuery(definition, input);
|
|
60
|
+
if (result.phase === "failure") {
|
|
61
|
+
throw result.error;
|
|
62
|
+
}
|
|
63
|
+
if (result.phase === "success") {
|
|
64
|
+
return result.data;
|
|
65
|
+
}
|
|
66
|
+
throw result.refetch();
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// src/query/service.ts
|
|
70
|
+
var fetchQuery2 = fetchQuery;
|
|
71
|
+
var prefetchQuery = (definition, input) => Effect2.flatMap(Data, (service) => service.prefetch(definition, input));
|
|
72
|
+
var invalidateQuery = (definition, input) => Effect2.flatMap(Data, (service) => service.invalidate(definition, input));
|
|
73
|
+
var prefetch = prefetchQuery;
|
|
74
|
+
var invalidate = invalidateQuery;
|
|
75
|
+
|
|
76
|
+
// src/query/react.tsx
|
|
77
|
+
import { Effect as Effect3 } from "effect";
|
|
78
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo2, useRef, useState } from "react";
|
|
79
|
+
var upsertInfiniteEntry = (entries, nextEntry) => {
|
|
80
|
+
const index = entries.findIndex((entry) => entry.key === nextEntry.key);
|
|
81
|
+
if (index < 0) {
|
|
82
|
+
return [...entries, nextEntry];
|
|
83
|
+
}
|
|
84
|
+
const next = [...entries];
|
|
85
|
+
next[index] = nextEntry;
|
|
86
|
+
return next;
|
|
87
|
+
};
|
|
88
|
+
var useDataService2 = () => {
|
|
89
|
+
const runtime = useEffectRuntime();
|
|
90
|
+
return useMemo2(() => runtime.runSync(Data), [runtime]);
|
|
91
|
+
};
|
|
92
|
+
var useQuery2 = (definition, input, options = {}) => useQuery(definition, input, options);
|
|
93
|
+
var useSuspenseQuery2 = (definition, input) => useSuspenseQuery(definition, input);
|
|
94
|
+
var useInfiniteQuery = (definition, options) => {
|
|
95
|
+
const optionsRef = useRef(options);
|
|
96
|
+
optionsRef.current = options;
|
|
97
|
+
const initialPageParam = options.initialPageParam;
|
|
98
|
+
const enabled = options.enabled;
|
|
99
|
+
const run = options.run;
|
|
100
|
+
const getInput = useCallback2(
|
|
101
|
+
(pageParam) => optionsRef.current.getInput(pageParam),
|
|
102
|
+
[]
|
|
103
|
+
);
|
|
104
|
+
const getNextPageParam = useCallback2(
|
|
105
|
+
(lastPage, allPages, lastPageParam, allPageParams) => optionsRef.current.getNextPageParam(lastPage, allPages, lastPageParam, allPageParams),
|
|
106
|
+
[]
|
|
107
|
+
);
|
|
108
|
+
const runtime = useEffectRuntime();
|
|
109
|
+
const data = useDataService2();
|
|
110
|
+
const [initialInput, setInitialInput] = useState(
|
|
111
|
+
() => getInput(initialPageParam)
|
|
112
|
+
);
|
|
113
|
+
const [entries, setEntries] = useState([]);
|
|
114
|
+
const [error, setError] = useState(void 0);
|
|
115
|
+
const [isFetchingNextPage, setIsFetchingNextPage] = useState(false);
|
|
116
|
+
const initialQuery = useQuery2(
|
|
117
|
+
definition,
|
|
118
|
+
initialInput,
|
|
119
|
+
{
|
|
120
|
+
...enabled !== void 0 ? { enabled } : {},
|
|
121
|
+
...run !== void 0 ? { run } : {}
|
|
122
|
+
}
|
|
123
|
+
);
|
|
124
|
+
useEffect2(() => {
|
|
125
|
+
setInitialInput(getInput(initialPageParam));
|
|
126
|
+
setEntries([]);
|
|
127
|
+
setError(void 0);
|
|
128
|
+
setIsFetchingNextPage(false);
|
|
129
|
+
}, [definition, getInput, initialPageParam]);
|
|
130
|
+
useEffect2(() => {
|
|
131
|
+
if (initialQuery.phase === "success") {
|
|
132
|
+
setEntries(
|
|
133
|
+
(current) => upsertInfiniteEntry(current, {
|
|
134
|
+
key: initialQuery.key,
|
|
135
|
+
pageParam: initialPageParam,
|
|
136
|
+
data: initialQuery.data
|
|
137
|
+
})
|
|
138
|
+
);
|
|
139
|
+
setError(void 0);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (initialQuery.phase === "failure") {
|
|
143
|
+
setError(initialQuery.error);
|
|
144
|
+
}
|
|
145
|
+
}, [
|
|
146
|
+
initialQuery.data,
|
|
147
|
+
initialQuery.error,
|
|
148
|
+
initialQuery.key,
|
|
149
|
+
initialQuery.phase,
|
|
150
|
+
initialPageParam
|
|
151
|
+
]);
|
|
152
|
+
const pages = useMemo2(() => entries.map((entry) => entry.data), [entries]);
|
|
153
|
+
const pageParams = useMemo2(() => entries.map((entry) => entry.pageParam), [entries]);
|
|
154
|
+
const resolveNextPageParam = useCallback2(() => {
|
|
155
|
+
const lastEntry = entries[entries.length - 1];
|
|
156
|
+
if (lastEntry !== void 0) {
|
|
157
|
+
return getNextPageParam(lastEntry.data, pages, lastEntry.pageParam, pageParams);
|
|
158
|
+
}
|
|
159
|
+
if (initialQuery.phase !== "success") {
|
|
160
|
+
return void 0;
|
|
161
|
+
}
|
|
162
|
+
const initialPage = initialQuery.data;
|
|
163
|
+
return getNextPageParam(
|
|
164
|
+
initialPage,
|
|
165
|
+
[initialPage],
|
|
166
|
+
initialPageParam,
|
|
167
|
+
[initialPageParam]
|
|
168
|
+
);
|
|
169
|
+
}, [
|
|
170
|
+
entries,
|
|
171
|
+
getNextPageParam,
|
|
172
|
+
initialPageParam,
|
|
173
|
+
initialQuery.data,
|
|
174
|
+
initialQuery.phase,
|
|
175
|
+
pageParams,
|
|
176
|
+
pages
|
|
177
|
+
]);
|
|
178
|
+
const fetchNextPage = useCallback2(() => {
|
|
179
|
+
const nextPageParam = resolveNextPageParam();
|
|
180
|
+
if (nextPageParam === void 0) {
|
|
181
|
+
return runtime.runPromise(Effect3.succeed(void 0));
|
|
182
|
+
}
|
|
183
|
+
setIsFetchingNextPage(true);
|
|
184
|
+
setError(void 0);
|
|
185
|
+
const input = getInput(nextPageParam);
|
|
186
|
+
const program = Effect3.gen(function* () {
|
|
187
|
+
const value = yield* data.fetch(definition, input, run);
|
|
188
|
+
const snapshot = yield* data.getSnapshot(definition, input);
|
|
189
|
+
return {
|
|
190
|
+
key: snapshot.key,
|
|
191
|
+
pageParam: nextPageParam,
|
|
192
|
+
value
|
|
193
|
+
};
|
|
194
|
+
});
|
|
195
|
+
return runtime.runPromise(program).then(
|
|
196
|
+
({ key, pageParam, value }) => {
|
|
197
|
+
setEntries(
|
|
198
|
+
(current) => upsertInfiniteEntry(current, {
|
|
199
|
+
key,
|
|
200
|
+
pageParam,
|
|
201
|
+
data: value
|
|
202
|
+
})
|
|
203
|
+
);
|
|
204
|
+
setIsFetchingNextPage(false);
|
|
205
|
+
return value;
|
|
206
|
+
},
|
|
207
|
+
(cause) => {
|
|
208
|
+
setIsFetchingNextPage(false);
|
|
209
|
+
const resolved = cause;
|
|
210
|
+
setError(resolved);
|
|
211
|
+
return runtime.runPromise(Effect3.fail(resolved));
|
|
212
|
+
}
|
|
213
|
+
);
|
|
214
|
+
}, [data, definition, getInput, resolveNextPageParam, run, runtime]);
|
|
215
|
+
const refetch = useCallback2(() => {
|
|
216
|
+
const targets = pageParams.length === 0 ? [initialPageParam] : pageParams;
|
|
217
|
+
setError(void 0);
|
|
218
|
+
const program = Effect3.forEach(
|
|
219
|
+
targets,
|
|
220
|
+
(pageParam) => {
|
|
221
|
+
const input = getInput(pageParam);
|
|
222
|
+
return Effect3.gen(function* () {
|
|
223
|
+
const value = yield* data.fetch(definition, input, {
|
|
224
|
+
...run,
|
|
225
|
+
forceRefresh: true
|
|
226
|
+
});
|
|
227
|
+
const snapshot = yield* data.getSnapshot(definition, input);
|
|
228
|
+
return {
|
|
229
|
+
key: snapshot.key,
|
|
230
|
+
pageParam,
|
|
231
|
+
data: value
|
|
232
|
+
};
|
|
233
|
+
});
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
concurrency: 1,
|
|
237
|
+
discard: false
|
|
238
|
+
}
|
|
239
|
+
);
|
|
240
|
+
return runtime.runPromise(program).then(
|
|
241
|
+
(fetchedEntries) => {
|
|
242
|
+
setEntries(fetchedEntries);
|
|
243
|
+
return fetchedEntries.map((entry) => entry.data);
|
|
244
|
+
},
|
|
245
|
+
(cause) => {
|
|
246
|
+
const resolved = cause;
|
|
247
|
+
setError(resolved);
|
|
248
|
+
return runtime.runPromise(Effect3.fail(resolved));
|
|
249
|
+
}
|
|
250
|
+
);
|
|
251
|
+
}, [data, definition, getInput, initialPageParam, pageParams, run, runtime]);
|
|
252
|
+
const invalidate2 = useCallback2(() => {
|
|
253
|
+
const targets = pageParams.length === 0 ? [initialPageParam] : pageParams;
|
|
254
|
+
const program = Effect3.forEach(
|
|
255
|
+
targets,
|
|
256
|
+
(pageParam) => data.invalidate(definition, getInput(pageParam)),
|
|
257
|
+
{
|
|
258
|
+
concurrency: 1,
|
|
259
|
+
discard: true
|
|
260
|
+
}
|
|
261
|
+
);
|
|
262
|
+
return runtime.runPromise(program).then(
|
|
263
|
+
() => {
|
|
264
|
+
setEntries([]);
|
|
265
|
+
setError(void 0);
|
|
266
|
+
setIsFetchingNextPage(false);
|
|
267
|
+
return void 0;
|
|
268
|
+
},
|
|
269
|
+
(cause) => {
|
|
270
|
+
const resolved = cause;
|
|
271
|
+
setError(resolved);
|
|
272
|
+
return runtime.runPromise(Effect3.fail(resolved));
|
|
273
|
+
}
|
|
274
|
+
);
|
|
275
|
+
}, [data, definition, getInput, initialPageParam, pageParams, runtime]);
|
|
276
|
+
const resolvedError = error ?? (initialQuery.phase === "failure" ? initialQuery.error : void 0);
|
|
277
|
+
const phase = resolvedError !== void 0 ? "failure" : entries.length > 0 ? "success" : initialQuery.phase;
|
|
278
|
+
return {
|
|
279
|
+
phase,
|
|
280
|
+
pages,
|
|
281
|
+
pageParams,
|
|
282
|
+
error: resolvedError,
|
|
283
|
+
hasNextPage: resolveNextPageParam() !== void 0,
|
|
284
|
+
isFetchingNextPage,
|
|
285
|
+
fetchNextPage,
|
|
286
|
+
refetch,
|
|
287
|
+
invalidate: invalidate2
|
|
288
|
+
};
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
export {
|
|
292
|
+
fetchQuery2 as fetchQuery,
|
|
293
|
+
prefetchQuery,
|
|
294
|
+
invalidateQuery,
|
|
295
|
+
prefetch,
|
|
296
|
+
invalidate,
|
|
297
|
+
useQuery2 as useQuery,
|
|
298
|
+
useSuspenseQuery2 as useSuspenseQuery,
|
|
299
|
+
useInfiniteQuery
|
|
300
|
+
};
|
|
301
|
+
//# sourceMappingURL=chunk-H7MOLKTU.js.map
|