@bleedingdev/modern-js-bff-effect 3.8.3-ultramodern.2

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 (230) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +25 -0
  3. package/dist/cjs/data-platform/batch/protocol.js +130 -0
  4. package/dist/cjs/data-platform/batch/queue.js +623 -0
  5. package/dist/cjs/data-platform/batch/request.js +121 -0
  6. package/dist/cjs/data-platform/batch/response.js +65 -0
  7. package/dist/cjs/data-platform/batch/telemetry.js +71 -0
  8. package/dist/cjs/data-platform/batch/url.js +69 -0
  9. package/dist/cjs/data-platform/batch-transport.js +59 -0
  10. package/dist/cjs/data-platform/codec.js +155 -0
  11. package/dist/cjs/data-platform/index.js +124 -0
  12. package/dist/cjs/data-platform/invalidation.js +61 -0
  13. package/dist/cjs/data-platform/trace.js +73 -0
  14. package/dist/cjs/data-platform/types.js +48 -0
  15. package/dist/cjs/data-platform/validation.js +178 -0
  16. package/dist/cjs/effect/context.js +59 -0
  17. package/dist/cjs/effect/dispatch.js +101 -0
  18. package/dist/cjs/effect/edge-context.js +61 -0
  19. package/dist/cjs/effect/edge-dispatcher.js +354 -0
  20. package/dist/cjs/effect/edge.js +62 -0
  21. package/dist/cjs/effect/endpoint-contracts.js +121 -0
  22. package/dist/cjs/effect/entry-shape-registry.js +31 -0
  23. package/dist/cjs/effect/entry-shape.js +113 -0
  24. package/dist/cjs/effect/handler/batch-handler.js +270 -0
  25. package/dist/cjs/effect/handler/batch.js +170 -0
  26. package/dist/cjs/effect/handler/definition.js +99 -0
  27. package/dist/cjs/effect/handler/envelope.js +104 -0
  28. package/dist/cjs/effect/handler/http.js +107 -0
  29. package/dist/cjs/effect/handler/openapi.js +58 -0
  30. package/dist/cjs/effect/handler/routing.js +100 -0
  31. package/dist/cjs/effect/handler/rpc.js +63 -0
  32. package/dist/cjs/effect/handler/types.js +43 -0
  33. package/dist/cjs/effect/handler.js +173 -0
  34. package/dist/cjs/effect/index.js +137 -0
  35. package/dist/cjs/effect/module.js +118 -0
  36. package/dist/cjs/effect/operation-context.js +116 -0
  37. package/dist/cjs/effect/rpcSerialization.js +54 -0
  38. package/dist/cjs/effect-client/batch.js +73 -0
  39. package/dist/cjs/effect-client/envelope.js +145 -0
  40. package/dist/cjs/effect-client/guards.js +44 -0
  41. package/dist/cjs/effect-client/index.js +208 -0
  42. package/dist/cjs/effect-client/request-context.js +64 -0
  43. package/dist/cjs/effect-client/request.js +66 -0
  44. package/dist/cjs/effect-client/runtime.js +100 -0
  45. package/dist/cjs/effect-client/types.js +18 -0
  46. package/dist/cjs/headers.js +46 -0
  47. package/dist/cjs/index.js +83 -0
  48. package/dist/esm/data-platform/batch/protocol.mjs +71 -0
  49. package/dist/esm/data-platform/batch/queue.mjs +582 -0
  50. package/dist/esm/data-platform/batch/request.mjs +68 -0
  51. package/dist/esm/data-platform/batch/response.mjs +24 -0
  52. package/dist/esm/data-platform/batch/telemetry.mjs +27 -0
  53. package/dist/esm/data-platform/batch/url.mjs +25 -0
  54. package/dist/esm/data-platform/batch-transport.mjs +12 -0
  55. package/dist/esm/data-platform/codec.mjs +90 -0
  56. package/dist/esm/data-platform/index.mjs +7 -0
  57. package/dist/esm/data-platform/invalidation.mjs +20 -0
  58. package/dist/esm/data-platform/trace.mjs +29 -0
  59. package/dist/esm/data-platform/types.mjs +4 -0
  60. package/dist/esm/data-platform/validation.mjs +128 -0
  61. package/dist/esm/effect/context.mjs +12 -0
  62. package/dist/esm/effect/dispatch.mjs +63 -0
  63. package/dist/esm/effect/edge-context.mjs +14 -0
  64. package/dist/esm/effect/edge-dispatcher.mjs +115 -0
  65. package/dist/esm/effect/edge.mjs +1 -0
  66. package/dist/esm/effect/endpoint-contracts.mjs +68 -0
  67. package/dist/esm/effect/entry-shape-registry.mjs +17 -0
  68. package/dist/esm/effect/entry-shape.mjs +51 -0
  69. package/dist/esm/effect/handler/batch-handler.mjs +232 -0
  70. package/dist/esm/effect/handler/batch.mjs +105 -0
  71. package/dist/esm/effect/handler/definition.mjs +58 -0
  72. package/dist/esm/effect/handler/envelope.mjs +63 -0
  73. package/dist/esm/effect/handler/http.mjs +69 -0
  74. package/dist/esm/effect/handler/openapi.mjs +20 -0
  75. package/dist/esm/effect/handler/routing.mjs +50 -0
  76. package/dist/esm/effect/handler/rpc.mjs +22 -0
  77. package/dist/esm/effect/handler/types.mjs +5 -0
  78. package/dist/esm/effect/handler.mjs +13 -0
  79. package/dist/esm/effect/index.mjs +7 -0
  80. package/dist/esm/effect/module.mjs +80 -0
  81. package/dist/esm/effect/operation-context.mjs +75 -0
  82. package/dist/esm/effect/rpcSerialization.mjs +16 -0
  83. package/dist/esm/effect-client/batch.mjs +35 -0
  84. package/dist/esm/effect-client/envelope.mjs +104 -0
  85. package/dist/esm/effect-client/guards.mjs +3 -0
  86. package/dist/esm/effect-client/index.mjs +110 -0
  87. package/dist/esm/effect-client/request-context.mjs +23 -0
  88. package/dist/esm/effect-client/request.mjs +28 -0
  89. package/dist/esm/effect-client/runtime.mjs +62 -0
  90. package/dist/esm/effect-client/types.mjs +0 -0
  91. package/dist/esm/headers.mjs +8 -0
  92. package/dist/esm/index.mjs +4 -0
  93. package/dist/esm/rslib-runtime.mjs +18 -0
  94. package/dist/esm-node/data-platform/batch/protocol.mjs +72 -0
  95. package/dist/esm-node/data-platform/batch/queue.mjs +583 -0
  96. package/dist/esm-node/data-platform/batch/request.mjs +69 -0
  97. package/dist/esm-node/data-platform/batch/response.mjs +25 -0
  98. package/dist/esm-node/data-platform/batch/telemetry.mjs +28 -0
  99. package/dist/esm-node/data-platform/batch/url.mjs +26 -0
  100. package/dist/esm-node/data-platform/batch-transport.mjs +13 -0
  101. package/dist/esm-node/data-platform/codec.mjs +91 -0
  102. package/dist/esm-node/data-platform/index.mjs +8 -0
  103. package/dist/esm-node/data-platform/invalidation.mjs +21 -0
  104. package/dist/esm-node/data-platform/trace.mjs +30 -0
  105. package/dist/esm-node/data-platform/types.mjs +5 -0
  106. package/dist/esm-node/data-platform/validation.mjs +129 -0
  107. package/dist/esm-node/effect/context.mjs +13 -0
  108. package/dist/esm-node/effect/dispatch.mjs +64 -0
  109. package/dist/esm-node/effect/edge-context.mjs +15 -0
  110. package/dist/esm-node/effect/edge-dispatcher.mjs +116 -0
  111. package/dist/esm-node/effect/edge.mjs +2 -0
  112. package/dist/esm-node/effect/endpoint-contracts.mjs +69 -0
  113. package/dist/esm-node/effect/entry-shape-registry.mjs +18 -0
  114. package/dist/esm-node/effect/entry-shape.mjs +63 -0
  115. package/dist/esm-node/effect/handler/batch-handler.mjs +233 -0
  116. package/dist/esm-node/effect/handler/batch.mjs +106 -0
  117. package/dist/esm-node/effect/handler/definition.mjs +59 -0
  118. package/dist/esm-node/effect/handler/envelope.mjs +64 -0
  119. package/dist/esm-node/effect/handler/http.mjs +70 -0
  120. package/dist/esm-node/effect/handler/openapi.mjs +21 -0
  121. package/dist/esm-node/effect/handler/routing.mjs +51 -0
  122. package/dist/esm-node/effect/handler/rpc.mjs +23 -0
  123. package/dist/esm-node/effect/handler/types.mjs +6 -0
  124. package/dist/esm-node/effect/handler.mjs +14 -0
  125. package/dist/esm-node/effect/index.mjs +8 -0
  126. package/dist/esm-node/effect/module.mjs +81 -0
  127. package/dist/esm-node/effect/operation-context.mjs +76 -0
  128. package/dist/esm-node/effect/rpcSerialization.mjs +17 -0
  129. package/dist/esm-node/effect-client/batch.mjs +36 -0
  130. package/dist/esm-node/effect-client/envelope.mjs +105 -0
  131. package/dist/esm-node/effect-client/guards.mjs +4 -0
  132. package/dist/esm-node/effect-client/index.mjs +111 -0
  133. package/dist/esm-node/effect-client/request-context.mjs +24 -0
  134. package/dist/esm-node/effect-client/request.mjs +29 -0
  135. package/dist/esm-node/effect-client/runtime.mjs +63 -0
  136. package/dist/esm-node/effect-client/types.mjs +1 -0
  137. package/dist/esm-node/headers.mjs +9 -0
  138. package/dist/esm-node/index.mjs +5 -0
  139. package/dist/esm-node/rslib-runtime.mjs +19 -0
  140. package/dist/types/data-platform/batch/protocol.d.ts +9 -0
  141. package/dist/types/data-platform/batch/queue.d.ts +47 -0
  142. package/dist/types/data-platform/batch/request.d.ts +19 -0
  143. package/dist/types/data-platform/batch/response.d.ts +3 -0
  144. package/dist/types/data-platform/batch/telemetry.d.ts +4 -0
  145. package/dist/types/data-platform/batch/url.d.ts +4 -0
  146. package/dist/types/data-platform/batch-transport.d.ts +3 -0
  147. package/dist/types/data-platform/codec.d.ts +17 -0
  148. package/dist/types/data-platform/index.d.ts +8 -0
  149. package/dist/types/data-platform/invalidation.d.ts +8 -0
  150. package/dist/types/data-platform/trace.d.ts +4 -0
  151. package/dist/types/data-platform/types.d.ts +151 -0
  152. package/dist/types/data-platform/validation.d.ts +29 -0
  153. package/dist/types/effect/context.d.ts +5 -0
  154. package/dist/types/effect/dispatch.d.ts +13 -0
  155. package/dist/types/effect/edge-context.d.ts +5 -0
  156. package/dist/types/effect/edge-dispatcher.d.ts +37 -0
  157. package/dist/types/effect/edge.d.ts +1 -0
  158. package/dist/types/effect/endpoint-contracts.d.ts +67 -0
  159. package/dist/types/effect/entry-shape.d.ts +32 -0
  160. package/dist/types/effect/handler/batch-handler.d.ts +11 -0
  161. package/dist/types/effect/handler/batch.d.ts +15 -0
  162. package/dist/types/effect/handler/definition.d.ts +7 -0
  163. package/dist/types/effect/handler/envelope.d.ts +13 -0
  164. package/dist/types/effect/handler/http.d.ts +15 -0
  165. package/dist/types/effect/handler/openapi.d.ts +4 -0
  166. package/dist/types/effect/handler/routing.d.ts +5 -0
  167. package/dist/types/effect/handler/rpc.d.ts +7 -0
  168. package/dist/types/effect/handler/types.d.ts +153 -0
  169. package/dist/types/effect/handler.d.ts +13 -0
  170. package/dist/types/effect/index.d.ts +6 -0
  171. package/dist/types/effect/module.d.ts +36 -0
  172. package/dist/types/effect/operation-context.d.ts +15 -0
  173. package/dist/types/effect/rpcSerialization.d.ts +3 -0
  174. package/dist/types/effect-client/batch.d.ts +2 -0
  175. package/dist/types/effect-client/envelope.d.ts +15 -0
  176. package/dist/types/effect-client/guards.d.ts +2 -0
  177. package/dist/types/effect-client/index.d.ts +58 -0
  178. package/dist/types/effect-client/request-context.d.ts +3 -0
  179. package/dist/types/effect-client/request.d.ts +1 -0
  180. package/dist/types/effect-client/runtime.d.ts +5 -0
  181. package/dist/types/effect-client/types.d.ts +68 -0
  182. package/dist/types/headers.d.ts +1 -0
  183. package/dist/types/index.d.ts +3 -0
  184. package/package.json +164 -0
  185. package/src/data-platform/batch/protocol.ts +112 -0
  186. package/src/data-platform/batch/queue.ts +876 -0
  187. package/src/data-platform/batch/request.ts +130 -0
  188. package/src/data-platform/batch/response.ts +74 -0
  189. package/src/data-platform/batch/telemetry.ts +47 -0
  190. package/src/data-platform/batch/url.ts +54 -0
  191. package/src/data-platform/batch-transport.ts +22 -0
  192. package/src/data-platform/codec.ts +145 -0
  193. package/src/data-platform/index.ts +63 -0
  194. package/src/data-platform/invalidation.ts +57 -0
  195. package/src/data-platform/trace.ts +49 -0
  196. package/src/data-platform/types.ts +186 -0
  197. package/src/data-platform/validation.ts +303 -0
  198. package/src/effect/context.ts +37 -0
  199. package/src/effect/dispatch.ts +132 -0
  200. package/src/effect/edge-context.ts +51 -0
  201. package/src/effect/edge-dispatcher.ts +310 -0
  202. package/src/effect/edge.ts +1 -0
  203. package/src/effect/endpoint-contracts.ts +191 -0
  204. package/src/effect/entry-shape-registry.cjs +16 -0
  205. package/src/effect/entry-shape-registry.d.cts +6 -0
  206. package/src/effect/entry-shape.ts +155 -0
  207. package/src/effect/handler/batch-handler.ts +475 -0
  208. package/src/effect/handler/batch.ts +182 -0
  209. package/src/effect/handler/definition.ts +121 -0
  210. package/src/effect/handler/envelope.ts +133 -0
  211. package/src/effect/handler/http.ts +133 -0
  212. package/src/effect/handler/openapi.ts +45 -0
  213. package/src/effect/handler/routing.ts +91 -0
  214. package/src/effect/handler/rpc.ts +45 -0
  215. package/src/effect/handler/types.ts +255 -0
  216. package/src/effect/handler.ts +33 -0
  217. package/src/effect/index.ts +22 -0
  218. package/src/effect/module.ts +186 -0
  219. package/src/effect/operation-context.ts +144 -0
  220. package/src/effect/rpcSerialization.ts +20 -0
  221. package/src/effect-client/batch.ts +50 -0
  222. package/src/effect-client/envelope.ts +238 -0
  223. package/src/effect-client/guards.ts +6 -0
  224. package/src/effect-client/index.ts +325 -0
  225. package/src/effect-client/request-context.ts +54 -0
  226. package/src/effect-client/request.ts +51 -0
  227. package/src/effect-client/runtime.ts +122 -0
  228. package/src/effect-client/types.ts +84 -0
  229. package/src/headers.ts +7 -0
  230. package/src/index.ts +3 -0
@@ -0,0 +1,50 @@
1
+ // @effect-diagnostics processEnv:off strictBooleanExpressions:off
2
+ import { createDataBatchTransport } from '../data-platform';
3
+ import { resolveOrigin } from './envelope';
4
+ import type {
5
+ EffectRequestRuntime,
6
+ GeneratedEffectClientConfig,
7
+ } from './types';
8
+
9
+ const resolveRuntimeFetch = (): typeof fetch | undefined =>
10
+ typeof fetch === 'function' ? fetch.bind(globalThis) : undefined;
11
+
12
+ export const configureGeneratedEffectClientRuntime = (
13
+ config: GeneratedEffectClientConfig,
14
+ configureRequest: EffectRequestRuntime['configure'] | undefined,
15
+ ): void => {
16
+ const defaultOrigin = config.defaultOrigin;
17
+
18
+ if (config.requestId && configureRequest) {
19
+ const configurePayload: Record<string, unknown> = {
20
+ requestId: config.requestId,
21
+ requireEnvelope: true,
22
+ identityBinding: {
23
+ enabled: true,
24
+ strict: true,
25
+ },
26
+ operationContract: {
27
+ enabled: true,
28
+ strict: true,
29
+ requireSchemaHash: true,
30
+ requireOperationVersion: true,
31
+ },
32
+ setDomain: () => resolveOrigin(defaultOrigin),
33
+ };
34
+
35
+ const runtimeFetch = resolveRuntimeFetch();
36
+ if (config.batch.enabled !== false && runtimeFetch) {
37
+ configurePayload.request = createDataBatchTransport({
38
+ fetch: runtimeFetch,
39
+ endpoint: config.batch.endpoint,
40
+ flushIntervalMs: config.batch.flushIntervalMs,
41
+ maxBatchSize: config.batch.maxBatchSize,
42
+ maxBatchBytes: config.batch.maxBatchBytes,
43
+ requestTimeoutMs: config.batch.requestTimeoutMs,
44
+ allowedMethods: config.batch.allowedMethods,
45
+ });
46
+ }
47
+
48
+ configureRequest(configurePayload);
49
+ }
50
+ };
@@ -0,0 +1,238 @@
1
+ // @effect-diagnostics processEnv:off strictBooleanExpressions:off
2
+ import {
3
+ createRequestEnvelope,
4
+ DEFAULT_DATA_BATCH_HEADER,
5
+ DEFAULT_DATA_ENVELOPE_HEADER,
6
+ encodeRequestEnvelopeHeader,
7
+ type RequestEnvelope,
8
+ type SelectionPlan,
9
+ type TraceContext,
10
+ } from '../data-platform';
11
+ import { isRecord, stringOrUndefined } from './guards';
12
+ import { normalizeRequest } from './request';
13
+ import { applyRequestContext } from './request-context';
14
+ import type {
15
+ EffectOperationDescriptor,
16
+ EffectRequestContext,
17
+ EffectRequestContextInput,
18
+ GeneratedEffectClientConfig,
19
+ GeneratedEffectEndpoint,
20
+ } from './types';
21
+
22
+ const DATA_REQUEST_MODES = new Set([
23
+ 'cache-first',
24
+ 'stale-while-revalidate',
25
+ 'network-only',
26
+ ]);
27
+ const DATA_MUTATION_MODES = new Set([
28
+ 'optimistic',
29
+ 'pessimistic',
30
+ 'fire-and-forget',
31
+ ]);
32
+
33
+ const isDataRequestMode = (value: unknown) =>
34
+ typeof value === 'string' && DATA_REQUEST_MODES.has(value);
35
+
36
+ const isDataMutationMode = (value: unknown) =>
37
+ typeof value === 'string' && DATA_MUTATION_MODES.has(value);
38
+
39
+ const normalizeOrigin = (value: unknown): string | undefined => {
40
+ if (typeof value !== 'string' || value.length === 0) {
41
+ return undefined;
42
+ }
43
+ try {
44
+ return new URL(value).origin;
45
+ } catch {
46
+ return undefined;
47
+ }
48
+ };
49
+
50
+ const resolveRuntimeFetch = (): typeof fetch | undefined =>
51
+ typeof fetch === 'function' ? fetch.bind(globalThis) : undefined;
52
+
53
+ /**
54
+ * Resolves the current page origin with a configurable fallback. Single
55
+ * implementation shared by the configure() bootstrap and the data envelope
56
+ * (the generated template previously duplicated data-platform's
57
+ * `resolveRuntimeOrigin` with a different fallback).
58
+ */
59
+
60
+ export const resolveOrigin = (defaultOrigin: string): string => {
61
+ if (
62
+ typeof window !== 'undefined' &&
63
+ window.location &&
64
+ typeof window.location.origin === 'string' &&
65
+ window.location.origin
66
+ ) {
67
+ return window.location.origin;
68
+ }
69
+
70
+ const globalLocation = (
71
+ globalThis as { location?: { origin?: unknown } } | undefined
72
+ )?.location;
73
+ if (
74
+ globalLocation &&
75
+ typeof globalLocation.origin === 'string' &&
76
+ globalLocation.origin
77
+ ) {
78
+ return globalLocation.origin;
79
+ }
80
+
81
+ return defaultOrigin;
82
+ };
83
+
84
+ const resolveTargetOrigin = (
85
+ dataPlatform: Record<string, unknown>,
86
+ defaultOrigin: string,
87
+ ): string => {
88
+ const explicitTargetOrigin =
89
+ stringOrUndefined(dataPlatform.targetOrigin) ||
90
+ stringOrUndefined(dataPlatform.endpointOrigin);
91
+ if (explicitTargetOrigin) {
92
+ return explicitTargetOrigin;
93
+ }
94
+ return defaultOrigin;
95
+ };
96
+
97
+ const shouldAttachEnvelopeHeader = (
98
+ dataPlatform: Record<string, unknown>,
99
+ defaultOrigin: string,
100
+ ): boolean => {
101
+ if (dataPlatform.allowCrossOriginEnvelope === true) {
102
+ return true;
103
+ }
104
+ const currentOrigin = normalizeOrigin(resolveOrigin(defaultOrigin));
105
+ const targetOrigin = normalizeOrigin(
106
+ resolveTargetOrigin(dataPlatform, defaultOrigin),
107
+ );
108
+ if (!currentOrigin || !targetOrigin) {
109
+ return true;
110
+ }
111
+ return currentOrigin === targetOrigin;
112
+ };
113
+
114
+ const toEnvelopeInput = (
115
+ normalizedRequest: Record<string, unknown>,
116
+ ): Record<string, unknown> => {
117
+ const payload: Record<string, unknown> = {};
118
+ if (isRecord(normalizedRequest.params)) {
119
+ payload.path = normalizedRequest.params;
120
+ }
121
+ if (isRecord(normalizedRequest.query)) {
122
+ payload.query = normalizedRequest.query;
123
+ }
124
+ if ('data' in normalizedRequest && normalizedRequest.data !== undefined) {
125
+ payload.data = normalizedRequest.data;
126
+ }
127
+ if ('body' in normalizedRequest && normalizedRequest.body !== undefined) {
128
+ payload.body = normalizedRequest.body;
129
+ }
130
+ if (
131
+ typeof FormData !== 'undefined' &&
132
+ normalizedRequest.formData instanceof FormData
133
+ ) {
134
+ payload.formData = Array.from(normalizedRequest.formData.entries()).map(
135
+ ([key, value]) => [key, String(value)],
136
+ );
137
+ }
138
+ if (
139
+ typeof URLSearchParams !== 'undefined' &&
140
+ normalizedRequest.formUrlencoded instanceof URLSearchParams
141
+ ) {
142
+ payload.formUrlencoded = normalizedRequest.formUrlencoded.toString();
143
+ }
144
+ return payload;
145
+ };
146
+
147
+ export const prepareEffectRequest = (options: {
148
+ endpoint: GeneratedEffectEndpoint;
149
+ operation: EffectOperationDescriptor;
150
+ request: unknown;
151
+ config: GeneratedEffectClientConfig;
152
+ createEffectRequestContext: (
153
+ requestContext: EffectRequestContextInput,
154
+ ) => EffectRequestContext;
155
+ }): Record<string, unknown> => {
156
+ const { endpoint, operation, request, config, createEffectRequestContext } =
157
+ options;
158
+ const defaultOrigin = config.defaultOrigin;
159
+ const normalizedRequest = applyRequestContext(
160
+ normalizeRequest(endpoint.method, request),
161
+ request,
162
+ createEffectRequestContext,
163
+ );
164
+ const dataPlatform =
165
+ isRecord(request) && isRecord(request.dataPlatform)
166
+ ? request.dataPlatform
167
+ : {};
168
+ const strictEnvelope =
169
+ dataPlatform.requireEnvelope === true || dataPlatform.strict === true;
170
+
171
+ if (
172
+ !strictEnvelope &&
173
+ !shouldAttachEnvelopeHeader(dataPlatform, defaultOrigin)
174
+ ) {
175
+ return normalizedRequest;
176
+ }
177
+
178
+ try {
179
+ const namespace =
180
+ stringOrUndefined(dataPlatform.appNamespace) || config.appNamespace;
181
+ const origin =
182
+ stringOrUndefined(dataPlatform.origin) || resolveOrigin(defaultOrigin);
183
+ const envelope: RequestEnvelope = createRequestEnvelope({
184
+ operation: {
185
+ ...operation,
186
+ appNamespace: namespace,
187
+ },
188
+ scope: {
189
+ appNamespace: namespace,
190
+ origin,
191
+ tenantId: stringOrUndefined(dataPlatform.tenantId),
192
+ userId: stringOrUndefined(dataPlatform.userId),
193
+ sessionId: stringOrUndefined(dataPlatform.sessionId),
194
+ },
195
+ requestInput: {
196
+ method: endpoint.method,
197
+ routePath: endpoint.routePath,
198
+ payload: toEnvelopeInput(normalizedRequest),
199
+ },
200
+ requestMode: isDataRequestMode(dataPlatform.requestMode)
201
+ ? (dataPlatform.requestMode as RequestEnvelope['requestMode'])
202
+ : undefined,
203
+ mutationMode: isDataMutationMode(dataPlatform.mutationMode)
204
+ ? (dataPlatform.mutationMode as RequestEnvelope['mutationMode'])
205
+ : undefined,
206
+ selectionPlan: isRecord(dataPlatform.selectionPlan)
207
+ ? (dataPlatform.selectionPlan as SelectionPlan)
208
+ : undefined,
209
+ traceContext: isRecord(dataPlatform.traceContext)
210
+ ? (dataPlatform.traceContext as unknown as TraceContext)
211
+ : undefined,
212
+ requireTraceContext: dataPlatform.requireTraceContext === true,
213
+ });
214
+
215
+ const headerName =
216
+ stringOrUndefined(dataPlatform.envelopeHeader) ||
217
+ DEFAULT_DATA_ENVELOPE_HEADER;
218
+ const headers = isRecord(normalizedRequest.headers)
219
+ ? { ...normalizedRequest.headers }
220
+ : {};
221
+
222
+ if (dataPlatform.batch === false) {
223
+ headers[DEFAULT_DATA_BATCH_HEADER] = 'off';
224
+ }
225
+
226
+ headers[headerName] = encodeRequestEnvelopeHeader(envelope);
227
+
228
+ return {
229
+ ...normalizedRequest,
230
+ headers,
231
+ };
232
+ } catch (error) {
233
+ if (strictEnvelope) {
234
+ throw error;
235
+ }
236
+ return normalizedRequest;
237
+ }
238
+ };
@@ -0,0 +1,6 @@
1
+ // @effect-diagnostics processEnv:off strictBooleanExpressions:off
2
+ export const isRecord = (value: unknown): value is Record<string, unknown> =>
3
+ typeof value === 'object' && value !== null;
4
+
5
+ export const stringOrUndefined = (value: unknown): string | undefined =>
6
+ typeof value === 'string' && value.length > 0 ? value : undefined;
@@ -0,0 +1,325 @@
1
+ // @effect-diagnostics anyUnknownInErrorContext:off asyncFunction:off newPromise:off strictEffectProvide:off
2
+ import {
3
+ createRequestContextHeaders,
4
+ type RequestContextInput,
5
+ } from '@modern-js/create-request';
6
+ import * as Data from 'effect/Data';
7
+ import * as Effect from 'effect/Effect';
8
+ import * as Exit from 'effect/Exit';
9
+ import * as Layer from 'effect/Layer';
10
+ import * as ManagedRuntime from 'effect/ManagedRuntime';
11
+ import * as Scope from 'effect/Scope';
12
+ import {
13
+ FetchHttpClient,
14
+ HttpClient,
15
+ HttpClientRequest,
16
+ } from 'effect/unstable/http';
17
+ import {
18
+ HttpApi,
19
+ HttpApiClient,
20
+ HttpApiEndpoint,
21
+ HttpApiGroup,
22
+ HttpApiSchema,
23
+ } from 'effect/unstable/httpapi';
24
+ import {
25
+ Rpc,
26
+ RpcClient,
27
+ RpcGroup,
28
+ RpcSchema,
29
+ RpcSerialization,
30
+ } from 'effect/unstable/rpc';
31
+
32
+ import { getRpcSerializationLayer } from '../effect/rpcSerialization';
33
+
34
+ export * as Effect from 'effect/Effect';
35
+ export * as Layer from 'effect/Layer';
36
+ export * as Schema from 'effect/Schema';
37
+ export * as HttpClientError from 'effect/unstable/http/HttpClientError';
38
+ export {
39
+ HttpApi,
40
+ HttpApiClient,
41
+ HttpApiEndpoint,
42
+ HttpApiGroup,
43
+ HttpApiSchema,
44
+ Rpc,
45
+ RpcClient,
46
+ RpcGroup,
47
+ RpcSchema,
48
+ RpcSerialization,
49
+ };
50
+
51
+ export type EffectHttpApiClientOptions = {
52
+ baseUrl?: URL | string;
53
+ requestContext?: RequestContextInput;
54
+ transformClient?: (client: HttpClient.HttpClient) => HttpClient.HttpClient;
55
+ transformResponse?:
56
+ | ((
57
+ effect: Effect.Effect<unknown, unknown, unknown>,
58
+ ) => Effect.Effect<unknown, unknown, unknown>)
59
+ | undefined;
60
+ };
61
+
62
+ type Nullish = null | undefined;
63
+ type NonNullish<T> = Exclude<T, Nullish>;
64
+ type PreserveNullish<T, Next> =
65
+ Extract<T, Nullish> extends never ? Next : Next | Extract<T, Nullish>;
66
+
67
+ type EffectViewObjectSelection<T> =
68
+ NonNullish<T> extends Record<string, unknown>
69
+ ? EffectViewSelection<NonNullish<T>>
70
+ : never;
71
+
72
+ type EffectViewSelectionValue<T> =
73
+ NonNullish<T> extends readonly (infer Item)[]
74
+ ? true | EffectViewObjectSelection<Item>
75
+ : NonNullish<T> extends Record<string, unknown>
76
+ ? true | EffectViewSelection<NonNullish<T>>
77
+ : true;
78
+
79
+ type EffectMaskedValue<T, Selection> = Selection extends true
80
+ ? T
81
+ : NonNullish<T> extends readonly (infer Item)[]
82
+ ? PreserveNullish<T, Array<EffectMaskedValue<Item, Selection>>>
83
+ : NonNullish<T> extends Record<string, unknown>
84
+ ? PreserveNullish<T, EffectViewData<NonNullish<T>, Selection>>
85
+ : T;
86
+
87
+ export type EffectViewSelection<T> = {
88
+ [K in keyof T]?: EffectViewSelectionValue<T[K]>;
89
+ };
90
+
91
+ export type EffectViewData<T, Selection> = Selection extends true
92
+ ? T
93
+ : Selection extends Record<string, unknown>
94
+ ? {
95
+ [K in keyof Selection & keyof T]: EffectMaskedValue<T[K], Selection[K]>;
96
+ }
97
+ : T;
98
+
99
+ export type EffectRpcSerialization =
100
+ | 'json'
101
+ | 'ndjson'
102
+ | 'jsonRpc'
103
+ | 'ndJsonRpc'
104
+ | 'msgPack';
105
+
106
+ export class EffectRpcClientError extends Data.TaggedError(
107
+ 'EffectRpcClientError',
108
+ )<{
109
+ readonly cause: unknown;
110
+ }> {}
111
+
112
+ type EffectRpcMiddlewareLayerOption<Rpcs extends Rpc.Any> = [
113
+ Rpc.MiddlewareClient<Rpcs>,
114
+ ] extends [never]
115
+ ? {
116
+ middlewareLayer?: Layer.Layer<Rpc.MiddlewareClient<Rpcs>, never, never>;
117
+ }
118
+ : {
119
+ middlewareLayer: Layer.Layer<Rpc.MiddlewareClient<Rpcs>, unknown, never>;
120
+ };
121
+
122
+ export type EffectRpcClientOptions<
123
+ Rpcs extends Rpc.Any,
124
+ Flatten extends boolean = false,
125
+ > = EffectRpcMiddlewareLayerOption<Rpcs> & {
126
+ url: string;
127
+ flatten?: Flatten;
128
+ serialization?: EffectRpcSerialization;
129
+ };
130
+
131
+ export type EffectRpcClientHandle<
132
+ Rpcs extends Rpc.Any,
133
+ Flatten extends boolean = false,
134
+ > = (Flatten extends true
135
+ ? RpcClient.RpcClient.Flat<Rpcs, unknown>
136
+ : RpcClient.RpcClient<Rpcs, unknown>) & {
137
+ dispose: () => Promise<void>;
138
+ };
139
+
140
+ function isRecord(value: unknown): value is Record<string, unknown> {
141
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
142
+ }
143
+
144
+ function applySelection(value: unknown, selection: unknown): unknown {
145
+ if (selection === true || selection === undefined) {
146
+ return value;
147
+ }
148
+
149
+ if (Array.isArray(value)) {
150
+ return value.map(item => applySelection(item, selection));
151
+ }
152
+
153
+ if (!isRecord(value) || !isRecord(selection)) {
154
+ return value;
155
+ }
156
+
157
+ const masked: Record<string, unknown> = {};
158
+ for (const key of Object.keys(selection)) {
159
+ const selected = selection[key];
160
+ if (
161
+ selected === undefined ||
162
+ !(key in value) ||
163
+ (selected !== true && !isRecord(selected))
164
+ ) {
165
+ continue;
166
+ }
167
+ masked[key] = applySelection(value[key], selected);
168
+ }
169
+ return masked;
170
+ }
171
+
172
+ export function view<T>() {
173
+ return <const Selection extends EffectViewSelection<T>>(
174
+ selection: Selection,
175
+ ) => selection;
176
+ }
177
+
178
+ export function mask<T, const Selection extends EffectViewSelection<T>>(
179
+ value: T,
180
+ selection: Selection,
181
+ ): EffectViewData<T, Selection> {
182
+ return applySelection(value, selection) as EffectViewData<T, Selection>;
183
+ }
184
+
185
+ export function runEffectView<
186
+ T,
187
+ const Selection extends EffectViewSelection<T>,
188
+ >(
189
+ request: PromiseLike<T>,
190
+ selection: Selection,
191
+ ): Promise<EffectViewData<T, Selection>> {
192
+ return new Promise((resolve, reject) => {
193
+ request.then(
194
+ value => resolve(mask(value, selection)),
195
+ reason => reject(reason),
196
+ );
197
+ });
198
+ }
199
+
200
+ export function makeEffectHttpApiClient<
201
+ ApiId extends string,
202
+ Groups extends HttpApiGroup.Constraint,
203
+ >(
204
+ api: HttpApi.HttpApi<ApiId, Groups>,
205
+ options?: EffectHttpApiClientOptions,
206
+ ): Effect.Effect<
207
+ HttpApiClient.Client<Groups, never, never>,
208
+ never,
209
+ HttpApiGroup.MiddlewareClient<Groups>
210
+ > {
211
+ const requestContextHeaders = createRequestContextHeaders(
212
+ options?.requestContext,
213
+ );
214
+ const transformClient = (client: HttpClient.HttpClient) => {
215
+ const contextClient =
216
+ Object.keys(requestContextHeaders).length === 0
217
+ ? client
218
+ : client.pipe(
219
+ HttpClient.mapRequest(request => {
220
+ let nextRequest = request;
221
+ for (const [header, value] of Object.entries(
222
+ requestContextHeaders,
223
+ )) {
224
+ if (nextRequest.headers[header.toLowerCase()] === undefined) {
225
+ nextRequest = HttpClientRequest.setHeader(
226
+ nextRequest,
227
+ header,
228
+ value,
229
+ );
230
+ }
231
+ }
232
+ return nextRequest;
233
+ }),
234
+ );
235
+
236
+ return typeof options?.transformClient === 'function'
237
+ ? options.transformClient(contextClient)
238
+ : contextClient;
239
+ };
240
+
241
+ return HttpApiClient.make(api, {
242
+ baseUrl: options?.baseUrl,
243
+ transformClient,
244
+ transformResponse: options?.transformResponse,
245
+ }).pipe(Effect.provide(FetchHttpClient.layer));
246
+ }
247
+
248
+ export function makeEffectRpcClient<
249
+ Rpcs extends Rpc.Any,
250
+ const Flatten extends boolean = false,
251
+ >(
252
+ group: RpcGroup.RpcGroup<Rpcs>,
253
+ options: EffectRpcClientOptions<Rpcs, Flatten>,
254
+ ) {
255
+ const protocolLayer = Layer.provide(
256
+ RpcClient.layerProtocolHttp({
257
+ url: options.url,
258
+ }),
259
+ Layer.mergeAll(
260
+ getRpcSerializationLayer(options.serialization),
261
+ FetchHttpClient.layer,
262
+ ),
263
+ );
264
+ const middlewareLayer = options.middlewareLayer ?? Layer.empty;
265
+ const runtimeLayer = Layer.mergeAll(protocolLayer, middlewareLayer);
266
+
267
+ return Effect.tryPromise({
268
+ try: async () => {
269
+ const runtime = ManagedRuntime.make(runtimeLayer);
270
+ let scope: Scope.Closeable | undefined;
271
+ try {
272
+ const clientScope = await runtime.runPromise(Scope.make());
273
+ scope = clientScope;
274
+ const client = await runtime.runPromise(
275
+ RpcClient.make(group, {
276
+ flatten: options.flatten,
277
+ }).pipe(Effect.provideService(Scope.Scope, clientScope)),
278
+ );
279
+ let disposePromise: Promise<void> | undefined;
280
+ const clientWithDispose: EffectRpcClientHandle<Rpcs, Flatten> =
281
+ Object.assign(client, {
282
+ dispose: () => {
283
+ disposePromise ??= (async () => {
284
+ let scopeCloseError: unknown;
285
+ try {
286
+ await runtime.runPromise(Scope.close(clientScope, Exit.void));
287
+ } catch (error) {
288
+ scopeCloseError = error;
289
+ }
290
+ try {
291
+ await runtime.dispose();
292
+ } catch (error) {
293
+ if (scopeCloseError === undefined) {
294
+ throw error;
295
+ }
296
+ }
297
+ if (scopeCloseError !== undefined) {
298
+ throw scopeCloseError;
299
+ }
300
+ })();
301
+ return disposePromise;
302
+ },
303
+ });
304
+ return clientWithDispose;
305
+ } catch (error) {
306
+ if (scope !== undefined) {
307
+ try {
308
+ await runtime.runPromise(Scope.close(scope, Exit.void));
309
+ } catch {
310
+ // ignore scope close errors and preserve the original construction error
311
+ }
312
+ }
313
+ try {
314
+ await runtime.dispose();
315
+ } catch {
316
+ // ignore disposal errors and preserve the original construction error
317
+ }
318
+ throw error;
319
+ }
320
+ },
321
+ catch: (error: unknown) => new EffectRpcClientError({ cause: error }),
322
+ });
323
+ }
324
+
325
+ export const runEffectRequest = Effect.runPromise;
@@ -0,0 +1,54 @@
1
+ // @effect-diagnostics processEnv:off strictBooleanExpressions:off
2
+ import { isRecord } from './guards';
3
+ import type {
4
+ EffectRequestContext,
5
+ EffectRequestContextInput,
6
+ EffectRequestRuntime,
7
+ } from './types';
8
+
9
+ export const createEffectRequestContext = (
10
+ createRequestContextHeaders: EffectRequestRuntime['createRequestContextHeaders'],
11
+ requestContext: EffectRequestContextInput,
12
+ ): EffectRequestContext => {
13
+ if (!isRecord(requestContext)) {
14
+ return {} as EffectRequestContext;
15
+ }
16
+
17
+ const headers = createRequestContextHeaders
18
+ ? createRequestContextHeaders(requestContext)
19
+ : {};
20
+
21
+ return {
22
+ ...requestContext,
23
+ headers,
24
+ };
25
+ };
26
+
27
+ export const applyRequestContext = (
28
+ normalizedRequest: Record<string, unknown>,
29
+ request: unknown,
30
+ createRequestContext: (
31
+ requestContext: EffectRequestContextInput,
32
+ ) => EffectRequestContext,
33
+ ): Record<string, unknown> => {
34
+ if (!isRecord(request) || !isRecord(request.requestContext)) {
35
+ return normalizedRequest;
36
+ }
37
+
38
+ const requestContext = createRequestContext(request.requestContext);
39
+ const requestHeaders = isRecord(requestContext.headers)
40
+ ? requestContext.headers
41
+ : {};
42
+
43
+ if (Object.keys(requestHeaders).length === 0) {
44
+ return normalizedRequest;
45
+ }
46
+
47
+ return {
48
+ ...normalizedRequest,
49
+ headers: {
50
+ ...(isRecord(normalizedRequest.headers) ? normalizedRequest.headers : {}),
51
+ ...requestHeaders,
52
+ },
53
+ };
54
+ };