@confect/server 1.0.0-next.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 +12 -0
- package/LICENSE +7 -0
- package/dist/ActionCtx.d.ts +12 -0
- package/dist/ActionCtx.d.ts.map +1 -0
- package/dist/ActionCtx.js +10 -0
- package/dist/ActionCtx.js.map +1 -0
- package/dist/ActionRunner.d.ts +15 -0
- package/dist/ActionRunner.d.ts.map +1 -0
- package/dist/ActionRunner.js +23 -0
- package/dist/ActionRunner.js.map +1 -0
- package/dist/Api.d.ts +27 -0
- package/dist/Api.d.ts.map +1 -0
- package/dist/Api.js +26 -0
- package/dist/Api.js.map +1 -0
- package/dist/Auth.d.ts +30 -0
- package/dist/Auth.d.ts.map +1 -0
- package/dist/Auth.js +24 -0
- package/dist/Auth.js.map +1 -0
- package/dist/DataModel.d.ts +33 -0
- package/dist/DataModel.d.ts.map +1 -0
- package/dist/DataModel.js +6 -0
- package/dist/DataModel.js.map +1 -0
- package/dist/DatabaseReader.d.ts +73 -0
- package/dist/DatabaseReader.d.ts.map +1 -0
- package/dist/DatabaseReader.js +32 -0
- package/dist/DatabaseReader.js.map +1 -0
- package/dist/DatabaseSchema.d.ts +139 -0
- package/dist/DatabaseSchema.d.ts.map +1 -0
- package/dist/DatabaseSchema.js +45 -0
- package/dist/DatabaseSchema.js.map +1 -0
- package/dist/DatabaseWriter.d.ts +39 -0
- package/dist/DatabaseWriter.d.ts.map +1 -0
- package/dist/DatabaseWriter.js +43 -0
- package/dist/DatabaseWriter.js.map +1 -0
- package/dist/Document.d.ts +47 -0
- package/dist/Document.d.ts.map +1 -0
- package/dist/Document.js +66 -0
- package/dist/Document.js.map +1 -0
- package/dist/FunctionImpl.d.ts +34 -0
- package/dist/FunctionImpl.d.ts.map +1 -0
- package/dist/FunctionImpl.js +35 -0
- package/dist/FunctionImpl.js.map +1 -0
- package/dist/GroupImpl.d.ts +24 -0
- package/dist/GroupImpl.d.ts.map +1 -0
- package/dist/GroupImpl.js +14 -0
- package/dist/GroupImpl.js.map +1 -0
- package/dist/Handler.d.ts +31 -0
- package/dist/Handler.d.ts.map +1 -0
- package/dist/Handler.js +6 -0
- package/dist/Handler.js.map +1 -0
- package/dist/HttpApi.d.ts +26 -0
- package/dist/HttpApi.d.ts.map +1 -0
- package/dist/HttpApi.js +74 -0
- package/dist/HttpApi.js.map +1 -0
- package/dist/Impl.d.ts +24 -0
- package/dist/Impl.d.ts.map +1 -0
- package/dist/Impl.js +28 -0
- package/dist/Impl.js.map +1 -0
- package/dist/MutationCtx.d.ts +12 -0
- package/dist/MutationCtx.d.ts.map +1 -0
- package/dist/MutationCtx.js +10 -0
- package/dist/MutationCtx.js.map +1 -0
- package/dist/MutationRunner.d.ts +24 -0
- package/dist/MutationRunner.d.ts.map +1 -0
- package/dist/MutationRunner.js +33 -0
- package/dist/MutationRunner.js.map +1 -0
- package/dist/OrderedQuery.d.ts +23 -0
- package/dist/OrderedQuery.d.ts.map +1 -0
- package/dist/OrderedQuery.js +34 -0
- package/dist/OrderedQuery.js.map +1 -0
- package/dist/QueryCtx.d.ts +12 -0
- package/dist/QueryCtx.d.ts.map +1 -0
- package/dist/QueryCtx.js +10 -0
- package/dist/QueryCtx.js.map +1 -0
- package/dist/QueryInitializer.d.ts +49 -0
- package/dist/QueryInitializer.d.ts.map +1 -0
- package/dist/QueryInitializer.js +83 -0
- package/dist/QueryInitializer.js.map +1 -0
- package/dist/QueryRunner.d.ts +14 -0
- package/dist/QueryRunner.d.ts.map +1 -0
- package/dist/QueryRunner.js +23 -0
- package/dist/QueryRunner.js.map +1 -0
- package/dist/RegisteredFunctions.d.ts +66 -0
- package/dist/RegisteredFunctions.d.ts.map +1 -0
- package/dist/RegisteredFunctions.js +71 -0
- package/dist/RegisteredFunctions.js.map +1 -0
- package/dist/Registry.d.ts +15 -0
- package/dist/Registry.d.ts.map +1 -0
- package/dist/Registry.js +10 -0
- package/dist/Registry.js.map +1 -0
- package/dist/RegistryItem.d.ts +31 -0
- package/dist/RegistryItem.d.ts.map +1 -0
- package/dist/RegistryItem.js +20 -0
- package/dist/RegistryItem.js.map +1 -0
- package/dist/Scheduler.d.ts +23 -0
- package/dist/Scheduler.d.ts.map +1 -0
- package/dist/Scheduler.js +24 -0
- package/dist/Scheduler.js.map +1 -0
- package/dist/SchemaToValidator.d.ts +88 -0
- package/dist/SchemaToValidator.d.ts.map +1 -0
- package/dist/SchemaToValidator.js +155 -0
- package/dist/SchemaToValidator.js.map +1 -0
- package/dist/Storage.d.ts +69 -0
- package/dist/Storage.d.ts.map +1 -0
- package/dist/Storage.js +46 -0
- package/dist/Storage.js.map +1 -0
- package/dist/Table.d.ts +247 -0
- package/dist/Table.d.ts.map +1 -0
- package/dist/Table.js +97 -0
- package/dist/Table.js.map +1 -0
- package/dist/TableInfo.d.ts +48 -0
- package/dist/TableInfo.d.ts.map +1 -0
- package/dist/TableInfo.js +6 -0
- package/dist/TableInfo.js.map +1 -0
- package/dist/VectorSearch.d.ts +42 -0
- package/dist/VectorSearch.d.ts.map +1 -0
- package/dist/VectorSearch.js +16 -0
- package/dist/VectorSearch.js.map +1 -0
- package/dist/_virtual/rolldown_runtime.js +13 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +31 -0
- package/dist/internal/utils.d.ts +15 -0
- package/dist/internal/utils.d.ts.map +1 -0
- package/dist/internal/utils.js +49 -0
- package/dist/internal/utils.js.map +1 -0
- package/package.json +90 -0
- package/src/ActionCtx.ts +9 -0
- package/src/ActionRunner.ts +28 -0
- package/src/Api.ts +63 -0
- package/src/Auth.ts +31 -0
- package/src/DataModel.ts +69 -0
- package/src/DatabaseReader.ts +75 -0
- package/src/DatabaseSchema.ts +134 -0
- package/src/DatabaseWriter.ts +166 -0
- package/src/Document.ts +200 -0
- package/src/FunctionImpl.ts +112 -0
- package/src/GroupImpl.ts +60 -0
- package/src/Handler.ts +105 -0
- package/src/HttpApi.ts +232 -0
- package/src/Impl.ts +57 -0
- package/src/MutationCtx.ts +11 -0
- package/src/MutationRunner.ts +41 -0
- package/src/OrderedQuery.ts +109 -0
- package/src/QueryCtx.ts +9 -0
- package/src/QueryInitializer.ts +308 -0
- package/src/QueryRunner.ts +29 -0
- package/src/RegisteredFunctions.ts +381 -0
- package/src/Registry.ts +13 -0
- package/src/RegistryItem.ts +44 -0
- package/src/Scheduler.ts +39 -0
- package/src/SchemaToValidator.ts +619 -0
- package/src/Storage.ts +86 -0
- package/src/Table.ts +439 -0
- package/src/TableInfo.ts +91 -0
- package/src/VectorSearch.ts +46 -0
- package/src/index.ts +29 -0
- package/src/internal/utils.ts +87 -0
|
@@ -0,0 +1,619 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
PropertyValidators,
|
|
3
|
+
Validator,
|
|
4
|
+
VAny,
|
|
5
|
+
VArray,
|
|
6
|
+
VBoolean,
|
|
7
|
+
VBytes,
|
|
8
|
+
VFloat64,
|
|
9
|
+
VId,
|
|
10
|
+
VInt64,
|
|
11
|
+
VLiteral,
|
|
12
|
+
VNull,
|
|
13
|
+
VObject,
|
|
14
|
+
VOptional,
|
|
15
|
+
VRecord,
|
|
16
|
+
VString,
|
|
17
|
+
VUnion,
|
|
18
|
+
} from "convex/values";
|
|
19
|
+
import { v } from "convex/values";
|
|
20
|
+
import {
|
|
21
|
+
Array,
|
|
22
|
+
Cause,
|
|
23
|
+
Data,
|
|
24
|
+
Effect,
|
|
25
|
+
Exit,
|
|
26
|
+
Match,
|
|
27
|
+
Number,
|
|
28
|
+
Option,
|
|
29
|
+
type ParseResult,
|
|
30
|
+
pipe,
|
|
31
|
+
Predicate,
|
|
32
|
+
Schema,
|
|
33
|
+
SchemaAST,
|
|
34
|
+
String,
|
|
35
|
+
} from "effect";
|
|
36
|
+
|
|
37
|
+
import * as GenericId from "@confect/core/GenericId";
|
|
38
|
+
import type {
|
|
39
|
+
DeepMutable,
|
|
40
|
+
IsAny,
|
|
41
|
+
IsOptional,
|
|
42
|
+
IsRecord,
|
|
43
|
+
IsRecursive,
|
|
44
|
+
IsUnion,
|
|
45
|
+
IsValueLiteral,
|
|
46
|
+
TypeError,
|
|
47
|
+
UnionToTuple,
|
|
48
|
+
} from "@confect/core/Types";
|
|
49
|
+
|
|
50
|
+
// Args
|
|
51
|
+
|
|
52
|
+
export const compileArgsSchema = <ConfectValue, ConvexValue>(
|
|
53
|
+
argsSchema: Schema.Schema<ConfectValue, ConvexValue>,
|
|
54
|
+
): PropertyValidators => {
|
|
55
|
+
const ast = Schema.encodedSchema(argsSchema).ast;
|
|
56
|
+
|
|
57
|
+
return pipe(
|
|
58
|
+
ast,
|
|
59
|
+
Match.value,
|
|
60
|
+
Match.tag("TypeLiteral", (typeLiteralAst) =>
|
|
61
|
+
Array.isEmptyReadonlyArray(typeLiteralAst.indexSignatures)
|
|
62
|
+
? handlePropertySignatures(typeLiteralAst)
|
|
63
|
+
: Effect.fail(new IndexSignaturesAreNotSupportedError()),
|
|
64
|
+
),
|
|
65
|
+
Match.orElse(() => Effect.fail(new TopLevelMustBeObjectError())),
|
|
66
|
+
runSyncThrow,
|
|
67
|
+
);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// Returns
|
|
71
|
+
|
|
72
|
+
export const compileReturnsSchema = <ConfectValue, ConvexValue>(
|
|
73
|
+
schema: Schema.Schema<ConfectValue, ConvexValue>,
|
|
74
|
+
): Validator<any, any, any> =>
|
|
75
|
+
runSyncThrow(compileAst(Schema.encodedSchema(schema).ast));
|
|
76
|
+
|
|
77
|
+
// Table
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Convert a table `Schema` to a table `Validator`.
|
|
81
|
+
*/
|
|
82
|
+
export type TableSchemaToTableValidator<
|
|
83
|
+
TableSchema extends Schema.Schema.AnyNoContext,
|
|
84
|
+
> =
|
|
85
|
+
ValueToValidator<TableSchema["Encoded"]> extends infer Vd extends
|
|
86
|
+
| VObject<any, any, any, any>
|
|
87
|
+
| VUnion<any, any, any, any>
|
|
88
|
+
? Vd
|
|
89
|
+
: // TODO: Add type error message
|
|
90
|
+
never;
|
|
91
|
+
|
|
92
|
+
export const compileTableSchema = <
|
|
93
|
+
TableSchema extends Schema.Schema.AnyNoContext,
|
|
94
|
+
>(
|
|
95
|
+
schema: TableSchema,
|
|
96
|
+
): TableSchemaToTableValidator<TableSchema> => {
|
|
97
|
+
const ast = Schema.encodedSchema(schema).ast;
|
|
98
|
+
|
|
99
|
+
return pipe(
|
|
100
|
+
ast,
|
|
101
|
+
Match.value,
|
|
102
|
+
Match.tag("TypeLiteral", ({ indexSignatures }) =>
|
|
103
|
+
Array.isEmptyReadonlyArray(indexSignatures)
|
|
104
|
+
? (compileAst(ast) as Effect.Effect<any>)
|
|
105
|
+
: Effect.fail(new IndexSignaturesAreNotSupportedError()),
|
|
106
|
+
),
|
|
107
|
+
Match.tag("Union", (unionAst) => compileAst(unionAst)),
|
|
108
|
+
Match.orElse(() => Effect.fail(new TopLevelMustBeObjectOrUnionError())),
|
|
109
|
+
runSyncThrow,
|
|
110
|
+
);
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// Compiler
|
|
114
|
+
|
|
115
|
+
export type ReadonlyValue =
|
|
116
|
+
| string
|
|
117
|
+
| number
|
|
118
|
+
| bigint
|
|
119
|
+
| boolean
|
|
120
|
+
| ArrayBuffer
|
|
121
|
+
| ReadonlyArrayValue
|
|
122
|
+
| ReadonlyRecordValue
|
|
123
|
+
| null;
|
|
124
|
+
|
|
125
|
+
type ReadonlyArrayValue = readonly ReadonlyValue[];
|
|
126
|
+
|
|
127
|
+
export type ReadonlyRecordValue = {
|
|
128
|
+
readonly [key: string]: ReadonlyValue | undefined;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
export type ValueToValidator<Vl> =
|
|
132
|
+
IsRecursive<Vl> extends true
|
|
133
|
+
? VAny
|
|
134
|
+
: [Vl] extends [never]
|
|
135
|
+
? never
|
|
136
|
+
: IsAny<Vl> extends true
|
|
137
|
+
? VAny
|
|
138
|
+
: [Vl] extends [ReadonlyValue]
|
|
139
|
+
? Vl extends {
|
|
140
|
+
__tableName: infer TableName extends string;
|
|
141
|
+
}
|
|
142
|
+
? VId<GenericId.GenericId<TableName>>
|
|
143
|
+
: IsValueLiteral<Vl> extends true
|
|
144
|
+
? VLiteral<Vl>
|
|
145
|
+
: [Vl] extends [null]
|
|
146
|
+
? VNull
|
|
147
|
+
: [Vl] extends [boolean]
|
|
148
|
+
? VBoolean
|
|
149
|
+
: IsUnion<Vl> extends true
|
|
150
|
+
? UnionValueToValidator<Vl>
|
|
151
|
+
: [Vl] extends [number]
|
|
152
|
+
? VFloat64
|
|
153
|
+
: [Vl] extends [bigint]
|
|
154
|
+
? VInt64
|
|
155
|
+
: [Vl] extends [string]
|
|
156
|
+
? VString
|
|
157
|
+
: [Vl] extends [ArrayBuffer]
|
|
158
|
+
? VBytes
|
|
159
|
+
: Vl extends ReadonlyArray<ReadonlyValue>
|
|
160
|
+
? ArrayValueToValidator<Vl>
|
|
161
|
+
: Vl extends ReadonlyRecordValue
|
|
162
|
+
? RecordValueToValidator<Vl>
|
|
163
|
+
: TypeError<"Unexpected value", Vl>
|
|
164
|
+
: TypeError<"Provided value is not a valid Convex value", Vl>;
|
|
165
|
+
|
|
166
|
+
type ArrayValueToValidator<Vl extends ReadonlyArray<ReadonlyValue>> =
|
|
167
|
+
Vl extends ReadonlyArray<infer El extends ReadonlyValue>
|
|
168
|
+
? ValueToValidator<El> extends infer Vd extends Validator<any, any, any>
|
|
169
|
+
? VArray<DeepMutable<El[]>, Vd>
|
|
170
|
+
: never
|
|
171
|
+
: never;
|
|
172
|
+
|
|
173
|
+
type RecordValueToValidator<Vl> = Vl extends ReadonlyRecordValue
|
|
174
|
+
? {
|
|
175
|
+
-readonly [K in keyof Vl]-?: IsAny<Vl[K]> extends true
|
|
176
|
+
? IsOptional<Vl, K> extends true
|
|
177
|
+
? VOptional<VAny>
|
|
178
|
+
: VAny
|
|
179
|
+
: UndefinedOrValueToValidator<Vl[K]>;
|
|
180
|
+
} extends infer VdRecord extends Record<string, any>
|
|
181
|
+
? {
|
|
182
|
+
-readonly [K in keyof Vl]: undefined extends Vl[K]
|
|
183
|
+
? DeepMutable<Exclude<Vl[K], undefined>>
|
|
184
|
+
: DeepMutable<Vl[K]>;
|
|
185
|
+
} extends infer VlRecord extends Record<string, any>
|
|
186
|
+
? IsRecord<VlRecord> extends true
|
|
187
|
+
? VRecord<VlRecord, VString, VdRecord[keyof VdRecord]>
|
|
188
|
+
: VObject<VlRecord, VdRecord>
|
|
189
|
+
: never
|
|
190
|
+
: never
|
|
191
|
+
: never;
|
|
192
|
+
|
|
193
|
+
export type UndefinedOrValueToValidator<Vl extends ReadonlyValue | undefined> =
|
|
194
|
+
undefined extends Vl
|
|
195
|
+
? [Vl] extends [(infer Val extends ReadonlyValue) | undefined]
|
|
196
|
+
? ValueToValidator<Val> extends infer Vd extends Validator<
|
|
197
|
+
any,
|
|
198
|
+
"required",
|
|
199
|
+
any
|
|
200
|
+
>
|
|
201
|
+
? VOptional<Vd>
|
|
202
|
+
: never
|
|
203
|
+
: never
|
|
204
|
+
: [Vl] extends [ReadonlyValue]
|
|
205
|
+
? ValueToValidator<Vl>
|
|
206
|
+
: never;
|
|
207
|
+
|
|
208
|
+
type UnionValueToValidator<Vl extends ReadonlyValue> = [Vl] extends [
|
|
209
|
+
ReadonlyValue,
|
|
210
|
+
]
|
|
211
|
+
? IsUnion<Vl> extends true
|
|
212
|
+
? UnionToTuple<Vl> extends infer VlTuple extends
|
|
213
|
+
ReadonlyArray<ReadonlyValue>
|
|
214
|
+
? ValueTupleToValidatorTuple<VlTuple> extends infer VdTuple extends
|
|
215
|
+
Validator<any, "required", any>[]
|
|
216
|
+
? VUnion<DeepMutable<Vl>, VdTuple>
|
|
217
|
+
: TypeError<"Failed to convert value tuple to validator tuple">
|
|
218
|
+
: TypeError<"Failed to convert union to tuple">
|
|
219
|
+
: TypeError<"Expected a union of values, but got a single value instead">
|
|
220
|
+
: TypeError<"Provided value is not a valid Convex value">;
|
|
221
|
+
|
|
222
|
+
type ValueTupleToValidatorTuple<VlTuple extends ReadonlyArray<ReadonlyValue>> =
|
|
223
|
+
VlTuple extends
|
|
224
|
+
| [true, false, ...infer VlRest extends ReadonlyArray<ReadonlyValue>]
|
|
225
|
+
| [false, true, ...infer VlRest extends ReadonlyArray<ReadonlyValue>]
|
|
226
|
+
? ValueTupleToValidatorTuple<VlRest> extends infer VdRest extends Validator<
|
|
227
|
+
any,
|
|
228
|
+
any,
|
|
229
|
+
any
|
|
230
|
+
>[]
|
|
231
|
+
? [VBoolean<boolean>, ...VdRest]
|
|
232
|
+
: never
|
|
233
|
+
: VlTuple extends [
|
|
234
|
+
infer Vl extends ReadonlyValue,
|
|
235
|
+
...infer VlRest extends ReadonlyArray<ReadonlyValue>,
|
|
236
|
+
]
|
|
237
|
+
? ValueToValidator<Vl> extends infer Vd extends Validator<any, any, any>
|
|
238
|
+
? ValueTupleToValidatorTuple<VlRest> extends infer VdRest extends
|
|
239
|
+
Validator<any, "required", any>[]
|
|
240
|
+
? [Vd, ...VdRest]
|
|
241
|
+
: never
|
|
242
|
+
: never
|
|
243
|
+
: [];
|
|
244
|
+
|
|
245
|
+
export const compileSchema = <T, E>(
|
|
246
|
+
schema: Schema.Schema<T, E>,
|
|
247
|
+
// TODO: Can `ValueToValidator` here just accept `E` directly?
|
|
248
|
+
): ValueToValidator<(typeof schema)["Encoded"]> =>
|
|
249
|
+
runSyncThrow(compileAst(schema.ast)) as any;
|
|
250
|
+
|
|
251
|
+
export const isRecursive = (ast: SchemaAST.AST): boolean =>
|
|
252
|
+
pipe(
|
|
253
|
+
ast,
|
|
254
|
+
Match.value,
|
|
255
|
+
Match.tag(
|
|
256
|
+
"Literal",
|
|
257
|
+
"BooleanKeyword",
|
|
258
|
+
"StringKeyword",
|
|
259
|
+
"NumberKeyword",
|
|
260
|
+
"BigIntKeyword",
|
|
261
|
+
"UnknownKeyword",
|
|
262
|
+
"AnyKeyword",
|
|
263
|
+
"Declaration",
|
|
264
|
+
"UniqueSymbol",
|
|
265
|
+
"SymbolKeyword",
|
|
266
|
+
"UndefinedKeyword",
|
|
267
|
+
"VoidKeyword",
|
|
268
|
+
"NeverKeyword",
|
|
269
|
+
"Enums",
|
|
270
|
+
"TemplateLiteral",
|
|
271
|
+
"ObjectKeyword",
|
|
272
|
+
"Transformation",
|
|
273
|
+
() => false,
|
|
274
|
+
),
|
|
275
|
+
Match.tag("Union", ({ types }) =>
|
|
276
|
+
Array.some(types, (type) => isRecursive(type)),
|
|
277
|
+
),
|
|
278
|
+
Match.tag("TypeLiteral", ({ propertySignatures }) =>
|
|
279
|
+
Array.some(propertySignatures, ({ type }) => isRecursive(type)),
|
|
280
|
+
),
|
|
281
|
+
Match.tag(
|
|
282
|
+
"TupleType",
|
|
283
|
+
({ elements: optionalElements, rest: elements }) =>
|
|
284
|
+
Array.some(optionalElements, (optionalElement) =>
|
|
285
|
+
isRecursive(optionalElement.type),
|
|
286
|
+
) || Array.some(elements, (element) => isRecursive(element.type)),
|
|
287
|
+
),
|
|
288
|
+
Match.tag("Refinement", ({ from }) => isRecursive(from)),
|
|
289
|
+
Match.tag("Suspend", () => true),
|
|
290
|
+
Match.exhaustive,
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
export const compileAst = (
|
|
294
|
+
ast: SchemaAST.AST,
|
|
295
|
+
isOptionalPropertyOfTypeLiteral = false,
|
|
296
|
+
): Effect.Effect<
|
|
297
|
+
Validator<any, any, any>,
|
|
298
|
+
| UnsupportedSchemaTypeError
|
|
299
|
+
| UnsupportedPropertySignatureKeyTypeError
|
|
300
|
+
| IndexSignaturesAreNotSupportedError
|
|
301
|
+
| MixedIndexAndPropertySignaturesAreNotSupportedError
|
|
302
|
+
| OptionalTupleElementsAreNotSupportedError
|
|
303
|
+
| EmptyTupleIsNotSupportedError
|
|
304
|
+
> =>
|
|
305
|
+
isRecursive(ast)
|
|
306
|
+
? Effect.succeed(v.any())
|
|
307
|
+
: pipe(
|
|
308
|
+
ast,
|
|
309
|
+
Match.value,
|
|
310
|
+
Match.tag("Literal", ({ literal }) =>
|
|
311
|
+
pipe(
|
|
312
|
+
literal,
|
|
313
|
+
Match.value,
|
|
314
|
+
Match.whenOr(
|
|
315
|
+
Match.string,
|
|
316
|
+
Match.number,
|
|
317
|
+
Match.bigint,
|
|
318
|
+
Match.boolean,
|
|
319
|
+
(l) => v.literal(l),
|
|
320
|
+
),
|
|
321
|
+
Match.when(Match.null, () => v.null()),
|
|
322
|
+
Match.exhaustive,
|
|
323
|
+
Effect.succeed,
|
|
324
|
+
),
|
|
325
|
+
),
|
|
326
|
+
Match.tag("BooleanKeyword", () => Effect.succeed(v.boolean())),
|
|
327
|
+
Match.tag("StringKeyword", (stringAst) =>
|
|
328
|
+
GenericId.tableName(stringAst).pipe(
|
|
329
|
+
Option.match({
|
|
330
|
+
onNone: () => Effect.succeed(v.string()),
|
|
331
|
+
onSome: (tableName) => Effect.succeed(v.id(tableName)),
|
|
332
|
+
}),
|
|
333
|
+
),
|
|
334
|
+
),
|
|
335
|
+
Match.tag("NumberKeyword", () => Effect.succeed(v.float64())),
|
|
336
|
+
Match.tag("BigIntKeyword", () => Effect.succeed(v.int64())),
|
|
337
|
+
Match.tag("Union", (unionAst) =>
|
|
338
|
+
handleUnion(unionAst, isOptionalPropertyOfTypeLiteral),
|
|
339
|
+
),
|
|
340
|
+
Match.tag("TypeLiteral", (typeLiteralAst) =>
|
|
341
|
+
handleTypeLiteral(typeLiteralAst),
|
|
342
|
+
),
|
|
343
|
+
Match.tag("TupleType", (tupleTypeAst) => handleTupleType(tupleTypeAst)),
|
|
344
|
+
Match.tag("UnknownKeyword", "AnyKeyword", () =>
|
|
345
|
+
Effect.succeed(v.any()),
|
|
346
|
+
),
|
|
347
|
+
Match.tag("Declaration", (declaration) =>
|
|
348
|
+
Effect.mapBoth(
|
|
349
|
+
declaration.decodeUnknown(...declaration.typeParameters)(
|
|
350
|
+
new ArrayBuffer(0),
|
|
351
|
+
{},
|
|
352
|
+
declaration,
|
|
353
|
+
) as Effect.Effect<ArrayBuffer, ParseResult.ParseIssue>,
|
|
354
|
+
{
|
|
355
|
+
onSuccess: () => v.bytes(),
|
|
356
|
+
onFailure: () =>
|
|
357
|
+
new UnsupportedSchemaTypeError({
|
|
358
|
+
schemaType: declaration._tag,
|
|
359
|
+
}),
|
|
360
|
+
},
|
|
361
|
+
),
|
|
362
|
+
),
|
|
363
|
+
Match.tag("Refinement", ({ from }) => compileAst(from)),
|
|
364
|
+
Match.tag("Suspend", () => Effect.succeed(v.any())),
|
|
365
|
+
Match.tag(
|
|
366
|
+
"UniqueSymbol",
|
|
367
|
+
"SymbolKeyword",
|
|
368
|
+
"UndefinedKeyword",
|
|
369
|
+
"VoidKeyword",
|
|
370
|
+
"NeverKeyword",
|
|
371
|
+
"Enums",
|
|
372
|
+
"TemplateLiteral",
|
|
373
|
+
"ObjectKeyword",
|
|
374
|
+
"Transformation",
|
|
375
|
+
() =>
|
|
376
|
+
new UnsupportedSchemaTypeError({
|
|
377
|
+
schemaType: ast._tag,
|
|
378
|
+
}),
|
|
379
|
+
),
|
|
380
|
+
Match.exhaustive,
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
const handleUnion = (
|
|
384
|
+
{ types: [first, second, ...rest] }: SchemaAST.Union,
|
|
385
|
+
isOptionalPropertyOfTypeLiteral: boolean,
|
|
386
|
+
) =>
|
|
387
|
+
Effect.gen(function* () {
|
|
388
|
+
const validatorEffects = isOptionalPropertyOfTypeLiteral
|
|
389
|
+
? Array.filterMap([first, second, ...rest], (type) =>
|
|
390
|
+
Predicate.not(SchemaAST.isUndefinedKeyword)(type)
|
|
391
|
+
? Option.some(compileAst(type))
|
|
392
|
+
: Option.none(),
|
|
393
|
+
)
|
|
394
|
+
: Array.map([first, second, ...rest], (type) => compileAst(type));
|
|
395
|
+
|
|
396
|
+
const [firstValidator, secondValidator, ...restValidators] =
|
|
397
|
+
yield* Effect.all(validatorEffects);
|
|
398
|
+
|
|
399
|
+
/* v8 ignore start */
|
|
400
|
+
if (firstValidator === undefined) {
|
|
401
|
+
return yield* Effect.dieMessage(
|
|
402
|
+
"First validator of union is undefined; this should be impossible.",
|
|
403
|
+
);
|
|
404
|
+
/* v8 ignore stop */
|
|
405
|
+
} else if (secondValidator === undefined) {
|
|
406
|
+
return firstValidator;
|
|
407
|
+
} else {
|
|
408
|
+
return v.union(firstValidator, secondValidator, ...restValidators);
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
const handleTypeLiteral = (typeLiteralAst: SchemaAST.TypeLiteral) =>
|
|
413
|
+
pipe(
|
|
414
|
+
typeLiteralAst.indexSignatures,
|
|
415
|
+
Array.head,
|
|
416
|
+
Option.match({
|
|
417
|
+
onNone: () =>
|
|
418
|
+
Effect.map(handlePropertySignatures(typeLiteralAst), v.object),
|
|
419
|
+
onSome: ({ parameter, type }) =>
|
|
420
|
+
pipe(
|
|
421
|
+
typeLiteralAst.propertySignatures,
|
|
422
|
+
Array.head,
|
|
423
|
+
Option.match({
|
|
424
|
+
onNone: () =>
|
|
425
|
+
Effect.map(
|
|
426
|
+
Effect.all({
|
|
427
|
+
parameter_: compileAst(parameter),
|
|
428
|
+
type_: compileAst(type),
|
|
429
|
+
}),
|
|
430
|
+
({ parameter_, type_ }) => v.record(parameter_, type_),
|
|
431
|
+
),
|
|
432
|
+
onSome: () =>
|
|
433
|
+
Effect.fail(
|
|
434
|
+
new MixedIndexAndPropertySignaturesAreNotSupportedError(),
|
|
435
|
+
),
|
|
436
|
+
}),
|
|
437
|
+
),
|
|
438
|
+
}),
|
|
439
|
+
);
|
|
440
|
+
|
|
441
|
+
const handleTupleType = ({ elements, rest }: SchemaAST.TupleType) =>
|
|
442
|
+
Effect.gen(function* () {
|
|
443
|
+
const restValidator = pipe(
|
|
444
|
+
rest,
|
|
445
|
+
Array.head,
|
|
446
|
+
Option.map(({ type }) => compileAst(type)),
|
|
447
|
+
Effect.flatten,
|
|
448
|
+
);
|
|
449
|
+
|
|
450
|
+
const [f, s, ...r] = elements;
|
|
451
|
+
|
|
452
|
+
const elementToValidator = ({ type, isOptional }: SchemaAST.OptionalType) =>
|
|
453
|
+
Effect.if(isOptional, {
|
|
454
|
+
onTrue: () =>
|
|
455
|
+
Effect.fail(new OptionalTupleElementsAreNotSupportedError()),
|
|
456
|
+
onFalse: () => compileAst(type),
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
const arrayItemsValidator = yield* f === undefined
|
|
460
|
+
? pipe(
|
|
461
|
+
restValidator,
|
|
462
|
+
Effect.catchTag("NoSuchElementException", () =>
|
|
463
|
+
Effect.fail(new EmptyTupleIsNotSupportedError()),
|
|
464
|
+
),
|
|
465
|
+
)
|
|
466
|
+
: s === undefined
|
|
467
|
+
? elementToValidator(f)
|
|
468
|
+
: Effect.gen(function* () {
|
|
469
|
+
const firstValidator = yield* elementToValidator(f);
|
|
470
|
+
const secondValidator = yield* elementToValidator(s);
|
|
471
|
+
const restValidators = yield* Effect.forEach(r, elementToValidator);
|
|
472
|
+
|
|
473
|
+
return v.union(firstValidator, secondValidator, ...restValidators);
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
return v.array(arrayItemsValidator);
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
const handlePropertySignatures = (typeLiteralAst: SchemaAST.TypeLiteral) =>
|
|
480
|
+
pipe(
|
|
481
|
+
typeLiteralAst.propertySignatures,
|
|
482
|
+
Effect.forEach(({ type, name, isOptional }) => {
|
|
483
|
+
if (String.isString(name)) {
|
|
484
|
+
// Somehow, somewhere, keys of type number are being coerced to strings…
|
|
485
|
+
return Option.match(Number.parse(name), {
|
|
486
|
+
onNone: () =>
|
|
487
|
+
Effect.gen(function* () {
|
|
488
|
+
const validator = yield* compileAst(type, isOptional);
|
|
489
|
+
|
|
490
|
+
return {
|
|
491
|
+
propertyName: name,
|
|
492
|
+
validator: isOptional ? v.optional(validator) : validator,
|
|
493
|
+
};
|
|
494
|
+
}),
|
|
495
|
+
onSome: (number) =>
|
|
496
|
+
Effect.fail(
|
|
497
|
+
new UnsupportedPropertySignatureKeyTypeError({
|
|
498
|
+
propertyKey: number,
|
|
499
|
+
}),
|
|
500
|
+
),
|
|
501
|
+
});
|
|
502
|
+
} else {
|
|
503
|
+
return Effect.fail(
|
|
504
|
+
new UnsupportedPropertySignatureKeyTypeError({ propertyKey: name }),
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
}),
|
|
508
|
+
Effect.andThen((propertyNamesWithValidators) =>
|
|
509
|
+
pipe(
|
|
510
|
+
propertyNamesWithValidators,
|
|
511
|
+
Array.reduce(
|
|
512
|
+
{} as Record<string, Validator<any, any, any>>,
|
|
513
|
+
(acc, { propertyName, validator }) => ({
|
|
514
|
+
[propertyName]: validator,
|
|
515
|
+
...acc,
|
|
516
|
+
}),
|
|
517
|
+
),
|
|
518
|
+
Effect.succeed,
|
|
519
|
+
),
|
|
520
|
+
),
|
|
521
|
+
);
|
|
522
|
+
|
|
523
|
+
// Errors
|
|
524
|
+
|
|
525
|
+
const runSyncThrow = <A, E>(effect: Effect.Effect<A, E>) =>
|
|
526
|
+
pipe(
|
|
527
|
+
effect,
|
|
528
|
+
Effect.runSyncExit,
|
|
529
|
+
Exit.match({
|
|
530
|
+
onSuccess: (validator) => validator,
|
|
531
|
+
onFailure: (cause) => {
|
|
532
|
+
throw Cause.squash(cause);
|
|
533
|
+
},
|
|
534
|
+
}),
|
|
535
|
+
);
|
|
536
|
+
|
|
537
|
+
export class TopLevelMustBeObjectError extends Data.TaggedError(
|
|
538
|
+
"TopLevelMustBeObjectError",
|
|
539
|
+
) {
|
|
540
|
+
/* v8 ignore start */
|
|
541
|
+
override get message() {
|
|
542
|
+
return "Top level schema must be an object";
|
|
543
|
+
}
|
|
544
|
+
/* v8 ignore stop */
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
export class TopLevelMustBeObjectOrUnionError extends Data.TaggedError(
|
|
548
|
+
"TopLevelMustBeObjectOrUnionError",
|
|
549
|
+
) {
|
|
550
|
+
/* v8 ignore start */
|
|
551
|
+
override get message() {
|
|
552
|
+
return "Top level schema must be an object or a union";
|
|
553
|
+
}
|
|
554
|
+
/* v8 ignore stop */
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
export class UnsupportedPropertySignatureKeyTypeError extends Data.TaggedError(
|
|
558
|
+
"UnsupportedPropertySignatureKeyTypeError",
|
|
559
|
+
)<{
|
|
560
|
+
readonly propertyKey: number | symbol;
|
|
561
|
+
}> {
|
|
562
|
+
/* v8 ignore start */
|
|
563
|
+
override get message() {
|
|
564
|
+
return `Unsupported property signature '${this.propertyKey.toString()}'. Property is of type '${typeof this.propertyKey}' but only 'string' properties are supported.`;
|
|
565
|
+
}
|
|
566
|
+
/* v8 ignore stop */
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
export class EmptyTupleIsNotSupportedError extends Data.TaggedError(
|
|
570
|
+
"EmptyTupleIsNotSupportedError",
|
|
571
|
+
) {
|
|
572
|
+
/* v8 ignore start */
|
|
573
|
+
override get message() {
|
|
574
|
+
return "Tuple must have at least one element";
|
|
575
|
+
}
|
|
576
|
+
/* v8 ignore stop */
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
export class UnsupportedSchemaTypeError extends Data.TaggedError(
|
|
580
|
+
"UnsupportedSchemaTypeError",
|
|
581
|
+
)<{
|
|
582
|
+
readonly schemaType: SchemaAST.AST["_tag"];
|
|
583
|
+
}> {
|
|
584
|
+
/* v8 ignore start */
|
|
585
|
+
override get message() {
|
|
586
|
+
return `Unsupported schema type '${this.schemaType}'`;
|
|
587
|
+
}
|
|
588
|
+
/* v8 ignore stop */
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
export class IndexSignaturesAreNotSupportedError extends Data.TaggedError(
|
|
592
|
+
"IndexSignaturesAreNotSupportedError",
|
|
593
|
+
) {
|
|
594
|
+
/* v8 ignore start */
|
|
595
|
+
override get message() {
|
|
596
|
+
return "Index signatures are not supported";
|
|
597
|
+
}
|
|
598
|
+
/* v8 ignore stop */
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
export class MixedIndexAndPropertySignaturesAreNotSupportedError extends Data.TaggedError(
|
|
602
|
+
"MixedIndexAndPropertySignaturesAreNotSupportedError",
|
|
603
|
+
) {
|
|
604
|
+
/* v8 ignore start */
|
|
605
|
+
override get message() {
|
|
606
|
+
return "Mixed index and property signatures are not supported";
|
|
607
|
+
}
|
|
608
|
+
/* v8 ignore stop */
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
export class OptionalTupleElementsAreNotSupportedError extends Data.TaggedError(
|
|
612
|
+
"OptionalTupleElementsAreNotSupportedError",
|
|
613
|
+
) {
|
|
614
|
+
/* v8 ignore start */
|
|
615
|
+
override get message() {
|
|
616
|
+
return "Optional tuple elements are not supported";
|
|
617
|
+
}
|
|
618
|
+
/* v8 ignore stop */
|
|
619
|
+
}
|
package/src/Storage.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
StorageActionWriter as ConvexStorageActionWriter,
|
|
3
|
+
StorageReader as ConvexStorageReader,
|
|
4
|
+
StorageWriter as ConvexStorageWriter,
|
|
5
|
+
} from "convex/server";
|
|
6
|
+
import type { GenericId } from "convex/values";
|
|
7
|
+
import { Effect, flow, Layer, Option, pipe, Schema } from "effect";
|
|
8
|
+
|
|
9
|
+
const makeStorageReader = (storageReader: ConvexStorageReader) => ({
|
|
10
|
+
getUrl: (storageId: GenericId<"_storage">) =>
|
|
11
|
+
Effect.promise(() => storageReader.getUrl(storageId)).pipe(
|
|
12
|
+
Effect.andThen(
|
|
13
|
+
flow(
|
|
14
|
+
Option.fromNullable,
|
|
15
|
+
Option.match({
|
|
16
|
+
onNone: () => Effect.fail(new BlobNotFoundError({ id: storageId })),
|
|
17
|
+
onSome: (doc) => pipe(doc, Schema.decode(Schema.URL), Effect.orDie),
|
|
18
|
+
}),
|
|
19
|
+
),
|
|
20
|
+
),
|
|
21
|
+
),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const makeStorageWriter = (storageWriter: ConvexStorageWriter) => ({
|
|
25
|
+
generateUploadUrl: () =>
|
|
26
|
+
Effect.promise(() => storageWriter.generateUploadUrl()).pipe(
|
|
27
|
+
Effect.andThen((url) =>
|
|
28
|
+
pipe(url, Schema.decode(Schema.URL), Effect.orDie),
|
|
29
|
+
),
|
|
30
|
+
),
|
|
31
|
+
delete: (storageId: GenericId<"_storage">) =>
|
|
32
|
+
Effect.tryPromise({
|
|
33
|
+
try: () => storageWriter.delete(storageId),
|
|
34
|
+
catch: () => new BlobNotFoundError({ id: storageId }),
|
|
35
|
+
}),
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const makeStorageActionWriter = (
|
|
39
|
+
storageActionWriter: ConvexStorageActionWriter,
|
|
40
|
+
) => ({
|
|
41
|
+
get: (storageId: GenericId<"_storage">) =>
|
|
42
|
+
Effect.promise(() => storageActionWriter.get(storageId)).pipe(
|
|
43
|
+
Effect.andThen(
|
|
44
|
+
flow(
|
|
45
|
+
Option.fromNullable,
|
|
46
|
+
Option.match({
|
|
47
|
+
onNone: () => Effect.fail(new BlobNotFoundError({ id: storageId })),
|
|
48
|
+
onSome: Effect.succeed,
|
|
49
|
+
}),
|
|
50
|
+
),
|
|
51
|
+
),
|
|
52
|
+
),
|
|
53
|
+
store: (blob: Blob, options?: { sha256?: string }) =>
|
|
54
|
+
Effect.promise(() => storageActionWriter.store(blob, options)),
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
export class StorageReader extends Effect.Tag(
|
|
58
|
+
"@confect/server/Storage/StorageReader",
|
|
59
|
+
)<StorageReader, ReturnType<typeof makeStorageReader>>() {
|
|
60
|
+
static readonly layer = (storageReader: ConvexStorageReader) =>
|
|
61
|
+
Layer.succeed(this, makeStorageReader(storageReader));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export class StorageWriter extends Effect.Tag(
|
|
65
|
+
"@confect/server/Storage/StorageWriter",
|
|
66
|
+
)<StorageWriter, ReturnType<typeof makeStorageWriter>>() {
|
|
67
|
+
static readonly layer = (storageWriter: ConvexStorageWriter) =>
|
|
68
|
+
Layer.succeed(this, makeStorageWriter(storageWriter));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export class StorageActionWriter extends Effect.Tag(
|
|
72
|
+
"@confect/server/Storage/StorageActionWriter",
|
|
73
|
+
)<StorageActionWriter, ReturnType<typeof makeStorageActionWriter>>() {
|
|
74
|
+
static readonly layer = (storageActionWriter: ConvexStorageActionWriter) =>
|
|
75
|
+
Layer.succeed(this, makeStorageActionWriter(storageActionWriter));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export class BlobNotFoundError extends Schema.TaggedError<BlobNotFoundError>(
|
|
79
|
+
"BlobNotFoundError",
|
|
80
|
+
)("BlobNotFoundError", {
|
|
81
|
+
id: Schema.String,
|
|
82
|
+
}) {
|
|
83
|
+
override get message(): string {
|
|
84
|
+
return `File with ID '${this.id}' not found`;
|
|
85
|
+
}
|
|
86
|
+
}
|