@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.
Files changed (157) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/LICENSE +7 -0
  3. package/dist/ActionCtx.d.ts +12 -0
  4. package/dist/ActionCtx.d.ts.map +1 -0
  5. package/dist/ActionCtx.js +10 -0
  6. package/dist/ActionCtx.js.map +1 -0
  7. package/dist/ActionRunner.d.ts +15 -0
  8. package/dist/ActionRunner.d.ts.map +1 -0
  9. package/dist/ActionRunner.js +23 -0
  10. package/dist/ActionRunner.js.map +1 -0
  11. package/dist/Api.d.ts +27 -0
  12. package/dist/Api.d.ts.map +1 -0
  13. package/dist/Api.js +26 -0
  14. package/dist/Api.js.map +1 -0
  15. package/dist/Auth.d.ts +30 -0
  16. package/dist/Auth.d.ts.map +1 -0
  17. package/dist/Auth.js +24 -0
  18. package/dist/Auth.js.map +1 -0
  19. package/dist/DataModel.d.ts +33 -0
  20. package/dist/DataModel.d.ts.map +1 -0
  21. package/dist/DataModel.js +6 -0
  22. package/dist/DataModel.js.map +1 -0
  23. package/dist/DatabaseReader.d.ts +73 -0
  24. package/dist/DatabaseReader.d.ts.map +1 -0
  25. package/dist/DatabaseReader.js +32 -0
  26. package/dist/DatabaseReader.js.map +1 -0
  27. package/dist/DatabaseSchema.d.ts +139 -0
  28. package/dist/DatabaseSchema.d.ts.map +1 -0
  29. package/dist/DatabaseSchema.js +45 -0
  30. package/dist/DatabaseSchema.js.map +1 -0
  31. package/dist/DatabaseWriter.d.ts +39 -0
  32. package/dist/DatabaseWriter.d.ts.map +1 -0
  33. package/dist/DatabaseWriter.js +43 -0
  34. package/dist/DatabaseWriter.js.map +1 -0
  35. package/dist/Document.d.ts +47 -0
  36. package/dist/Document.d.ts.map +1 -0
  37. package/dist/Document.js +66 -0
  38. package/dist/Document.js.map +1 -0
  39. package/dist/FunctionImpl.d.ts +34 -0
  40. package/dist/FunctionImpl.d.ts.map +1 -0
  41. package/dist/FunctionImpl.js +35 -0
  42. package/dist/FunctionImpl.js.map +1 -0
  43. package/dist/GroupImpl.d.ts +24 -0
  44. package/dist/GroupImpl.d.ts.map +1 -0
  45. package/dist/GroupImpl.js +14 -0
  46. package/dist/GroupImpl.js.map +1 -0
  47. package/dist/Handler.d.ts +31 -0
  48. package/dist/Handler.d.ts.map +1 -0
  49. package/dist/Handler.js +6 -0
  50. package/dist/Handler.js.map +1 -0
  51. package/dist/HttpApi.d.ts +26 -0
  52. package/dist/HttpApi.d.ts.map +1 -0
  53. package/dist/HttpApi.js +74 -0
  54. package/dist/HttpApi.js.map +1 -0
  55. package/dist/Impl.d.ts +24 -0
  56. package/dist/Impl.d.ts.map +1 -0
  57. package/dist/Impl.js +28 -0
  58. package/dist/Impl.js.map +1 -0
  59. package/dist/MutationCtx.d.ts +12 -0
  60. package/dist/MutationCtx.d.ts.map +1 -0
  61. package/dist/MutationCtx.js +10 -0
  62. package/dist/MutationCtx.js.map +1 -0
  63. package/dist/MutationRunner.d.ts +24 -0
  64. package/dist/MutationRunner.d.ts.map +1 -0
  65. package/dist/MutationRunner.js +33 -0
  66. package/dist/MutationRunner.js.map +1 -0
  67. package/dist/OrderedQuery.d.ts +23 -0
  68. package/dist/OrderedQuery.d.ts.map +1 -0
  69. package/dist/OrderedQuery.js +34 -0
  70. package/dist/OrderedQuery.js.map +1 -0
  71. package/dist/QueryCtx.d.ts +12 -0
  72. package/dist/QueryCtx.d.ts.map +1 -0
  73. package/dist/QueryCtx.js +10 -0
  74. package/dist/QueryCtx.js.map +1 -0
  75. package/dist/QueryInitializer.d.ts +49 -0
  76. package/dist/QueryInitializer.d.ts.map +1 -0
  77. package/dist/QueryInitializer.js +83 -0
  78. package/dist/QueryInitializer.js.map +1 -0
  79. package/dist/QueryRunner.d.ts +14 -0
  80. package/dist/QueryRunner.d.ts.map +1 -0
  81. package/dist/QueryRunner.js +23 -0
  82. package/dist/QueryRunner.js.map +1 -0
  83. package/dist/RegisteredFunctions.d.ts +66 -0
  84. package/dist/RegisteredFunctions.d.ts.map +1 -0
  85. package/dist/RegisteredFunctions.js +71 -0
  86. package/dist/RegisteredFunctions.js.map +1 -0
  87. package/dist/Registry.d.ts +15 -0
  88. package/dist/Registry.d.ts.map +1 -0
  89. package/dist/Registry.js +10 -0
  90. package/dist/Registry.js.map +1 -0
  91. package/dist/RegistryItem.d.ts +31 -0
  92. package/dist/RegistryItem.d.ts.map +1 -0
  93. package/dist/RegistryItem.js +20 -0
  94. package/dist/RegistryItem.js.map +1 -0
  95. package/dist/Scheduler.d.ts +23 -0
  96. package/dist/Scheduler.d.ts.map +1 -0
  97. package/dist/Scheduler.js +24 -0
  98. package/dist/Scheduler.js.map +1 -0
  99. package/dist/SchemaToValidator.d.ts +88 -0
  100. package/dist/SchemaToValidator.d.ts.map +1 -0
  101. package/dist/SchemaToValidator.js +155 -0
  102. package/dist/SchemaToValidator.js.map +1 -0
  103. package/dist/Storage.d.ts +69 -0
  104. package/dist/Storage.d.ts.map +1 -0
  105. package/dist/Storage.js +46 -0
  106. package/dist/Storage.js.map +1 -0
  107. package/dist/Table.d.ts +247 -0
  108. package/dist/Table.d.ts.map +1 -0
  109. package/dist/Table.js +97 -0
  110. package/dist/Table.js.map +1 -0
  111. package/dist/TableInfo.d.ts +48 -0
  112. package/dist/TableInfo.d.ts.map +1 -0
  113. package/dist/TableInfo.js +6 -0
  114. package/dist/TableInfo.js.map +1 -0
  115. package/dist/VectorSearch.d.ts +42 -0
  116. package/dist/VectorSearch.d.ts.map +1 -0
  117. package/dist/VectorSearch.js +16 -0
  118. package/dist/VectorSearch.js.map +1 -0
  119. package/dist/_virtual/rolldown_runtime.js +13 -0
  120. package/dist/index.d.ts +30 -0
  121. package/dist/index.js +31 -0
  122. package/dist/internal/utils.d.ts +15 -0
  123. package/dist/internal/utils.d.ts.map +1 -0
  124. package/dist/internal/utils.js +49 -0
  125. package/dist/internal/utils.js.map +1 -0
  126. package/package.json +90 -0
  127. package/src/ActionCtx.ts +9 -0
  128. package/src/ActionRunner.ts +28 -0
  129. package/src/Api.ts +63 -0
  130. package/src/Auth.ts +31 -0
  131. package/src/DataModel.ts +69 -0
  132. package/src/DatabaseReader.ts +75 -0
  133. package/src/DatabaseSchema.ts +134 -0
  134. package/src/DatabaseWriter.ts +166 -0
  135. package/src/Document.ts +200 -0
  136. package/src/FunctionImpl.ts +112 -0
  137. package/src/GroupImpl.ts +60 -0
  138. package/src/Handler.ts +105 -0
  139. package/src/HttpApi.ts +232 -0
  140. package/src/Impl.ts +57 -0
  141. package/src/MutationCtx.ts +11 -0
  142. package/src/MutationRunner.ts +41 -0
  143. package/src/OrderedQuery.ts +109 -0
  144. package/src/QueryCtx.ts +9 -0
  145. package/src/QueryInitializer.ts +308 -0
  146. package/src/QueryRunner.ts +29 -0
  147. package/src/RegisteredFunctions.ts +381 -0
  148. package/src/Registry.ts +13 -0
  149. package/src/RegistryItem.ts +44 -0
  150. package/src/Scheduler.ts +39 -0
  151. package/src/SchemaToValidator.ts +619 -0
  152. package/src/Storage.ts +86 -0
  153. package/src/Table.ts +439 -0
  154. package/src/TableInfo.ts +91 -0
  155. package/src/VectorSearch.ts +46 -0
  156. package/src/index.ts +29 -0
  157. package/src/internal/utils.ts +87 -0
package/src/Auth.ts ADDED
@@ -0,0 +1,31 @@
1
+ import type { Auth as ConvexAuth } from "convex/server";
2
+ import { Effect, flow, Layer, Option, Schema } from "effect";
3
+
4
+ const make = (auth: ConvexAuth) => ({
5
+ getUserIdentity: Effect.promise(() => auth.getUserIdentity()).pipe(
6
+ Effect.andThen(
7
+ flow(
8
+ Option.fromNullable,
9
+ Option.match({
10
+ onNone: () => Effect.fail(new NoUserIdentityFoundError()),
11
+ onSome: Effect.succeed,
12
+ }),
13
+ ),
14
+ ),
15
+ ),
16
+ });
17
+
18
+ export class Auth extends Effect.Tag("@confect/server/Auth")<
19
+ Auth,
20
+ ReturnType<typeof make>
21
+ >() {}
22
+
23
+ export const layer = (auth: ConvexAuth) => Layer.succeed(Auth, make(auth));
24
+
25
+ export class NoUserIdentityFoundError extends Schema.TaggedError<NoUserIdentityFoundError>(
26
+ "NoUserIdentityFoundError",
27
+ )("NoUserIdentityFoundError", {}) {
28
+ override get message(): string {
29
+ return "No user identity found";
30
+ }
31
+ }
@@ -0,0 +1,69 @@
1
+ import type * as DatabaseSchema from "./DatabaseSchema";
2
+ import type * as Table from "./Table";
3
+ import type * as TableInfo from "./TableInfo";
4
+
5
+ export declare const TypeId: "@confect/server/DataModel";
6
+ export type TypeId = typeof TypeId;
7
+
8
+ export interface DataModel<Tables_ extends Table.AnyWithProps> {
9
+ readonly [TypeId]: TypeId;
10
+ readonly tables: Table.TablesRecord<Tables_>;
11
+ }
12
+
13
+ export interface Any {
14
+ readonly [TypeId]: TypeId;
15
+ }
16
+
17
+ export interface AnyWithProps extends Any {
18
+ readonly tables: Record<string, Table.AnyWithProps>;
19
+ }
20
+
21
+ export type FromSchema<Schema extends DatabaseSchema.AnyWithProps> = DataModel<
22
+ DatabaseSchema.Tables<Schema>
23
+ >;
24
+
25
+ export type FromTables<Tables_ extends Table.AnyWithProps> = DataModel<Tables_>;
26
+
27
+ export type ToConvex<DataModel_ extends AnyWithProps> = {
28
+ [TableName_ in TableNames<DataModel_>]: TableInfoWithName<
29
+ DataModel_,
30
+ TableName_
31
+ >;
32
+ };
33
+
34
+ export type Tables<DataModel_ extends AnyWithProps> =
35
+ DataModel_ extends DataModel<infer Tables_> ? Tables_ : never;
36
+
37
+ export type TableNames<DataModel_ extends AnyWithProps> = Table.Name<
38
+ Tables<DataModel_>
39
+ > &
40
+ string;
41
+
42
+ export type TableWithName<
43
+ DataModel_ extends AnyWithProps,
44
+ TableName extends TableNames<DataModel_>,
45
+ > = Table.WithName<Tables<DataModel_>, TableName>;
46
+
47
+ export type TableInfoWithName_<
48
+ DataModel_ extends AnyWithProps,
49
+ TableName extends TableNames<DataModel_>,
50
+ > = TableInfo.TableInfo<Table.WithName<Tables<DataModel_>, TableName>>;
51
+
52
+ export type TableInfoWithName<
53
+ DataModel_ extends AnyWithProps,
54
+ TableName extends TableNames<DataModel_>,
55
+ > = TableInfo.ConvexTableInfo<
56
+ TableInfo.TableInfo<Table.WithName<Tables<DataModel_>, TableName>>
57
+ >;
58
+
59
+ export type DocumentWithName<
60
+ DataModel_ extends AnyWithProps,
61
+ TableName extends TableNames<DataModel_>,
62
+ > = TableInfo.Document<
63
+ TableInfo.TableInfo<Table.WithName<Tables<DataModel_>, TableName>>
64
+ >;
65
+
66
+ export type DocumentByName<
67
+ DataModel_ extends AnyWithProps,
68
+ TableName extends TableNames<DataModel_>,
69
+ > = DocumentWithName<DataModel_, TableName>;
@@ -0,0 +1,75 @@
1
+ import type { GenericDatabaseReader } from "convex/server";
2
+ import { Array, Context, Layer } from "effect";
3
+ import type { BaseDatabaseReader } from "@confect/core/Types";
4
+ import * as DatabaseSchema from "./DatabaseSchema";
5
+ import type * as DataModel from "./DataModel";
6
+ import * as QueryInitializer from "./QueryInitializer";
7
+ import * as Table from "./Table";
8
+
9
+ export const make = <DatabaseSchema_ extends DatabaseSchema.AnyWithProps>(
10
+ databaseSchema: DatabaseSchema_,
11
+ convexDatabaseReader: GenericDatabaseReader<
12
+ DataModel.ToConvex<DataModel.FromSchema<DatabaseSchema_>>
13
+ >,
14
+ ) => {
15
+ type Tables = DatabaseSchema.Tables<DatabaseSchema_>;
16
+ type IncludedTables = DatabaseSchema.IncludeSystemTables<Tables>;
17
+ const extendedTables = DatabaseSchema.extendWithSystemTables(
18
+ databaseSchema.tables as Table.TablesRecord<Tables>,
19
+ );
20
+
21
+ return {
22
+ table: <const TableName extends Table.Name<IncludedTables>>(
23
+ tableName: TableName,
24
+ ) => {
25
+ const table = Object.values(extendedTables).find(
26
+ (def) => def.name === tableName,
27
+ ) as Table.WithName<IncludedTables, TableName>;
28
+
29
+ const baseDatabaseReader: BaseDatabaseReader<any> = Array.some(
30
+ Object.values(Table.systemTables),
31
+ (systemTableDef) => systemTableDef.name === tableName,
32
+ )
33
+ ? ({
34
+ get: convexDatabaseReader.system.get,
35
+ query: convexDatabaseReader.system.query,
36
+ } as BaseDatabaseReader<
37
+ DataModel.ToConvex<DataModel.FromTables<Table.SystemTables>>
38
+ >)
39
+ : ({
40
+ get: convexDatabaseReader.get,
41
+ query: convexDatabaseReader.query,
42
+ } as BaseDatabaseReader<
43
+ DataModel.ToConvex<DataModel.FromSchema<DatabaseSchema_>>
44
+ >);
45
+
46
+ return QueryInitializer.make<IncludedTables, TableName>(
47
+ tableName,
48
+ baseDatabaseReader,
49
+ table,
50
+ );
51
+ },
52
+ };
53
+ };
54
+
55
+ export const DatabaseReader = <
56
+ DatabaseSchema_ extends DatabaseSchema.AnyWithProps,
57
+ >() =>
58
+ Context.GenericTag<ReturnType<typeof make<DatabaseSchema_>>>(
59
+ "@confect/server/DatabaseReader",
60
+ );
61
+
62
+ export type DatabaseReader<
63
+ DatabaseSchema_ extends DatabaseSchema.AnyWithProps,
64
+ > = ReturnType<typeof DatabaseReader<DatabaseSchema_>>["Identifier"];
65
+
66
+ export const layer = <DatabaseSchema_ extends DatabaseSchema.AnyWithProps>(
67
+ databaseSchema: DatabaseSchema_,
68
+ convexDatabaseReader: GenericDatabaseReader<
69
+ DataModel.ToConvex<DataModel.FromSchema<DatabaseSchema_>>
70
+ >,
71
+ ) =>
72
+ Layer.succeed(
73
+ DatabaseReader<DatabaseSchema_>(),
74
+ make(databaseSchema, convexDatabaseReader),
75
+ );
@@ -0,0 +1,134 @@
1
+ import type { Expand, GenericSchema } from "convex/server";
2
+ import {
3
+ defineSchema as defineConvexSchema,
4
+ type SchemaDefinition,
5
+ } from "convex/server";
6
+ import { Array, pipe, Predicate, Record } from "effect";
7
+ import * as Table from "./Table";
8
+
9
+ export const TypeId = "@confect/server/DatabaseSchema";
10
+ export type TypeId = typeof TypeId;
11
+
12
+ export const isSchema = (u: unknown): u is Any =>
13
+ Predicate.hasProperty(u, TypeId);
14
+
15
+ /**
16
+ * A schema definition tracks the schema and its Convex schema definition.
17
+ */
18
+ export interface DatabaseSchema<Tables_ extends Table.AnyWithProps = never> {
19
+ readonly [TypeId]: TypeId;
20
+ readonly tables: Table.TablesRecord<Tables_>;
21
+ readonly convexSchemaDefinition: SchemaDefinition<
22
+ ConvexDatabaseSchemaFromTables<Tables_>,
23
+ true
24
+ >;
25
+
26
+ /**
27
+ * Add a table definition to the schema.
28
+ */
29
+ addTable<TableDef extends Table.AnyWithProps>(
30
+ table: TableDef,
31
+ ): DatabaseSchema<Tables_ | TableDef>;
32
+ }
33
+
34
+ export interface Any {
35
+ readonly [TypeId]: TypeId;
36
+ }
37
+
38
+ export interface AnyWithProps {
39
+ readonly [TypeId]: TypeId;
40
+ readonly tables: Record<string, Table.AnyWithProps>;
41
+ readonly convexSchemaDefinition: SchemaDefinition<GenericSchema, true>;
42
+ addTable<TableDef extends Table.AnyWithProps>(table: TableDef): AnyWithProps;
43
+ }
44
+
45
+ export type Tables<DatabaseSchema_ extends AnyWithProps> =
46
+ DatabaseSchema_ extends DatabaseSchema<infer Tables_> ? Tables_ : never;
47
+
48
+ export type TableNames<DatabaseSchema_ extends AnyWithProps> = Table.Name<
49
+ Tables<DatabaseSchema_>
50
+ > &
51
+ string;
52
+
53
+ export type TableWithName<
54
+ DatabaseSchema_ extends AnyWithProps,
55
+ TableName extends TableNames<DatabaseSchema_>,
56
+ > = Extract<Tables<DatabaseSchema_>, { readonly name: TableName }>;
57
+
58
+ const Proto = {
59
+ [TypeId]: TypeId,
60
+
61
+ addTable<TableDef extends Table.AnyWithProps>(
62
+ this: DatabaseSchema<Table.AnyWithProps>,
63
+ table: TableDef,
64
+ ) {
65
+ const tablesArray = Object.values(this.tables) as Table.AnyWithProps[];
66
+ const newTablesArray = [...tablesArray, table];
67
+
68
+ return makeProto({
69
+ tables: Record.set(this.tables, table.name, table),
70
+ convexSchemaDefinition: pipe(
71
+ newTablesArray,
72
+ Array.map(
73
+ ({ name, tableDefinition }) => [name, tableDefinition] as const,
74
+ ),
75
+ Record.fromEntries,
76
+ defineConvexSchema,
77
+ ),
78
+ });
79
+ },
80
+ };
81
+
82
+ const makeProto = <Tables_ extends Table.AnyWithProps>({
83
+ tables,
84
+ convexSchemaDefinition,
85
+ }: {
86
+ tables: Record.ReadonlyRecord<string, Tables_>;
87
+ convexSchemaDefinition: SchemaDefinition<GenericSchema, true>;
88
+ }): DatabaseSchema<Tables_> =>
89
+ Object.assign(Object.create(Proto), {
90
+ tables,
91
+ convexSchemaDefinition,
92
+ });
93
+
94
+ /**
95
+ * Create an empty schema definition. Add tables incrementally via `addTable`.
96
+ */
97
+ export const make = (): DatabaseSchema<never> =>
98
+ makeProto({
99
+ tables: Record.empty(),
100
+ convexSchemaDefinition: defineConvexSchema({}),
101
+ });
102
+
103
+ export type ConvexDatabaseSchemaFromTables<Tables_ extends Table.AnyWithProps> =
104
+ Expand<{
105
+ [TableName in Table.Name<Tables_> & string]: Table.WithName<
106
+ Tables_,
107
+ TableName
108
+ >["tableDefinition"];
109
+ }>;
110
+
111
+ // System tables
112
+
113
+ export const systemSchema = make()
114
+ .addTable(Table.scheduledFunctionsTable)
115
+ .addTable(Table.storageTable);
116
+
117
+ export const extendWithSystemTables = <Tables_ extends Table.AnyWithProps>(
118
+ tables: Table.TablesRecord<Tables_>,
119
+ ): ExtendWithSystemTables<Tables_> =>
120
+ ({
121
+ ...tables,
122
+ ...Table.systemTables,
123
+ }) as ExtendWithSystemTables<Tables_>;
124
+
125
+ export type ExtendWithSystemTables<Tables_ extends Table.AnyWithProps> =
126
+ Table.TablesRecord<Tables_ | Table.SystemTables>;
127
+
128
+ export type IncludeSystemTables<Tables_ extends Table.AnyWithProps> =
129
+ | Tables_
130
+ | Table.SystemTables extends infer T
131
+ ? T extends Table.AnyWithProps
132
+ ? T
133
+ : never
134
+ : never;
@@ -0,0 +1,166 @@
1
+ import type {
2
+ BetterOmit,
3
+ DocumentByName,
4
+ Expand,
5
+ GenericDatabaseWriter,
6
+ WithoutSystemFields,
7
+ } from "convex/server";
8
+ import type { GenericId } from "convex/values";
9
+ import { Context, Effect, Layer, pipe, Record } from "effect";
10
+ import type * as DatabaseSchema from "./DatabaseSchema";
11
+ import type * as DataModel from "./DataModel";
12
+ import type { DocumentByName as DocumentByName_ } from "./DataModel";
13
+ import * as Document from "./Document";
14
+ import * as QueryInitializer from "./QueryInitializer";
15
+ import type * as Table from "./Table";
16
+ import type * as TableInfo from "./TableInfo";
17
+
18
+ export const make = <DatabaseSchema_ extends DatabaseSchema.AnyWithProps>(
19
+ databaseSchema: DatabaseSchema_,
20
+ convexDatabaseWriter: GenericDatabaseWriter<
21
+ DataModel.ToConvex<DataModel.FromSchema<DatabaseSchema_>>
22
+ >,
23
+ ) => {
24
+ type DataModel_ = DataModel.FromSchema<DatabaseSchema_>;
25
+
26
+ const insert = <TableName extends DataModel.TableNames<DataModel_>>(
27
+ tableName: TableName,
28
+ document: Document.WithoutSystemFields<
29
+ DocumentByName_<DataModel_, TableName>
30
+ >,
31
+ ) =>
32
+ Effect.gen(function* () {
33
+ const table = (
34
+ databaseSchema.tables as Record<string, Table.AnyWithProps>
35
+ )[tableName]!;
36
+
37
+ const encodedDocument = yield* Document.encode(
38
+ document,
39
+ tableName,
40
+ table.Fields,
41
+ );
42
+
43
+ const id = yield* Effect.promise(() =>
44
+ convexDatabaseWriter.insert(
45
+ tableName,
46
+ encodedDocument as WithoutSystemFields<
47
+ DocumentByName<DataModel.ToConvex<DataModel_>, TableName>
48
+ >,
49
+ ),
50
+ );
51
+
52
+ return id;
53
+ });
54
+
55
+ const patch = <TableName extends DataModel.TableNames<DataModel_>>(
56
+ tableName: TableName,
57
+ id: GenericId<TableName>,
58
+ patchedValues: Partial<
59
+ WithoutSystemFields<DocumentByName_<DataModel_, TableName>>
60
+ >,
61
+ ) =>
62
+ Effect.gen(function* () {
63
+ const table = (
64
+ databaseSchema.tables as Record<string, Table.AnyWithProps>
65
+ )[tableName]!;
66
+
67
+ const tableSchema = table.Fields as TableInfo.TableSchema<
68
+ DataModel.TableInfoWithName_<DataModel_, TableName>
69
+ >;
70
+
71
+ const originalDecodedDoc = yield* QueryInitializer.getById(
72
+ tableName,
73
+ convexDatabaseWriter as any,
74
+ table,
75
+ )(id);
76
+
77
+ const updatedEncodedDoc = yield* pipe(
78
+ patchedValues,
79
+ Record.reduce(originalDecodedDoc, (acc, value, key) =>
80
+ value === undefined
81
+ ? Record.remove(acc, key)
82
+ : Record.set(acc, key, value),
83
+ ),
84
+ Document.encode(tableName, tableSchema),
85
+ );
86
+
87
+ yield* Effect.promise(() =>
88
+ convexDatabaseWriter.replace(
89
+ id,
90
+ updatedEncodedDoc as Expand<
91
+ BetterOmit<
92
+ DocumentByName<DataModel.ToConvex<DataModel_>, TableName>,
93
+ "_creationTime" | "_id"
94
+ >
95
+ >,
96
+ ),
97
+ );
98
+ });
99
+
100
+ const replace = <TableName extends DataModel.TableNames<DataModel_>>(
101
+ tableName: TableName,
102
+ id: GenericId<TableName>,
103
+ value: WithoutSystemFields<DocumentByName_<DataModel_, TableName>>,
104
+ ) =>
105
+ Effect.gen(function* () {
106
+ const table = (
107
+ databaseSchema.tables as Record<string, Table.AnyWithProps>
108
+ )[tableName]!;
109
+
110
+ const tableSchema = table.Fields as TableInfo.TableSchema<
111
+ DataModel.TableInfoWithName_<DataModel_, TableName>
112
+ >;
113
+
114
+ const updatedEncodedDoc = yield* Document.encode(
115
+ value,
116
+ tableName,
117
+ tableSchema,
118
+ );
119
+
120
+ yield* Effect.promise(() =>
121
+ convexDatabaseWriter.replace(
122
+ id,
123
+ updatedEncodedDoc as Expand<
124
+ BetterOmit<
125
+ DocumentByName<DataModel.ToConvex<DataModel_>, TableName>,
126
+ "_creationTime" | "_id"
127
+ >
128
+ >,
129
+ ),
130
+ );
131
+ });
132
+
133
+ const delete_ = <TableName extends DataModel.TableNames<DataModel_>>(
134
+ _tableName: TableName,
135
+ id: GenericId<TableName>,
136
+ ) => Effect.promise(() => convexDatabaseWriter.delete(id));
137
+
138
+ return {
139
+ insert,
140
+ patch,
141
+ replace,
142
+ delete: delete_,
143
+ };
144
+ };
145
+
146
+ export const DatabaseWriter = <
147
+ DatabaseSchema_ extends DatabaseSchema.AnyWithProps,
148
+ >() =>
149
+ Context.GenericTag<ReturnType<typeof make<DatabaseSchema_>>>(
150
+ "@confect/server/DatabaseWriter",
151
+ );
152
+
153
+ export type DatabaseWriter<
154
+ DatabaseSchema_ extends DatabaseSchema.AnyWithProps,
155
+ > = ReturnType<typeof DatabaseWriter<DatabaseSchema_>>["Identifier"];
156
+
157
+ export const layer = <DatabaseSchema_ extends DatabaseSchema.AnyWithProps>(
158
+ databaseSchema: DatabaseSchema_,
159
+ convexDatabaseWriter: GenericDatabaseWriter<
160
+ DataModel.ToConvex<DataModel.FromSchema<DatabaseSchema_>>
161
+ >,
162
+ ) =>
163
+ Layer.succeed(
164
+ DatabaseWriter<DatabaseSchema_>(),
165
+ make(databaseSchema, convexDatabaseWriter),
166
+ );
@@ -0,0 +1,200 @@
1
+ import { Effect, Function, ParseResult, pipe, Schema } from "effect";
2
+ import type { ReadonlyRecord } from "effect/Record";
3
+ import * as SystemFields from "@confect/core/SystemFields";
4
+ import type * as DataModel from "./DataModel";
5
+ import type { ReadonlyValue } from "./SchemaToValidator";
6
+ import type * as TableInfo from "./TableInfo";
7
+
8
+ export type WithoutSystemFields<Doc> = Omit<Doc, "_creationTime" | "_id">;
9
+
10
+ export type Any = any;
11
+ export type AnyEncoded = ReadonlyRecord<string, ReadonlyValue>;
12
+
13
+ export const decode = Function.dual<
14
+ <
15
+ DataModel_ extends DataModel.AnyWithProps,
16
+ TableName extends DataModel.TableNames<DataModel_>,
17
+ >(
18
+ tableName: TableName,
19
+ tableSchema: TableInfo.TableSchema<
20
+ DataModel.TableInfoWithName_<DataModel_, TableName>
21
+ >,
22
+ ) => (
23
+ self: DataModel.TableInfoWithName_<DataModel_, TableName>["convexDocument"],
24
+ ) => Effect.Effect<
25
+ DataModel.TableInfoWithName_<DataModel_, TableName>["document"],
26
+ DocumentDecodeError
27
+ >,
28
+ <
29
+ DataModel_ extends DataModel.AnyWithProps,
30
+ TableName extends DataModel.TableNames<DataModel_>,
31
+ >(
32
+ self: DataModel.TableInfoWithName_<DataModel_, TableName>["convexDocument"],
33
+ tableName: TableName,
34
+ tableSchema: TableInfo.TableSchema<
35
+ DataModel.TableInfoWithName_<DataModel_, TableName>
36
+ >,
37
+ ) => Effect.Effect<
38
+ DataModel.TableInfoWithName_<DataModel_, TableName>["document"],
39
+ DocumentDecodeError
40
+ >
41
+ >(
42
+ 3,
43
+ <
44
+ DataModel_ extends DataModel.AnyWithProps,
45
+ TableName extends DataModel.TableNames<DataModel_>,
46
+ >(
47
+ self: DataModel.TableInfoWithName_<DataModel_, TableName>["convexDocument"],
48
+ tableName: TableName,
49
+ tableSchema: TableInfo.TableSchema<
50
+ DataModel.TableInfoWithName_<DataModel_, TableName>
51
+ >,
52
+ ): Effect.Effect<
53
+ DataModel.TableInfoWithName_<DataModel_, TableName>["document"],
54
+ DocumentDecodeError
55
+ > =>
56
+ Effect.gen(function* () {
57
+ const TableSchemaWithSystemFields = SystemFields.extendWithSystemFields(
58
+ tableName,
59
+ tableSchema,
60
+ );
61
+
62
+ const encodedDoc =
63
+ self as (typeof TableSchemaWithSystemFields)["Encoded"];
64
+
65
+ const decodedDoc = yield* pipe(
66
+ encodedDoc,
67
+ Schema.decode(TableSchemaWithSystemFields),
68
+ Effect.catchTag("ParseError", (parseError) =>
69
+ Effect.gen(function* () {
70
+ const formattedParseError =
71
+ yield* ParseResult.TreeFormatter.formatError(parseError);
72
+
73
+ return yield* new DocumentDecodeError({
74
+ tableName,
75
+ id: encodedDoc._id,
76
+ parseError: formattedParseError,
77
+ });
78
+ }),
79
+ ),
80
+ );
81
+
82
+ return decodedDoc;
83
+ }),
84
+ );
85
+
86
+ export const encode = Function.dual<
87
+ <
88
+ DataModel_ extends DataModel.AnyWithProps,
89
+ TableName extends DataModel.TableNames<DataModel_>,
90
+ >(
91
+ tableName: TableName,
92
+ tableSchema: TableInfo.TableSchema<
93
+ DataModel.TableInfoWithName_<DataModel_, TableName>
94
+ >,
95
+ ) => (
96
+ self: DataModel.TableInfoWithName_<DataModel_, TableName>["document"],
97
+ ) => Effect.Effect<
98
+ DataModel.TableInfoWithName_<DataModel_, TableName>["encodedDocument"],
99
+ DocumentEncodeError
100
+ >,
101
+ <
102
+ DataModel_ extends DataModel.AnyWithProps,
103
+ TableName extends DataModel.TableNames<DataModel_>,
104
+ >(
105
+ self: DataModel.TableInfoWithName_<DataModel_, TableName>["document"],
106
+ tableName: TableName,
107
+ tableSchema: TableInfo.TableSchema<
108
+ DataModel.TableInfoWithName_<DataModel_, TableName>
109
+ >,
110
+ ) => Effect.Effect<
111
+ DataModel.TableInfoWithName_<DataModel_, TableName>["encodedDocument"],
112
+ DocumentEncodeError
113
+ >
114
+ >(
115
+ 3,
116
+ <
117
+ DataModel_ extends DataModel.AnyWithProps,
118
+ TableName extends DataModel.TableNames<DataModel_>,
119
+ >(
120
+ self: DataModel.TableInfoWithName_<DataModel_, TableName>["document"],
121
+ tableName: TableName,
122
+ tableSchema: TableInfo.TableSchema<
123
+ DataModel.TableInfoWithName_<DataModel_, TableName>
124
+ >,
125
+ ): Effect.Effect<
126
+ DataModel.TableInfoWithName_<DataModel_, TableName>["encodedDocument"],
127
+ DocumentEncodeError
128
+ > =>
129
+ Effect.gen(function* () {
130
+ type TableSchemaWithSystemFields = SystemFields.ExtendWithSystemFields<
131
+ TableName,
132
+ TableInfo.TableSchema<
133
+ DataModel.TableInfoWithName_<DataModel_, TableName>
134
+ >
135
+ >;
136
+
137
+ const decodedDoc = self as TableSchemaWithSystemFields["Type"];
138
+
139
+ const encodedDoc = yield* pipe(
140
+ decodedDoc,
141
+ Schema.encode(tableSchema),
142
+ Effect.catchTag("ParseError", (parseError) =>
143
+ Effect.gen(function* () {
144
+ const formattedParseError =
145
+ yield* ParseResult.TreeFormatter.formatError(parseError);
146
+
147
+ return yield* new DocumentEncodeError({
148
+ tableName,
149
+ id: decodedDoc._id,
150
+ parseError: formattedParseError,
151
+ });
152
+ }),
153
+ ),
154
+ );
155
+
156
+ return encodedDoc;
157
+ }),
158
+ );
159
+
160
+ export class DocumentDecodeError extends Schema.TaggedError<DocumentDecodeError>(
161
+ "DocumentDecodeError",
162
+ )("DocumentDecodeError", {
163
+ tableName: Schema.String,
164
+ id: Schema.String,
165
+ parseError: Schema.String,
166
+ }) {
167
+ override get message(): string {
168
+ return documentErrorMessage({
169
+ id: this.id,
170
+ tableName: this.tableName,
171
+ message: `could not be decoded:\n\n${this.parseError}`,
172
+ });
173
+ }
174
+ }
175
+
176
+ export class DocumentEncodeError extends Schema.TaggedError<DocumentEncodeError>(
177
+ "DocumentEncodeError",
178
+ )("DocumentEncodeError", {
179
+ tableName: Schema.String,
180
+ id: Schema.String,
181
+ parseError: Schema.String,
182
+ }) {
183
+ override get message(): string {
184
+ return documentErrorMessage({
185
+ id: this.id,
186
+ tableName: this.tableName,
187
+ message: `could not be encoded:\n\n${this.parseError}`,
188
+ });
189
+ }
190
+ }
191
+
192
+ export const documentErrorMessage = ({
193
+ id,
194
+ tableName,
195
+ message,
196
+ }: {
197
+ id: string;
198
+ tableName: string;
199
+ message: string;
200
+ }) => `Document with ID '${id}' in table '${tableName}' ${message}`;