@confect/core 9.2.4 → 9.3.0
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/CHANGELOG.md +51 -0
- package/dist/FunctionProvenance.d.ts +74 -3
- package/dist/FunctionProvenance.d.ts.map +1 -1
- package/dist/FunctionProvenance.js +43 -2
- package/dist/FunctionProvenance.js.map +1 -1
- package/dist/FunctionSpec.d.ts +42 -9
- package/dist/FunctionSpec.d.ts.map +1 -1
- package/dist/FunctionSpec.js +16 -2
- package/dist/FunctionSpec.js.map +1 -1
- package/dist/PaginationOptions.d.ts +31 -0
- package/dist/PaginationOptions.d.ts.map +1 -0
- package/dist/PaginationOptions.js +29 -0
- package/dist/PaginationOptions.js.map +1 -0
- package/dist/PaginationResult.d.ts +1 -0
- package/dist/PaginationResult.d.ts.map +1 -1
- package/dist/PaginationResult.js.map +1 -1
- package/dist/Ref.d.ts +34 -5
- package/dist/Ref.d.ts.map +1 -1
- package/dist/Ref.js +52 -14
- package/dist/Ref.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/tsconfig.src.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/src/FunctionProvenance.ts +147 -4
- package/src/FunctionSpec.ts +67 -1
- package/src/PaginationOptions.ts +25 -0
- package/src/PaginationResult.ts +3 -0
- package/src/Ref.ts +97 -28
- package/src/index.ts +1 -0
package/package.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@confect/core",
|
|
3
3
|
"description": "Shared specs and schemas used by all Confect packages",
|
|
4
|
-
"version": "9.
|
|
4
|
+
"version": "9.3.0",
|
|
5
5
|
"author": "RJ Dellecese",
|
|
6
6
|
"bugs": {
|
|
7
7
|
"url": "https://github.com/rjdellecese/confect/issues"
|
|
8
8
|
},
|
|
9
9
|
"devDependencies": {
|
|
10
10
|
"@ark/attest": "0.56.3",
|
|
11
|
-
"@effect/vitest": "0.
|
|
11
|
+
"@effect/vitest": "0.30.0",
|
|
12
12
|
"@types/node": "26.1.1",
|
|
13
|
-
"effect": "3.
|
|
14
|
-
"tsdown": "0.22.
|
|
15
|
-
"tsx": "4.23.
|
|
13
|
+
"effect": "3.22.0",
|
|
14
|
+
"tsdown": "0.22.14",
|
|
15
|
+
"tsx": "4.23.1",
|
|
16
16
|
"typescript": "6.0.3",
|
|
17
17
|
"unrun": "0.3.1",
|
|
18
18
|
"vitest": "4.1.10"
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import type { DefaultFunctionArgs } from "convex/server";
|
|
2
|
-
import type { Schema } from "effect";
|
|
3
2
|
import * as Data from "effect/Data";
|
|
3
|
+
import * as Schema from "effect/Schema";
|
|
4
4
|
import * as Lazy from "./Lazy";
|
|
5
|
+
import * as PaginationOptions from "./PaginationOptions";
|
|
6
|
+
import * as PaginationResult from "./PaginationResult";
|
|
5
7
|
|
|
6
8
|
export type FunctionProvenance = Data.TaggedEnum<{
|
|
7
9
|
Confect: {
|
|
8
10
|
args: Schema.Schema.AnyNoContext;
|
|
9
11
|
returns: Schema.Schema.AnyNoContext;
|
|
10
12
|
error?: Schema.Schema.AnyNoContext;
|
|
13
|
+
kind: ConfectKind;
|
|
11
14
|
};
|
|
12
15
|
Convex: {
|
|
13
16
|
/** @internal */
|
|
@@ -17,15 +20,43 @@ export type FunctionProvenance = Data.TaggedEnum<{
|
|
|
17
20
|
};
|
|
18
21
|
}>;
|
|
19
22
|
|
|
23
|
+
/**
|
|
24
|
+
* The declaration shape of a Confect function — orthogonal to both the
|
|
25
|
+
* provenance origin (Confect vs Convex) and the function type
|
|
26
|
+
* (query/mutation/action). A `Standard` function declares its `args` and
|
|
27
|
+
* `returns` schemas directly; a `Paginated` function declares `userArgs` and
|
|
28
|
+
* `item` schemas from which the Convex-facing `args`/`returns` are composed.
|
|
29
|
+
*/
|
|
30
|
+
export type ConfectKind = Standard | Paginated;
|
|
31
|
+
|
|
32
|
+
export interface Standard {
|
|
33
|
+
readonly _tag: "Standard";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface Paginated<
|
|
37
|
+
UserArgs extends AnyUserArgs = AnyUserArgs,
|
|
38
|
+
Item extends Schema.Schema.AnyNoContext = Schema.Schema.AnyNoContext,
|
|
39
|
+
> {
|
|
40
|
+
readonly _tag: "Paginated";
|
|
41
|
+
/** User-declared args — no `paginationOpts`. */
|
|
42
|
+
readonly userArgs: UserArgs;
|
|
43
|
+
/** Page element schema. */
|
|
44
|
+
readonly item: Item;
|
|
45
|
+
/** Mutable array of items — the page decode target. */
|
|
46
|
+
readonly page: Schema.Schema.AnyNoContext;
|
|
47
|
+
}
|
|
48
|
+
|
|
20
49
|
export interface Confect<
|
|
21
50
|
Args extends Schema.Schema.AnyNoContext,
|
|
22
51
|
Returns extends Schema.Schema.AnyNoContext,
|
|
23
52
|
Error extends Schema.Schema.AnyNoContext = never,
|
|
53
|
+
Kind extends ConfectKind = ConfectKind,
|
|
24
54
|
> {
|
|
25
55
|
readonly _tag: "Confect";
|
|
26
56
|
readonly args: Args;
|
|
27
57
|
readonly returns: Returns;
|
|
28
58
|
readonly error?: Error;
|
|
59
|
+
readonly kind: Kind;
|
|
29
60
|
}
|
|
30
61
|
|
|
31
62
|
export interface AnyConfect extends Confect<
|
|
@@ -44,6 +75,8 @@ export interface AnyConvex extends Convex<DefaultFunctionArgs, any> {}
|
|
|
44
75
|
|
|
45
76
|
export const FunctionProvenance = Data.taggedEnum<FunctionProvenance>();
|
|
46
77
|
|
|
78
|
+
const Standard: Standard = { _tag: "Standard" };
|
|
79
|
+
|
|
47
80
|
/**
|
|
48
81
|
* Build a `Confect` provenance from lazy schema thunks. `args`, `returns`,
|
|
49
82
|
* and `error` are exposed as sync lazy memoised getters (via {@link Lazy.defineProperty})
|
|
@@ -68,8 +101,8 @@ export const Confect = <
|
|
|
68
101
|
args: () => Args,
|
|
69
102
|
returns: () => Returns,
|
|
70
103
|
error?: () => Error,
|
|
71
|
-
): Confect<Args, Returns, Error> => {
|
|
72
|
-
const provenance = { _tag: "Confect" as const };
|
|
104
|
+
): Confect<Args, Returns, Error, Standard> => {
|
|
105
|
+
const provenance = { _tag: "Confect" as const, kind: Standard };
|
|
73
106
|
|
|
74
107
|
Lazy.defineProperty(provenance, "args", args);
|
|
75
108
|
Lazy.defineProperty(provenance, "returns", returns);
|
|
@@ -77,7 +110,117 @@ export const Confect = <
|
|
|
77
110
|
Lazy.defineProperty(provenance, "error", error);
|
|
78
111
|
}
|
|
79
112
|
|
|
80
|
-
return provenance as Confect<Args, Returns, Error>;
|
|
113
|
+
return provenance as Confect<Args, Returns, Error, Standard>;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Any struct-shaped, context-free schema usable as a paginated query's user
|
|
118
|
+
* args. A structural bound rather than `Schema.Struct<Schema.Struct.Fields>`
|
|
119
|
+
* because `Schema.Struct` is not covariant in its fields parameter — concrete
|
|
120
|
+
* structs do not extend `Schema.Struct<Schema.Struct.Fields>`.
|
|
121
|
+
*/
|
|
122
|
+
// eslint-disable-next-line import/namespace -- oxlint's namespace resolution misses type-only exports, and `Schema` is an interface/namespace
|
|
123
|
+
export interface AnyUserArgs extends Schema.Schema.AnyNoContext {
|
|
124
|
+
readonly fields: Schema.Struct.Fields;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* The composed args schema of a paginated query: the user-declared fields
|
|
129
|
+
* plus the `paginationOpts` field managed by Convex's pagination protocol.
|
|
130
|
+
*
|
|
131
|
+
* The `extends infer ... extends` conditional lets TypeScript accept the
|
|
132
|
+
* composed struct where `Schema.Schema.AnyNoContext` is required even while
|
|
133
|
+
* `UserArgs` is an unresolved type parameter; for concrete user args it
|
|
134
|
+
* evaluates away entirely.
|
|
135
|
+
*/
|
|
136
|
+
export type PaginatedArgs<UserArgs extends AnyUserArgs> =
|
|
137
|
+
Schema.Struct<
|
|
138
|
+
UserArgs["fields"] & {
|
|
139
|
+
paginationOpts: typeof PaginationOptions.PaginationOptions;
|
|
140
|
+
}
|
|
141
|
+
> extends infer ComposedArgs extends Schema.Schema.AnyNoContext
|
|
142
|
+
? ComposedArgs
|
|
143
|
+
: never;
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* The composed returns schema of a paginated query: a `PaginationResult` of
|
|
147
|
+
* the item schema. Same deferred-conditional device as {@link PaginatedArgs}.
|
|
148
|
+
*/
|
|
149
|
+
export type PaginatedReturns<Item extends Schema.Schema.AnyNoContext> =
|
|
150
|
+
PaginationResult.PaginationResult<Item> extends infer ComposedReturns extends
|
|
151
|
+
Schema.Schema.AnyNoContext
|
|
152
|
+
? ComposedReturns
|
|
153
|
+
: never;
|
|
154
|
+
|
|
155
|
+
export interface ConfectPaginated<
|
|
156
|
+
UserArgs extends AnyUserArgs,
|
|
157
|
+
Item extends Schema.Schema.AnyNoContext,
|
|
158
|
+
Error extends Schema.Schema.AnyNoContext = never,
|
|
159
|
+
> extends Confect<
|
|
160
|
+
PaginatedArgs<UserArgs>,
|
|
161
|
+
PaginatedReturns<Item>,
|
|
162
|
+
Error,
|
|
163
|
+
Paginated<UserArgs, Item>
|
|
164
|
+
> {}
|
|
165
|
+
|
|
166
|
+
export interface AnyConfectPaginated extends ConfectPaginated<
|
|
167
|
+
AnyUserArgs,
|
|
168
|
+
Schema.Schema.AnyNoContext,
|
|
169
|
+
Schema.Schema.AnyNoContext
|
|
170
|
+
> {}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Build the provenance of a paginated query from lazy schema thunks, with the
|
|
174
|
+
* same laziness contract as {@link Confect}. The user-facing schemas live on
|
|
175
|
+
* the `Paginated` kind (`kind.userArgs`, `kind.item`, `kind.page`) — the kind
|
|
176
|
+
* container is built eagerly (it is cheap) while the schemas inside it stay
|
|
177
|
+
* lazy. The composed Convex-facing `args`/`returns` are derived lazily from
|
|
178
|
+
* them: `args` spreads the user fields plus `paginationOpts`, and `returns`
|
|
179
|
+
* wraps the item in `PaginationResult`. Composition living here means the
|
|
180
|
+
* composed schemas can never drift from the stored user schemas.
|
|
181
|
+
*/
|
|
182
|
+
export const ConfectPaginated = <
|
|
183
|
+
UserArgs extends AnyUserArgs,
|
|
184
|
+
Item extends Schema.Schema.AnyNoContext,
|
|
185
|
+
Error extends Schema.Schema.AnyNoContext = never,
|
|
186
|
+
>(
|
|
187
|
+
userArgs: () => UserArgs,
|
|
188
|
+
item: () => Item,
|
|
189
|
+
error?: () => Error,
|
|
190
|
+
): ConfectPaginated<UserArgs, Item, Error> => {
|
|
191
|
+
const kind = { _tag: "Paginated" as const };
|
|
192
|
+
const paginatedKind = kind as Paginated<UserArgs, Item>;
|
|
193
|
+
|
|
194
|
+
Lazy.defineProperty(kind, "userArgs", userArgs);
|
|
195
|
+
Lazy.defineProperty(kind, "item", item);
|
|
196
|
+
Lazy.defineProperty(kind, "page", () =>
|
|
197
|
+
Schema.mutable(Schema.Array(paginatedKind.item)),
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
const provenance = { _tag: "Confect" as const, kind: paginatedKind };
|
|
201
|
+
const self = provenance as ConfectPaginated<UserArgs, Item, Error>;
|
|
202
|
+
|
|
203
|
+
Lazy.defineProperty(provenance, "args", () => {
|
|
204
|
+
const fields = paginatedKind.userArgs.fields;
|
|
205
|
+
if ("paginationOpts" in fields) {
|
|
206
|
+
throw new globalThis.Error(
|
|
207
|
+
"A paginated query's args schema must not declare `paginationOpts` — " +
|
|
208
|
+
"it is added automatically from the `PaginationOptions` schema",
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
return Schema.Struct({
|
|
212
|
+
...fields,
|
|
213
|
+
paginationOpts: PaginationOptions.PaginationOptions,
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
Lazy.defineProperty(provenance, "returns", () =>
|
|
217
|
+
PaginationResult.PaginationResult(paginatedKind.item),
|
|
218
|
+
);
|
|
219
|
+
if (error !== undefined) {
|
|
220
|
+
Lazy.defineProperty(provenance, "error", error);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return self;
|
|
81
224
|
};
|
|
82
225
|
|
|
83
226
|
export const Convex = <_Args extends DefaultFunctionArgs, _Returns>() =>
|
package/src/FunctionSpec.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type {
|
|
|
5
5
|
RegisteredMutation,
|
|
6
6
|
RegisteredQuery,
|
|
7
7
|
} from "convex/server";
|
|
8
|
-
import
|
|
8
|
+
import * as Schema from "effect/Schema";
|
|
9
9
|
import * as Predicate from "effect/Predicate";
|
|
10
10
|
import * as FunctionProvenance from "./FunctionProvenance";
|
|
11
11
|
import { validateConfectFunctionIdentifier } from "./Identifier";
|
|
@@ -244,11 +244,77 @@ const make =
|
|
|
244
244
|
});
|
|
245
245
|
};
|
|
246
246
|
|
|
247
|
+
/**
|
|
248
|
+
* `Schema.Struct` fields that must not declare `paginationOpts`. Used to
|
|
249
|
+
* reject a user args schema that redeclares the field the paginated
|
|
250
|
+
* constructors add themselves; the check reports at the `args` thunk with an
|
|
251
|
+
* unsatisfiable type when violated.
|
|
252
|
+
*/
|
|
253
|
+
type ForbidPaginationOpts<UserArgs extends FunctionProvenance.AnyUserArgs> =
|
|
254
|
+
"paginationOpts" extends keyof UserArgs["fields"]
|
|
255
|
+
? { readonly fields: { readonly paginationOpts: never } }
|
|
256
|
+
: unknown;
|
|
257
|
+
|
|
258
|
+
const makePaginated =
|
|
259
|
+
<
|
|
260
|
+
RuntimeAndFunctionType_ extends
|
|
261
|
+
RuntimeAndFunctionType.RuntimeAndFunctionType,
|
|
262
|
+
FunctionVisibility_ extends FunctionVisibility,
|
|
263
|
+
>(
|
|
264
|
+
runtimeAndFunctionType: RuntimeAndFunctionType_,
|
|
265
|
+
functionVisibility: FunctionVisibility_,
|
|
266
|
+
) =>
|
|
267
|
+
<
|
|
268
|
+
const Name_ extends string,
|
|
269
|
+
Item_ extends Schema.Schema.AnyNoContext,
|
|
270
|
+
UserArgs_ extends FunctionProvenance.AnyUserArgs = Schema.Struct<{}>,
|
|
271
|
+
Error_ extends Schema.Schema.AnyNoContext = never,
|
|
272
|
+
>({
|
|
273
|
+
name,
|
|
274
|
+
args,
|
|
275
|
+
item,
|
|
276
|
+
error,
|
|
277
|
+
}: {
|
|
278
|
+
name: Name_;
|
|
279
|
+
/** User-declared args, without `paginationOpts` — it is added automatically. */
|
|
280
|
+
args?: (() => UserArgs_) & ForbidPaginationOpts<UserArgs_>;
|
|
281
|
+
/** The page element schema. */
|
|
282
|
+
item: () => Item_;
|
|
283
|
+
error?: () => Error_;
|
|
284
|
+
}): FunctionSpec<
|
|
285
|
+
RuntimeAndFunctionType_,
|
|
286
|
+
FunctionVisibility_,
|
|
287
|
+
Name_,
|
|
288
|
+
FunctionProvenance.ConfectPaginated<UserArgs_, Item_, Error_>
|
|
289
|
+
> => {
|
|
290
|
+
validateConfectFunctionIdentifier(name);
|
|
291
|
+
|
|
292
|
+
return Object.assign(Object.create(Proto), {
|
|
293
|
+
runtimeAndFunctionType,
|
|
294
|
+
functionVisibility,
|
|
295
|
+
name,
|
|
296
|
+
functionProvenance: FunctionProvenance.ConfectPaginated(
|
|
297
|
+
// When `args` is omitted, `UserArgs_` is its `Schema.Struct<{}>` default.
|
|
298
|
+
args ?? ((() => Schema.Struct({})) as unknown as () => UserArgs_),
|
|
299
|
+
item,
|
|
300
|
+
error,
|
|
301
|
+
),
|
|
302
|
+
});
|
|
303
|
+
};
|
|
304
|
+
|
|
247
305
|
export const publicQuery = make(RuntimeAndFunctionType.ConvexQuery, "public");
|
|
248
306
|
export const internalQuery = make(
|
|
249
307
|
RuntimeAndFunctionType.ConvexQuery,
|
|
250
308
|
"internal",
|
|
251
309
|
);
|
|
310
|
+
export const publicPaginatedQuery = makePaginated(
|
|
311
|
+
RuntimeAndFunctionType.ConvexQuery,
|
|
312
|
+
"public",
|
|
313
|
+
);
|
|
314
|
+
export const internalPaginatedQuery = makePaginated(
|
|
315
|
+
RuntimeAndFunctionType.ConvexQuery,
|
|
316
|
+
"internal",
|
|
317
|
+
);
|
|
252
318
|
export const publicMutation = make(
|
|
253
319
|
RuntimeAndFunctionType.ConvexMutation,
|
|
254
320
|
"public",
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as Schema from "effect/Schema";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Schema for the `paginationOpts` argument that every paginated query
|
|
5
|
+
* receives. Mirrors `paginationOptsValidator` from `convex/server`.
|
|
6
|
+
*
|
|
7
|
+
* Use this as the `paginationOpts` field of a paginated query's args schema.
|
|
8
|
+
* Beyond `numItems` and `cursor`, Convex's pagination protocol sends the
|
|
9
|
+
* optional fields (`endCursor`, `id`, `maximumRowsRead`, `maximumBytesRead`)
|
|
10
|
+
* with real requests — most notably `usePaginatedQuery` from `convex/react`
|
|
11
|
+
* always includes `id`, and includes `endCursor` when splitting pages.
|
|
12
|
+
* Declaring them is required for the generated Convex argument validator to
|
|
13
|
+
* accept those requests and for the fields to survive decoding and reach
|
|
14
|
+
* `paginate`.
|
|
15
|
+
*/
|
|
16
|
+
export const PaginationOptions = Schema.Struct({
|
|
17
|
+
numItems: Schema.Number,
|
|
18
|
+
cursor: Schema.Union(Schema.String, Schema.Null),
|
|
19
|
+
endCursor: Schema.optionalWith(Schema.Union(Schema.String, Schema.Null), {
|
|
20
|
+
exact: true,
|
|
21
|
+
}),
|
|
22
|
+
id: Schema.optionalWith(Schema.Number, { exact: true }),
|
|
23
|
+
maximumRowsRead: Schema.optionalWith(Schema.Number, { exact: true }),
|
|
24
|
+
maximumBytesRead: Schema.optionalWith(Schema.Number, { exact: true }),
|
|
25
|
+
});
|
package/src/PaginationResult.ts
CHANGED
package/src/Ref.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
FunctionReference as ConvexFunctionReference,
|
|
3
3
|
FunctionVisibility,
|
|
4
|
+
PaginationOptions,
|
|
5
|
+
PaginationResult,
|
|
4
6
|
} from "convex/server";
|
|
5
7
|
import { makeFunctionReference } from "convex/server";
|
|
6
8
|
import type { Value } from "convex/values";
|
|
@@ -45,6 +47,17 @@ export interface AnyQuery extends Ref<
|
|
|
45
47
|
any
|
|
46
48
|
> {}
|
|
47
49
|
|
|
50
|
+
export interface AnyPublicPaginatedQuery extends Ref<
|
|
51
|
+
RuntimeAndFunctionType.AnyQuery,
|
|
52
|
+
"public",
|
|
53
|
+
{
|
|
54
|
+
[key: string]: any;
|
|
55
|
+
paginationOpts: PaginationOptions;
|
|
56
|
+
},
|
|
57
|
+
PaginationResult<any>,
|
|
58
|
+
any
|
|
59
|
+
> {}
|
|
60
|
+
|
|
48
61
|
export interface AnyMutation extends Ref<
|
|
49
62
|
RuntimeAndFunctionType.AnyMutation,
|
|
50
63
|
FunctionVisibility,
|
|
@@ -239,7 +252,7 @@ export const decodeReturns = <Ref_ extends Any>(
|
|
|
239
252
|
): Effect.Effect<Returns<Ref_>, ParseResult.ParseError> =>
|
|
240
253
|
Match.value(ref.functionSpec.functionProvenance).pipe(
|
|
241
254
|
Match.tag("Confect", (confectFunctionProvenance) =>
|
|
242
|
-
Schema.
|
|
255
|
+
Schema.decodeUnknown(confectFunctionProvenance.returns)(returns),
|
|
243
256
|
),
|
|
244
257
|
Match.tag("Convex", () => Effect.succeed(returns)),
|
|
245
258
|
Match.exhaustive,
|
|
@@ -263,7 +276,7 @@ export const decodeArgsSync = <Ref_ extends Any>(
|
|
|
263
276
|
): Args<Ref_> =>
|
|
264
277
|
Match.value(ref.functionSpec.functionProvenance).pipe(
|
|
265
278
|
Match.tag("Confect", (confectFunctionProvenance) =>
|
|
266
|
-
Schema.
|
|
279
|
+
Schema.decodeUnknownSync(confectFunctionProvenance.args)(encodedArgs),
|
|
267
280
|
),
|
|
268
281
|
Match.tag("Convex", () => encodedArgs),
|
|
269
282
|
Match.exhaustive,
|
|
@@ -287,7 +300,9 @@ export const decodeReturnsSync = <Ref_ extends Any>(
|
|
|
287
300
|
): Returns<Ref_> =>
|
|
288
301
|
Match.value(ref.functionSpec.functionProvenance).pipe(
|
|
289
302
|
Match.tag("Confect", (confectFunctionProvenance) =>
|
|
290
|
-
Schema.
|
|
303
|
+
Schema.decodeUnknownSync(confectFunctionProvenance.returns)(
|
|
304
|
+
encodedReturns,
|
|
305
|
+
),
|
|
291
306
|
),
|
|
292
307
|
Match.tag("Convex", () => encodedReturns),
|
|
293
308
|
Match.exhaustive,
|
|
@@ -314,7 +329,7 @@ export const decodeErrorOrElse =
|
|
|
314
329
|
<Ref_ extends Any, E>(ref: Ref_, mapUnknownError: (error: unknown) => E) =>
|
|
315
330
|
(error: unknown): Error<Ref_> | E => {
|
|
316
331
|
if (isConvexError(error)) {
|
|
317
|
-
const decoded =
|
|
332
|
+
const decoded = decodeErrorOption(ref, error.data);
|
|
318
333
|
if (Option.isSome(decoded)) {
|
|
319
334
|
return decoded.value;
|
|
320
335
|
}
|
|
@@ -337,7 +352,7 @@ export const decodeError = <Ref_ extends Any>(
|
|
|
337
352
|
Match.tag("Confect", (confectFunctionProvenance) =>
|
|
338
353
|
"error" in confectFunctionProvenance
|
|
339
354
|
? Effect.map(
|
|
340
|
-
Schema.
|
|
355
|
+
Schema.decodeUnknown(confectFunctionProvenance.error)(encodedError),
|
|
341
356
|
Option.some,
|
|
342
357
|
)
|
|
343
358
|
: Effect.succeed(Option.none<Error<Ref_>>()),
|
|
@@ -347,42 +362,96 @@ export const decodeError = <Ref_ extends Any>(
|
|
|
347
362
|
);
|
|
348
363
|
|
|
349
364
|
/**
|
|
350
|
-
* Synchronous counterpart to `decodeError`.
|
|
351
|
-
*
|
|
365
|
+
* Synchronous counterpart to `decodeError`. Returns `None` when the value is
|
|
366
|
+
* not this ref's typed error — either because the ref declares no `error`
|
|
367
|
+
* schema, or because `encodedError` doesn't match the one it declares.
|
|
368
|
+
*
|
|
369
|
+
* The second case is reachable in normal operation: Convex raises its own
|
|
370
|
+
* `ConvexError`s (an `InvalidCursor` pagination error, for instance), and
|
|
371
|
+
* those never match a user-declared error schema. Callers pair this with a
|
|
372
|
+
* fallback that surfaces the original error, so failing to decode must not
|
|
373
|
+
* throw — a `ParseError` here would replace the real error with an opaque one
|
|
374
|
+
* and lose the only useful diagnostic. Hence the `Option` suffix rather than
|
|
375
|
+
* `Sync`, matching `Schema.decodeUnknownOption`: the sibling `*Sync` helpers
|
|
376
|
+
* in this module all throw on a parse failure, and this one deliberately
|
|
377
|
+
* doesn't.
|
|
352
378
|
*/
|
|
353
|
-
export const
|
|
379
|
+
export const decodeErrorOption = <Ref_ extends Any>(
|
|
354
380
|
ref: Ref_,
|
|
355
381
|
encodedError: unknown,
|
|
356
382
|
): Option.Option<Error<Ref_>> =>
|
|
357
383
|
Match.value(ref.functionSpec.functionProvenance).pipe(
|
|
358
384
|
Match.tag("Confect", (confectFunctionProvenance) =>
|
|
359
385
|
"error" in confectFunctionProvenance
|
|
360
|
-
?
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
) as Error<Ref_>,
|
|
364
|
-
)
|
|
386
|
+
? (Schema.decodeUnknownOption(confectFunctionProvenance.error)(
|
|
387
|
+
encodedError,
|
|
388
|
+
) as Option.Option<Error<Ref_>>)
|
|
365
389
|
: Option.none<Error<Ref_>>(),
|
|
366
390
|
),
|
|
367
391
|
Match.tag("Convex", () => Option.none<Error<Ref_>>()),
|
|
368
392
|
Match.exhaustive,
|
|
369
393
|
);
|
|
370
394
|
|
|
371
|
-
|
|
395
|
+
const missingPaginatedProvenanceError = (ref: Any) =>
|
|
396
|
+
new globalThis.Error(
|
|
397
|
+
`Paginated query ref "${getConvexFunctionName(ref)}" was not built with ` +
|
|
398
|
+
"`FunctionSpec.publicPaginatedQuery` (or `FunctionSpec.internalPaginatedQuery`). " +
|
|
399
|
+
"Paginated encoding and decoding require the user-args and item schemas " +
|
|
400
|
+
"those constructors store. Define the function as, e.g.:\n\n" +
|
|
401
|
+
" FunctionSpec.publicPaginatedQuery({\n" +
|
|
402
|
+
' name: "...",\n' +
|
|
403
|
+
" args: () => Schema.Struct({ ... }), // optional; without paginationOpts\n" +
|
|
404
|
+
" item: () => ItemSchema,\n" +
|
|
405
|
+
" })",
|
|
406
|
+
);
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Encode the args of a paginated query ref via its user-args schema —
|
|
410
|
+
* `paginationOpts` is excluded, since the pagination protocol fields are
|
|
411
|
+
* managed by the client (e.g. `usePaginatedQuery` from `convex/react`), not by
|
|
412
|
+
* the caller. Requires a ref built with `FunctionSpec.publicPaginatedQuery`
|
|
413
|
+
* (or `internalPaginatedQuery`).
|
|
414
|
+
*/
|
|
415
|
+
export const encodePaginatedQueryArgsSync = <
|
|
416
|
+
Ref_ extends AnyPublicPaginatedQuery,
|
|
417
|
+
>(
|
|
372
418
|
ref: Ref_,
|
|
373
|
-
|
|
419
|
+
args: Omit<Args<Ref_>, "paginationOpts">,
|
|
374
420
|
): unknown =>
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
421
|
+
Match.value(ref.functionSpec.functionProvenance).pipe(
|
|
422
|
+
Match.tag("Confect", (confectFunctionProvenance) => {
|
|
423
|
+
if (confectFunctionProvenance.kind._tag !== "Paginated") {
|
|
424
|
+
throw missingPaginatedProvenanceError(ref);
|
|
425
|
+
}
|
|
426
|
+
return Schema.encodeUnknownSync(confectFunctionProvenance.kind.userArgs)(
|
|
427
|
+
args,
|
|
428
|
+
);
|
|
429
|
+
}),
|
|
430
|
+
Match.tag("Convex", () => args),
|
|
431
|
+
Match.exhaustive,
|
|
432
|
+
);
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Decode a page of a paginated query's results via the ref's item schema.
|
|
436
|
+
* Requires a ref built with `FunctionSpec.publicPaginatedQuery` (or
|
|
437
|
+
* `internalPaginatedQuery`).
|
|
438
|
+
*/
|
|
439
|
+
export const decodePaginationPageSync = <Ref_ extends AnyPublicPaginatedQuery>(
|
|
440
|
+
ref: Ref_,
|
|
441
|
+
encodedPage: unknown,
|
|
442
|
+
): Returns<Ref_>["page"] =>
|
|
443
|
+
Match.value(ref.functionSpec.functionProvenance).pipe(
|
|
444
|
+
Match.tag("Confect", (confectFunctionProvenance) => {
|
|
445
|
+
if (confectFunctionProvenance.kind._tag !== "Paginated") {
|
|
446
|
+
throw missingPaginatedProvenanceError(ref);
|
|
447
|
+
}
|
|
448
|
+
return Schema.decodeUnknownSync(confectFunctionProvenance.kind.page)(
|
|
449
|
+
encodedPage,
|
|
450
|
+
);
|
|
451
|
+
}),
|
|
452
|
+
Match.tag("Convex", () => encodedPage),
|
|
453
|
+
Match.exhaustive,
|
|
454
|
+
) as Returns<Ref_>["page"];
|
|
386
455
|
|
|
387
456
|
/**
|
|
388
457
|
* Encode args via the ref's args schema, invoke `call`, decode returns via the
|
|
@@ -411,7 +480,7 @@ export const runWithCodec = <Ref_ extends Any, E = never>(
|
|
|
411
480
|
try: () => Promise.resolve(call(functionReference, encodedArgs)),
|
|
412
481
|
catch: (error): Error<Ref_> | E => {
|
|
413
482
|
if (isConvexError(error)) {
|
|
414
|
-
const decoded =
|
|
483
|
+
const decoded = decodeErrorOption(ref, error.data);
|
|
415
484
|
if (Option.isSome(decoded)) {
|
|
416
485
|
return decoded.value;
|
|
417
486
|
}
|
|
@@ -429,7 +498,7 @@ export const runWithCodec = <Ref_ extends Any, E = never>(
|
|
|
429
498
|
confectFunctionProvenance.args,
|
|
430
499
|
)(args);
|
|
431
500
|
const encodedReturns = yield* invoke(encodedArgs);
|
|
432
|
-
return yield* Schema.
|
|
501
|
+
return yield* Schema.decodeUnknown(confectFunctionProvenance.returns)(
|
|
433
502
|
encodedReturns,
|
|
434
503
|
);
|
|
435
504
|
}),
|
package/src/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ export * as GroupPath from "./GroupPath";
|
|
|
5
5
|
export * as GroupSpec from "./GroupSpec";
|
|
6
6
|
export * as Identifier from "./Identifier";
|
|
7
7
|
export * as Lazy from "./Lazy";
|
|
8
|
+
export * as PaginationOptions from "./PaginationOptions";
|
|
8
9
|
export * as PaginationResult from "./PaginationResult";
|
|
9
10
|
export * as Ref from "./Ref";
|
|
10
11
|
export * as Refs from "./Refs";
|