@confect/core 10.0.0-next.14 → 10.0.0-next.17

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/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@confect/core",
3
3
  "description": "Shared specs and schemas used by all Confect packages",
4
- "version": "10.0.0-next.14",
4
+ "version": "10.0.0-next.17",
5
5
  "author": "RJ Dellecese",
6
6
  "bugs": {
7
7
  "url": "https://github.com/rjdellecese/confect/issues"
8
8
  },
9
9
  "devDependencies": {
10
- "@effect/vitest": "4.0.0-rc.108",
11
- "@types/node": "26.2.0",
12
- "effect": "4.0.0-rc.108",
10
+ "@effect/vitest": "4.0.0-rc.111",
11
+ "@types/node": "22.20.1",
12
+ "effect": "4.0.0-rc.111",
13
13
  "tsdown": "0.22.14",
14
- "tsx": "4.23.11",
14
+ "tsx": "4.23.12",
15
15
  "typescript": "7.0.2",
16
16
  "unrun": "0.3.1",
17
17
  "vitest": "4.1.10",
@@ -37,6 +37,7 @@
37
37
  "LICENSE",
38
38
  "README.md",
39
39
  "dist",
40
+ "!dist/*.tsbuildinfo",
40
41
  "package.json",
41
42
  "src"
42
43
  ],
@@ -50,11 +51,11 @@
50
51
  "module": "./dist/index.js",
51
52
  "peerDependencies": {
52
53
  "convex": "^1.32.0",
53
- "effect": "^4.0.0-rc.108"
54
+ "effect": "^4.0.0-rc.111"
54
55
  },
55
56
  "repository": {
56
57
  "type": "git",
57
- "url": "https://github.com/rjdellecese/confect.git"
58
+ "url": "git+https://github.com/rjdellecese/confect.git"
58
59
  },
59
60
  "sideEffects": false,
60
61
  "type": "module",
@@ -62,7 +63,7 @@
62
63
  "scripts": {
63
64
  "bench": "tsx test/Refs.bench.ts",
64
65
  "clean": "shx rm -rf dist coverage node_modules",
65
- "dev": "tsdown --watch --config-loader unrun",
66
+ "dev": "tsdown --watch --no-clean --config-loader unrun",
66
67
  "test": "vitest run",
67
68
  "typecheck": "tsc -b tsconfig.src.json"
68
69
  }
@@ -9,6 +9,7 @@ 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";
12
+ import type * as MiddlewareSpec from "./MiddlewareSpec";
12
13
  import * as RuntimeAndFunctionType from "./RuntimeAndFunctionType";
13
14
 
14
15
  export const TypeId = "@confect/core/FunctionSpec";
@@ -22,12 +23,44 @@ export interface FunctionSpec<
22
23
  FunctionVisibility_ extends FunctionVisibility,
23
24
  Name_ extends string,
24
25
  FunctionProvenance_ extends FunctionProvenance.FunctionProvenance,
26
+ MiddlewareSpecs_ extends MiddlewareSpec.AnyMiddlewareSpec = never,
25
27
  > {
26
28
  readonly [TypeId]: TypeId;
27
29
  readonly runtimeAndFunctionType: RuntimeAndFunctionType_;
28
30
  readonly functionVisibility: FunctionVisibility_;
29
31
  readonly name: Name_;
30
32
  readonly functionProvenance: FunctionProvenance_;
33
+ readonly middlewareSpecs: ReadonlyArray<MiddlewareSpecs_>;
34
+ }
35
+
36
+ export interface Builder<
37
+ RuntimeAndFunctionType_ extends RuntimeAndFunctionType.RuntimeAndFunctionType,
38
+ FunctionVisibility_ extends FunctionVisibility,
39
+ Name_ extends string,
40
+ FunctionProvenance_ extends FunctionProvenance.FunctionProvenance,
41
+ MiddlewareSpecs_ extends MiddlewareSpec.AnyMiddlewareSpec = never,
42
+ > extends FunctionSpec<
43
+ RuntimeAndFunctionType_,
44
+ FunctionVisibility_,
45
+ Name_,
46
+ FunctionProvenance_,
47
+ MiddlewareSpecs_
48
+ > {
49
+ middleware<MiddlewareSpec_ extends MiddlewareSpec.AnyMiddlewareSpec>(
50
+ middlewareSpec: MiddlewareSpec_ &
51
+ MiddlewareSpec.ValidateFunctionAttach<
52
+ MiddlewareSpec_,
53
+ RuntimeAndFunctionType_,
54
+ FunctionProvenance_,
55
+ MiddlewareSpecs_
56
+ >,
57
+ ): Builder<
58
+ RuntimeAndFunctionType_,
59
+ FunctionVisibility_,
60
+ Name_,
61
+ FunctionProvenance_,
62
+ MiddlewareSpecs_ | MiddlewareSpec_
63
+ >;
31
64
  }
32
65
 
33
66
  export interface Any {
@@ -38,21 +71,24 @@ export interface AnyWithProps extends FunctionSpec<
38
71
  RuntimeAndFunctionType.RuntimeAndFunctionType,
39
72
  FunctionVisibility,
40
73
  string,
41
- FunctionProvenance.FunctionProvenance
74
+ FunctionProvenance.FunctionProvenance,
75
+ MiddlewareSpec.AnyMiddlewareSpec
42
76
  > {}
43
77
 
44
78
  export interface AnyConfect extends FunctionSpec<
45
79
  RuntimeAndFunctionType.RuntimeAndFunctionType,
46
80
  FunctionVisibility,
47
81
  string,
48
- FunctionProvenance.AnyConfect
82
+ FunctionProvenance.AnyConfect,
83
+ MiddlewareSpec.AnyMiddlewareSpec
49
84
  > {}
50
85
 
51
86
  export interface AnyConvex extends FunctionSpec<
52
87
  RuntimeAndFunctionType.RuntimeAndFunctionType,
53
88
  FunctionVisibility,
54
89
  string,
55
- FunctionProvenance.AnyConvex
90
+ FunctionProvenance.AnyConvex,
91
+ MiddlewareSpec.AnyMiddlewareSpec
56
92
  > {}
57
93
 
58
94
  export interface AnyWithPropsWithRuntime<
@@ -61,7 +97,8 @@ export interface AnyWithPropsWithRuntime<
61
97
  RuntimeAndFunctionType.WithRuntime<Runtime>,
62
98
  FunctionVisibility,
63
99
  string,
64
- FunctionProvenance.FunctionProvenance
100
+ FunctionProvenance.FunctionProvenance,
101
+ MiddlewareSpec.AnyMiddlewareSpec
65
102
  > {}
66
103
 
67
104
  export interface AnyWithPropsWithFunctionType<
@@ -70,7 +107,8 @@ export interface AnyWithPropsWithFunctionType<
70
107
  RuntimeAndFunctionType_,
71
108
  FunctionVisibility,
72
109
  string,
73
- FunctionProvenance.FunctionProvenance
110
+ FunctionProvenance.FunctionProvenance,
111
+ MiddlewareSpec.AnyMiddlewareSpec
74
112
  > {}
75
113
 
76
114
  export interface AnyWithPropsWithFunctionProvenance<
@@ -79,7 +117,8 @@ export interface AnyWithPropsWithFunctionProvenance<
79
117
  RuntimeAndFunctionType.RuntimeAndFunctionType,
80
118
  FunctionVisibility,
81
119
  string,
82
- FunctionProvenance_
120
+ FunctionProvenance_,
121
+ MiddlewareSpec.AnyMiddlewareSpec
83
122
  > {}
84
123
 
85
124
  export type GetRuntimeAndFunctionType<FunctionSpec_ extends AnyWithProps> =
@@ -90,6 +129,9 @@ export type GetFunctionVisibility<FunctionSpec_ extends AnyWithProps> =
90
129
 
91
130
  export type Name<FunctionSpec_ extends AnyWithProps> = FunctionSpec_["name"];
92
131
 
132
+ export type MiddlewareSpecs<FunctionSpec_ extends AnyWithProps> =
133
+ FunctionSpec_["middlewareSpecs"][number];
134
+
93
135
  export type Args<FunctionSpec_ extends AnyWithProps> = FunctionSpec_ extends {
94
136
  functionProvenance: {
95
137
  _tag: "Confect";
@@ -202,6 +244,40 @@ export type WithoutName<
202
244
 
203
245
  const Proto = {
204
246
  [TypeId]: TypeId,
247
+
248
+ middleware(
249
+ this: AnyWithProps,
250
+ middlewareSpec: MiddlewareSpec.AnyMiddlewareSpec,
251
+ ) {
252
+ if (this.functionProvenance._tag === "Convex") {
253
+ throw new Error(
254
+ `Plain Convex function "${this.name}" cannot have middleware`,
255
+ );
256
+ }
257
+ const functionType = this.runtimeAndFunctionType.functionType;
258
+ if (!middlewareSpec.functionTypes[functionType]) {
259
+ throw new Error(
260
+ `Middleware "${middlewareSpec.key}" does not declare function type "${functionType}" of function "${this.name}"`,
261
+ );
262
+ }
263
+ if (
264
+ this.middlewareSpecs.some(
265
+ (existing) => existing.key === middlewareSpec.key,
266
+ )
267
+ ) {
268
+ throw new Error(
269
+ `Middleware "${middlewareSpec.key}" is already attached to function "${this.name}"`,
270
+ );
271
+ }
272
+
273
+ return Object.assign(Object.create(Proto), {
274
+ runtimeAndFunctionType: this.runtimeAndFunctionType,
275
+ functionVisibility: this.functionVisibility,
276
+ name: this.name,
277
+ functionProvenance: this.functionProvenance,
278
+ middlewareSpecs: [...this.middlewareSpecs, middlewareSpec],
279
+ });
280
+ },
205
281
  };
206
282
 
207
283
  const make =
@@ -228,7 +304,7 @@ const make =
228
304
  args: () => Args_;
229
305
  returns: () => Returns_;
230
306
  error?: () => Error_;
231
- }): FunctionSpec<
307
+ }): Builder<
232
308
  RuntimeAndFunctionType_,
233
309
  FunctionVisibility_,
234
310
  Name_,
@@ -241,6 +317,7 @@ const make =
241
317
  functionVisibility,
242
318
  name,
243
319
  functionProvenance: FunctionProvenance.Confect(args, returns, error),
320
+ middlewareSpecs: [],
244
321
  });
245
322
  };
246
323
 
@@ -281,7 +358,7 @@ const makePaginated =
281
358
  /** The page element schema. */
282
359
  item: () => Item_;
283
360
  error?: () => Error_;
284
- }): FunctionSpec<
361
+ }): Builder<
285
362
  RuntimeAndFunctionType_,
286
363
  FunctionVisibility_,
287
364
  Name_,
@@ -299,6 +376,7 @@ const makePaginated =
299
376
  item,
300
377
  error,
301
378
  ),
379
+ middlewareSpecs: [],
302
380
  });
303
381
  };
304
382
 
@@ -385,7 +463,7 @@ const makeConvex =
385
463
  >() =>
386
464
  <const Name_ extends string>(
387
465
  name: Name_,
388
- ): FunctionSpec<
466
+ ): Builder<
389
467
  RuntimeAndFunctionType_,
390
468
  FunctionVisibility_,
391
469
  Name_,
@@ -401,6 +479,7 @@ const makeConvex =
401
479
  ExtractArgs<F>,
402
480
  ExtractReturns<F>
403
481
  >(),
482
+ middlewareSpecs: [],
404
483
  }) as any;
405
484
  };
406
485
 
package/src/GroupSpec.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as Predicate from "effect/Predicate";
2
2
  import * as Record from "effect/Record";
3
3
  import type * as FunctionSpec from "./FunctionSpec";
4
+ import type * as MiddlewareSpec from "./MiddlewareSpec";
4
5
  import type * as RuntimeAndFunctionType from "./RuntimeAndFunctionType";
5
6
  import { validateConfectFunctionIdentifier } from "./Identifier";
6
7
 
@@ -19,31 +20,63 @@ export interface GroupSpec<
19
20
  // modules. Functions, by contrast, stay homogeneous (a Node group only accepts
20
21
  // Node actions) — `addFunction` keeps the `<Runtime>` bound below.
21
22
  Groups_ extends AnyWithProps = never,
23
+ MiddlewareSpecs_ extends MiddlewareSpec.AnyMiddlewareSpec = never,
22
24
  > {
23
25
  readonly [TypeId]: TypeId;
24
26
  readonly runtime: Runtime;
25
27
  readonly name: Name_;
26
28
  readonly functions: {
27
- [FunctionName in FunctionSpec.Name<
28
- FunctionSpec.AnyWithPropsWithRuntime<Runtime>
29
- >]: FunctionSpec.WithName<Functions_, FunctionName>;
29
+ [
30
+ FunctionName in FunctionSpec.Name<
31
+ FunctionSpec.AnyWithPropsWithRuntime<Runtime>
32
+ >
33
+ ]: FunctionSpec.WithName<Functions_, FunctionName>;
30
34
  };
31
35
  readonly groups: {
32
36
  [GroupName in Name<Groups_>]: WithName<Groups_, GroupName>;
33
37
  };
38
+ readonly middlewareSpecs: ReadonlyArray<MiddlewareSpecs_>;
34
39
 
35
40
  addFunction<Function extends FunctionSpec.AnyWithPropsWithRuntime<Runtime>>(
36
- function_: Function,
37
- ): GroupSpec<Runtime, Name_, Functions_ | Function, Groups_>;
41
+ function_: Function &
42
+ MiddlewareSpec.ValidateAddedFunction<Function, MiddlewareSpecs_>,
43
+ ): GroupSpec<
44
+ Runtime,
45
+ Name_,
46
+ Functions_ | Function,
47
+ Groups_,
48
+ MiddlewareSpecs_
49
+ >;
38
50
 
39
51
  addGroup<Group extends AnyWithProps>(
40
52
  group: Group,
41
- ): GroupSpec<Runtime, Name_, Functions_, Groups_ | Group>;
53
+ ): GroupSpec<Runtime, Name_, Functions_, Groups_ | Group, MiddlewareSpecs_>;
42
54
 
43
55
  addGroupAt<const AtName extends string, Group extends AnyWithProps>(
44
56
  name: AtName,
45
57
  group: Group,
46
- ): GroupSpec<Runtime, Name_, Functions_, Groups_ | NamedAt<Group, AtName>>;
58
+ ): GroupSpec<
59
+ Runtime,
60
+ Name_,
61
+ Functions_,
62
+ Groups_ | NamedAt<Group, AtName>,
63
+ MiddlewareSpecs_
64
+ >;
65
+
66
+ middleware<MiddlewareSpec_ extends MiddlewareSpec.AnyMiddlewareSpec>(
67
+ middlewareSpec: MiddlewareSpec_ &
68
+ MiddlewareSpec.ValidateAttach<
69
+ MiddlewareSpec_,
70
+ Functions_,
71
+ MiddlewareSpecs_
72
+ >,
73
+ ): GroupSpec<
74
+ Runtime,
75
+ Name_,
76
+ Functions_,
77
+ Groups_,
78
+ MiddlewareSpecs_ | MiddlewareSpec_
79
+ >;
47
80
  }
48
81
 
49
82
  export interface Any {
@@ -54,7 +87,8 @@ export interface AnyWithProps extends GroupSpec<
54
87
  RuntimeAndFunctionType.Runtime,
55
88
  string,
56
89
  FunctionSpec.AnyWithProps,
57
- AnyWithProps
90
+ AnyWithProps,
91
+ MiddlewareSpec.AnyMiddlewareSpec
58
92
  > {}
59
93
 
60
94
  export interface AnyWithPropsWithRuntime<
@@ -63,7 +97,8 @@ export interface AnyWithPropsWithRuntime<
63
97
  Runtime,
64
98
  string,
65
99
  FunctionSpec.AnyWithPropsWithRuntime<Runtime>,
66
- AnyWithPropsWithRuntime<Runtime>
100
+ AnyWithPropsWithRuntime<Runtime>,
101
+ MiddlewareSpec.AnyMiddlewareSpec
67
102
  > {}
68
103
 
69
104
  export type Name<Group extends AnyWithProps> = Group["name"];
@@ -74,6 +109,9 @@ export type Functions<Group extends AnyWithProps> =
74
109
  export type Groups<Group extends AnyWithProps> =
75
110
  Group["groups"][keyof Group["groups"]];
76
111
 
112
+ export type MiddlewareSpecs<Group extends AnyWithProps> =
113
+ Group["middlewareSpecs"][number];
114
+
77
115
  export type GroupNames<Group extends AnyWithProps> = [Groups<Group>] extends [
78
116
  never,
79
117
  ]
@@ -101,9 +139,16 @@ export type AddGroups<
101
139
  infer Runtime,
102
140
  infer Name_,
103
141
  infer Functions_,
104
- infer Groups_
142
+ infer Groups_,
143
+ infer MiddlewareSpecs_
105
144
  >
106
- ? GroupSpec<Runtime, Name_, Functions_, Groups_ | ExtraGroups>
145
+ ? GroupSpec<
146
+ Runtime,
147
+ Name_,
148
+ Functions_,
149
+ Groups_ | ExtraGroups,
150
+ MiddlewareSpecs_
151
+ >
107
152
  : never;
108
153
 
109
154
  const Proto = {
@@ -115,11 +160,25 @@ const Proto = {
115
160
  ) {
116
161
  const this_ = this as AnyWithProps;
117
162
 
163
+ const overlapping = function_.middlewareSpecs.find(
164
+ (functionMiddlewareSpec) =>
165
+ this_.middlewareSpecs.some(
166
+ (groupMiddlewareSpec) =>
167
+ groupMiddlewareSpec.key === functionMiddlewareSpec.key,
168
+ ),
169
+ );
170
+ if (overlapping !== undefined) {
171
+ throw new Error(
172
+ `Middleware "${overlapping.key}" is attached to both function "${function_.name}" and its group`,
173
+ );
174
+ }
175
+
118
176
  return makeProto({
119
177
  runtime: this_.runtime,
120
178
  name: this_.name,
121
179
  functions: Record.set(this_.functions, function_.name, function_),
122
180
  groups: this_.groups,
181
+ middlewareSpecs: this_.middlewareSpecs,
123
182
  });
124
183
  },
125
184
 
@@ -132,6 +191,7 @@ const Proto = {
132
191
  name: this_.name,
133
192
  functions: this_.functions,
134
193
  groups: Record.set(this_.groups, group_.name, group_),
194
+ middlewareSpecs: this_.middlewareSpecs,
135
195
  });
136
196
  },
137
197
 
@@ -144,6 +204,44 @@ const Proto = {
144
204
  name: this_.name,
145
205
  functions: this_.functions,
146
206
  groups: Record.set(this_.groups, name, withName(name, group_)),
207
+ middlewareSpecs: this_.middlewareSpecs,
208
+ });
209
+ },
210
+
211
+ middleware<MiddlewareSpec_ extends MiddlewareSpec.AnyMiddlewareSpec>(
212
+ this: Any,
213
+ middlewareSpec: MiddlewareSpec_,
214
+ ) {
215
+ const this_ = this as AnyWithProps;
216
+
217
+ if (
218
+ this_.middlewareSpecs.some(
219
+ (existing) => existing.key === middlewareSpec.key,
220
+ )
221
+ ) {
222
+ throw new Error(
223
+ `Middleware "${middlewareSpec.key}" is already attached to this group`,
224
+ );
225
+ }
226
+
227
+ for (const function_ of Object.values(this_.functions)) {
228
+ if (
229
+ function_.middlewareSpecs.some(
230
+ (existing) => existing.key === middlewareSpec.key,
231
+ )
232
+ ) {
233
+ throw new Error(
234
+ `Middleware "${middlewareSpec.key}" is attached to both function "${function_.name}" and its group`,
235
+ );
236
+ }
237
+ }
238
+
239
+ return makeProto({
240
+ runtime: this_.runtime,
241
+ name: this_.name,
242
+ functions: this_.functions,
243
+ groups: this_.groups,
244
+ middlewareSpecs: [...this_.middlewareSpecs, middlewareSpec],
147
245
  });
148
246
  },
149
247
  };
@@ -153,22 +251,26 @@ const makeProto = <
153
251
  Name_ extends string,
154
252
  Functions_ extends FunctionSpec.AnyWithPropsWithRuntime<Runtime>,
155
253
  Groups_ extends AnyWithPropsWithRuntime<Runtime>,
254
+ MiddlewareSpecs_ extends MiddlewareSpec.AnyMiddlewareSpec,
156
255
  >({
157
256
  runtime,
158
257
  name,
159
258
  functions,
160
259
  groups,
260
+ middlewareSpecs,
161
261
  }: {
162
262
  runtime: Runtime;
163
263
  name: Name_;
164
264
  functions: Record.ReadonlyRecord<string, Functions_>;
165
265
  groups: Record.ReadonlyRecord<string, Groups_>;
166
- }): GroupSpec<Runtime, Name_, Functions_, Groups_> =>
266
+ middlewareSpecs: ReadonlyArray<MiddlewareSpecs_>;
267
+ }): GroupSpec<Runtime, Name_, Functions_, Groups_, MiddlewareSpecs_> =>
167
268
  Object.assign(Object.create(Proto), {
168
269
  runtime,
169
270
  name,
170
271
  functions,
171
272
  groups,
273
+ middlewareSpecs,
172
274
  });
173
275
 
174
276
  export const make = (): GroupSpec<"Convex", ""> =>
@@ -177,6 +279,7 @@ export const make = (): GroupSpec<"Convex", ""> =>
177
279
  name: "",
178
280
  functions: Record.empty(),
179
281
  groups: Record.empty(),
282
+ middlewareSpecs: [],
180
283
  });
181
284
 
182
285
  export const makeAt = <const Name_ extends string>(
@@ -189,6 +292,7 @@ export const makeAt = <const Name_ extends string>(
189
292
  name,
190
293
  functions: Record.empty(),
191
294
  groups: Record.empty(),
295
+ middlewareSpecs: [],
192
296
  });
193
297
  };
194
298
 
@@ -198,6 +302,7 @@ export const makeNode = (): GroupSpec<"Node", ""> =>
198
302
  name: "",
199
303
  functions: Record.empty(),
200
304
  groups: Record.empty(),
305
+ middlewareSpecs: [],
201
306
  });
202
307
 
203
308
  export const makeNodeAt = <const Name_ extends string>(
@@ -210,6 +315,7 @@ export const makeNodeAt = <const Name_ extends string>(
210
315
  name,
211
316
  functions: Record.empty(),
212
317
  groups: Record.empty(),
318
+ middlewareSpecs: [],
213
319
  });
214
320
  };
215
321
 
@@ -229,5 +335,6 @@ export const withName = <const Name_ extends string>(
229
335
  name,
230
336
  functions: group_.functions,
231
337
  groups: group_.groups,
338
+ middlewareSpecs: group_.middlewareSpecs,
232
339
  });
233
340
  };