@gaganref/confect-server 10.3.1-fork.6 → 10.3.1-fork.61
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/ActionCall.d.ts +30 -0
- package/dist/ActionCall.d.ts.map +1 -0
- package/dist/ActionCall.js +41 -0
- package/dist/ActionCall.js.map +1 -0
- package/dist/ActionRunner.d.ts +1 -1
- package/dist/ActionRunner.d.ts.map +1 -1
- package/dist/ActionRunner.js +3 -3
- package/dist/ActionRunner.js.map +1 -1
- package/dist/AiGatewayDecisionClient.js +1 -1
- package/dist/AiGatewayLanguageClient.js +1 -1
- package/dist/Auth.d.ts +13 -1
- package/dist/Auth.d.ts.map +1 -1
- package/dist/Auth.js +13 -3
- package/dist/Auth.js.map +1 -1
- package/dist/Callback.d.ts +1 -1
- package/dist/Callback.d.ts.map +1 -1
- package/dist/Callback.js +6 -6
- package/dist/Callback.js.map +1 -1
- package/dist/ConvexConfigProvider.js +2 -2
- package/dist/ConvexFunctionDefinitions.d.ts.map +1 -1
- package/dist/ConvexFunctionDefinitions.js +4 -4
- package/dist/ConvexFunctionDefinitions.js.map +1 -1
- package/dist/ConvexLogger.js +1 -1
- package/dist/CronJobs.d.ts.map +1 -1
- package/dist/CronJobs.js +7 -6
- package/dist/CronJobs.js.map +1 -1
- package/dist/DatabaseSchema.js +2 -2
- package/dist/DatabaseWriter.d.ts +36 -2
- package/dist/DatabaseWriter.d.ts.map +1 -1
- package/dist/DatabaseWriter.js +55 -8
- package/dist/DatabaseWriter.js.map +1 -1
- package/dist/ExecutionMetadata.js +1 -1
- package/dist/FunctionImpl.js +2 -2
- package/dist/FunctionRegistryItem.d.ts +1 -0
- package/dist/FunctionRegistryItem.d.ts.map +1 -1
- package/dist/FunctionRegistryItem.js +2 -1
- package/dist/FunctionRegistryItem.js.map +1 -1
- package/dist/GroupImpl.d.ts +21 -2
- package/dist/GroupImpl.d.ts.map +1 -1
- package/dist/GroupImpl.js +21 -22
- package/dist/GroupImpl.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/Handler.d.ts +8 -4
- package/dist/Handler.d.ts.map +1 -1
- package/dist/Handler.js.map +1 -1
- package/dist/HttpRouter.d.ts +2 -0
- package/dist/HttpRouter.d.ts.map +1 -1
- package/dist/HttpRouter.js +17 -8
- package/dist/HttpRouter.js.map +1 -1
- package/dist/MiddlewareImpl.js +1 -1
- package/dist/MutationRunner.d.ts +1 -1
- package/dist/MutationRunner.d.ts.map +1 -1
- package/dist/MutationRunner.js +3 -3
- package/dist/MutationRunner.js.map +1 -1
- package/dist/MutationTransactionContext.d.ts.map +1 -1
- package/dist/MutationTransactionContext.js +4 -3
- package/dist/MutationTransactionContext.js.map +1 -1
- package/dist/OfficialComponent.d.ts +60 -0
- package/dist/OfficialComponent.d.ts.map +1 -0
- package/dist/OfficialComponent.js +72 -0
- package/dist/OfficialComponent.js.map +1 -0
- package/dist/QueryInitializer.js +2 -2
- package/dist/QueryRunner.js +2 -2
- package/dist/QueryStream.d.ts +120 -12
- package/dist/QueryStream.d.ts.map +1 -1
- package/dist/QueryStream.js +143 -41
- package/dist/QueryStream.js.map +1 -1
- package/dist/QueryStreamCursor.js +1 -1
- package/dist/QueryStreamIndexPrefix.js +1 -1
- package/dist/QueryStreamIndexRange.js +2 -2
- package/dist/QueryStreamKey.js +1 -1
- package/dist/QueryStreamKeyBounds.js +2 -2
- package/dist/QueryStreamKeyLayout.js +2 -2
- package/dist/QueryStreamKeyValues.js +2 -2
- package/dist/QueryStreamPagination.js +1 -1
- package/dist/QueryStreamReadBudget.d.ts +3 -2
- package/dist/QueryStreamReadBudget.d.ts.map +1 -1
- package/dist/QueryStreamReadBudget.js +8 -5
- package/dist/QueryStreamReadBudget.js.map +1 -1
- package/dist/QueryTransactionContext.js +2 -2
- package/dist/RegisteredConvexFunction.d.ts +10 -5
- package/dist/RegisteredConvexFunction.d.ts.map +1 -1
- package/dist/RegisteredConvexFunction.js +45 -29
- package/dist/RegisteredConvexFunction.js.map +1 -1
- package/dist/RegisteredFunction.d.ts +39 -22
- package/dist/RegisteredFunction.d.ts.map +1 -1
- package/dist/RegisteredFunction.js +155 -42
- 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 +10 -6
- package/dist/RegisteredNodeFunction.js.map +1 -1
- package/dist/RegistryItems.d.ts +7 -0
- package/dist/RegistryItems.d.ts.map +1 -1
- package/dist/RegistryItems.js +8 -3
- package/dist/RegistryItems.js.map +1 -1
- package/dist/RequestMetadata.js +1 -1
- package/dist/Scheduler.d.ts +1 -1
- package/dist/Scheduler.d.ts.map +1 -1
- package/dist/Scheduler.js +14 -13
- package/dist/Scheduler.js.map +1 -1
- package/dist/Storage.js +1 -1
- package/dist/StorageActionWriter.js +1 -1
- package/dist/StorageReader.js +1 -1
- package/dist/StorageWriter.js +1 -1
- package/dist/TransactionMetadata.js +1 -1
- package/dist/VectorSearch.js +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -18
- package/dist/internal/AiGatewayDecisionClient.js +1 -1
- package/dist/internal/AiGatewayLanguageClient.js +1 -1
- package/dist/internal/AiGatewayServiceToken.js +1 -1
- package/dist/internal/DocumentPatch.d.ts +16 -0
- package/dist/internal/DocumentPatch.d.ts.map +1 -0
- package/dist/internal/DocumentPatch.js +36 -0
- package/dist/internal/DocumentPatch.js.map +1 -0
- 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/dist/internal/reservedArgument.d.ts +8 -0
- package/dist/internal/reservedArgument.d.ts.map +1 -0
- package/dist/internal/reservedArgument.js +30 -0
- package/dist/internal/reservedArgument.js.map +1 -0
- package/package.json +5 -5
- package/src/ActionCall.ts +83 -0
- package/src/ActionRunner.ts +3 -2
- package/src/Auth.ts +23 -1
- package/src/Callback.ts +3 -2
- package/src/ConvexFunctionDefinitions.ts +2 -1
- package/src/CronJobs.ts +2 -1
- package/src/DatabaseWriter.ts +183 -25
- package/src/FunctionRegistryItem.ts +4 -0
- package/src/GroupImpl.ts +61 -8
- package/src/GroupRegistry.ts +7 -3
- package/src/Handler.ts +21 -5
- package/src/HttpRouter.ts +54 -4
- package/src/MutationRunner.ts +3 -2
- package/src/MutationTransactionContext.ts +2 -1
- package/src/OfficialComponent.ts +168 -0
- package/src/QueryStream.ts +391 -61
- package/src/QueryStreamReadBudget.ts +19 -6
- package/src/RegisteredConvexFunction.ts +35 -2
- package/src/RegisteredFunction.ts +251 -45
- package/src/RegisteredNodeFunction.ts +11 -1
- package/src/RegistryItems.ts +31 -0
- package/src/Scheduler.ts +17 -19
- package/src/index.ts +2 -0
- package/src/internal/DocumentPatch.ts +87 -0
- package/src/internal/observability.ts +27 -0
- package/src/internal/reservedArgument.ts +56 -0
|
@@ -61,12 +61,14 @@ export const prepare = (
|
|
|
61
61
|
databaseSchema: DatabaseSchema.AnyWithProps,
|
|
62
62
|
item: FunctionRegistryItem.ConfectFunctionRegistryItem,
|
|
63
63
|
resolvedMiddlewares: ReadonlyArray<ResolvedMiddleware.ResolvedMiddleware> = [],
|
|
64
|
+
observability?: Layer.Layer<never>,
|
|
64
65
|
) =>
|
|
65
66
|
Match.value(item.functionType).pipe(
|
|
66
67
|
Match.when("query", () => {
|
|
67
68
|
const definition = queryFunction({
|
|
68
69
|
databaseSchema,
|
|
69
70
|
name: item.name,
|
|
71
|
+
observability,
|
|
70
72
|
functionVisibility: item.functionVisibility,
|
|
71
73
|
args: item.args,
|
|
72
74
|
returns: item.returns,
|
|
@@ -84,6 +86,8 @@ export const prepare = (
|
|
|
84
86
|
const definition = mutationFunction({
|
|
85
87
|
databaseSchema,
|
|
86
88
|
name: item.name,
|
|
89
|
+
reservedArgument: item.reservedArgument,
|
|
90
|
+
observability,
|
|
87
91
|
functionVisibility: item.functionVisibility,
|
|
88
92
|
args: item.args,
|
|
89
93
|
returns: item.returns,
|
|
@@ -100,6 +104,8 @@ export const prepare = (
|
|
|
100
104
|
Match.when("action", () => {
|
|
101
105
|
const definition = convexActionFunction(databaseSchema, {
|
|
102
106
|
name: item.name,
|
|
107
|
+
reservedArgument: item.reservedArgument,
|
|
108
|
+
observability,
|
|
103
109
|
functionVisibility: item.functionVisibility,
|
|
104
110
|
args: item.args,
|
|
105
111
|
returns: item.returns,
|
|
@@ -188,6 +194,7 @@ const queryFunction = <
|
|
|
188
194
|
error,
|
|
189
195
|
handler,
|
|
190
196
|
resolvedMiddlewares,
|
|
197
|
+
observability,
|
|
191
198
|
}: {
|
|
192
199
|
databaseSchema: DatabaseSchema_;
|
|
193
200
|
name: string;
|
|
@@ -199,6 +206,7 @@ const queryFunction = <
|
|
|
199
206
|
a: Args,
|
|
200
207
|
) => Effect.Effect<Returns, E, Handler.QueryServices<DatabaseSchema_>>;
|
|
201
208
|
resolvedMiddlewares: ReadonlyArray<ResolvedMiddleware.ResolvedMiddleware>;
|
|
209
|
+
observability?: Layer.Layer<never> | undefined;
|
|
202
210
|
}) => ({
|
|
203
211
|
args: compileArgsSchema(args, databaseSchema.target.scope),
|
|
204
212
|
returns: compileReturnsSchema(returns, databaseSchema.target.scope),
|
|
@@ -258,6 +266,9 @@ const queryFunction = <
|
|
|
258
266
|
RegisteredFunction.combineErrorSchemas(error, resolvedMiddlewares),
|
|
259
267
|
{
|
|
260
268
|
scheduler: new EffectScheduler.MixedScheduler("sync"),
|
|
269
|
+
invocation: Effect.promise(() => ctx.meta.getFunctionMetadata()),
|
|
270
|
+
clock: observability === undefined ? undefined : queryClock,
|
|
271
|
+
observability,
|
|
261
272
|
},
|
|
262
273
|
),
|
|
263
274
|
),
|
|
@@ -274,6 +285,7 @@ export const mutationLayer = <Schema extends DatabaseSchema.AnyWithProps>(
|
|
|
274
285
|
RequestMetadata.layer(ctx.meta),
|
|
275
286
|
TransactionMetadata.layer(ctx.meta),
|
|
276
287
|
Auth.layerForTarget<Schema["target"]>(schema.target, ctx.auth),
|
|
288
|
+
Auth.mutationLayer(ctx.auth),
|
|
277
289
|
Scheduler.layer<DatabaseSchema.Scope<Schema>>(
|
|
278
290
|
ctx.scheduler,
|
|
279
291
|
schema.target.scope,
|
|
@@ -313,9 +325,12 @@ const mutationFunction = <
|
|
|
313
325
|
error,
|
|
314
326
|
handler,
|
|
315
327
|
resolvedMiddlewares,
|
|
328
|
+
observability,
|
|
329
|
+
reservedArgument,
|
|
316
330
|
}: {
|
|
317
331
|
databaseSchema: DatabaseSchema_;
|
|
318
332
|
name: string;
|
|
333
|
+
reservedArgument?: string | undefined;
|
|
319
334
|
functionVisibility: FunctionVisibility;
|
|
320
335
|
args: Schema.Codec<Args, ConvexArgs>;
|
|
321
336
|
returns: Schema.Codec<Returns, ConvexReturns>;
|
|
@@ -324,8 +339,14 @@ const mutationFunction = <
|
|
|
324
339
|
a: Args,
|
|
325
340
|
) => Effect.Effect<Returns, E, MutationServices<DatabaseSchema_>>;
|
|
326
341
|
resolvedMiddlewares: ReadonlyArray<ResolvedMiddleware.ResolvedMiddleware>;
|
|
342
|
+
observability?: Layer.Layer<never> | undefined;
|
|
327
343
|
}) => ({
|
|
328
|
-
args: compileArgsSchema(
|
|
344
|
+
args: compileArgsSchema(
|
|
345
|
+
args,
|
|
346
|
+
databaseSchema.target.scope,
|
|
347
|
+
reservedArgument,
|
|
348
|
+
name,
|
|
349
|
+
),
|
|
329
350
|
returns: compileReturnsSchema(returns, databaseSchema.target.scope),
|
|
330
351
|
handler: (
|
|
331
352
|
ctx: GenericMutationCtx<
|
|
@@ -335,7 +356,7 @@ const mutationFunction = <
|
|
|
335
356
|
): Promise<ConvexReturns> =>
|
|
336
357
|
Effect.gen(function* () {
|
|
337
358
|
const decodedArgs = yield* pipe(
|
|
338
|
-
actualArgs,
|
|
359
|
+
RegisteredFunction.domainArgs(actualArgs, reservedArgument),
|
|
339
360
|
Schema.decodeUnknownEffect(args),
|
|
340
361
|
Effect.catchTag("SchemaError", Effect.die),
|
|
341
362
|
);
|
|
@@ -360,6 +381,12 @@ const mutationFunction = <
|
|
|
360
381
|
RegisteredFunction.combineErrorSchemas(error, resolvedMiddlewares),
|
|
361
382
|
{
|
|
362
383
|
scheduler: new EffectScheduler.MixedScheduler("sync"),
|
|
384
|
+
invocation: RegisteredFunction.invocationMetadata(
|
|
385
|
+
ctx.meta,
|
|
386
|
+
actualArgs,
|
|
387
|
+
reservedArgument,
|
|
388
|
+
),
|
|
389
|
+
observability,
|
|
363
390
|
},
|
|
364
391
|
),
|
|
365
392
|
),
|
|
@@ -382,8 +409,11 @@ const convexActionFunction = <
|
|
|
382
409
|
error,
|
|
383
410
|
handler,
|
|
384
411
|
resolvedMiddlewares,
|
|
412
|
+
observability,
|
|
413
|
+
reservedArgument,
|
|
385
414
|
}: {
|
|
386
415
|
name: string;
|
|
416
|
+
reservedArgument?: string | undefined;
|
|
387
417
|
functionVisibility: FunctionVisibility;
|
|
388
418
|
args: Schema.Codec<Args, ConvexArgs>;
|
|
389
419
|
returns: Schema.Codec<Returns, ConvexReturns>;
|
|
@@ -396,6 +426,7 @@ const convexActionFunction = <
|
|
|
396
426
|
RegisteredFunction.ActionServices<DatabaseSchema_>
|
|
397
427
|
>;
|
|
398
428
|
resolvedMiddlewares: ReadonlyArray<ResolvedMiddleware.ResolvedMiddleware>;
|
|
429
|
+
observability?: Layer.Layer<never> | undefined;
|
|
399
430
|
},
|
|
400
431
|
) =>
|
|
401
432
|
RegisteredFunction.actionFunctionBase({
|
|
@@ -407,6 +438,8 @@ const convexActionFunction = <
|
|
|
407
438
|
error,
|
|
408
439
|
handler,
|
|
409
440
|
resolvedMiddlewares,
|
|
441
|
+
observability,
|
|
442
|
+
reservedArgument,
|
|
410
443
|
createLayer: (ctx) =>
|
|
411
444
|
Layer.mergeAll(
|
|
412
445
|
RegisteredFunction.actionLayer(schema, ctx),
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
export { Forward } from "./internal/reservedArgument.js";
|
|
2
|
+
import * as ActionCallSpec from "@confect/core/ActionCall";
|
|
3
|
+
import * as ActionCall from "./ActionCall.js";
|
|
1
4
|
import type { FunctionSpec, RuntimeAndFunctionType } from "@confect/core";
|
|
2
5
|
import * as IdScope from "@confect/core/IdScope";
|
|
3
6
|
import type * as FunctionProvenance from "@confect/core/FunctionProvenance";
|
|
@@ -9,6 +12,8 @@ import {
|
|
|
9
12
|
import {
|
|
10
13
|
type DefaultFunctionArgs,
|
|
11
14
|
type FunctionVisibility,
|
|
15
|
+
type FunctionMetadata,
|
|
16
|
+
type MutationMeta,
|
|
12
17
|
type GenericActionCtx,
|
|
13
18
|
type GenericDataModel,
|
|
14
19
|
type StorageReader as ConvexStorageReader,
|
|
@@ -19,8 +24,15 @@ import {
|
|
|
19
24
|
import type { Value } from "convex/values";
|
|
20
25
|
import { ConvexError } from "convex/values";
|
|
21
26
|
import { pipe } from "effect/Function";
|
|
27
|
+
import * as Config from "effect/Config";
|
|
28
|
+
import * as Option from "effect/Option";
|
|
29
|
+
import * as ConvexConfigProvider from "./ConvexConfigProvider.js";
|
|
30
|
+
import * as Clock from "effect/Clock";
|
|
31
|
+
import * as Cause from "effect/Cause";
|
|
22
32
|
import * as Console from "effect/Console";
|
|
33
|
+
import * as Context from "effect/Context";
|
|
23
34
|
import * as Effect from "effect/Effect";
|
|
35
|
+
import * as Exit from "effect/Exit";
|
|
24
36
|
import * as Result from "effect/Result";
|
|
25
37
|
import * as Layer from "effect/Layer";
|
|
26
38
|
import * as Schema from "effect/Schema";
|
|
@@ -41,9 +53,50 @@ import * as Storage from "./Storage.js";
|
|
|
41
53
|
import * as StorageActionWriter from "./StorageActionWriter.js";
|
|
42
54
|
import type * as StorageReader from "./StorageReader.js";
|
|
43
55
|
import * as StorageWriter from "./StorageWriter.js";
|
|
56
|
+
import * as Tracer from "effect/Tracer";
|
|
57
|
+
import * as ObservabilityRuntime from "./internal/observability.js";
|
|
44
58
|
import * as VectorSearch from "./VectorSearch.js";
|
|
45
59
|
import type * as Handler from "./Handler.js";
|
|
46
60
|
|
|
61
|
+
export interface ReceivedValue {
|
|
62
|
+
readonly value: unknown;
|
|
63
|
+
readonly requestId: string;
|
|
64
|
+
readonly deployment: string | undefined;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const Received = Context.Reference<ReceivedValue | undefined>(
|
|
68
|
+
"@confect/server/RegisteredFunction/Received",
|
|
69
|
+
{ defaultValue: () => undefined },
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
export const invocationMetadata = (
|
|
73
|
+
meta: Pick<MutationMeta, "getFunctionMetadata" | "getRequestMetadata">,
|
|
74
|
+
args: DefaultFunctionArgs,
|
|
75
|
+
reservedArgument?: string,
|
|
76
|
+
): Effect.Effect<FunctionMetadata & { readonly received?: ReceivedValue }> =>
|
|
77
|
+
Effect.gen(function* () {
|
|
78
|
+
const metadata = yield* Effect.promise(() => meta.getFunctionMetadata());
|
|
79
|
+
if (reservedArgument === undefined) return metadata;
|
|
80
|
+
const { requestId } = yield* Effect.promise(() =>
|
|
81
|
+
meta.getRequestMetadata(),
|
|
82
|
+
);
|
|
83
|
+
const deployment = yield* Config.String("CONVEX_CLOUD_URL").pipe(
|
|
84
|
+
Config.option,
|
|
85
|
+
Effect.map(Option.getOrUndefined),
|
|
86
|
+
Effect.provide(ConvexConfigProvider.layer),
|
|
87
|
+
Effect.orDie,
|
|
88
|
+
);
|
|
89
|
+
return {
|
|
90
|
+
...metadata,
|
|
91
|
+
received: { value: args[reservedArgument], requestId, deployment },
|
|
92
|
+
};
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
export const Observability = Context.Reference<Layer.Layer<never> | undefined>(
|
|
96
|
+
"@confect/server/Observability",
|
|
97
|
+
{ defaultValue: () => undefined },
|
|
98
|
+
);
|
|
99
|
+
|
|
47
100
|
export type Any =
|
|
48
101
|
| RegisteredQuery<FunctionVisibility, DefaultFunctionArgs, any>
|
|
49
102
|
| RegisteredMutation<FunctionVisibility, DefaultFunctionArgs, any>
|
|
@@ -183,67 +236,150 @@ export const combineErrorSchemas = (
|
|
|
183
236
|
};
|
|
184
237
|
|
|
185
238
|
/**
|
|
186
|
-
* Run the `Effect` as a `Promise`.
|
|
187
|
-
*
|
|
239
|
+
* Run the `Effect` as a `Promise`. Inspect the complete `Exit` before encoding
|
|
240
|
+
* a declared error so a typed failure cannot hide a defect.
|
|
188
241
|
*
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
* - Without a schema: every failure is converted to a defect via `Effect.orDie`,
|
|
195
|
-
* so nothing—not even a `ConvexError` the handler placed in its error
|
|
196
|
-
* channel—reaches the client as a `ConvexError`. The fiber dies and
|
|
197
|
-
* `runPromise` rejects with a generic failure.
|
|
242
|
+
* Any `Die` reason takes the defect path. Without a `Die`, the first `Fail` in
|
|
243
|
+
* cause order is schema-encoded and thrown as a `ConvexError` outside the
|
|
244
|
+
* Effect runtime so Convex surfaces its data to the client. Other failures and
|
|
245
|
+
* interruptions alongside that `Fail` are ignored. Without an error schema, the
|
|
246
|
+
* selected failure is treated as a defect.
|
|
198
247
|
*
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
248
|
+
* A lone `ConvexError` defect or a selected undeclared `ConvexError` failure is
|
|
249
|
+
* rethrown bare so it keeps Convex's meaning. Pagination and Workpool rely on
|
|
250
|
+
* its data for their control signals. Other causes with a `Die`, interrupt-only
|
|
251
|
+
* or empty causes, and error-encoding failures reject with a plain `Error`
|
|
252
|
+
* whose message renders the complete cause and whose `cause` holds it for
|
|
253
|
+
* server diagnostics.
|
|
204
254
|
*
|
|
205
255
|
* A `scheduler` in `runOptions` must be passed here as a run option rather than
|
|
206
256
|
* provided via `Effect.provideService` inside `effect`: the run option lands in
|
|
207
257
|
* the fiber's root context, while a service provided within `effect` pops
|
|
208
|
-
* before the
|
|
209
|
-
*
|
|
210
|
-
*
|
|
258
|
+
* before the exit/encoding wrappers this function adds. The fiber's op counter
|
|
259
|
+
* survives context pops, so a cooperative yield can fire inside those wrappers.
|
|
260
|
+
* Only a root-context scheduler covers them.
|
|
211
261
|
*/
|
|
212
262
|
export const runHandlerPromise =
|
|
213
263
|
(
|
|
214
264
|
errorSchema: Schema.Codec<any, any> | undefined,
|
|
215
265
|
runOptions?: {
|
|
216
266
|
readonly scheduler?: EffectScheduler.Scheduler | undefined;
|
|
267
|
+
readonly clock?: Clock.Clock | undefined;
|
|
268
|
+
readonly invocation?:
|
|
269
|
+
| Effect.Effect<
|
|
270
|
+
FunctionMetadata & { readonly received?: ReceivedValue }
|
|
271
|
+
>
|
|
272
|
+
| undefined;
|
|
273
|
+
readonly observability?: Layer.Layer<never> | undefined;
|
|
217
274
|
},
|
|
218
275
|
) =>
|
|
219
276
|
<A, E>(effect: Effect.Effect<A, E>): Promise<A> => {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
277
|
+
const { observability, invocation, clock } = runOptions ?? {};
|
|
278
|
+
let encodedError: Exit.Exit<any, any> | undefined;
|
|
279
|
+
let undeclared: { readonly error: unknown } | undefined;
|
|
280
|
+
const observed = Effect.tapCause(effect, (cause) =>
|
|
281
|
+
Effect.gen(function* () {
|
|
282
|
+
const reason =
|
|
283
|
+
cause.reasons.find(Cause.isDieReason) ??
|
|
284
|
+
cause.reasons.find(Cause.isFailReason);
|
|
285
|
+
if (reason?._tag === "Fail" && errorSchema !== undefined) {
|
|
286
|
+
encodedError = yield* Effect.exit(
|
|
287
|
+
Schema.encodeEffect(errorSchema)(reason.error),
|
|
288
|
+
);
|
|
289
|
+
// An encoding failure is a defect, even when it dies with a ConvexError.
|
|
290
|
+
if (Exit.isFailure(encodedError)) {
|
|
291
|
+
const encoding = encodedError.cause;
|
|
292
|
+
return yield* Cause.squash(encoding) instanceof ConvexError
|
|
293
|
+
? Effect.die(
|
|
294
|
+
new Error(Cause.pretty(encoding), { cause: encoding }),
|
|
295
|
+
)
|
|
296
|
+
: Effect.orDie(encodedError);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
// With no error schema a failure is a defect, so the root span ends as one.
|
|
300
|
+
if (
|
|
301
|
+
reason?._tag === "Fail" &&
|
|
302
|
+
errorSchema === undefined &&
|
|
303
|
+
!(reason.error instanceof ConvexError)
|
|
304
|
+
) {
|
|
305
|
+
undeclared = { error: reason.error };
|
|
306
|
+
return yield* Effect.die(reason.error);
|
|
307
|
+
}
|
|
308
|
+
}),
|
|
227
309
|
);
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
Effect.
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
310
|
+
const withConvexError: Effect.Effect<Result.Result<A, ConvexError<any>>> =
|
|
311
|
+
Effect.gen(function* () {
|
|
312
|
+
const exit = yield* Effect.exit(
|
|
313
|
+
observability === undefined || invocation === undefined
|
|
314
|
+
? effect
|
|
315
|
+
: Effect.flatMap(
|
|
316
|
+
invocation,
|
|
317
|
+
({ name, type, componentPath, received }) =>
|
|
318
|
+
Effect.useSpan(
|
|
319
|
+
name,
|
|
320
|
+
{
|
|
321
|
+
root: true,
|
|
322
|
+
attributes: { kind: type, componentPath },
|
|
323
|
+
},
|
|
324
|
+
(span) =>
|
|
325
|
+
Effect.provideService(observed, Tracer.ParentSpan, span),
|
|
326
|
+
).pipe(Effect.provideService(Received, received)),
|
|
240
327
|
),
|
|
241
|
-
|
|
328
|
+
);
|
|
329
|
+
if (Exit.isSuccess(exit)) {
|
|
330
|
+
return Result.succeed(exit.value);
|
|
331
|
+
}
|
|
332
|
+
if (undeclared !== undefined)
|
|
333
|
+
return yield* Effect.die(undeclared.error);
|
|
334
|
+
|
|
335
|
+
const cause =
|
|
336
|
+
encodedError !== undefined && Exit.isFailure(encodedError)
|
|
337
|
+
? encodedError.cause
|
|
338
|
+
: exit.cause;
|
|
339
|
+
const reason =
|
|
340
|
+
cause.reasons.find(Cause.isDieReason) ??
|
|
341
|
+
cause.reasons.find(Cause.isFailReason);
|
|
342
|
+
if (
|
|
343
|
+
encodedError === undefined &&
|
|
344
|
+
reason?._tag === "Die" &&
|
|
345
|
+
cause.reasons.length === 1 &&
|
|
346
|
+
reason.defect instanceof ConvexError
|
|
347
|
+
) {
|
|
348
|
+
return Result.fail(reason.defect);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (reason?._tag === "Fail") {
|
|
352
|
+
if (errorSchema === undefined) {
|
|
353
|
+
if (reason.error instanceof ConvexError) {
|
|
354
|
+
return Result.fail(reason.error);
|
|
355
|
+
}
|
|
356
|
+
return yield* Effect.die(reason.error);
|
|
357
|
+
}
|
|
358
|
+
const encoded =
|
|
359
|
+
encodedError ??
|
|
360
|
+
(yield* Effect.exit(
|
|
361
|
+
Schema.encodeEffect(errorSchema)(reason.error),
|
|
362
|
+
));
|
|
363
|
+
if (Exit.isSuccess(encoded)) {
|
|
364
|
+
return Result.fail(new ConvexError(encoded.value));
|
|
365
|
+
}
|
|
366
|
+
return yield* Effect.die(
|
|
367
|
+
new Error(Cause.pretty(encoded.cause), { cause: encoded.cause }),
|
|
242
368
|
);
|
|
369
|
+
}
|
|
370
|
+
return yield* Effect.die(new Error(Cause.pretty(cause), { cause }));
|
|
371
|
+
});
|
|
243
372
|
return Effect.runPromise(
|
|
244
|
-
|
|
245
|
-
Effect.provide(
|
|
373
|
+
withConvexError.pipe(
|
|
374
|
+
Effect.provide(
|
|
375
|
+
observability === undefined
|
|
376
|
+
? ConvexLogger.layer
|
|
377
|
+
: ObservabilityRuntime.memoized(observability),
|
|
378
|
+
),
|
|
246
379
|
Effect.provideService(Console.Console, globalThis.console),
|
|
380
|
+
clock === undefined
|
|
381
|
+
? (self) => self
|
|
382
|
+
: Effect.provideService(Clock.Clock, clock),
|
|
247
383
|
),
|
|
248
384
|
runOptions,
|
|
249
385
|
).then(
|
|
@@ -256,6 +392,16 @@ export const runHandlerPromise =
|
|
|
256
392
|
);
|
|
257
393
|
};
|
|
258
394
|
|
|
395
|
+
export const domainArgs = (
|
|
396
|
+
args: DefaultFunctionArgs,
|
|
397
|
+
reservedArgument?: string,
|
|
398
|
+
): DefaultFunctionArgs =>
|
|
399
|
+
reservedArgument === undefined
|
|
400
|
+
? args
|
|
401
|
+
: Object.fromEntries(
|
|
402
|
+
Object.entries(args).filter(([key]) => key !== reservedArgument),
|
|
403
|
+
);
|
|
404
|
+
|
|
259
405
|
export const actionFunctionBase = <
|
|
260
406
|
Schema extends DatabaseSchema.AnyWithProps,
|
|
261
407
|
Args,
|
|
@@ -274,8 +420,11 @@ export const actionFunctionBase = <
|
|
|
274
420
|
resolvedMiddlewares = [],
|
|
275
421
|
createLayer,
|
|
276
422
|
scope = IdScope.app,
|
|
423
|
+
observability,
|
|
424
|
+
reservedArgument,
|
|
277
425
|
}: {
|
|
278
426
|
name: string;
|
|
427
|
+
reservedArgument?: string | undefined;
|
|
279
428
|
functionVisibility: FunctionVisibility;
|
|
280
429
|
args: Schema.Codec<Args, ConvexArgs>;
|
|
281
430
|
returns: Schema.Codec<Returns, ConvexReturns>;
|
|
@@ -286,8 +435,9 @@ export const actionFunctionBase = <
|
|
|
286
435
|
ctx: GenericActionCtx<DataModel.ToConvex<DataModel.FromSchema<Schema>>>,
|
|
287
436
|
) => Layer.Layer<R>;
|
|
288
437
|
scope?: IdScope.IdScope;
|
|
438
|
+
observability?: Layer.Layer<never> | undefined;
|
|
289
439
|
}) => ({
|
|
290
|
-
args: compileArgsSchema(args, scope),
|
|
440
|
+
args: compileArgsSchema(args, scope, reservedArgument, name),
|
|
291
441
|
returns: compileReturnsSchema(returns, scope),
|
|
292
442
|
handler: (
|
|
293
443
|
ctx: GenericActionCtx<DataModel.ToConvex<DataModel.FromSchema<Schema>>>,
|
|
@@ -295,10 +445,16 @@ export const actionFunctionBase = <
|
|
|
295
445
|
): Promise<ConvexReturns> =>
|
|
296
446
|
Effect.gen(function* () {
|
|
297
447
|
const decodedArgs = yield* pipe(
|
|
298
|
-
actualArgs,
|
|
448
|
+
domainArgs(actualArgs, reservedArgument),
|
|
299
449
|
Schema.decodeUnknownEffect(args),
|
|
300
450
|
Effect.catchTag("SchemaError", Effect.die),
|
|
301
451
|
);
|
|
452
|
+
const restricted = resolvedMiddlewares.some(
|
|
453
|
+
({ middlewareSpec }) =>
|
|
454
|
+
middlewareSpec.key === ActionCallSpec.Middleware.key,
|
|
455
|
+
)
|
|
456
|
+
? restrict(ctx)
|
|
457
|
+
: undefined;
|
|
302
458
|
// oxlint-disable-next-line effecttsgo/any-unknown-in-error-context -- Middleware errors are intentionally erased here and validated by runHandlerPromise against the combined error schema below.
|
|
303
459
|
const decodedReturns = yield* applyMiddleware(
|
|
304
460
|
handler(decodedArgs),
|
|
@@ -309,13 +465,19 @@ export const actionFunctionBase = <
|
|
|
309
465
|
functionVisibility,
|
|
310
466
|
args: decodedArgs,
|
|
311
467
|
},
|
|
312
|
-
).pipe(Effect.provide(createLayer(ctx)));
|
|
468
|
+
).pipe(Effect.provide(createLayer(restricted?.ctx ?? ctx)));
|
|
469
|
+
if (restricted !== undefined) yield* restricted.check;
|
|
313
470
|
return yield* pipe(
|
|
314
471
|
decodedReturns,
|
|
315
472
|
Schema.encodeEffect(returns),
|
|
316
473
|
Effect.catchTag("SchemaError", Effect.die),
|
|
317
474
|
);
|
|
318
|
-
}).pipe(
|
|
475
|
+
}).pipe(
|
|
476
|
+
runHandlerPromise(combineErrorSchemas(error, resolvedMiddlewares), {
|
|
477
|
+
invocation: invocationMetadata(ctx.meta, actualArgs, reservedArgument),
|
|
478
|
+
observability,
|
|
479
|
+
}),
|
|
480
|
+
),
|
|
319
481
|
});
|
|
320
482
|
|
|
321
483
|
export type ActionServices<
|
|
@@ -389,3 +551,47 @@ export const storageLayer = <Scope extends IdScope.IdScope>(
|
|
|
389
551
|
Storage.layer(reader) as Layer.Layer<
|
|
390
552
|
Storage.Storage | StorageReader.ForScope<Scope>
|
|
391
553
|
>;
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Shares one dispatch budget across the entire middleware chain.
|
|
557
|
+
*/
|
|
558
|
+
const restrict = <Model extends GenericDataModel>(
|
|
559
|
+
ctx: GenericActionCtx<Model>,
|
|
560
|
+
) => {
|
|
561
|
+
let dispatched = false;
|
|
562
|
+
const claim = () => {
|
|
563
|
+
if (dispatched) throw new ActionCall.ActionCallAlreadyUsed();
|
|
564
|
+
dispatched = true;
|
|
565
|
+
};
|
|
566
|
+
const restricted: GenericActionCtx<Model> = {
|
|
567
|
+
...ctx,
|
|
568
|
+
scheduler: {
|
|
569
|
+
runAfter: () => {
|
|
570
|
+
throw new ActionCall.SchedulingNotAllowed();
|
|
571
|
+
},
|
|
572
|
+
runAt: () => {
|
|
573
|
+
throw new ActionCall.SchedulingNotAllowed();
|
|
574
|
+
},
|
|
575
|
+
cancel: () => {
|
|
576
|
+
throw new ActionCall.SchedulingNotAllowed();
|
|
577
|
+
},
|
|
578
|
+
},
|
|
579
|
+
runQuery: (...args) => {
|
|
580
|
+
claim();
|
|
581
|
+
return ctx.runQuery(...args);
|
|
582
|
+
},
|
|
583
|
+
runMutation: (...args) => {
|
|
584
|
+
claim();
|
|
585
|
+
return ctx.runMutation(...args);
|
|
586
|
+
},
|
|
587
|
+
runAction: () => {
|
|
588
|
+
throw new ActionCall.NestedActionNotAllowed();
|
|
589
|
+
},
|
|
590
|
+
};
|
|
591
|
+
return {
|
|
592
|
+
ctx: restricted,
|
|
593
|
+
check: Effect.suspend(() =>
|
|
594
|
+
dispatched ? Effect.void : Effect.die(new ActionCall.ActionCallNotUsed()),
|
|
595
|
+
),
|
|
596
|
+
};
|
|
597
|
+
};
|
|
@@ -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,8 @@ export const make = (
|
|
|
59
61
|
return genericFunction(
|
|
60
62
|
nodeActionFunction(databaseSchema, {
|
|
61
63
|
name: item.name,
|
|
64
|
+
reservedArgument: item.reservedArgument,
|
|
65
|
+
observability,
|
|
62
66
|
functionVisibility: item.functionVisibility,
|
|
63
67
|
args: item.args,
|
|
64
68
|
returns: item.returns,
|
|
@@ -86,8 +90,11 @@ const nodeActionFunction = <
|
|
|
86
90
|
error,
|
|
87
91
|
handler,
|
|
88
92
|
resolvedMiddlewares,
|
|
93
|
+
observability,
|
|
94
|
+
reservedArgument,
|
|
89
95
|
}: {
|
|
90
96
|
name: string;
|
|
97
|
+
reservedArgument?: string | undefined;
|
|
91
98
|
functionVisibility: FunctionVisibility;
|
|
92
99
|
args: Schema.Codec<Args, ConvexArgs>;
|
|
93
100
|
returns: Schema.Codec<Returns, ConvexReturns>;
|
|
@@ -101,6 +108,7 @@ const nodeActionFunction = <
|
|
|
101
108
|
| NodeServices.NodeServices
|
|
102
109
|
>;
|
|
103
110
|
resolvedMiddlewares: ReadonlyArray<ResolvedMiddleware.ResolvedMiddleware>;
|
|
111
|
+
observability?: Layer.Layer<never> | undefined;
|
|
104
112
|
},
|
|
105
113
|
) =>
|
|
106
114
|
RegisteredFunction.actionFunctionBase({
|
|
@@ -112,6 +120,8 @@ const nodeActionFunction = <
|
|
|
112
120
|
error,
|
|
113
121
|
handler,
|
|
114
122
|
resolvedMiddlewares,
|
|
123
|
+
observability,
|
|
124
|
+
reservedArgument,
|
|
115
125
|
createLayer: (ctx) =>
|
|
116
126
|
Layer.mergeAll(
|
|
117
127
|
RegisteredFunction.actionLayer(databaseSchema, ctx),
|
package/src/RegistryItems.ts
CHANGED
|
@@ -38,3 +38,34 @@ export const middlewareKeys = (items: RegistryItems): ReadonlyArray<string> =>
|
|
|
38
38
|
Array.filter(MiddlewareRegistryItem.isMiddlewareRegistryItem),
|
|
39
39
|
Array.map((item) => item.middlewareSpec.key),
|
|
40
40
|
);
|
|
41
|
+
|
|
42
|
+
export interface MissingMiddleware {
|
|
43
|
+
readonly functionName: string;
|
|
44
|
+
readonly middlewareKey: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const missingRequired = (
|
|
48
|
+
items: RegistryItems,
|
|
49
|
+
required: ReadonlyArray<{ readonly key: string }>,
|
|
50
|
+
groupPath = "",
|
|
51
|
+
): ReadonlyArray<MissingMiddleware> =>
|
|
52
|
+
Object.entries(items)
|
|
53
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
54
|
+
.flatMap(([functionName, value]) =>
|
|
55
|
+
FunctionRegistryItem.isFunctionRegistryItem(value)
|
|
56
|
+
? required.flatMap(({ key }) =>
|
|
57
|
+
value._tag === "Confect" &&
|
|
58
|
+
value.middlewareAttachments.some(({ spec }) => spec.key === key)
|
|
59
|
+
? []
|
|
60
|
+
: [
|
|
61
|
+
{
|
|
62
|
+
functionName:
|
|
63
|
+
groupPath === ""
|
|
64
|
+
? functionName
|
|
65
|
+
: `${groupPath}.${functionName}`,
|
|
66
|
+
middlewareKey: key,
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
)
|
|
70
|
+
: [],
|
|
71
|
+
);
|