@confect/core 10.0.0-next.20 → 10.0.0-next.22
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 +162 -80
- package/dist/FunctionProvenance.d.ts +5 -6
- package/dist/FunctionProvenance.d.ts.map +1 -1
- package/dist/FunctionProvenance.js +3 -4
- package/dist/FunctionProvenance.js.map +1 -1
- package/dist/FunctionSpec.d.ts +3 -1
- package/dist/FunctionSpec.d.ts.map +1 -1
- package/dist/FunctionSpec.js +12 -6
- package/dist/FunctionSpec.js.map +1 -1
- package/dist/GroupSpec.d.ts +5 -1
- package/dist/GroupSpec.d.ts.map +1 -1
- package/dist/GroupSpec.js +31 -17
- package/dist/GroupSpec.js.map +1 -1
- package/dist/Identifier.d.ts +1 -1
- package/dist/Identifier.js +1 -1
- package/dist/Identifier.js.map +1 -1
- package/dist/Lazy.d.ts +4 -5
- package/dist/Lazy.d.ts.map +1 -1
- package/dist/Lazy.js +4 -5
- package/dist/Lazy.js.map +1 -1
- package/dist/MiddlewareAttachment.d.ts +171 -0
- package/dist/MiddlewareAttachment.d.ts.map +1 -0
- package/dist/MiddlewareAttachment.js +48 -0
- package/dist/MiddlewareAttachment.js.map +1 -0
- package/dist/MiddlewareSpec.d.ts +35 -23
- package/dist/MiddlewareSpec.d.ts.map +1 -1
- package/dist/MiddlewareSpec.js +1 -0
- package/dist/MiddlewareSpec.js.map +1 -1
- package/dist/PaginationOptions.d.ts +1 -1
- package/dist/PaginationOptions.js +1 -1
- package/dist/PaginationOptions.js.map +1 -1
- package/dist/Ref.d.ts +8 -7
- package/dist/Ref.d.ts.map +1 -1
- package/dist/Ref.js +11 -9
- package/dist/Ref.js.map +1 -1
- package/dist/Refs.js +1 -1
- package/dist/Refs.js.map +1 -1
- package/dist/SchemaToValidator.d.ts.map +1 -1
- package/dist/SchemaToValidator.js +4 -2
- package/dist/SchemaToValidator.js.map +1 -1
- package/dist/Spec.d.ts +1 -1
- package/dist/Spec.js.map +1 -1
- package/dist/SystemFields.d.ts +3 -4
- package/dist/SystemFields.d.ts.map +1 -1
- package/dist/SystemFields.js +5 -6
- package/dist/SystemFields.js.map +1 -1
- package/dist/Table.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/package.json +10 -10
- package/src/FunctionProvenance.ts +6 -8
- package/src/FunctionSpec.ts +21 -7
- package/src/GroupSpec.ts +74 -26
- package/src/Identifier.ts +1 -1
- package/src/Lazy.ts +4 -5
- package/src/MiddlewareAttachment.ts +106 -0
- package/src/MiddlewareSpec.ts +55 -21
- package/src/PaginationOptions.ts +1 -1
- package/src/Ref.ts +42 -37
- package/src/Refs.ts +1 -1
- package/src/SchemaToValidator.ts +56 -45
- package/src/Spec.ts +1 -1
- package/src/SystemFields.ts +9 -10
- package/src/Table.ts +2 -2
- package/src/index.ts +1 -0
package/src/GroupSpec.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import * as MiddlewareAttachment from "./MiddlewareAttachment";
|
|
1
2
|
import * as Predicate from "effect/Predicate";
|
|
2
3
|
import * as Record from "effect/Record";
|
|
4
|
+
import * as Result from "effect/Result";
|
|
3
5
|
import type * as FunctionSpec from "./FunctionSpec";
|
|
4
6
|
import type * as MiddlewareSpec from "./MiddlewareSpec";
|
|
5
7
|
import type * as RuntimeAndFunctionType from "./RuntimeAndFunctionType";
|
|
@@ -18,7 +20,7 @@ export interface GroupSpec<
|
|
|
18
20
|
// Subgroups may be of any runtime, independent of this group's own runtime: a
|
|
19
21
|
// group is only a namespace for its children, which are otherwise-independent
|
|
20
22
|
// modules. Functions, by contrast, stay homogeneous (a Node group only accepts
|
|
21
|
-
// Node actions)
|
|
23
|
+
// Node actions)—`addFunction` keeps the `<Runtime>` bound below.
|
|
22
24
|
Groups_ extends AnyWithProps = never,
|
|
23
25
|
MiddlewareSpecs_ extends MiddlewareSpec.AnyMiddlewareSpec = never,
|
|
24
26
|
> {
|
|
@@ -36,6 +38,9 @@ export interface GroupSpec<
|
|
|
36
38
|
[GroupName in Name<Groups_>]: WithName<Groups_, GroupName>;
|
|
37
39
|
};
|
|
38
40
|
readonly middlewareSpecs: ReadonlyArray<MiddlewareSpecs_>;
|
|
41
|
+
readonly middlewareAttachments: ReadonlyArray<
|
|
42
|
+
MiddlewareAttachment.MiddlewareAttachment<MiddlewareSpecs_>
|
|
43
|
+
>;
|
|
39
44
|
readonly "~Functions": Functions_;
|
|
40
45
|
readonly "~Groups": Groups_;
|
|
41
46
|
|
|
@@ -72,6 +77,7 @@ export interface GroupSpec<
|
|
|
72
77
|
Functions_,
|
|
73
78
|
MiddlewareSpecs_
|
|
74
79
|
>,
|
|
80
|
+
...options: MiddlewareAttachment.Args<NoInfer<MiddlewareSpec_>>
|
|
75
81
|
): GroupSpec<
|
|
76
82
|
Runtime,
|
|
77
83
|
Name_,
|
|
@@ -154,22 +160,27 @@ export type AddGroups<
|
|
|
154
160
|
const Proto = {
|
|
155
161
|
[TypeId]: TypeId,
|
|
156
162
|
|
|
163
|
+
get middlewareSpecs() {
|
|
164
|
+
return this.middlewareAttachments.map(({ spec }) => spec);
|
|
165
|
+
},
|
|
166
|
+
|
|
157
167
|
addFunction<Function extends FunctionSpec.AnyWithProps>(
|
|
158
168
|
this: Any,
|
|
159
169
|
function_: Function,
|
|
160
170
|
) {
|
|
161
171
|
const this_ = this as AnyWithProps;
|
|
162
172
|
|
|
163
|
-
const overlapping = function_.
|
|
164
|
-
(functionMiddlewareSpec) =>
|
|
165
|
-
|
|
166
|
-
|
|
173
|
+
const overlapping = function_.middlewareAttachments.find(
|
|
174
|
+
({ spec: functionMiddlewareSpec }) =>
|
|
175
|
+
!("options" in functionMiddlewareSpec) &&
|
|
176
|
+
this_.middlewareAttachments.some(
|
|
177
|
+
({ spec: groupMiddlewareSpec }) =>
|
|
167
178
|
groupMiddlewareSpec.key === functionMiddlewareSpec.key,
|
|
168
179
|
),
|
|
169
180
|
);
|
|
170
181
|
if (overlapping !== undefined) {
|
|
171
182
|
throw new Error(
|
|
172
|
-
`Middleware "${overlapping.key}" is attached to both function "${function_.name}" and its group`,
|
|
183
|
+
`Middleware "${overlapping.spec.key}" is attached to both function "${function_.name}" and its group`,
|
|
173
184
|
);
|
|
174
185
|
}
|
|
175
186
|
|
|
@@ -178,7 +189,7 @@ const Proto = {
|
|
|
178
189
|
name: this_.name,
|
|
179
190
|
functions: Record.set(this_.functions, function_.name, function_),
|
|
180
191
|
groups: this_.groups,
|
|
181
|
-
|
|
192
|
+
middlewareAttachments: this_.middlewareAttachments,
|
|
182
193
|
});
|
|
183
194
|
},
|
|
184
195
|
|
|
@@ -191,7 +202,7 @@ const Proto = {
|
|
|
191
202
|
name: this_.name,
|
|
192
203
|
functions: this_.functions,
|
|
193
204
|
groups: Record.set(this_.groups, group_.name, group_),
|
|
194
|
-
|
|
205
|
+
middlewareAttachments: this_.middlewareAttachments,
|
|
195
206
|
});
|
|
196
207
|
},
|
|
197
208
|
|
|
@@ -204,19 +215,21 @@ const Proto = {
|
|
|
204
215
|
name: this_.name,
|
|
205
216
|
functions: this_.functions,
|
|
206
217
|
groups: Record.set(this_.groups, name, withName(name, group_)),
|
|
207
|
-
|
|
218
|
+
middlewareAttachments: this_.middlewareAttachments,
|
|
208
219
|
});
|
|
209
220
|
},
|
|
210
221
|
|
|
211
222
|
middleware<MiddlewareSpec_ extends MiddlewareSpec.AnyMiddlewareSpec>(
|
|
212
223
|
this: Any,
|
|
213
224
|
middlewareSpec: MiddlewareSpec_,
|
|
225
|
+
...options: ReadonlyArray<unknown>
|
|
214
226
|
) {
|
|
215
227
|
const this_ = this as AnyWithProps;
|
|
216
228
|
|
|
217
229
|
if (
|
|
218
|
-
|
|
219
|
-
|
|
230
|
+
!("options" in middlewareSpec) &&
|
|
231
|
+
this_.middlewareAttachments.some(
|
|
232
|
+
({ spec }) => spec.key === middlewareSpec.key,
|
|
220
233
|
)
|
|
221
234
|
) {
|
|
222
235
|
throw new Error(
|
|
@@ -226,8 +239,9 @@ const Proto = {
|
|
|
226
239
|
|
|
227
240
|
for (const function_ of Object.values(this_.functions)) {
|
|
228
241
|
if (
|
|
229
|
-
|
|
230
|
-
|
|
242
|
+
!("options" in middlewareSpec) &&
|
|
243
|
+
function_.middlewareAttachments.some(
|
|
244
|
+
({ spec }) => spec.key === middlewareSpec.key,
|
|
231
245
|
)
|
|
232
246
|
) {
|
|
233
247
|
throw new Error(
|
|
@@ -241,37 +255,53 @@ const Proto = {
|
|
|
241
255
|
name: this_.name,
|
|
242
256
|
functions: this_.functions,
|
|
243
257
|
groups: this_.groups,
|
|
244
|
-
|
|
258
|
+
middlewareAttachments: [
|
|
259
|
+
...this_.middlewareAttachments,
|
|
260
|
+
{ spec: middlewareSpec, options: options[0] },
|
|
261
|
+
],
|
|
245
262
|
});
|
|
246
263
|
},
|
|
247
|
-
}
|
|
264
|
+
} satisfies ThisType<AnyWithProps>;
|
|
248
265
|
|
|
249
266
|
const makeProto = <
|
|
250
267
|
Runtime extends RuntimeAndFunctionType.Runtime,
|
|
251
268
|
Name_ extends string,
|
|
252
269
|
Functions_ extends FunctionSpec.AnyWithPropsWithRuntime<Runtime>,
|
|
253
270
|
Groups_ extends AnyWithPropsWithRuntime<Runtime>,
|
|
254
|
-
|
|
271
|
+
MiddlewareAttachments_ extends
|
|
272
|
+
ReadonlyArray<MiddlewareAttachment.MiddlewareAttachment>,
|
|
255
273
|
>({
|
|
256
274
|
runtime,
|
|
257
275
|
name,
|
|
258
276
|
functions,
|
|
259
277
|
groups,
|
|
260
|
-
|
|
278
|
+
middlewareAttachments,
|
|
261
279
|
}: {
|
|
262
280
|
runtime: Runtime;
|
|
263
281
|
name: Name_;
|
|
264
282
|
functions: Record.ReadonlyRecord<string, Functions_>;
|
|
265
283
|
groups: Record.ReadonlyRecord<string, Groups_>;
|
|
266
|
-
|
|
267
|
-
}): GroupSpec<
|
|
284
|
+
middlewareAttachments: MiddlewareAttachments_;
|
|
285
|
+
}): GroupSpec<
|
|
286
|
+
Runtime,
|
|
287
|
+
Name_,
|
|
288
|
+
Functions_,
|
|
289
|
+
Groups_,
|
|
290
|
+
MiddlewareAttachments_[number]["spec"]
|
|
291
|
+
> =>
|
|
268
292
|
Object.assign(Object.create(Proto), {
|
|
269
293
|
runtime,
|
|
270
294
|
name,
|
|
271
295
|
functions,
|
|
272
296
|
groups,
|
|
273
|
-
|
|
274
|
-
}) as GroupSpec<
|
|
297
|
+
middlewareAttachments,
|
|
298
|
+
}) as GroupSpec<
|
|
299
|
+
Runtime,
|
|
300
|
+
Name_,
|
|
301
|
+
Functions_,
|
|
302
|
+
Groups_,
|
|
303
|
+
MiddlewareAttachments_[number]["spec"]
|
|
304
|
+
>;
|
|
275
305
|
|
|
276
306
|
export const make = (): GroupSpec<"Convex", ""> =>
|
|
277
307
|
makeProto({
|
|
@@ -279,7 +309,7 @@ export const make = (): GroupSpec<"Convex", ""> =>
|
|
|
279
309
|
name: "",
|
|
280
310
|
functions: Record.empty(),
|
|
281
311
|
groups: Record.empty(),
|
|
282
|
-
|
|
312
|
+
middlewareAttachments: [],
|
|
283
313
|
});
|
|
284
314
|
|
|
285
315
|
export const makeAt = <const Name_ extends string>(
|
|
@@ -292,7 +322,7 @@ export const makeAt = <const Name_ extends string>(
|
|
|
292
322
|
name,
|
|
293
323
|
functions: Record.empty(),
|
|
294
324
|
groups: Record.empty(),
|
|
295
|
-
|
|
325
|
+
middlewareAttachments: [],
|
|
296
326
|
});
|
|
297
327
|
};
|
|
298
328
|
|
|
@@ -302,7 +332,7 @@ export const makeNode = (): GroupSpec<"Node", ""> =>
|
|
|
302
332
|
name: "",
|
|
303
333
|
functions: Record.empty(),
|
|
304
334
|
groups: Record.empty(),
|
|
305
|
-
|
|
335
|
+
middlewareAttachments: [],
|
|
306
336
|
});
|
|
307
337
|
|
|
308
338
|
export const makeNodeAt = <const Name_ extends string>(
|
|
@@ -315,7 +345,7 @@ export const makeNodeAt = <const Name_ extends string>(
|
|
|
315
345
|
name,
|
|
316
346
|
functions: Record.empty(),
|
|
317
347
|
groups: Record.empty(),
|
|
318
|
-
|
|
348
|
+
middlewareAttachments: [],
|
|
319
349
|
});
|
|
320
350
|
};
|
|
321
351
|
|
|
@@ -335,6 +365,24 @@ export const withName = <const Name_ extends string>(
|
|
|
335
365
|
name,
|
|
336
366
|
functions: group_.functions,
|
|
337
367
|
groups: group_.groups,
|
|
338
|
-
|
|
368
|
+
middlewareAttachments: group_.middlewareAttachments,
|
|
339
369
|
});
|
|
340
370
|
};
|
|
371
|
+
|
|
372
|
+
export const validateMiddleware = (
|
|
373
|
+
group: AnyWithProps,
|
|
374
|
+
): Result.Result<void, MiddlewareAttachment.ValidationError> =>
|
|
375
|
+
Result.gen(function* () {
|
|
376
|
+
yield* MiddlewareAttachment.validateAll(
|
|
377
|
+
group.middlewareAttachments,
|
|
378
|
+
`group "${group.name}"`,
|
|
379
|
+
);
|
|
380
|
+
for (const function_ of Object.values(group.functions)) {
|
|
381
|
+
yield* MiddlewareAttachment.validateAll(
|
|
382
|
+
[...group.middlewareAttachments, ...function_.middlewareAttachments],
|
|
383
|
+
`function "${function_.name}"`,
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
for (const child of Object.values(group.groups))
|
|
387
|
+
yield* validateMiddleware(child);
|
|
388
|
+
});
|
package/src/Identifier.ts
CHANGED
|
@@ -99,7 +99,7 @@ export const validateConfectFunctionIdentifier = (identifier: string) => {
|
|
|
99
99
|
* as a `confect/tables/<identifier>.ts` filename).
|
|
100
100
|
*
|
|
101
101
|
* Rules:
|
|
102
|
-
* - Must match `/^[A-Za-z][A-Za-z0-9_]
|
|
102
|
+
* - Must match `/^[A-Za-z][A-Za-z0-9_]*$/`—letter-leading, alphanumeric plus
|
|
103
103
|
* underscore. No `$` (not a valid Convex table name character); no leading
|
|
104
104
|
* `_` (Convex reserves `_<name>` for its system tables).
|
|
105
105
|
* - Must not be a reserved JavaScript identifier, so the name can also be used
|
package/src/Lazy.ts
CHANGED
|
@@ -2,20 +2,19 @@
|
|
|
2
2
|
* Install a lazy memoised property on `target`. The first access runs
|
|
3
3
|
* `compute()` and replaces the getter with a plain, non-writable data
|
|
4
4
|
* property whose value is the computed result. Subsequent accesses hit
|
|
5
|
-
* the V8 fast path for own data properties
|
|
6
|
-
* returned reference
|
|
5
|
+
* the V8 fast path for own data properties—no function call, identical
|
|
6
|
+
* returned reference—so first and second-and-subsequent accesses are
|
|
7
7
|
* observably indistinguishable.
|
|
8
8
|
*
|
|
9
9
|
* The replacement property is `enumerable: true` so the lazy property
|
|
10
|
-
* still participates in `Object.keys
|
|
10
|
+
* still participates in `Object.keys`/`JSON.stringify` after it
|
|
11
11
|
* materialises, matching the shape of a plain data property. The property
|
|
12
12
|
* is also `enumerable` before materialising, so presence checks
|
|
13
13
|
* (`"key" in target`, `Object.hasOwn(target, key)`) observe it without
|
|
14
14
|
* forcing the computation.
|
|
15
15
|
*
|
|
16
16
|
* This is the single shared implementation consumed across packages (e.g.
|
|
17
|
-
* `@confect/core`'s lazy `FunctionSpec` schemas and lazy `Table` `Fields`
|
|
18
|
-
* `Doc`), so there is no chance of the two drifting apart.
|
|
17
|
+
* `@confect/core`'s lazy `FunctionSpec` schemas and lazy `Table` `Fields`/`Doc`), so there is no chance of the two drifting apart.
|
|
19
18
|
*/
|
|
20
19
|
export const defineProperty = <T extends object, K extends PropertyKey>(
|
|
21
20
|
target: T,
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import * as Data from "effect/Data";
|
|
2
|
+
import * as Result from "effect/Result";
|
|
3
|
+
import * as Schema from "effect/Schema";
|
|
4
|
+
import type * as MiddlewareSpec from "./MiddlewareSpec";
|
|
5
|
+
|
|
6
|
+
export type MiddlewareAttachment<
|
|
7
|
+
MiddlewareSpec_ extends MiddlewareSpec.AnyMiddlewareSpec =
|
|
8
|
+
MiddlewareSpec.AnyMiddlewareSpec,
|
|
9
|
+
> = MiddlewareSpec_ extends MiddlewareSpec.AnyMiddlewareSpec
|
|
10
|
+
? {
|
|
11
|
+
readonly spec: MiddlewareSpec_;
|
|
12
|
+
readonly options: [MiddlewareSpec_["~Options"]] extends [never]
|
|
13
|
+
? undefined
|
|
14
|
+
: MiddlewareSpec.Options<MiddlewareSpec_>;
|
|
15
|
+
}
|
|
16
|
+
: never;
|
|
17
|
+
|
|
18
|
+
export type Args<MiddlewareSpec_ extends MiddlewareSpec.AnyMiddlewareSpec> = [
|
|
19
|
+
MiddlewareSpec_["~Options"],
|
|
20
|
+
] extends [never]
|
|
21
|
+
? []
|
|
22
|
+
: [options: MiddlewareSpec.Options<MiddlewareSpec_>];
|
|
23
|
+
|
|
24
|
+
export type ValidationError = Data.TaggedEnum<{
|
|
25
|
+
InvalidOptions: {
|
|
26
|
+
readonly middlewareKey: string;
|
|
27
|
+
readonly location: string;
|
|
28
|
+
readonly attachmentIndex: number;
|
|
29
|
+
};
|
|
30
|
+
ConflictingSpecs: {
|
|
31
|
+
readonly middlewareKey: string;
|
|
32
|
+
readonly location: string;
|
|
33
|
+
readonly attachmentIndex: number;
|
|
34
|
+
readonly previousIndex: number;
|
|
35
|
+
};
|
|
36
|
+
EquivalentOptions: {
|
|
37
|
+
readonly middlewareKey: string;
|
|
38
|
+
readonly location: string;
|
|
39
|
+
readonly attachmentIndex: number;
|
|
40
|
+
readonly previousIndex: number;
|
|
41
|
+
};
|
|
42
|
+
}>;
|
|
43
|
+
|
|
44
|
+
export const ValidationError = Data.taggedEnum<ValidationError>();
|
|
45
|
+
|
|
46
|
+
export const formatValidationError = ValidationError.$match({
|
|
47
|
+
InvalidOptions: ({ middlewareKey, location, attachmentIndex }) =>
|
|
48
|
+
`Middleware "${middlewareKey}" has invalid options at attachment ${attachmentIndex + 1} on ${location}`,
|
|
49
|
+
ConflictingSpecs: ({ middlewareKey, location }) =>
|
|
50
|
+
`Different middleware specs share key "${middlewareKey}" on ${location}`,
|
|
51
|
+
EquivalentOptions: ({
|
|
52
|
+
middlewareKey,
|
|
53
|
+
location,
|
|
54
|
+
attachmentIndex,
|
|
55
|
+
previousIndex,
|
|
56
|
+
}) =>
|
|
57
|
+
`Middleware "${middlewareKey}" has equivalent options at attachments ${previousIndex + 1} and ${attachmentIndex + 1} on ${location}`,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
export const validateAll = (
|
|
61
|
+
attachments: ReadonlyArray<MiddlewareAttachment>,
|
|
62
|
+
location: string,
|
|
63
|
+
): Result.Result<void, ValidationError> => {
|
|
64
|
+
for (const [index, attachment] of attachments.entries()) {
|
|
65
|
+
const schema = attachment.spec.options;
|
|
66
|
+
if (schema !== undefined && !Schema.is(schema)(attachment.options)) {
|
|
67
|
+
return Result.fail(
|
|
68
|
+
ValidationError.InvalidOptions({
|
|
69
|
+
middlewareKey: attachment.spec.key,
|
|
70
|
+
location,
|
|
71
|
+
attachmentIndex: index,
|
|
72
|
+
}),
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
const equivalent =
|
|
76
|
+
schema === undefined ? undefined : Schema.toEquivalence(schema);
|
|
77
|
+
for (const [previousIndex, previous] of attachments.entries()) {
|
|
78
|
+
if (previousIndex === index) break;
|
|
79
|
+
if (previous.spec.key !== attachment.spec.key) continue;
|
|
80
|
+
if (previous.spec !== attachment.spec) {
|
|
81
|
+
return Result.fail(
|
|
82
|
+
ValidationError.ConflictingSpecs({
|
|
83
|
+
middlewareKey: attachment.spec.key,
|
|
84
|
+
location,
|
|
85
|
+
attachmentIndex: index,
|
|
86
|
+
previousIndex,
|
|
87
|
+
}),
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
if (
|
|
91
|
+
equivalent === undefined ||
|
|
92
|
+
equivalent(previous.options, attachment.options)
|
|
93
|
+
) {
|
|
94
|
+
return Result.fail(
|
|
95
|
+
ValidationError.EquivalentOptions({
|
|
96
|
+
middlewareKey: attachment.spec.key,
|
|
97
|
+
location,
|
|
98
|
+
attachmentIndex: index,
|
|
99
|
+
previousIndex,
|
|
100
|
+
}),
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return Result.void;
|
|
106
|
+
};
|
package/src/MiddlewareSpec.ts
CHANGED
|
@@ -44,25 +44,32 @@ export interface SuccessValue {
|
|
|
44
44
|
*
|
|
45
45
|
* Mirrors `RpcMiddleware.RpcMiddleware` in Effect.
|
|
46
46
|
*/
|
|
47
|
-
export interface MiddlewareImpl<Provides_, E, R> {
|
|
47
|
+
export interface MiddlewareImpl<Provides_, E, R, Options_ = never> {
|
|
48
48
|
(
|
|
49
49
|
effect: Effect.Effect<SuccessValue, E | unhandled, Provides_>,
|
|
50
|
-
|
|
50
|
+
context: MiddlewareOptions<Options_>,
|
|
51
51
|
): Effect.Effect<SuccessValue, E | unhandled, R>;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
export
|
|
55
|
-
readonly
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
54
|
+
export type MiddlewareOptions<Options_ = never> = {
|
|
55
|
+
readonly invocation: {
|
|
56
|
+
readonly name: string;
|
|
57
|
+
readonly functionType: FunctionType;
|
|
58
|
+
readonly functionVisibility: FunctionVisibility;
|
|
59
|
+
readonly args: unknown;
|
|
60
|
+
};
|
|
61
|
+
} & ([Options_] extends [never] ? unknown : { readonly options: Options_ });
|
|
60
62
|
|
|
61
|
-
export interface AnyMiddlewareImpl
|
|
63
|
+
export interface AnyMiddlewareImpl {
|
|
64
|
+
(
|
|
65
|
+
effect: Effect.Effect<SuccessValue, any, any>,
|
|
66
|
+
context: MiddlewareOptions | MiddlewareOptions<unknown>,
|
|
67
|
+
): Effect.Effect<SuccessValue, any, any>;
|
|
68
|
+
}
|
|
62
69
|
|
|
63
70
|
/**
|
|
64
71
|
* The class shape produced by {@link MiddlewareSpec}. Only the static side is ever
|
|
65
|
-
* used
|
|
72
|
+
* used—the class is a value-level carrier for the middleware's key, its
|
|
66
73
|
* declared functionTypes, and its (lazily-evaluated) error schema, plus the
|
|
67
74
|
* type-level `Provides`/`Error` metadata. It is deliberately not a
|
|
68
75
|
* `Context.Tag`: implementations are registered through the group `Registry`
|
|
@@ -75,6 +82,7 @@ export interface MiddlewareSpec<
|
|
|
75
82
|
Requires_,
|
|
76
83
|
ErrorSchema_ extends Schema.Codec<any, any>,
|
|
77
84
|
FunctionTypes_ extends FunctionType,
|
|
85
|
+
OptionsSchema_ extends Schema.Schema<any> = never,
|
|
78
86
|
> {
|
|
79
87
|
new (_: never): {
|
|
80
88
|
readonly [TypeId]: TypeId;
|
|
@@ -84,11 +92,13 @@ export interface MiddlewareSpec<
|
|
|
84
92
|
readonly key: Key_;
|
|
85
93
|
readonly functionTypes: SupportedFunctionTypes;
|
|
86
94
|
readonly error?: ErrorSchema_;
|
|
95
|
+
readonly options?: OptionsSchema_;
|
|
87
96
|
readonly "~Provides": Provides_;
|
|
88
97
|
readonly "~Requires": Requires_;
|
|
89
98
|
readonly "~Error": ErrorSchema_;
|
|
90
99
|
readonly "~FunctionTypes": FunctionTypes_;
|
|
91
100
|
readonly "~Self": Self;
|
|
101
|
+
readonly "~Options": OptionsSchema_;
|
|
92
102
|
}
|
|
93
103
|
|
|
94
104
|
export interface AnyMiddlewareSpec {
|
|
@@ -96,12 +106,24 @@ export interface AnyMiddlewareSpec {
|
|
|
96
106
|
readonly key: string;
|
|
97
107
|
readonly functionTypes: SupportedFunctionTypes;
|
|
98
108
|
readonly error?: Schema.Codec<any, any>;
|
|
109
|
+
readonly options?: Schema.Schema<any>;
|
|
99
110
|
readonly "~Provides": any;
|
|
100
111
|
readonly "~Requires": any;
|
|
101
112
|
readonly "~Error": any;
|
|
102
113
|
readonly "~FunctionTypes": FunctionType;
|
|
114
|
+
readonly "~Options": any;
|
|
103
115
|
}
|
|
104
116
|
|
|
117
|
+
export type Options<MiddlewareSpec_ extends AnyMiddlewareSpec> =
|
|
118
|
+
MiddlewareSpec_["~Options"]["Type"];
|
|
119
|
+
|
|
120
|
+
export type WithoutOptions<MiddlewareSpec_ extends AnyMiddlewareSpec> =
|
|
121
|
+
MiddlewareSpec_ extends AnyMiddlewareSpec
|
|
122
|
+
? [MiddlewareSpec_["~Options"]] extends [never]
|
|
123
|
+
? MiddlewareSpec_
|
|
124
|
+
: never
|
|
125
|
+
: never;
|
|
126
|
+
|
|
105
127
|
export type Key<MiddlewareSpec_ extends AnyMiddlewareSpec> =
|
|
106
128
|
MiddlewareSpec_["key"];
|
|
107
129
|
|
|
@@ -160,10 +182,12 @@ export const MiddlewareSpec =
|
|
|
160
182
|
const Key_ extends string,
|
|
161
183
|
const FunctionTypesConfig_ extends SupportedFunctionTypes,
|
|
162
184
|
ErrorSchema_ extends Schema.Codec<any, any> = never,
|
|
185
|
+
OptionsSchema_ extends Schema.Schema<any> = never,
|
|
163
186
|
>(
|
|
164
187
|
key: Key_,
|
|
165
188
|
options: {
|
|
166
189
|
readonly error?: () => ErrorSchema_;
|
|
190
|
+
readonly options?: () => OptionsSchema_;
|
|
167
191
|
readonly functionTypes: FunctionTypesConfig_ &
|
|
168
192
|
ValidateFunctionTypesConfig<FunctionTypesConfig_>;
|
|
169
193
|
},
|
|
@@ -173,7 +197,8 @@ export const MiddlewareSpec =
|
|
|
173
197
|
"provides" extends keyof Config ? Config["provides"] : never,
|
|
174
198
|
"requires" extends keyof Config ? Config["requires"] : never,
|
|
175
199
|
ErrorSchema_,
|
|
176
|
-
FunctionTypesFromConfig<FunctionTypesConfig_
|
|
200
|
+
FunctionTypesFromConfig<FunctionTypesConfig_>,
|
|
201
|
+
OptionsSchema_
|
|
177
202
|
> => {
|
|
178
203
|
const { query, mutation, action } = options.functionTypes;
|
|
179
204
|
if (!query && !mutation && !action) {
|
|
@@ -194,6 +219,9 @@ export const MiddlewareSpec =
|
|
|
194
219
|
if (options.error !== undefined) {
|
|
195
220
|
Lazy.defineProperty(class_, "error", options.error);
|
|
196
221
|
}
|
|
222
|
+
if (options.options !== undefined) {
|
|
223
|
+
Lazy.defineProperty(class_, "options", options.options);
|
|
224
|
+
}
|
|
197
225
|
return class_;
|
|
198
226
|
};
|
|
199
227
|
|
|
@@ -225,7 +253,7 @@ export const errorSchemas = (
|
|
|
225
253
|
/**
|
|
226
254
|
* A branded, unconstructible type carrying a spec-authoring error message.
|
|
227
255
|
* Surfaced as (part of) a parameter type so the diagnostic names the exact
|
|
228
|
-
* problem at the `.middleware()
|
|
256
|
+
* problem at the `.middleware()`/`.addFunction()` call site.
|
|
229
257
|
*/
|
|
230
258
|
export interface AttachmentError<Message extends string> {
|
|
231
259
|
readonly "~@confect/core/MiddlewareSpec/AttachmentError": Message;
|
|
@@ -251,12 +279,12 @@ export type ValidateFunction<
|
|
|
251
279
|
readonly functionProvenance: { readonly _tag: "Convex" };
|
|
252
280
|
}
|
|
253
281
|
? FunctionTypeOf<FunctionSpec_> extends FunctionTypes<MiddlewareSpec_>
|
|
254
|
-
? AttachmentError<`Convex-provenance function "${FunctionSpec_["name"]}" cannot be covered by middleware "${Key<MiddlewareSpec_>}"
|
|
282
|
+
? 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`>
|
|
255
283
|
: never
|
|
256
284
|
: [
|
|
257
285
|
Extract<
|
|
258
286
|
FunctionSpec.MiddlewareSpecs<FunctionSpec_>,
|
|
259
|
-
{ readonly key: Key<MiddlewareSpec_
|
|
287
|
+
{ readonly key: Key<WithoutOptions<MiddlewareSpec_>> }
|
|
260
288
|
>,
|
|
261
289
|
] extends [never]
|
|
262
290
|
? FunctionTypeOf<FunctionSpec_> extends FunctionTypes<MiddlewareSpec_>
|
|
@@ -276,13 +304,16 @@ export type ValidateAttach<
|
|
|
276
304
|
Functions_ extends FunctionSpec.AnyWithProps,
|
|
277
305
|
MiddlewareSpecs_ extends AnyMiddlewareSpec,
|
|
278
306
|
> = [
|
|
279
|
-
Extract<
|
|
307
|
+
Extract<
|
|
308
|
+
MiddlewareSpecs_,
|
|
309
|
+
{ readonly key: Key<WithoutOptions<MiddlewareSpec_>> }
|
|
310
|
+
>,
|
|
280
311
|
] extends [never]
|
|
281
312
|
? [Exclude<Requires<MiddlewareSpec_>, Provides<MiddlewareSpecs_>>] extends [
|
|
282
313
|
never,
|
|
283
314
|
]
|
|
284
315
|
? ValidationResult<ValidateFunction<Functions_, MiddlewareSpec_>>
|
|
285
|
-
: AttachmentError<`Middleware "${Key<MiddlewareSpec_>}" requires services that no middleware attached earlier to this group provides
|
|
316
|
+
: AttachmentError<`Middleware "${Key<MiddlewareSpec_>}" requires services that no middleware attached earlier to this group provides—attach the providing middleware first`>
|
|
286
317
|
: AttachmentError<`Middleware "${Key<MiddlewareSpec_>}" is already attached to this group`>;
|
|
287
318
|
|
|
288
319
|
export type ValidateAddedFunction<
|
|
@@ -304,7 +335,7 @@ type GroupOverlap<
|
|
|
304
335
|
? [
|
|
305
336
|
Extract<
|
|
306
337
|
MiddlewareSpecs_,
|
|
307
|
-
{ readonly key: Key<FunctionMiddlewareSpec
|
|
338
|
+
{ readonly key: Key<WithoutOptions<FunctionMiddlewareSpec>> }
|
|
308
339
|
>,
|
|
309
340
|
] extends [never]
|
|
310
341
|
? never
|
|
@@ -316,8 +347,8 @@ type GroupOverlap<
|
|
|
316
347
|
* The parameter-type validation applied to the group spec at
|
|
317
348
|
* `GroupImpl.make`: every function's attached middleware must have its
|
|
318
349
|
* `requires` services provided by some middleware covering that function
|
|
319
|
-
* (the group's or the function's own). Checked here
|
|
320
|
-
* assembled
|
|
350
|
+
* (the group's or the function's own). Checked here—with the group fully
|
|
351
|
+
* assembled—rather than at the spec builders, because a function-level
|
|
321
352
|
* middleware's `requires` may legitimately be satisfied by a group
|
|
322
353
|
* middleware the function spec never sees, and group attachment is
|
|
323
354
|
* declaratively order-independent with respect to `addFunction`.
|
|
@@ -346,9 +377,12 @@ export type ValidateFunctionAttach<
|
|
|
346
377
|
FunctionProvenance_ extends FunctionProvenance.FunctionProvenance,
|
|
347
378
|
MiddlewareSpecs_ extends AnyMiddlewareSpec,
|
|
348
379
|
> = FunctionProvenance_ extends { readonly _tag: "Convex" }
|
|
349
|
-
? AttachmentError<`Plain Convex functions cannot have middleware
|
|
380
|
+
? AttachmentError<`Plain Convex functions cannot have middleware—their raw handlers are passed through untouched`>
|
|
350
381
|
: [
|
|
351
|
-
Extract<
|
|
382
|
+
Extract<
|
|
383
|
+
MiddlewareSpecs_,
|
|
384
|
+
{ readonly key: Key<WithoutOptions<MiddlewareSpec_>> }
|
|
385
|
+
>,
|
|
352
386
|
] extends [never]
|
|
353
387
|
? RuntimeAndFunctionType_["functionType"] extends FunctionTypes<MiddlewareSpec_>
|
|
354
388
|
? unknown
|
package/src/PaginationOptions.ts
CHANGED
|
@@ -7,7 +7,7 @@ import * as Schema from "effect/Schema";
|
|
|
7
7
|
* Use this as the `paginationOpts` field of a paginated query's args schema.
|
|
8
8
|
* Beyond `numItems` and `cursor`, Convex's pagination protocol sends the
|
|
9
9
|
* optional fields (`endCursor`, `id`, `maximumRowsRead`, `maximumBytesRead`)
|
|
10
|
-
* with real requests
|
|
10
|
+
* with real requests—most notably `usePaginatedQuery` from `convex/react`
|
|
11
11
|
* always includes `id`, and includes `endCursor` when splitting pages.
|
|
12
12
|
* Declaring them is required for the generated Convex argument validator to
|
|
13
13
|
* accept those requests and for the fields to survive decoding and reach
|