@gaganref/confect-server 10.3.1-fork.36 → 10.3.1-fork.39
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/dist/ConvexFunctionDefinitions.d.ts.map +1 -1
- package/dist/ConvexFunctionDefinitions.js +2 -2
- package/dist/ConvexFunctionDefinitions.js.map +1 -1
- package/dist/GroupRegistry.d.ts +1 -0
- package/dist/GroupRegistry.d.ts.map +1 -1
- package/dist/GroupRegistry.js +10 -3
- package/dist/GroupRegistry.js.map +1 -1
- package/dist/QueryStreamReadBudget.js +1 -1
- package/dist/RegisteredConvexFunction.d.ts +7 -4
- package/dist/RegisteredConvexFunction.d.ts.map +1 -1
- package/dist/RegisteredConvexFunction.js +27 -14
- package/dist/RegisteredConvexFunction.js.map +1 -1
- package/dist/RegisteredFunction.d.ts +25 -21
- package/dist/RegisteredFunction.d.ts.map +1 -1
- package/dist/RegisteredFunction.js +68 -30
- package/dist/RegisteredFunction.js.map +1 -1
- package/dist/RegisteredNodeFunction.d.ts +1 -1
- package/dist/RegisteredNodeFunction.d.ts.map +1 -1
- package/dist/RegisteredNodeFunction.js +7 -5
- package/dist/RegisteredNodeFunction.js.map +1 -1
- package/dist/index.js +9 -9
- package/dist/internal/observability.d.ts +5 -0
- package/dist/internal/observability.d.ts.map +1 -0
- package/dist/internal/observability.js +21 -0
- package/dist/internal/observability.js.map +1 -0
- package/package.json +2 -2
- package/src/ConvexFunctionDefinitions.ts +2 -1
- package/src/GroupRegistry.ts +7 -3
- package/src/RegisteredConvexFunction.ts +16 -0
- package/src/RegisteredFunction.ts +118 -41
- package/src/RegisteredNodeFunction.ts +7 -1
- package/src/internal/observability.ts +27 -0
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
import {
|
|
12
12
|
type DefaultFunctionArgs,
|
|
13
13
|
type FunctionVisibility,
|
|
14
|
+
type FunctionMetadata,
|
|
14
15
|
type GenericActionCtx,
|
|
15
16
|
type GenericDataModel,
|
|
16
17
|
type StorageReader as ConvexStorageReader,
|
|
@@ -21,8 +22,12 @@ import {
|
|
|
21
22
|
import type { Value } from "convex/values";
|
|
22
23
|
import { ConvexError } from "convex/values";
|
|
23
24
|
import { pipe } from "effect/Function";
|
|
25
|
+
import * as Clock from "effect/Clock";
|
|
26
|
+
import * as Cause from "effect/Cause";
|
|
24
27
|
import * as Console from "effect/Console";
|
|
28
|
+
import * as Context from "effect/Context";
|
|
25
29
|
import * as Effect from "effect/Effect";
|
|
30
|
+
import * as Exit from "effect/Exit";
|
|
26
31
|
import * as Result from "effect/Result";
|
|
27
32
|
import * as Layer from "effect/Layer";
|
|
28
33
|
import * as Schema from "effect/Schema";
|
|
@@ -43,9 +48,16 @@ import * as Storage from "./Storage.js";
|
|
|
43
48
|
import * as StorageActionWriter from "./StorageActionWriter.js";
|
|
44
49
|
import type * as StorageReader from "./StorageReader.js";
|
|
45
50
|
import * as StorageWriter from "./StorageWriter.js";
|
|
51
|
+
import * as Tracer from "effect/Tracer";
|
|
52
|
+
import * as ObservabilityRuntime from "./internal/observability.js";
|
|
46
53
|
import * as VectorSearch from "./VectorSearch.js";
|
|
47
54
|
import type * as Handler from "./Handler.js";
|
|
48
55
|
|
|
56
|
+
export const Observability = Context.Reference<Layer.Layer<never> | undefined>(
|
|
57
|
+
"@confect/server/Observability",
|
|
58
|
+
{ defaultValue: () => undefined },
|
|
59
|
+
);
|
|
60
|
+
|
|
49
61
|
export type Any =
|
|
50
62
|
| RegisteredQuery<FunctionVisibility, DefaultFunctionArgs, any>
|
|
51
63
|
| RegisteredMutation<FunctionVisibility, DefaultFunctionArgs, any>
|
|
@@ -185,67 +197,125 @@ export const combineErrorSchemas = (
|
|
|
185
197
|
};
|
|
186
198
|
|
|
187
199
|
/**
|
|
188
|
-
* Run the `Effect` as a `Promise`.
|
|
189
|
-
*
|
|
200
|
+
* Run the `Effect` as a `Promise`. Inspect the complete `Exit` before encoding
|
|
201
|
+
* a declared error so a typed failure cannot hide a defect.
|
|
190
202
|
*
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
* - Without a schema: every failure is converted to a defect via `Effect.orDie`,
|
|
197
|
-
* so nothing—not even a `ConvexError` the handler placed in its error
|
|
198
|
-
* channel—reaches the client as a `ConvexError`. The fiber dies and
|
|
199
|
-
* `runPromise` rejects with a generic failure.
|
|
203
|
+
* Any `Die` reason takes the defect path. Without a `Die`, the first `Fail` in
|
|
204
|
+
* cause order is schema-encoded and thrown as a `ConvexError` outside the
|
|
205
|
+
* Effect runtime so Convex surfaces its data to the client. Other failures and
|
|
206
|
+
* interruptions alongside that `Fail` are ignored. Without an error schema, the
|
|
207
|
+
* selected failure is treated as a defect.
|
|
200
208
|
*
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
209
|
+
* A lone `ConvexError` defect or a selected undeclared `ConvexError` failure is
|
|
210
|
+
* rethrown bare so it keeps Convex's meaning. Pagination and Workpool rely on
|
|
211
|
+
* its data for their control signals. Other causes with a `Die`, interrupt-only
|
|
212
|
+
* or empty causes, and error-encoding failures reject with a plain `Error`
|
|
213
|
+
* whose message renders the complete cause and whose `cause` holds it for
|
|
214
|
+
* server diagnostics.
|
|
206
215
|
*
|
|
207
216
|
* A `scheduler` in `runOptions` must be passed here as a run option rather than
|
|
208
217
|
* provided via `Effect.provideService` inside `effect`: the run option lands in
|
|
209
218
|
* the fiber's root context, while a service provided within `effect` pops
|
|
210
|
-
* before the
|
|
211
|
-
*
|
|
212
|
-
*
|
|
219
|
+
* before the exit/encoding wrappers this function adds. The fiber's op counter
|
|
220
|
+
* survives context pops, so a cooperative yield can fire inside those wrappers.
|
|
221
|
+
* Only a root-context scheduler covers them.
|
|
213
222
|
*/
|
|
214
223
|
export const runHandlerPromise =
|
|
215
224
|
(
|
|
216
225
|
errorSchema: Schema.Codec<any, any> | undefined,
|
|
217
226
|
runOptions?: {
|
|
218
227
|
readonly scheduler?: EffectScheduler.Scheduler | undefined;
|
|
228
|
+
readonly clock?: Clock.Clock | undefined;
|
|
229
|
+
readonly invocation?: Effect.Effect<FunctionMetadata> | undefined;
|
|
230
|
+
readonly observability?: Layer.Layer<never> | undefined;
|
|
219
231
|
},
|
|
220
232
|
) =>
|
|
221
233
|
<A, E>(effect: Effect.Effect<A, E>): Promise<A> => {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
const
|
|
225
|
-
(
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
234
|
+
const { observability, invocation, clock } = runOptions ?? {};
|
|
235
|
+
let encodedError: Exit.Exit<any, any> | undefined;
|
|
236
|
+
const observed = Effect.tapCause(effect, (cause) =>
|
|
237
|
+
Effect.gen(function* () {
|
|
238
|
+
const reason =
|
|
239
|
+
cause.reasons.find(Cause.isDieReason) ??
|
|
240
|
+
cause.reasons.find(Cause.isFailReason);
|
|
241
|
+
if (reason?._tag === "Fail" && errorSchema !== undefined) {
|
|
242
|
+
encodedError = yield* Effect.exit(
|
|
243
|
+
Schema.encodeEffect(errorSchema)(reason.error),
|
|
244
|
+
);
|
|
245
|
+
if (Exit.isFailure(encodedError))
|
|
246
|
+
return yield* Effect.orDie(encodedError);
|
|
247
|
+
}
|
|
248
|
+
}),
|
|
229
249
|
);
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
Effect.
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
250
|
+
const withConvexError: Effect.Effect<Result.Result<A, ConvexError<any>>> =
|
|
251
|
+
Effect.gen(function* () {
|
|
252
|
+
const exit = yield* Effect.exit(
|
|
253
|
+
observability === undefined || invocation === undefined
|
|
254
|
+
? effect
|
|
255
|
+
: Effect.flatMap(invocation, ({ name, type, componentPath }) =>
|
|
256
|
+
Effect.useSpan(
|
|
257
|
+
name,
|
|
258
|
+
{
|
|
259
|
+
root: true,
|
|
260
|
+
attributes: { kind: type, componentPath },
|
|
261
|
+
},
|
|
262
|
+
(span) =>
|
|
263
|
+
Effect.provideService(observed, Tracer.ParentSpan, span),
|
|
241
264
|
),
|
|
242
265
|
),
|
|
243
|
-
|
|
266
|
+
);
|
|
267
|
+
if (Exit.isSuccess(exit)) {
|
|
268
|
+
return Result.succeed(exit.value);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const cause =
|
|
272
|
+
encodedError !== undefined && Exit.isFailure(encodedError)
|
|
273
|
+
? encodedError.cause
|
|
274
|
+
: exit.cause;
|
|
275
|
+
const reason =
|
|
276
|
+
cause.reasons.find(Cause.isDieReason) ??
|
|
277
|
+
cause.reasons.find(Cause.isFailReason);
|
|
278
|
+
if (
|
|
279
|
+
encodedError === undefined &&
|
|
280
|
+
reason?._tag === "Die" &&
|
|
281
|
+
cause.reasons.length === 1 &&
|
|
282
|
+
reason.defect instanceof ConvexError
|
|
283
|
+
) {
|
|
284
|
+
return Result.fail(reason.defect);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (reason?._tag === "Fail") {
|
|
288
|
+
if (errorSchema === undefined) {
|
|
289
|
+
if (reason.error instanceof ConvexError) {
|
|
290
|
+
return Result.fail(reason.error);
|
|
291
|
+
}
|
|
292
|
+
return yield* Effect.die(reason.error);
|
|
293
|
+
}
|
|
294
|
+
const encoded =
|
|
295
|
+
encodedError ??
|
|
296
|
+
(yield* Effect.exit(
|
|
297
|
+
Schema.encodeEffect(errorSchema)(reason.error),
|
|
298
|
+
));
|
|
299
|
+
if (Exit.isSuccess(encoded)) {
|
|
300
|
+
return Result.fail(new ConvexError(encoded.value));
|
|
301
|
+
}
|
|
302
|
+
return yield* Effect.die(
|
|
303
|
+
new Error(Cause.pretty(encoded.cause), { cause: encoded.cause }),
|
|
244
304
|
);
|
|
305
|
+
}
|
|
306
|
+
return yield* Effect.die(new Error(Cause.pretty(cause), { cause }));
|
|
307
|
+
});
|
|
245
308
|
return Effect.runPromise(
|
|
246
|
-
|
|
247
|
-
Effect.provide(
|
|
309
|
+
withConvexError.pipe(
|
|
310
|
+
Effect.provide(
|
|
311
|
+
observability === undefined
|
|
312
|
+
? ConvexLogger.layer
|
|
313
|
+
: ObservabilityRuntime.memoized(observability),
|
|
314
|
+
),
|
|
248
315
|
Effect.provideService(Console.Console, globalThis.console),
|
|
316
|
+
clock === undefined
|
|
317
|
+
? (self) => self
|
|
318
|
+
: Effect.provideService(Clock.Clock, clock),
|
|
249
319
|
),
|
|
250
320
|
runOptions,
|
|
251
321
|
).then(
|
|
@@ -276,6 +346,7 @@ export const actionFunctionBase = <
|
|
|
276
346
|
resolvedMiddlewares = [],
|
|
277
347
|
createLayer,
|
|
278
348
|
scope = IdScope.app,
|
|
349
|
+
observability,
|
|
279
350
|
}: {
|
|
280
351
|
name: string;
|
|
281
352
|
functionVisibility: FunctionVisibility;
|
|
@@ -288,6 +359,7 @@ export const actionFunctionBase = <
|
|
|
288
359
|
ctx: GenericActionCtx<DataModel.ToConvex<DataModel.FromSchema<Schema>>>,
|
|
289
360
|
) => Layer.Layer<R>;
|
|
290
361
|
scope?: IdScope.IdScope;
|
|
362
|
+
observability?: Layer.Layer<never> | undefined;
|
|
291
363
|
}) => ({
|
|
292
364
|
args: compileArgsSchema(args, scope),
|
|
293
365
|
returns: compileReturnsSchema(returns, scope),
|
|
@@ -324,7 +396,12 @@ export const actionFunctionBase = <
|
|
|
324
396
|
Schema.encodeEffect(returns),
|
|
325
397
|
Effect.catchTag("SchemaError", Effect.die),
|
|
326
398
|
);
|
|
327
|
-
}).pipe(
|
|
399
|
+
}).pipe(
|
|
400
|
+
runHandlerPromise(combineErrorSchemas(error, resolvedMiddlewares), {
|
|
401
|
+
invocation: Effect.promise(() => ctx.meta.getFunctionMetadata()),
|
|
402
|
+
observability,
|
|
403
|
+
}),
|
|
404
|
+
),
|
|
328
405
|
});
|
|
329
406
|
|
|
330
407
|
export type ActionServices<
|
|
@@ -23,7 +23,7 @@ export const buildForGroup = <Group extends GroupSpec.AnyWithProps>(
|
|
|
23
23
|
databaseSchema: DatabaseSchema.AnyWithProps,
|
|
24
24
|
groupLayer: Layer.Layer<GroupImpl.GroupImpl<"Finalized">>,
|
|
25
25
|
): RegisteredFunctions.RegisteredFunctionsForGroupSpec<Group> => {
|
|
26
|
-
const { functionRegistryItems, middlewareRegistryItems } =
|
|
26
|
+
const { functionRegistryItems, middlewareRegistryItems, observability } =
|
|
27
27
|
GroupRegistry.make(groupLayer);
|
|
28
28
|
return mapLeaves<FunctionRegistryItem.AnyWithProps, RegisteredFunction.Any>(
|
|
29
29
|
functionRegistryItems as {
|
|
@@ -38,6 +38,7 @@ export const buildForGroup = <Group extends GroupSpec.AnyWithProps>(
|
|
|
38
38
|
databaseSchema,
|
|
39
39
|
confect,
|
|
40
40
|
ResolvedMiddleware.resolve(confect, middlewareRegistryItems),
|
|
41
|
+
observability,
|
|
41
42
|
),
|
|
42
43
|
),
|
|
43
44
|
Match.exhaustive,
|
|
@@ -49,6 +50,7 @@ export const make = (
|
|
|
49
50
|
databaseSchema: DatabaseSchema.AnyWithProps,
|
|
50
51
|
item: FunctionRegistryItem.ConfectFunctionRegistryItem,
|
|
51
52
|
resolvedMiddlewares: ReadonlyArray<ResolvedMiddleware.ResolvedMiddleware> = [],
|
|
53
|
+
observability?: Layer.Layer<never>,
|
|
52
54
|
): RegisteredFunction.Any => {
|
|
53
55
|
const genericFunction = Match.value(item.functionVisibility).pipe(
|
|
54
56
|
Match.when("public", () => actionGeneric),
|
|
@@ -59,6 +61,7 @@ export const make = (
|
|
|
59
61
|
return genericFunction(
|
|
60
62
|
nodeActionFunction(databaseSchema, {
|
|
61
63
|
name: item.name,
|
|
64
|
+
observability,
|
|
62
65
|
functionVisibility: item.functionVisibility,
|
|
63
66
|
args: item.args,
|
|
64
67
|
returns: item.returns,
|
|
@@ -86,6 +89,7 @@ const nodeActionFunction = <
|
|
|
86
89
|
error,
|
|
87
90
|
handler,
|
|
88
91
|
resolvedMiddlewares,
|
|
92
|
+
observability,
|
|
89
93
|
}: {
|
|
90
94
|
name: string;
|
|
91
95
|
functionVisibility: FunctionVisibility;
|
|
@@ -101,6 +105,7 @@ const nodeActionFunction = <
|
|
|
101
105
|
| NodeServices.NodeServices
|
|
102
106
|
>;
|
|
103
107
|
resolvedMiddlewares: ReadonlyArray<ResolvedMiddleware.ResolvedMiddleware>;
|
|
108
|
+
observability?: Layer.Layer<never> | undefined;
|
|
104
109
|
},
|
|
105
110
|
) =>
|
|
106
111
|
RegisteredFunction.actionFunctionBase({
|
|
@@ -112,6 +117,7 @@ const nodeActionFunction = <
|
|
|
112
117
|
error,
|
|
113
118
|
handler,
|
|
114
119
|
resolvedMiddlewares,
|
|
120
|
+
observability,
|
|
115
121
|
createLayer: (ctx) =>
|
|
116
122
|
Layer.mergeAll(
|
|
117
123
|
RegisteredFunction.actionLayer(databaseSchema, ctx),
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as Duration from "effect/Duration";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as Exit from "effect/Exit";
|
|
4
|
+
import * as Layer from "effect/Layer";
|
|
5
|
+
import * as Scope from "effect/Scope";
|
|
6
|
+
import * as ConvexConfigProvider from "../ConvexConfigProvider.js";
|
|
7
|
+
|
|
8
|
+
const memoMap = Layer.makeMemoMapUnsafe();
|
|
9
|
+
export const scope = Scope.makeUnsafe();
|
|
10
|
+
const layers = new WeakMap<Layer.Layer<never>, Layer.Layer<never>>();
|
|
11
|
+
|
|
12
|
+
export const memoized = (layer: Layer.Layer<never>): Layer.Layer<never> => {
|
|
13
|
+
const cached = layers.get(layer);
|
|
14
|
+
if (cached !== undefined) return cached;
|
|
15
|
+
const built = Layer.buildWithMemoMap(layer, memoMap, scope).pipe(
|
|
16
|
+
Effect.provide(ConvexConfigProvider.layer),
|
|
17
|
+
// A failed first build is retried on the next invocation instead of failing every later one.
|
|
18
|
+
Effect.cachedWithTTL((exit) =>
|
|
19
|
+
Exit.isSuccess(exit) ? Duration.infinity : Duration.zero,
|
|
20
|
+
),
|
|
21
|
+
Effect.runSync,
|
|
22
|
+
Effect.uninterruptible,
|
|
23
|
+
Layer.effectContext,
|
|
24
|
+
);
|
|
25
|
+
layers.set(layer, built);
|
|
26
|
+
return built;
|
|
27
|
+
};
|