@confect/core 10.0.0-next.8 → 10.0.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.
Files changed (103) hide show
  1. package/CHANGELOG.md +966 -46
  2. package/README.md +3 -1
  3. package/dist/FunctionProvenance.d.ts +166 -68
  4. package/dist/FunctionProvenance.d.ts.map +1 -1
  5. package/dist/FunctionProvenance.js +55 -13
  6. package/dist/FunctionProvenance.js.map +1 -1
  7. package/dist/FunctionSpec.d.ts +320 -138
  8. package/dist/FunctionSpec.d.ts.map +1 -1
  9. package/dist/FunctionSpec.js +56 -13
  10. package/dist/FunctionSpec.js.map +1 -1
  11. package/dist/GenericId.d.ts.map +1 -1
  12. package/dist/GenericId.js +1 -1
  13. package/dist/GroupPath.d.ts +6 -10
  14. package/dist/GroupPath.d.ts.map +1 -1
  15. package/dist/GroupPath.js.map +1 -1
  16. package/dist/GroupSpec.d.ts +25 -12
  17. package/dist/GroupSpec.d.ts.map +1 -1
  18. package/dist/GroupSpec.js +51 -14
  19. package/dist/GroupSpec.js.map +1 -1
  20. package/dist/Identifier.d.ts +6 -5
  21. package/dist/Identifier.d.ts.map +1 -1
  22. package/dist/Identifier.js +6 -5
  23. package/dist/Identifier.js.map +1 -1
  24. package/dist/Lazy.d.ts +11 -14
  25. package/dist/Lazy.d.ts.map +1 -1
  26. package/dist/Lazy.js +11 -14
  27. package/dist/Lazy.js.map +1 -1
  28. package/dist/MiddlewareAttachment.d.ts +171 -0
  29. package/dist/MiddlewareAttachment.d.ts.map +1 -0
  30. package/dist/MiddlewareAttachment.js +48 -0
  31. package/dist/MiddlewareAttachment.js.map +1 -0
  32. package/dist/MiddlewareSpec.d.ts +198 -0
  33. package/dist/MiddlewareSpec.d.ts.map +1 -0
  34. package/dist/MiddlewareSpec.js +60 -0
  35. package/dist/MiddlewareSpec.js.map +1 -0
  36. package/dist/PaginationError.d.ts +20 -0
  37. package/dist/PaginationError.d.ts.map +1 -0
  38. package/dist/PaginationError.js +31 -0
  39. package/dist/PaginationError.js.map +1 -0
  40. package/dist/PaginationOptions.d.ts +23 -0
  41. package/dist/PaginationOptions.d.ts.map +1 -0
  42. package/dist/PaginationOptions.js +29 -0
  43. package/dist/PaginationOptions.js.map +1 -0
  44. package/dist/PaginationResult.d.ts +1 -0
  45. package/dist/PaginationResult.d.ts.map +1 -1
  46. package/dist/PaginationResult.js.map +1 -1
  47. package/dist/Ref.d.ts +135 -47
  48. package/dist/Ref.d.ts.map +1 -1
  49. package/dist/Ref.js +104 -46
  50. package/dist/Ref.js.map +1 -1
  51. package/dist/Refs.d.ts +10 -5
  52. package/dist/Refs.d.ts.map +1 -1
  53. package/dist/Refs.js +5 -4
  54. package/dist/Refs.js.map +1 -1
  55. package/dist/SchemaToValidator.d.ts +99 -0
  56. package/dist/SchemaToValidator.d.ts.map +1 -0
  57. package/dist/SchemaToValidator.js +158 -0
  58. package/dist/SchemaToValidator.js.map +1 -0
  59. package/dist/Spec.d.ts +16 -12
  60. package/dist/Spec.d.ts.map +1 -1
  61. package/dist/Spec.js +14 -9
  62. package/dist/Spec.js.map +1 -1
  63. package/dist/SystemFields.d.ts +16 -15
  64. package/dist/SystemFields.d.ts.map +1 -1
  65. package/dist/SystemFields.js +16 -15
  66. package/dist/SystemFields.js.map +1 -1
  67. package/dist/Table.d.ts +75 -0
  68. package/dist/Table.d.ts.map +1 -0
  69. package/dist/Table.js +77 -0
  70. package/dist/Table.js.map +1 -0
  71. package/dist/Types.d.ts +8 -2
  72. package/dist/Types.d.ts.map +1 -1
  73. package/dist/Types.js.map +1 -1
  74. package/dist/UserIdentity.d.ts.map +1 -1
  75. package/dist/index.d.ts +6 -1
  76. package/dist/index.d.ts.map +1 -1
  77. package/dist/index.js +9 -4
  78. package/package.json +17 -15
  79. package/src/FunctionProvenance.ts +197 -34
  80. package/src/FunctionSpec.ts +332 -86
  81. package/src/GroupPath.ts +6 -12
  82. package/src/GroupSpec.ts +184 -25
  83. package/src/Identifier.ts +6 -5
  84. package/src/Lazy.ts +11 -14
  85. package/src/MiddlewareAttachment.ts +106 -0
  86. package/src/MiddlewareSpec.ts +392 -0
  87. package/src/PaginationError.ts +78 -0
  88. package/src/PaginationOptions.ts +23 -0
  89. package/src/PaginationResult.ts +4 -0
  90. package/src/Ref.ts +468 -236
  91. package/src/Refs.ts +43 -18
  92. package/src/SchemaToValidator.ts +612 -0
  93. package/src/Spec.ts +50 -39
  94. package/src/SystemFields.ts +32 -36
  95. package/src/Table.ts +427 -0
  96. package/src/Types.ts +9 -3
  97. package/src/index.ts +6 -1
  98. package/dist/Registry.d.ts +0 -24
  99. package/dist/Registry.d.ts.map +0 -1
  100. package/dist/Registry.js +0 -19
  101. package/dist/Registry.js.map +0 -1
  102. package/dist/tsconfig.src.tsbuildinfo +0 -1
  103. package/src/Registry.ts +0 -30
@@ -0,0 +1,392 @@
1
+ import type { FunctionType, FunctionVisibility } from "convex/server";
2
+ import type * as Effect from "effect/Effect";
3
+ import * as Predicate from "effect/Predicate";
4
+ import type * as Schema from "effect/Schema";
5
+ import type { unhandled } from "effect/Types";
6
+ import type * as FunctionProvenance from "./FunctionProvenance";
7
+ import type * as FunctionSpec from "./FunctionSpec";
8
+ import * as Lazy from "./Lazy";
9
+ import type * as RuntimeAndFunctionType from "./RuntimeAndFunctionType";
10
+
11
+ export const TypeId = "~@confect/core/MiddlewareSpec";
12
+ export type TypeId = typeof TypeId;
13
+
14
+ export const isMiddlewareSpec = (u: unknown): u is AnyMiddlewareSpec =>
15
+ Predicate.hasProperty(u, TypeId);
16
+
17
+ export const allFunctionTypes: ReadonlyArray<FunctionType> = [
18
+ "query",
19
+ "mutation",
20
+ "action",
21
+ ];
22
+
23
+ export interface SupportedFunctionTypes {
24
+ readonly query: boolean;
25
+ readonly mutation: boolean;
26
+ readonly action: boolean;
27
+ }
28
+
29
+ /**
30
+ * Marker success type used by middleware to represent the handler's outcome
31
+ * without exposing its concrete success value.
32
+ *
33
+ * Mirrors `RpcMiddleware.SuccessValue` in Effect.
34
+ */
35
+ export interface SuccessValue {
36
+ readonly _: unique symbol;
37
+ }
38
+
39
+ /**
40
+ * The wrap-style middleware implementation shape: receives the downstream
41
+ * effect (the remaining middleware plus the function handler) and the covered
42
+ * invocation's metadata, and returns the effect that runs in its place.
43
+ *
44
+ * Mirrors `RpcMiddleware.RpcMiddleware` in Effect.
45
+ */
46
+ export interface MiddlewareImpl<Provides_, E, R, Options_ = never> {
47
+ (
48
+ effect: Effect.Effect<SuccessValue, E | unhandled, Provides_>,
49
+ context: MiddlewareOptions<Options_>,
50
+ ): Effect.Effect<SuccessValue, E | unhandled, R>;
51
+ }
52
+
53
+ export type MiddlewareOptions<Options_ = never> = {
54
+ readonly invocation: {
55
+ readonly name: string;
56
+ readonly functionType: FunctionType;
57
+ readonly functionVisibility: FunctionVisibility;
58
+ readonly args: unknown;
59
+ };
60
+ } & ([Options_] extends [never] ? unknown : { readonly options: Options_ });
61
+
62
+ export interface AnyMiddlewareImpl {
63
+ (
64
+ effect: Effect.Effect<SuccessValue, any, any>,
65
+ context: MiddlewareOptions | MiddlewareOptions<unknown>,
66
+ ): Effect.Effect<SuccessValue, any, any>;
67
+ }
68
+
69
+ /**
70
+ * The class shape produced by {@link MiddlewareSpec}. Only the static side is
71
+ * ever used—the class is a value-level carrier for the middleware's key, its
72
+ * declared functionTypes, and its (lazily-evaluated) error schema, plus the
73
+ * type-level `Provides`/`Error` metadata. It is deliberately not a
74
+ * `Context.Tag`: implementations are registered through the group `Registry`
75
+ * (like `FunctionImpl`s), not resolved from a Layer context.
76
+ */
77
+ export interface MiddlewareSpec<
78
+ in out Self,
79
+ Key_ extends string,
80
+ Provides_,
81
+ Requires_,
82
+ ErrorSchema_ extends Schema.Codec<any, any>,
83
+ FunctionTypes_ extends FunctionType,
84
+ OptionsSchema_ extends Schema.Schema<any> = never,
85
+ > {
86
+ new (_: never): {
87
+ readonly [TypeId]: TypeId;
88
+ readonly key: Key_;
89
+ };
90
+ readonly [TypeId]: TypeId;
91
+ readonly key: Key_;
92
+ readonly functionTypes: SupportedFunctionTypes;
93
+ readonly error?: ErrorSchema_;
94
+ readonly options?: OptionsSchema_;
95
+ readonly "~Provides": Provides_;
96
+ readonly "~Requires": Requires_;
97
+ readonly "~Error": ErrorSchema_;
98
+ readonly "~FunctionTypes": FunctionTypes_;
99
+ readonly "~Self": Self;
100
+ readonly "~Options": OptionsSchema_;
101
+ }
102
+
103
+ export interface AnyMiddlewareSpec {
104
+ readonly [TypeId]: TypeId;
105
+ readonly key: string;
106
+ readonly functionTypes: SupportedFunctionTypes;
107
+ readonly error?: Schema.Codec<any, any>;
108
+ readonly options?: Schema.Schema<any>;
109
+ readonly "~Provides": any;
110
+ readonly "~Requires": any;
111
+ readonly "~Error": any;
112
+ readonly "~FunctionTypes": FunctionType;
113
+ readonly "~Options": any;
114
+ }
115
+
116
+ export type Options<MiddlewareSpec_ extends AnyMiddlewareSpec> =
117
+ MiddlewareSpec_["~Options"]["Type"];
118
+
119
+ export type WithoutOptions<MiddlewareSpec_ extends AnyMiddlewareSpec> =
120
+ MiddlewareSpec_ extends AnyMiddlewareSpec
121
+ ? [MiddlewareSpec_["~Options"]] extends [never]
122
+ ? MiddlewareSpec_
123
+ : never
124
+ : never;
125
+
126
+ export type Key<MiddlewareSpec_ extends AnyMiddlewareSpec> =
127
+ MiddlewareSpec_["key"];
128
+
129
+ export type Provides<MiddlewareSpec_ extends AnyMiddlewareSpec> =
130
+ MiddlewareSpec_ extends AnyMiddlewareSpec
131
+ ? MiddlewareSpec_["~Provides"]
132
+ : never;
133
+
134
+ /**
135
+ * The services a middleware's implementation may consume from middleware that
136
+ * runs earlier in the same chain (type-level only, like `provides`).
137
+ */
138
+ export type Requires<MiddlewareSpec_ extends AnyMiddlewareSpec> =
139
+ MiddlewareSpec_ extends AnyMiddlewareSpec
140
+ ? MiddlewareSpec_["~Requires"]
141
+ : never;
142
+
143
+ export type ErrorSchema<MiddlewareSpec_ extends AnyMiddlewareSpec> =
144
+ MiddlewareSpec_ extends AnyMiddlewareSpec ? MiddlewareSpec_["~Error"] : never;
145
+
146
+ export type Error<MiddlewareSpec_ extends AnyMiddlewareSpec> =
147
+ MiddlewareSpec_ extends AnyMiddlewareSpec
148
+ ? MiddlewareSpec_["~Error"]["Type"]
149
+ : never;
150
+
151
+ export type EncodedError<MiddlewareSpec_ extends AnyMiddlewareSpec> =
152
+ MiddlewareSpec_ extends AnyMiddlewareSpec
153
+ ? MiddlewareSpec_["~Error"]["Encoded"]
154
+ : never;
155
+
156
+ export type FunctionTypes<MiddlewareSpec_ extends AnyMiddlewareSpec> =
157
+ MiddlewareSpec_ extends AnyMiddlewareSpec
158
+ ? MiddlewareSpec_["~FunctionTypes"]
159
+ : never;
160
+
161
+ /**
162
+ * Declare a middleware's client-safe interface: its identifying key, the
163
+ * service it provides to downstream handlers, the error schema its failures
164
+ * encode through, and the function types it may attach to.
165
+ *
166
+ * ```ts
167
+ * class RequireUser extends MiddlewareSpec.MiddlewareSpec<
168
+ * RequireUser,
169
+ * {
170
+ * provides: CurrentUser;
171
+ * }
172
+ * >()("RequireUser", {
173
+ * error: () => NotSignedIn,
174
+ * functionTypes: { query: true, mutation: true, action: false },
175
+ * }) {}
176
+ * ```
177
+ *
178
+ * The implementation is server-only and supplied separately via
179
+ * `MiddlewareImpl.make` (or `makeByFunctionType`/`provides`) in
180
+ * `@confect/server`.
181
+ */
182
+ export const MiddlewareSpec =
183
+ <Self, Config extends { provides?: any; requires?: any } = {}>() =>
184
+ <
185
+ const Key_ extends string,
186
+ const FunctionTypesConfig_ extends SupportedFunctionTypes,
187
+ ErrorSchema_ extends Schema.Codec<any, any> = never,
188
+ OptionsSchema_ extends Schema.Schema<any> = never,
189
+ >(
190
+ key: Key_,
191
+ options: {
192
+ readonly error?: () => ErrorSchema_;
193
+ readonly options?: () => OptionsSchema_;
194
+ readonly functionTypes: FunctionTypesConfig_ &
195
+ ValidateFunctionTypesConfig<FunctionTypesConfig_>;
196
+ },
197
+ ): MiddlewareSpec<
198
+ Self,
199
+ Key_,
200
+ "provides" extends keyof Config ? Config["provides"] : never,
201
+ "requires" extends keyof Config ? Config["requires"] : never,
202
+ ErrorSchema_,
203
+ FunctionTypesFromConfig<FunctionTypesConfig_>,
204
+ OptionsSchema_
205
+ > => {
206
+ const { query, mutation, action } = options.functionTypes;
207
+ if (!query && !mutation && !action) {
208
+ throw new Error(
209
+ `Middleware "${key}" must declare at least one function type`,
210
+ );
211
+ }
212
+
213
+ function MiddlewareSpecClass() {}
214
+ const class_ = MiddlewareSpecClass as any;
215
+ class_[TypeId] = TypeId;
216
+ class_.key = key;
217
+ class_.functionTypes = {
218
+ query,
219
+ mutation,
220
+ action,
221
+ } satisfies SupportedFunctionTypes;
222
+ if (options.error !== undefined) {
223
+ Lazy.defineProperty(class_, "error", options.error);
224
+ }
225
+ if (options.options !== undefined) {
226
+ Lazy.defineProperty(class_, "options", options.options);
227
+ }
228
+ return class_;
229
+ };
230
+
231
+ type FunctionTypesFromConfig<Config extends SupportedFunctionTypes> = {
232
+ [K in FunctionType]: Config[K] extends true ? K : never;
233
+ }[FunctionType];
234
+
235
+ type ValidateFunctionTypesConfig<Config extends SupportedFunctionTypes> =
236
+ ValidationResult<
237
+ | {
238
+ [K in keyof Config]: boolean extends Config[K]
239
+ ? AttachmentError<`Function type flag "${K & string}" must be a literal \`true\` or \`false\`, not a computed \`boolean\``>
240
+ : never;
241
+ }[keyof Config]
242
+ | ([FunctionTypesFromConfig<Config>] extends [never]
243
+ ? AttachmentError<`A middleware must declare at least one function type`>
244
+ : never)
245
+ >;
246
+
247
+ export const errorSchemas = (
248
+ middlewareSpecs: ReadonlyArray<AnyMiddlewareSpec>,
249
+ ): ReadonlyArray<Schema.Codec<any, any>> =>
250
+ middlewareSpecs.flatMap((middlewareSpec) =>
251
+ "error" in middlewareSpec && middlewareSpec.error !== undefined
252
+ ? [middlewareSpec.error]
253
+ : [],
254
+ );
255
+
256
+ /**
257
+ * A branded, unconstructible type carrying a spec-authoring error message.
258
+ * Surfaced as (part of) a parameter type so the diagnostic names the exact
259
+ * problem at the `.middleware()`/`.addFunction()` call site.
260
+ */
261
+ export interface AttachmentError<Message extends string> {
262
+ readonly "~@confect/core/MiddlewareSpec/AttachmentError": Message;
263
+ }
264
+
265
+ type FunctionTypeOf<FunctionSpec_ extends FunctionSpec.AnyWithProps> =
266
+ FunctionSpec_["runtimeAndFunctionType"]["functionType"];
267
+
268
+ type ValidationResult<Errors> = [Errors] extends [never] ? unknown : Errors;
269
+
270
+ /**
271
+ * A matching-type Convex-provenance function is rejected rather than silently
272
+ * skipped: its raw handler passes through Confect untouched, so the middleware
273
+ * could not actually cover it, and skipping it would be a policy hole.
274
+ */
275
+ export type ValidateFunction<
276
+ FunctionSpec_ extends FunctionSpec.AnyWithProps,
277
+ MiddlewareSpec_ extends AnyMiddlewareSpec,
278
+ > = FunctionSpec_ extends any
279
+ ? MiddlewareSpec_ extends any
280
+ ? FunctionSpec_ extends {
281
+ readonly functionProvenance: { readonly _tag: "Convex" };
282
+ }
283
+ ? FunctionTypeOf<FunctionSpec_> extends FunctionTypes<MiddlewareSpec_>
284
+ ? AttachmentError<`Convex-provenance function "${FunctionSpec_["name"]}" cannot be covered by middleware "${Key<MiddlewareSpec_>}"—attach middleware only to groups whose matching-type functions are all Confect-provenance`>
285
+ : never
286
+ : [
287
+ Extract<
288
+ FunctionSpec.MiddlewareSpecs<FunctionSpec_>,
289
+ { readonly key: Key<WithoutOptions<MiddlewareSpec_>> }
290
+ >,
291
+ ] extends [never]
292
+ ? FunctionTypeOf<FunctionSpec_> extends FunctionTypes<MiddlewareSpec_>
293
+ ? never
294
+ : AttachmentError<`Middleware "${Key<MiddlewareSpec_>}" does not declare function type "${FunctionTypeOf<FunctionSpec_>}", the type of function "${FunctionSpec_["name"]}"`>
295
+ : AttachmentError<`Middleware "${Key<MiddlewareSpec_>}" is already attached to function "${FunctionSpec_["name"]}"`>
296
+ : never
297
+ : never;
298
+
299
+ /**
300
+ * Attachment order is chain order, so requiring `requires` to be satisfied by
301
+ * already-attached middleware is exactly requiring the provider to run
302
+ * earlier.
303
+ */
304
+ export type ValidateAttach<
305
+ MiddlewareSpec_ extends AnyMiddlewareSpec,
306
+ Functions_ extends FunctionSpec.AnyWithProps,
307
+ MiddlewareSpecs_ extends AnyMiddlewareSpec,
308
+ > = [
309
+ Extract<
310
+ MiddlewareSpecs_,
311
+ { readonly key: Key<WithoutOptions<MiddlewareSpec_>> }
312
+ >,
313
+ ] extends [never]
314
+ ? [Exclude<Requires<MiddlewareSpec_>, Provides<MiddlewareSpecs_>>] extends [
315
+ never,
316
+ ]
317
+ ? ValidationResult<ValidateFunction<Functions_, MiddlewareSpec_>>
318
+ : AttachmentError<`Middleware "${Key<MiddlewareSpec_>}" requires services that no middleware attached earlier to this group provides—attach the providing middleware first`>
319
+ : AttachmentError<`Middleware "${Key<MiddlewareSpec_>}" is already attached to this group`>;
320
+
321
+ export type ValidateAddedFunction<
322
+ FunctionSpec_ extends FunctionSpec.AnyWithProps,
323
+ MiddlewareSpecs_ extends AnyMiddlewareSpec,
324
+ > = [MiddlewareSpecs_] extends [never]
325
+ ? unknown
326
+ : ValidationResult<
327
+ | ValidateFunction<FunctionSpec_, MiddlewareSpecs_>
328
+ | GroupOverlap<FunctionSpec_, MiddlewareSpecs_>
329
+ >;
330
+
331
+ type GroupOverlap<
332
+ FunctionSpec_ extends FunctionSpec.AnyWithProps,
333
+ MiddlewareSpecs_ extends AnyMiddlewareSpec,
334
+ > =
335
+ FunctionSpec.MiddlewareSpecs<FunctionSpec_> extends infer FunctionMiddlewareSpec
336
+ ? FunctionMiddlewareSpec extends AnyMiddlewareSpec
337
+ ? [
338
+ Extract<
339
+ MiddlewareSpecs_,
340
+ { readonly key: Key<WithoutOptions<FunctionMiddlewareSpec>> }
341
+ >,
342
+ ] extends [never]
343
+ ? never
344
+ : AttachmentError<`Middleware "${Key<FunctionMiddlewareSpec>}" is attached to both function "${FunctionSpec_["name"]}" and its group`>
345
+ : never
346
+ : never;
347
+
348
+ /**
349
+ * The parameter-type validation applied to the group spec at `GroupImpl.make`:
350
+ * every function's attached middleware must have its `requires` services
351
+ * provided by some middleware covering that function (the group's or the
352
+ * function's own). Checked here—with the group fully assembled—rather than at
353
+ * the spec builders, because a function-level middleware's `requires` may
354
+ * legitimately be satisfied by a group middleware the function spec never sees,
355
+ * and group attachment is declaratively order-independent with respect to
356
+ * `addFunction`.
357
+ */
358
+ export type ValidateImplRequires<
359
+ Functions_ extends FunctionSpec.AnyWithProps,
360
+ GroupMiddlewareSpecs_ extends AnyMiddlewareSpec,
361
+ > = ValidationResult<
362
+ Functions_ extends any
363
+ ? [
364
+ Exclude<
365
+ Requires<FunctionSpec.MiddlewareSpecs<Functions_>>,
366
+ Provides<
367
+ GroupMiddlewareSpecs_ | FunctionSpec.MiddlewareSpecs<Functions_>
368
+ >
369
+ >,
370
+ ] extends [never]
371
+ ? never
372
+ : AttachmentError<`Function "${Functions_["name"]}" has middleware requiring services that no middleware covering it provides`>
373
+ : never
374
+ >;
375
+
376
+ export type ValidateFunctionAttach<
377
+ MiddlewareSpec_ extends AnyMiddlewareSpec,
378
+ RuntimeAndFunctionType_ extends RuntimeAndFunctionType.RuntimeAndFunctionType,
379
+ FunctionProvenance_ extends FunctionProvenance.FunctionProvenance,
380
+ MiddlewareSpecs_ extends AnyMiddlewareSpec,
381
+ > = FunctionProvenance_ extends { readonly _tag: "Convex" }
382
+ ? AttachmentError<`Plain Convex functions cannot have middleware—their raw handlers are passed through untouched`>
383
+ : [
384
+ Extract<
385
+ MiddlewareSpecs_,
386
+ { readonly key: Key<WithoutOptions<MiddlewareSpec_>> }
387
+ >,
388
+ ] extends [never]
389
+ ? RuntimeAndFunctionType_["functionType"] extends FunctionTypes<MiddlewareSpec_>
390
+ ? unknown
391
+ : AttachmentError<`Middleware "${Key<MiddlewareSpec_>}" does not declare function type "${RuntimeAndFunctionType_["functionType"]}"`>
392
+ : AttachmentError<`Middleware "${Key<MiddlewareSpec_>}" is already attached to this function`>;
@@ -0,0 +1,78 @@
1
+ import { ConvexError } from "convex/values";
2
+ import type { Value } from "convex/values";
3
+ import * as Match from "effect/Match";
4
+ import * as Option from "effect/Option";
5
+ import * as Schema from "effect/Schema";
6
+
7
+ /**
8
+ * A Convex pagination cursor that no longer describes the current query.
9
+ */
10
+ export class InvalidCursor extends Schema.TaggedError<InvalidCursor>()(
11
+ "InvalidCursor",
12
+ {
13
+ cause: Schema.Defect(),
14
+ },
15
+ ) {}
16
+
17
+ interface InvalidCursorData {
18
+ readonly isConvexSystemError: true;
19
+ readonly paginationError: "InvalidCursor";
20
+ }
21
+
22
+ const ConvexErrorIdentifier = Symbol.for("ConvexError");
23
+
24
+ const isConvexError = (error: unknown): error is ConvexError<Value> =>
25
+ error instanceof ConvexError ||
26
+ (typeof error === "object" &&
27
+ error !== null &&
28
+ ConvexErrorIdentifier in error);
29
+
30
+ const isInvalidCursorData = (value: unknown): value is InvalidCursorData =>
31
+ typeof value === "object" &&
32
+ value !== null &&
33
+ "isConvexSystemError" in value &&
34
+ value.isConvexSystemError === true &&
35
+ "paginationError" in value &&
36
+ value.paginationError === "InvalidCursor";
37
+
38
+ const fromErrorMessage = (
39
+ error: globalThis.Error,
40
+ ): Option.Option<InvalidCursor> =>
41
+ error.message.includes("InvalidCursor")
42
+ ? Option.some(new InvalidCursor({ cause: error }))
43
+ : Option.none();
44
+
45
+ /**
46
+ * Recognizes an invalid-cursor error emitted by a Convex query.
47
+ */
48
+ export const fromConvexQueryError = (
49
+ error: unknown,
50
+ ): Option.Option<InvalidCursor> =>
51
+ Match.value(error).pipe(
52
+ Match.when(isConvexError, (convexError) =>
53
+ Match.value(isInvalidCursorData(convexError.data)).pipe(
54
+ Match.when(true, () =>
55
+ Option.some(new InvalidCursor({ cause: error })),
56
+ ),
57
+ Match.when(false, () => fromErrorMessage(convexError)),
58
+ Match.exhaustive,
59
+ ),
60
+ ),
61
+ Match.when(Match.instanceOf(globalThis.Error), fromErrorMessage),
62
+ Match.when(Match.any, () => Option.none<InvalidCursor>()),
63
+ Match.exhaustive,
64
+ );
65
+
66
+ /**
67
+ * Recognizes the error data attached to Convex's invalid-cursor error.
68
+ */
69
+ export const fromConvexErrorData = (
70
+ errorData: unknown,
71
+ ): Option.Option<InvalidCursor> =>
72
+ Match.value(errorData).pipe(
73
+ Match.when(isInvalidCursorData, (cause) =>
74
+ Option.some(new InvalidCursor({ cause })),
75
+ ),
76
+ Match.when(Match.any, () => Option.none<InvalidCursor>()),
77
+ Match.exhaustive,
78
+ );
@@ -0,0 +1,23 @@
1
+ import * as Schema from "effect/Schema";
2
+
3
+ /**
4
+ * Schema for the `paginationOpts` argument that every paginated query receives.
5
+ * 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.Finite,
18
+ cursor: Schema.Union([Schema.String, Schema.Null]),
19
+ endCursor: Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])),
20
+ id: Schema.optionalKey(Schema.Finite),
21
+ maximumRowsRead: Schema.optionalKey(Schema.Finite),
22
+ maximumBytesRead: Schema.optionalKey(Schema.Finite),
23
+ });
@@ -1,5 +1,9 @@
1
1
  import * as Schema from "effect/Schema";
2
2
 
3
+ export type PaginationResult<Doc extends Schema.Codec<any, any>> = ReturnType<
4
+ typeof PaginationResult<Doc>
5
+ >;
6
+
3
7
  export const PaginationResult = <Doc extends Schema.Codec<any, any>>(
4
8
  Doc: Doc,
5
9
  ) =>