@confect/core 10.0.0-next.9 → 10.1.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 +964 -47
- package/README.md +3 -1
- package/dist/FunctionProvenance.d.ts +166 -68
- package/dist/FunctionProvenance.d.ts.map +1 -1
- package/dist/FunctionProvenance.js +55 -13
- package/dist/FunctionProvenance.js.map +1 -1
- package/dist/FunctionSpec.d.ts +320 -138
- package/dist/FunctionSpec.d.ts.map +1 -1
- package/dist/FunctionSpec.js +56 -13
- package/dist/FunctionSpec.js.map +1 -1
- package/dist/GenericId.d.ts.map +1 -1
- package/dist/GenericId.js +1 -1
- package/dist/GroupPath.d.ts +6 -10
- package/dist/GroupPath.d.ts.map +1 -1
- package/dist/GroupPath.js.map +1 -1
- package/dist/GroupSpec.d.ts +25 -12
- package/dist/GroupSpec.d.ts.map +1 -1
- package/dist/GroupSpec.js +51 -14
- package/dist/GroupSpec.js.map +1 -1
- package/dist/Identifier.d.ts +6 -5
- package/dist/Identifier.d.ts.map +1 -1
- package/dist/Identifier.js +6 -5
- package/dist/Identifier.js.map +1 -1
- package/dist/Lazy.d.ts +11 -14
- package/dist/Lazy.d.ts.map +1 -1
- package/dist/Lazy.js +11 -14
- 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 +198 -0
- package/dist/MiddlewareSpec.d.ts.map +1 -0
- package/dist/MiddlewareSpec.js +60 -0
- package/dist/MiddlewareSpec.js.map +1 -0
- package/dist/PaginationError.d.ts +20 -0
- package/dist/PaginationError.d.ts.map +1 -0
- package/dist/PaginationError.js +31 -0
- package/dist/PaginationError.js.map +1 -0
- package/dist/PaginationOptions.d.ts +23 -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 +135 -47
- package/dist/Ref.d.ts.map +1 -1
- package/dist/Ref.js +104 -46
- package/dist/Ref.js.map +1 -1
- package/dist/Refs.d.ts +10 -5
- package/dist/Refs.d.ts.map +1 -1
- package/dist/Refs.js +5 -4
- package/dist/Refs.js.map +1 -1
- package/dist/SchemaToValidator.d.ts +99 -0
- package/dist/SchemaToValidator.d.ts.map +1 -0
- package/dist/SchemaToValidator.js +158 -0
- package/dist/SchemaToValidator.js.map +1 -0
- package/dist/Spec.d.ts +16 -12
- package/dist/Spec.d.ts.map +1 -1
- package/dist/Spec.js +14 -9
- package/dist/Spec.js.map +1 -1
- package/dist/SystemFields.d.ts +20 -19
- package/dist/SystemFields.d.ts.map +1 -1
- package/dist/SystemFields.js +22 -21
- package/dist/SystemFields.js.map +1 -1
- package/dist/Table.d.ts +75 -0
- package/dist/Table.d.ts.map +1 -0
- package/dist/Table.js +77 -0
- package/dist/Table.js.map +1 -0
- package/dist/Types.d.ts +8 -2
- package/dist/Types.d.ts.map +1 -1
- package/dist/Types.js.map +1 -1
- package/dist/UserIdentity.d.ts.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -4
- package/package.json +17 -15
- package/src/FunctionProvenance.ts +197 -34
- package/src/FunctionSpec.ts +332 -86
- package/src/GroupPath.ts +6 -12
- package/src/GroupSpec.ts +184 -25
- package/src/Identifier.ts +6 -5
- package/src/Lazy.ts +11 -14
- package/src/MiddlewareAttachment.ts +106 -0
- package/src/MiddlewareSpec.ts +392 -0
- package/src/PaginationError.ts +78 -0
- package/src/PaginationOptions.ts +23 -0
- package/src/PaginationResult.ts +4 -0
- package/src/Ref.ts +468 -236
- package/src/Refs.ts +43 -18
- package/src/SchemaToValidator.ts +612 -0
- package/src/Spec.ts +50 -39
- package/src/SystemFields.ts +40 -42
- package/src/Table.ts +427 -0
- package/src/Types.ts +9 -3
- package/src/index.ts +6 -1
- package/dist/Registry.d.ts +0 -24
- package/dist/Registry.d.ts.map +0 -1
- package/dist/Registry.js +0 -19
- package/dist/Registry.js.map +0 -1
- package/dist/tsconfig.src.tsbuildinfo +0 -1
- package/src/Registry.ts +0 -30
package/src/GroupSpec.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
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";
|
|
6
|
+
import type * as MiddlewareSpec from "./MiddlewareSpec";
|
|
4
7
|
import type * as RuntimeAndFunctionType from "./RuntimeAndFunctionType";
|
|
5
8
|
import { validateConfectFunctionIdentifier } from "./Identifier";
|
|
6
9
|
|
|
7
|
-
export const TypeId = "
|
|
10
|
+
export const TypeId = "~@confect/core/GroupSpec";
|
|
8
11
|
export type TypeId = typeof TypeId;
|
|
9
12
|
|
|
10
13
|
export const isGroupSpec = (u: unknown): u is AnyWithProps =>
|
|
@@ -17,33 +20,71 @@ export interface GroupSpec<
|
|
|
17
20
|
// Subgroups may be of any runtime, independent of this group's own runtime: a
|
|
18
21
|
// group is only a namespace for its children, which are otherwise-independent
|
|
19
22
|
// modules. Functions, by contrast, stay homogeneous (a Node group only accepts
|
|
20
|
-
// Node actions)
|
|
23
|
+
// Node actions)—`addFunction` keeps the `<Runtime>` bound below.
|
|
21
24
|
Groups_ extends AnyWithProps = never,
|
|
25
|
+
MiddlewareSpecs_ extends MiddlewareSpec.AnyMiddlewareSpec = never,
|
|
22
26
|
> {
|
|
23
27
|
readonly [TypeId]: TypeId;
|
|
24
28
|
readonly runtime: Runtime;
|
|
25
29
|
readonly name: Name_;
|
|
26
30
|
readonly functions: {
|
|
27
|
-
[
|
|
28
|
-
FunctionSpec.
|
|
29
|
-
|
|
31
|
+
[
|
|
32
|
+
FunctionName in FunctionSpec.Name<
|
|
33
|
+
FunctionSpec.AnyWithPropsWithRuntime<Runtime>
|
|
34
|
+
>
|
|
35
|
+
]: FunctionSpec.WithName<Functions_, FunctionName>;
|
|
30
36
|
};
|
|
31
37
|
readonly groups: {
|
|
32
38
|
[GroupName in Name<Groups_>]: WithName<Groups_, GroupName>;
|
|
33
39
|
};
|
|
40
|
+
readonly middlewareSpecs: ReadonlyArray<MiddlewareSpecs_>;
|
|
41
|
+
readonly middlewareAttachments: ReadonlyArray<
|
|
42
|
+
MiddlewareAttachment.MiddlewareAttachment<MiddlewareSpecs_>
|
|
43
|
+
>;
|
|
44
|
+
readonly "~Functions": Functions_;
|
|
45
|
+
readonly "~Groups": Groups_;
|
|
34
46
|
|
|
35
47
|
addFunction<Function extends FunctionSpec.AnyWithPropsWithRuntime<Runtime>>(
|
|
36
|
-
function_: Function
|
|
37
|
-
|
|
48
|
+
function_: Function &
|
|
49
|
+
MiddlewareSpec.ValidateAddedFunction<Function, MiddlewareSpecs_>,
|
|
50
|
+
): GroupSpec<
|
|
51
|
+
Runtime,
|
|
52
|
+
Name_,
|
|
53
|
+
Functions_ | Function,
|
|
54
|
+
Groups_,
|
|
55
|
+
MiddlewareSpecs_
|
|
56
|
+
>;
|
|
38
57
|
|
|
39
58
|
addGroup<Group extends AnyWithProps>(
|
|
40
59
|
group: Group,
|
|
41
|
-
): GroupSpec<Runtime, Name_, Functions_, Groups_ | Group>;
|
|
60
|
+
): GroupSpec<Runtime, Name_, Functions_, Groups_ | Group, MiddlewareSpecs_>;
|
|
42
61
|
|
|
43
62
|
addGroupAt<const AtName extends string, Group extends AnyWithProps>(
|
|
44
63
|
name: AtName,
|
|
45
64
|
group: Group,
|
|
46
|
-
): GroupSpec<
|
|
65
|
+
): GroupSpec<
|
|
66
|
+
Runtime,
|
|
67
|
+
Name_,
|
|
68
|
+
Functions_,
|
|
69
|
+
Groups_ | NamedAt<Group, AtName>,
|
|
70
|
+
MiddlewareSpecs_
|
|
71
|
+
>;
|
|
72
|
+
|
|
73
|
+
middleware<MiddlewareSpec_ extends MiddlewareSpec.AnyMiddlewareSpec>(
|
|
74
|
+
middlewareSpec: MiddlewareSpec_ &
|
|
75
|
+
MiddlewareSpec.ValidateAttach<
|
|
76
|
+
MiddlewareSpec_,
|
|
77
|
+
Functions_,
|
|
78
|
+
MiddlewareSpecs_
|
|
79
|
+
>,
|
|
80
|
+
...options: MiddlewareAttachment.Args<NoInfer<MiddlewareSpec_>>
|
|
81
|
+
): GroupSpec<
|
|
82
|
+
Runtime,
|
|
83
|
+
Name_,
|
|
84
|
+
Functions_,
|
|
85
|
+
Groups_,
|
|
86
|
+
MiddlewareSpecs_ | MiddlewareSpec_
|
|
87
|
+
>;
|
|
47
88
|
}
|
|
48
89
|
|
|
49
90
|
export interface Any {
|
|
@@ -54,7 +95,8 @@ export interface AnyWithProps extends GroupSpec<
|
|
|
54
95
|
RuntimeAndFunctionType.Runtime,
|
|
55
96
|
string,
|
|
56
97
|
FunctionSpec.AnyWithProps,
|
|
57
|
-
AnyWithProps
|
|
98
|
+
AnyWithProps,
|
|
99
|
+
MiddlewareSpec.AnyMiddlewareSpec
|
|
58
100
|
> {}
|
|
59
101
|
|
|
60
102
|
export interface AnyWithPropsWithRuntime<
|
|
@@ -63,16 +105,18 @@ export interface AnyWithPropsWithRuntime<
|
|
|
63
105
|
Runtime,
|
|
64
106
|
string,
|
|
65
107
|
FunctionSpec.AnyWithPropsWithRuntime<Runtime>,
|
|
66
|
-
AnyWithPropsWithRuntime<Runtime
|
|
108
|
+
AnyWithPropsWithRuntime<Runtime>,
|
|
109
|
+
MiddlewareSpec.AnyMiddlewareSpec
|
|
67
110
|
> {}
|
|
68
111
|
|
|
69
112
|
export type Name<Group extends AnyWithProps> = Group["name"];
|
|
70
113
|
|
|
71
|
-
export type Functions<Group extends AnyWithProps> =
|
|
72
|
-
|
|
114
|
+
export type Functions<Group extends AnyWithProps> = Group["~Functions"];
|
|
115
|
+
|
|
116
|
+
export type Groups<Group extends AnyWithProps> = Group["~Groups"];
|
|
73
117
|
|
|
74
|
-
export type
|
|
75
|
-
Group["
|
|
118
|
+
export type MiddlewareSpecs<Group extends AnyWithProps> =
|
|
119
|
+
Group["middlewareSpecs"][number];
|
|
76
120
|
|
|
77
121
|
export type GroupNames<Group extends AnyWithProps> = [Groups<Group>] extends [
|
|
78
122
|
never,
|
|
@@ -85,7 +129,10 @@ export type WithName<
|
|
|
85
129
|
Name_ extends Name<Group>,
|
|
86
130
|
> = Extract<Group, { readonly name: Name_ }>;
|
|
87
131
|
|
|
88
|
-
/**
|
|
132
|
+
/**
|
|
133
|
+
* Assigns a segment name to a leaf group created with {@link make} for typing
|
|
134
|
+
* and refs.
|
|
135
|
+
*/
|
|
89
136
|
export type NamedAt<Group extends Any, Name_ extends string> = Omit<
|
|
90
137
|
Group,
|
|
91
138
|
"name"
|
|
@@ -101,25 +148,51 @@ export type AddGroups<
|
|
|
101
148
|
infer Runtime,
|
|
102
149
|
infer Name_,
|
|
103
150
|
infer Functions_,
|
|
104
|
-
infer Groups_
|
|
151
|
+
infer Groups_,
|
|
152
|
+
infer MiddlewareSpecs_
|
|
105
153
|
>
|
|
106
|
-
? GroupSpec<
|
|
154
|
+
? GroupSpec<
|
|
155
|
+
Runtime,
|
|
156
|
+
Name_,
|
|
157
|
+
Functions_,
|
|
158
|
+
Groups_ | ExtraGroups,
|
|
159
|
+
MiddlewareSpecs_
|
|
160
|
+
>
|
|
107
161
|
: never;
|
|
108
162
|
|
|
109
163
|
const Proto = {
|
|
110
164
|
[TypeId]: TypeId,
|
|
111
165
|
|
|
166
|
+
get middlewareSpecs() {
|
|
167
|
+
return this.middlewareAttachments.map(({ spec }) => spec);
|
|
168
|
+
},
|
|
169
|
+
|
|
112
170
|
addFunction<Function extends FunctionSpec.AnyWithProps>(
|
|
113
171
|
this: Any,
|
|
114
172
|
function_: Function,
|
|
115
173
|
) {
|
|
116
174
|
const this_ = this as AnyWithProps;
|
|
117
175
|
|
|
176
|
+
const overlapping = function_.middlewareAttachments.find(
|
|
177
|
+
({ spec: functionMiddlewareSpec }) =>
|
|
178
|
+
!("options" in functionMiddlewareSpec) &&
|
|
179
|
+
this_.middlewareAttachments.some(
|
|
180
|
+
({ spec: groupMiddlewareSpec }) =>
|
|
181
|
+
groupMiddlewareSpec.key === functionMiddlewareSpec.key,
|
|
182
|
+
),
|
|
183
|
+
);
|
|
184
|
+
if (overlapping !== undefined) {
|
|
185
|
+
throw new Error(
|
|
186
|
+
`Middleware "${overlapping.spec.key}" is attached to both function "${function_.name}" and its group`,
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
118
190
|
return makeProto({
|
|
119
191
|
runtime: this_.runtime,
|
|
120
192
|
name: this_.name,
|
|
121
193
|
functions: Record.set(this_.functions, function_.name, function_),
|
|
122
194
|
groups: this_.groups,
|
|
195
|
+
middlewareAttachments: this_.middlewareAttachments,
|
|
123
196
|
});
|
|
124
197
|
},
|
|
125
198
|
|
|
@@ -132,6 +205,7 @@ const Proto = {
|
|
|
132
205
|
name: this_.name,
|
|
133
206
|
functions: this_.functions,
|
|
134
207
|
groups: Record.set(this_.groups, group_.name, group_),
|
|
208
|
+
middlewareAttachments: this_.middlewareAttachments,
|
|
135
209
|
});
|
|
136
210
|
},
|
|
137
211
|
|
|
@@ -144,32 +218,93 @@ const Proto = {
|
|
|
144
218
|
name: this_.name,
|
|
145
219
|
functions: this_.functions,
|
|
146
220
|
groups: Record.set(this_.groups, name, withName(name, group_)),
|
|
221
|
+
middlewareAttachments: this_.middlewareAttachments,
|
|
147
222
|
});
|
|
148
223
|
},
|
|
149
|
-
|
|
224
|
+
|
|
225
|
+
middleware<MiddlewareSpec_ extends MiddlewareSpec.AnyMiddlewareSpec>(
|
|
226
|
+
this: Any,
|
|
227
|
+
middlewareSpec: MiddlewareSpec_,
|
|
228
|
+
...options: ReadonlyArray<unknown>
|
|
229
|
+
) {
|
|
230
|
+
const this_ = this as AnyWithProps;
|
|
231
|
+
|
|
232
|
+
if (
|
|
233
|
+
!("options" in middlewareSpec) &&
|
|
234
|
+
this_.middlewareAttachments.some(
|
|
235
|
+
({ spec }) => spec.key === middlewareSpec.key,
|
|
236
|
+
)
|
|
237
|
+
) {
|
|
238
|
+
throw new Error(
|
|
239
|
+
`Middleware "${middlewareSpec.key}" is already attached to this group`,
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
for (const function_ of Object.values(this_.functions)) {
|
|
244
|
+
if (
|
|
245
|
+
!("options" in middlewareSpec) &&
|
|
246
|
+
function_.middlewareAttachments.some(
|
|
247
|
+
({ spec }) => spec.key === middlewareSpec.key,
|
|
248
|
+
)
|
|
249
|
+
) {
|
|
250
|
+
throw new Error(
|
|
251
|
+
`Middleware "${middlewareSpec.key}" is attached to both function "${function_.name}" and its group`,
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return makeProto({
|
|
257
|
+
runtime: this_.runtime,
|
|
258
|
+
name: this_.name,
|
|
259
|
+
functions: this_.functions,
|
|
260
|
+
groups: this_.groups,
|
|
261
|
+
middlewareAttachments: [
|
|
262
|
+
...this_.middlewareAttachments,
|
|
263
|
+
{ spec: middlewareSpec, options: options[0] },
|
|
264
|
+
],
|
|
265
|
+
});
|
|
266
|
+
},
|
|
267
|
+
} satisfies ThisType<AnyWithProps>;
|
|
150
268
|
|
|
151
269
|
const makeProto = <
|
|
152
270
|
Runtime extends RuntimeAndFunctionType.Runtime,
|
|
153
271
|
Name_ extends string,
|
|
154
272
|
Functions_ extends FunctionSpec.AnyWithPropsWithRuntime<Runtime>,
|
|
155
273
|
Groups_ extends AnyWithPropsWithRuntime<Runtime>,
|
|
274
|
+
MiddlewareAttachments_ extends
|
|
275
|
+
ReadonlyArray<MiddlewareAttachment.MiddlewareAttachment>,
|
|
156
276
|
>({
|
|
157
277
|
runtime,
|
|
158
278
|
name,
|
|
159
279
|
functions,
|
|
160
280
|
groups,
|
|
281
|
+
middlewareAttachments,
|
|
161
282
|
}: {
|
|
162
283
|
runtime: Runtime;
|
|
163
284
|
name: Name_;
|
|
164
285
|
functions: Record.ReadonlyRecord<string, Functions_>;
|
|
165
286
|
groups: Record.ReadonlyRecord<string, Groups_>;
|
|
166
|
-
|
|
287
|
+
middlewareAttachments: MiddlewareAttachments_;
|
|
288
|
+
}): GroupSpec<
|
|
289
|
+
Runtime,
|
|
290
|
+
Name_,
|
|
291
|
+
Functions_,
|
|
292
|
+
Groups_,
|
|
293
|
+
MiddlewareAttachments_[number]["spec"]
|
|
294
|
+
> =>
|
|
167
295
|
Object.assign(Object.create(Proto), {
|
|
168
296
|
runtime,
|
|
169
297
|
name,
|
|
170
298
|
functions,
|
|
171
299
|
groups,
|
|
172
|
-
|
|
300
|
+
middlewareAttachments,
|
|
301
|
+
}) as GroupSpec<
|
|
302
|
+
Runtime,
|
|
303
|
+
Name_,
|
|
304
|
+
Functions_,
|
|
305
|
+
Groups_,
|
|
306
|
+
MiddlewareAttachments_[number]["spec"]
|
|
307
|
+
>;
|
|
173
308
|
|
|
174
309
|
export const make = (): GroupSpec<"Convex", ""> =>
|
|
175
310
|
makeProto({
|
|
@@ -177,6 +312,7 @@ export const make = (): GroupSpec<"Convex", ""> =>
|
|
|
177
312
|
name: "",
|
|
178
313
|
functions: Record.empty(),
|
|
179
314
|
groups: Record.empty(),
|
|
315
|
+
middlewareAttachments: [],
|
|
180
316
|
});
|
|
181
317
|
|
|
182
318
|
export const makeAt = <const Name_ extends string>(
|
|
@@ -189,6 +325,7 @@ export const makeAt = <const Name_ extends string>(
|
|
|
189
325
|
name,
|
|
190
326
|
functions: Record.empty(),
|
|
191
327
|
groups: Record.empty(),
|
|
328
|
+
middlewareAttachments: [],
|
|
192
329
|
});
|
|
193
330
|
};
|
|
194
331
|
|
|
@@ -198,6 +335,7 @@ export const makeNode = (): GroupSpec<"Node", ""> =>
|
|
|
198
335
|
name: "",
|
|
199
336
|
functions: Record.empty(),
|
|
200
337
|
groups: Record.empty(),
|
|
338
|
+
middlewareAttachments: [],
|
|
201
339
|
});
|
|
202
340
|
|
|
203
341
|
export const makeNodeAt = <const Name_ extends string>(
|
|
@@ -210,13 +348,15 @@ export const makeNodeAt = <const Name_ extends string>(
|
|
|
210
348
|
name,
|
|
211
349
|
functions: Record.empty(),
|
|
212
350
|
groups: Record.empty(),
|
|
351
|
+
middlewareAttachments: [],
|
|
213
352
|
});
|
|
214
353
|
};
|
|
215
354
|
|
|
216
|
-
export
|
|
355
|
+
export function withName<const Name_ extends string, Group extends Any>(
|
|
217
356
|
name: Name_,
|
|
218
|
-
group:
|
|
219
|
-
):
|
|
357
|
+
group: Group,
|
|
358
|
+
): NamedAt<Group, Name_>;
|
|
359
|
+
export function withName(name: string, group: Any): AnyWithProps {
|
|
220
360
|
validateConfectFunctionIdentifier(name);
|
|
221
361
|
const group_ = group as AnyWithProps;
|
|
222
362
|
|
|
@@ -229,5 +369,24 @@ export const withName = <const Name_ extends string>(
|
|
|
229
369
|
name,
|
|
230
370
|
functions: group_.functions,
|
|
231
371
|
groups: group_.groups,
|
|
372
|
+
middlewareAttachments: group_.middlewareAttachments,
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
export const validateMiddleware = (
|
|
377
|
+
group: AnyWithProps,
|
|
378
|
+
): Result.Result<void, MiddlewareAttachment.ValidationError> =>
|
|
379
|
+
Result.gen(function* () {
|
|
380
|
+
yield* MiddlewareAttachment.validateAll(
|
|
381
|
+
group.middlewareAttachments,
|
|
382
|
+
`group "${group.name}"`,
|
|
383
|
+
);
|
|
384
|
+
for (const function_ of Object.values(group.functions)) {
|
|
385
|
+
yield* MiddlewareAttachment.validateAll(
|
|
386
|
+
[...group.middlewareAttachments, ...function_.middlewareAttachments],
|
|
387
|
+
`function "${function_.name}"`,
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
for (const child of Object.values(group.groups))
|
|
391
|
+
yield* validateMiddleware(child);
|
|
232
392
|
});
|
|
233
|
-
};
|
package/src/Identifier.ts
CHANGED
|
@@ -95,15 +95,16 @@ export const validateConfectFunctionIdentifier = (identifier: string) => {
|
|
|
95
95
|
};
|
|
96
96
|
|
|
97
97
|
/**
|
|
98
|
-
* Validate that `identifier` is suitable as a Convex table name (and,
|
|
99
|
-
* as a `confect/tables/<identifier>.ts` filename).
|
|
98
|
+
* Validate that `identifier` is suitable as a Convex table name (and,
|
|
99
|
+
* equivalently, as a `confect/tables/<identifier>.ts` filename).
|
|
100
100
|
*
|
|
101
101
|
* Rules:
|
|
102
|
-
*
|
|
102
|
+
*
|
|
103
|
+
* - Must match `/^[A-Za-z][A-Za-z0-9_]*$/`—letter-leading, alphanumeric plus
|
|
103
104
|
* underscore. No `$` (not a valid Convex table name character); no leading
|
|
104
105
|
* `_` (Convex reserves `_<name>` for its system tables).
|
|
105
|
-
* - Must not be a reserved JavaScript identifier, so the name can also be used
|
|
106
|
-
*
|
|
106
|
+
* - Must not be a reserved JavaScript identifier, so the name can also be used as
|
|
107
|
+
* a binding name in generated code without escaping.
|
|
107
108
|
*/
|
|
108
109
|
export const validateConfectTableIdentifier = (identifier: string) => {
|
|
109
110
|
if (!matchesTableNamePattern(identifier)) {
|
package/src/Lazy.ts
CHANGED
|
@@ -1,22 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Install a lazy memoised property on `target`. The first access runs
|
|
3
|
-
* `compute()` and replaces the getter with a plain, non-writable data
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* observably indistinguishable.
|
|
3
|
+
* `compute()` and replaces the getter with a plain, non-writable data property
|
|
4
|
+
* whose value is the computed result. Subsequent accesses hit the V8 fast path
|
|
5
|
+
* for own data properties—no function call, identical returned reference—so
|
|
6
|
+
* first and second-and-subsequent accesses are observably indistinguishable.
|
|
8
7
|
*
|
|
9
|
-
* The replacement property is `enumerable: true` so the lazy property
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* forcing the computation.
|
|
8
|
+
* The replacement property is `enumerable: true` so the lazy property still
|
|
9
|
+
* participates in `Object.keys`/`JSON.stringify` after it materialises,
|
|
10
|
+
* matching the shape of a plain data property. The property is also
|
|
11
|
+
* `enumerable` before materialising, so presence checks (`"key" in target`,
|
|
12
|
+
* `Object.hasOwn(target, key)`) observe it without forcing the computation.
|
|
15
13
|
*
|
|
16
14
|
* This is the single shared implementation consumed across packages (e.g.
|
|
17
|
-
* `@confect/core`'s lazy `FunctionSpec` schemas and
|
|
18
|
-
* `
|
|
19
|
-
* two drifting apart.
|
|
15
|
+
* `@confect/core`'s lazy `FunctionSpec` schemas and lazy `Table`
|
|
16
|
+
* `Fields`/`Doc`), so there is no chance of the two drifting apart.
|
|
20
17
|
*/
|
|
21
18
|
export const defineProperty = <T extends object, K extends PropertyKey>(
|
|
22
19
|
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
|
+
};
|