@effect-app/infra 2.0.2 → 2.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.
Files changed (110) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/_cjs/api/internal/events.cjs +2 -2
  3. package/_cjs/api/internal/events.cjs.map +1 -1
  4. package/_cjs/fileUtil.cjs +48 -0
  5. package/_cjs/fileUtil.cjs.map +1 -0
  6. package/_cjs/services/CUPS.cjs +118 -0
  7. package/_cjs/services/CUPS.cjs.map +1 -0
  8. package/_cjs/services/QueueMaker/SQLQueue.cjs +1 -1
  9. package/_cjs/services/QueueMaker/SQLQueue.cjs.map +1 -1
  10. package/_cjs/services/QueueMaker/memQueue.cjs +1 -1
  11. package/_cjs/services/QueueMaker/memQueue.cjs.map +1 -1
  12. package/_cjs/services/QueueMaker/sbqueue.cjs +1 -1
  13. package/_cjs/services/QueueMaker/sbqueue.cjs.map +1 -1
  14. package/_cjs/services/Store/Cosmos.cjs +1 -1
  15. package/_cjs/services/Store/Cosmos.cjs.map +1 -1
  16. package/_cjs/services/Store/Disk.cjs +1 -1
  17. package/_cjs/services/adapters/SQL/Model.cjs +500 -0
  18. package/_cjs/services/adapters/SQL/Model.cjs.map +1 -0
  19. package/_cjs/services/adapters/SQL.cjs +11 -0
  20. package/_cjs/services/adapters/SQL.cjs.map +1 -0
  21. package/_cjs/services/adapters/ServiceBus.cjs +76 -0
  22. package/_cjs/services/adapters/ServiceBus.cjs.map +1 -0
  23. package/_cjs/services/adapters/cosmos-client.cjs +18 -0
  24. package/_cjs/services/adapters/cosmos-client.cjs.map +1 -0
  25. package/_cjs/services/adapters/index.cjs +6 -0
  26. package/_cjs/services/adapters/index.cjs.map +1 -0
  27. package/_cjs/services/adapters/logger.cjs +9 -0
  28. package/_cjs/services/adapters/logger.cjs.map +1 -0
  29. package/_cjs/services/adapters/memQueue.cjs +31 -0
  30. package/_cjs/services/adapters/memQueue.cjs.map +1 -0
  31. package/_cjs/services/adapters/mongo-client.cjs +20 -0
  32. package/_cjs/services/adapters/mongo-client.cjs.map +1 -0
  33. package/_cjs/services/adapters/redis-client.cjs +83 -0
  34. package/_cjs/services/adapters/redis-client.cjs.map +1 -0
  35. package/dist/api/internal/events.d.ts.map +1 -1
  36. package/dist/api/internal/events.js +3 -3
  37. package/dist/fileUtil.d.ts +23 -0
  38. package/dist/fileUtil.d.ts.map +1 -0
  39. package/dist/fileUtil.js +41 -0
  40. package/dist/services/CUPS.d.ts +26 -0
  41. package/dist/services/CUPS.d.ts.map +1 -0
  42. package/dist/services/CUPS.js +111 -0
  43. package/dist/services/QueueMaker/SQLQueue.d.ts.map +1 -1
  44. package/dist/services/QueueMaker/SQLQueue.js +2 -2
  45. package/dist/services/QueueMaker/memQueue.d.ts +1 -1
  46. package/dist/services/QueueMaker/memQueue.d.ts.map +1 -1
  47. package/dist/services/QueueMaker/memQueue.js +2 -2
  48. package/dist/services/QueueMaker/sbqueue.d.ts +3 -3
  49. package/dist/services/QueueMaker/sbqueue.d.ts.map +1 -1
  50. package/dist/services/QueueMaker/sbqueue.js +2 -2
  51. package/dist/services/Repository/ext.d.ts +11 -11
  52. package/dist/services/RepositoryBase.d.ts +6 -6
  53. package/dist/services/Store/Cosmos.d.ts.map +1 -1
  54. package/dist/services/Store/Cosmos.js +2 -2
  55. package/dist/services/Store/Disk.js +2 -2
  56. package/dist/services/adapters/SQL/Model.d.ts +538 -0
  57. package/dist/services/adapters/SQL/Model.d.ts.map +1 -0
  58. package/dist/services/adapters/SQL/Model.js +508 -0
  59. package/dist/services/adapters/SQL.d.ts +2 -0
  60. package/dist/services/adapters/SQL.d.ts.map +1 -0
  61. package/dist/services/adapters/SQL.js +2 -0
  62. package/dist/services/adapters/ServiceBus.d.ts +50 -0
  63. package/dist/services/adapters/ServiceBus.d.ts.map +1 -0
  64. package/dist/services/adapters/ServiceBus.js +73 -0
  65. package/dist/services/adapters/cosmos-client.d.ts +10 -0
  66. package/dist/services/adapters/cosmos-client.d.ts.map +1 -0
  67. package/dist/services/adapters/cosmos-client.js +8 -0
  68. package/dist/services/adapters/index.d.ts +2 -0
  69. package/dist/services/adapters/index.d.ts.map +1 -0
  70. package/dist/services/adapters/index.js +2 -0
  71. package/dist/services/adapters/logger.d.ts +8 -0
  72. package/dist/services/adapters/logger.d.ts.map +1 -0
  73. package/dist/services/adapters/logger.js +3 -0
  74. package/dist/services/adapters/memQueue.d.ts +34 -0
  75. package/dist/services/adapters/memQueue.d.ts.map +1 -0
  76. package/dist/services/adapters/memQueue.js +24 -0
  77. package/dist/services/adapters/mongo-client.d.ts +10 -0
  78. package/dist/services/adapters/mongo-client.d.ts.map +1 -0
  79. package/dist/services/adapters/mongo-client.js +12 -0
  80. package/dist/services/adapters/redis-client.d.ts +29 -0
  81. package/dist/services/adapters/redis-client.d.ts.map +1 -0
  82. package/dist/services/adapters/redis-client.js +93 -0
  83. package/package.json +128 -12
  84. package/src/api/internal/events.ts +2 -2
  85. package/src/fileUtil.ts +85 -0
  86. package/src/services/CUPS.ts +151 -0
  87. package/src/services/QueueMaker/SQLQueue.ts +1 -1
  88. package/src/services/QueueMaker/memQueue.ts +1 -1
  89. package/src/services/QueueMaker/sbqueue.ts +7 -7
  90. package/src/services/Store/Cosmos.ts +1 -1
  91. package/src/services/Store/Disk.ts +1 -1
  92. package/src/services/adapters/SQL/Model.ts +939 -0
  93. package/src/services/adapters/SQL.ts +1 -0
  94. package/src/services/adapters/ServiceBus.ts +140 -0
  95. package/src/services/adapters/cosmos-client.ts +16 -0
  96. package/src/services/adapters/index.ts +0 -0
  97. package/src/services/adapters/logger.ts +3 -0
  98. package/src/services/adapters/memQueue.ts +26 -0
  99. package/src/services/adapters/mongo-client.ts +23 -0
  100. package/src/services/adapters/redis-client.ts +123 -0
  101. package/tsconfig.src.json +0 -3
  102. package/src/services/Store/Redis.ts.bak +0 -88
  103. package/src/services/simpledb/cosmosdb.ts.bak +0 -149
  104. package/src/services/simpledb/diskdb.ts.bak +0 -165
  105. package/src/services/simpledb/index.ts.bak +0 -6
  106. package/src/services/simpledb/memdb.ts.bak +0 -78
  107. package/src/services/simpledb/mongodb.ts.bak +0 -107
  108. package/src/services/simpledb/redisdb.ts.bak +0 -202
  109. package/src/services/simpledb/shared.ts.bak +0 -117
  110. package/src/services/simpledb/simpledb.ts.bak +0 -121
@@ -0,0 +1,939 @@
1
+ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
2
+ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
3
+ /* eslint-disable @typescript-eslint/no-empty-object-type */
4
+ /* eslint-disable @typescript-eslint/no-explicit-any */
5
+ /* eslint-disable @typescript-eslint/no-unsafe-argument */
6
+ /* eslint-disable @typescript-eslint/no-unsafe-return */
7
+ /**
8
+ * @since 1.0.0
9
+ */
10
+ import * as RRX from "@effect/experimental/RequestResolver"
11
+ import * as VariantSchema from "@effect/experimental/VariantSchema"
12
+ import { SqlClient } from "@effect/sql/SqlClient"
13
+ import * as SqlResolver from "@effect/sql/SqlResolver"
14
+ import * as SqlSchema from "@effect/sql/SqlSchema"
15
+ import { randomUUID } from "crypto" // TODO
16
+ import type { Brand } from "effect/Brand"
17
+ import * as DateTime from "effect/DateTime"
18
+ import type { DurationInput } from "effect/Duration"
19
+ import * as Effect from "effect/Effect"
20
+ import * as Option from "effect/Option"
21
+ import * as ParseResult from "effect/ParseResult"
22
+ import * as Schema from "effect/Schema"
23
+ import type { Scope } from "effect/Scope"
24
+
25
+ const {
26
+ Class,
27
+ Field,
28
+ FieldExcept,
29
+ FieldOnly,
30
+ Struct,
31
+ Union,
32
+ extract,
33
+ fieldEvolve,
34
+ fieldFromKey
35
+ } = VariantSchema.make({
36
+ variants: ["select", "insert", "update", "json", "jsonCreate", "jsonUpdate"],
37
+ defaultVariant: "select"
38
+ })
39
+
40
+ /**
41
+ * @since 1.0.0
42
+ * @category models
43
+ */
44
+ export type Any = Schema.Schema.Any & {
45
+ readonly fields: Schema.Struct.Fields
46
+ readonly insert: Schema.Schema.Any
47
+ readonly update: Schema.Schema.Any
48
+ readonly json: Schema.Schema.Any
49
+ readonly jsonCreate: Schema.Schema.Any
50
+ readonly jsonUpdate: Schema.Schema.Any
51
+ }
52
+
53
+ /**
54
+ * @since 1.0.0
55
+ * @category models
56
+ */
57
+ export type AnyNoContext = Schema.Schema.AnyNoContext & {
58
+ readonly fields: Schema.Struct.Fields
59
+ readonly insert: Schema.Schema.AnyNoContext
60
+ readonly update: Schema.Schema.AnyNoContext
61
+ readonly json: Schema.Schema.AnyNoContext
62
+ readonly jsonCreate: Schema.Schema.AnyNoContext
63
+ readonly jsonUpdate: Schema.Schema.AnyNoContext
64
+ }
65
+
66
+ /**
67
+ * @since 1.0.0
68
+ * @category models
69
+ */
70
+ export type VariantsDatabase = "select" | "insert" | "update"
71
+
72
+ /**
73
+ * @since 1.0.0
74
+ * @category models
75
+ */
76
+ export type VariantsJson = "json" | "jsonCreate" | "jsonUpdate"
77
+
78
+ export {
79
+ /**
80
+ * A base class used for creating domain model schemas.
81
+ *
82
+ * It supports common variants for database and JSON apis.
83
+ *
84
+ * @since 1.0.0
85
+ * @category constructors
86
+ * @example
87
+ * import { Schema } from "effect/Schema"
88
+ * import { Model } from "@effect/sql"
89
+ *
90
+ * export const GroupId = Schema.Number.pipe(Schema.brand("GroupId"))
91
+ *
92
+ * export class Group extends Model.Class<Group>("Group")({
93
+ * id: Model.Generated(GroupId),
94
+ * name: Schema.NonEmptyTrimmedString,
95
+ * createdAt: Model.DateTimeInsertFromDate,
96
+ * updatedAt: Model.DateTimeUpdateFromDate
97
+ * }) {}
98
+ *
99
+ * // schema used for selects
100
+ * Group
101
+ *
102
+ * // schema used for inserts
103
+ * Group.insert
104
+ *
105
+ * // schema used for updates
106
+ * Group.update
107
+ *
108
+ * // schema used for json api
109
+ * Group.json
110
+ * Group.jsonCreate
111
+ * Group.jsonUpdate
112
+ *
113
+ * // you can also turn them into classes
114
+ * class GroupJson extends Schema.Class<GroupJson>("GroupJson")(Group.json) {
115
+ * get upperName() {
116
+ * return this.name.toUpperCase()
117
+ * }
118
+ * }
119
+ */
120
+ Class,
121
+ /**
122
+ * @since 1.0.0
123
+ * @category extraction
124
+ */
125
+ extract,
126
+ /**
127
+ * @since 1.0.0
128
+ * @category fields
129
+ */
130
+ Field,
131
+ /**
132
+ * @since 1.0.0
133
+ * @category fields
134
+ */
135
+ fieldEvolve,
136
+ /**
137
+ * @since 1.0.0
138
+ * @category fields
139
+ */
140
+ FieldExcept,
141
+ /**
142
+ * @since 1.0.0
143
+ * @category fields
144
+ */
145
+ fieldFromKey,
146
+ /**
147
+ * @since 1.0.0
148
+ * @category fields
149
+ */
150
+ FieldOnly,
151
+ /**
152
+ * @since 1.0.0
153
+ * @category constructors
154
+ */
155
+ Struct,
156
+ /**
157
+ * @since 1.0.0
158
+ * @category constructors
159
+ */
160
+ Union
161
+ }
162
+
163
+ /**
164
+ * @since 1.0.0
165
+ * @category fields
166
+ */
167
+ export const fields: <A extends VariantSchema.Struct<any>>(self: A) => A[VariantSchema.TypeId] = VariantSchema.fields
168
+
169
+ /**
170
+ * @since 1.0.0
171
+ * @category overrideable
172
+ */
173
+ export const Override: <A>(value: A) => A & Brand<"Override"> = VariantSchema.Override
174
+
175
+ /**
176
+ * @since 1.0.0
177
+ * @category generated
178
+ */
179
+ export interface Generated<S extends Schema.Schema.All | Schema.PropertySignature.All> extends
180
+ VariantSchema.Field<{
181
+ readonly select: S
182
+ readonly update: S
183
+ readonly json: S
184
+ }>
185
+ {}
186
+
187
+ /**
188
+ * A field that represents a column that is generated by the database.
189
+ *
190
+ * It is available for selection and update, but not for insertion.
191
+ *
192
+ * @since 1.0.0
193
+ * @category generated
194
+ */
195
+ export const Generated = <S extends Schema.Schema.All | Schema.PropertySignature.All>(
196
+ schema: S
197
+ ): Generated<S> =>
198
+ Field({
199
+ select: schema,
200
+ update: schema,
201
+ json: schema
202
+ })
203
+
204
+ /**
205
+ * @since 1.0.0
206
+ * @category generated
207
+ */
208
+ export interface GeneratedByApp<S extends Schema.Schema.All | Schema.PropertySignature.All>
209
+ extends
210
+ VariantSchema.Field<{
211
+ readonly select: S
212
+ readonly insert: S
213
+ readonly update: S
214
+ readonly json: S
215
+ }>
216
+ {}
217
+
218
+ /**
219
+ * A field that represents a column that is generated by the application.
220
+ *
221
+ * It is required by the database, but not by the JSON variants.
222
+ *
223
+ * @since 1.0.0
224
+ * @category generated
225
+ */
226
+ export const GeneratedByApp = <S extends Schema.Schema.All | Schema.PropertySignature.All>(
227
+ schema: S
228
+ ): GeneratedByApp<S> =>
229
+ Field({
230
+ select: schema,
231
+ insert: schema,
232
+ update: schema,
233
+ json: schema
234
+ })
235
+
236
+ /**
237
+ * @since 1.0.0
238
+ * @category sensitive
239
+ */
240
+ export interface Sensitive<S extends Schema.Schema.All | Schema.PropertySignature.All> extends
241
+ VariantSchema.Field<{
242
+ readonly select: S
243
+ readonly insert: S
244
+ readonly update: S
245
+ }>
246
+ {}
247
+
248
+ /**
249
+ * A field that represents a sensitive value that should not be exposed in the
250
+ * JSON variants.
251
+ *
252
+ * @since 1.0.0
253
+ * @category sensitive
254
+ */
255
+ export const Sensitive = <S extends Schema.Schema.All | Schema.PropertySignature.All>(
256
+ schema: S
257
+ ): Sensitive<S> =>
258
+ Field({
259
+ select: schema,
260
+ insert: schema,
261
+ update: schema
262
+ })
263
+
264
+ /**
265
+ * Convert a field to one that is optional for all variants.
266
+ *
267
+ * For the database variants, it will accept `null`able values.
268
+ * For the JSON variants, it will also accept missing keys.
269
+ *
270
+ * @since 1.0.0
271
+ * @category optional
272
+ */
273
+ export interface FieldOption<S extends Schema.Schema.Any> extends
274
+ VariantSchema.Field<{
275
+ readonly select: Schema.OptionFromNullOr<S>
276
+ readonly insert: Schema.OptionFromNullOr<S>
277
+ readonly update: Schema.OptionFromNullOr<S>
278
+ readonly json: Schema.optionalWith<S, { as: "Option" }>
279
+ readonly jsonCreate: Schema.optionalWith<S, { as: "Option"; nullable: true }>
280
+ readonly jsonUpdate: Schema.optionalWith<S, { as: "Option"; nullable: true }>
281
+ }>
282
+ {}
283
+
284
+ /**
285
+ * Convert a field to one that is optional for all variants.
286
+ *
287
+ * For the database variants, it will accept `null`able values.
288
+ * For the JSON variants, it will also accept missing keys.
289
+ *
290
+ * @since 1.0.0
291
+ * @category optional
292
+ */
293
+ export const FieldOption: <Field extends VariantSchema.Field<any> | Schema.Schema.Any>(
294
+ self: Field
295
+ ) => Field extends Schema.Schema.Any ? FieldOption<Field>
296
+ : Field extends VariantSchema.Field<infer S> ? VariantSchema.Field<
297
+ {
298
+ readonly [K in keyof S]: S[K] extends Schema.Schema.Any
299
+ ? K extends VariantsDatabase ? Schema.OptionFromNullOr<S[K]>
300
+ : Schema.optionalWith<S[K], { as: "Option"; nullable: true }>
301
+ : never
302
+ }
303
+ >
304
+ : never = fieldEvolve({
305
+ select: Schema.OptionFromNullOr,
306
+ insert: Schema.OptionFromNullOr,
307
+ update: Schema.OptionFromNullOr,
308
+ json: Schema.optionalWith({ as: "Option" }),
309
+ jsonCreate: Schema.optionalWith({ as: "Option", nullable: true }),
310
+ jsonUpdate: Schema.optionalWith({ as: "Option", nullable: true })
311
+ }) as any
312
+
313
+ /**
314
+ * @since 1.0.0
315
+ * @category date & time
316
+ */
317
+ export interface DateTimeFromDate extends
318
+ Schema.transform<
319
+ typeof Schema.ValidDateFromSelf,
320
+ typeof Schema.DateTimeUtcFromSelf
321
+ >
322
+ {}
323
+
324
+ /**
325
+ * @since 1.0.0
326
+ * @category date & time
327
+ */
328
+ export const DateTimeFromDate: DateTimeFromDate = Schema.transform(
329
+ Schema.ValidDateFromSelf,
330
+ Schema.DateTimeUtcFromSelf,
331
+ {
332
+ decode: DateTime.unsafeFromDate,
333
+ encode: DateTime.toDateUtc
334
+ }
335
+ )
336
+
337
+ /**
338
+ * @since 1.0.0
339
+ * @category date & time
340
+ */
341
+ export interface Date extends Schema.transformOrFail<typeof Schema.String, typeof Schema.DateTimeUtcFromSelf> {}
342
+
343
+ /**
344
+ * A schema for a `DateTime.Utc` that is serialized as a date string in the
345
+ * format `YYYY-MM-DD`.
346
+ *
347
+ * @since 1.0.0
348
+ * @category date & time
349
+ */
350
+ export const Date: Date = Schema.transformOrFail(
351
+ Schema.String,
352
+ Schema.DateTimeUtcFromSelf,
353
+ {
354
+ decode: (s, _, ast) =>
355
+ DateTime.make(s).pipe(
356
+ Option.map(DateTime.removeTime),
357
+ Option.match({
358
+ onNone: () => ParseResult.fail(new ParseResult.Type(ast, s)),
359
+ onSome: (dt) => ParseResult.succeed(dt)
360
+ })
361
+ ),
362
+ encode: (dt) => ParseResult.succeed(DateTime.formatIsoDate(dt))
363
+ }
364
+ )
365
+
366
+ /**
367
+ * @since 1.0.0
368
+ * @category date & time
369
+ */
370
+ export const DateWithNow = VariantSchema.Overrideable(Date, Schema.DateTimeUtcFromSelf, {
371
+ generate: Option.match({
372
+ onNone: () => Effect.map(DateTime.now, DateTime.removeTime),
373
+ onSome: (dt) => Effect.succeed(DateTime.removeTime(dt))
374
+ })
375
+ })
376
+
377
+ /**
378
+ * @since 1.0.0
379
+ * @category date & time
380
+ */
381
+ export const DateTimeWithNow = VariantSchema.Overrideable(Schema.String, Schema.DateTimeUtcFromSelf, {
382
+ generate: Option.match({
383
+ onNone: () => Effect.map(DateTime.now, DateTime.formatIso),
384
+ onSome: (dt) => Effect.succeed(DateTime.formatIso(dt))
385
+ })
386
+ })
387
+
388
+ /**
389
+ * @since 1.0.0
390
+ * @category date & time
391
+ */
392
+ export const DateTimeFromDateWithNow = VariantSchema.Overrideable(Schema.DateFromSelf, Schema.DateTimeUtcFromSelf, {
393
+ generate: Option.match({
394
+ onNone: () => Effect.map(DateTime.now, DateTime.toDateUtc),
395
+ onSome: (dt) => Effect.succeed(DateTime.toDateUtc(dt))
396
+ })
397
+ })
398
+
399
+ /**
400
+ * @since 1.0.0
401
+ * @category date & time
402
+ */
403
+ export const DateTimeFromNumberWithNow = VariantSchema.Overrideable(Schema.Number, Schema.DateTimeUtcFromSelf, {
404
+ generate: Option.match({
405
+ onNone: () => Effect.map(DateTime.now, DateTime.toEpochMillis),
406
+ onSome: (dt) => Effect.succeed(DateTime.toEpochMillis(dt))
407
+ })
408
+ })
409
+
410
+ /**
411
+ * @since 1.0.0
412
+ * @category date & time
413
+ */
414
+ export interface DateTimeInsert extends
415
+ VariantSchema.Field<{
416
+ readonly select: typeof Schema.DateTimeUtc
417
+ readonly insert: VariantSchema.Overrideable<DateTime.Utc, string>
418
+ readonly json: typeof Schema.DateTimeUtc
419
+ }>
420
+ {}
421
+
422
+ /**
423
+ * A field that represents a date-time value that is inserted as the current
424
+ * `DateTime.Utc`. It is serialized as a string for the database.
425
+ *
426
+ * It is omitted from updates and is available for selection.
427
+ *
428
+ * @since 1.0.0
429
+ * @category date & time
430
+ */
431
+ export const DateTimeInsert: DateTimeInsert = Field({
432
+ select: Schema.DateTimeUtc,
433
+ insert: DateTimeWithNow,
434
+ json: Schema.DateTimeUtc
435
+ })
436
+
437
+ /**
438
+ * @since 1.0.0
439
+ * @category date & time
440
+ */
441
+ export interface DateTimeInsertFromDate extends
442
+ VariantSchema.Field<{
443
+ readonly select: DateTimeFromDate
444
+ readonly insert: VariantSchema.Overrideable<DateTime.Utc, globalThis.Date>
445
+ readonly json: typeof Schema.DateTimeUtc
446
+ }>
447
+ {}
448
+
449
+ /**
450
+ * A field that represents a date-time value that is inserted as the current
451
+ * `DateTime.Utc`. It is serialized as a `Date` for the database.
452
+ *
453
+ * It is omitted from updates and is available for selection.
454
+ *
455
+ * @since 1.0.0
456
+ * @category date & time
457
+ */
458
+ export const DateTimeInsertFromDate: DateTimeInsertFromDate = Field({
459
+ select: DateTimeFromDate,
460
+ insert: DateTimeFromDateWithNow,
461
+ json: Schema.DateTimeUtc
462
+ })
463
+
464
+ /**
465
+ * @since 1.0.0
466
+ * @category date & time
467
+ */
468
+ export interface DateTimeInsertFromNumber extends
469
+ VariantSchema.Field<{
470
+ readonly select: typeof Schema.DateTimeUtcFromNumber
471
+ readonly insert: VariantSchema.Overrideable<DateTime.Utc, number>
472
+ readonly json: typeof Schema.DateTimeUtcFromNumber
473
+ }>
474
+ {}
475
+
476
+ /**
477
+ * A field that represents a date-time value that is inserted as the current
478
+ * `DateTime.Utc`. It is serialized as a `number`.
479
+ *
480
+ * It is omitted from updates and is available for selection.
481
+ *
482
+ * @since 1.0.0
483
+ * @category date & time
484
+ */
485
+ export const DateTimeInsertFromNumber: DateTimeInsertFromNumber = Field({
486
+ select: Schema.DateTimeUtcFromNumber,
487
+ insert: DateTimeFromNumberWithNow,
488
+ json: Schema.DateTimeUtcFromNumber
489
+ })
490
+
491
+ /**
492
+ * @since 1.0.0
493
+ * @category date & time
494
+ */
495
+ export interface DateTimeUpdate extends
496
+ VariantSchema.Field<{
497
+ readonly select: typeof Schema.DateTimeUtc
498
+ readonly insert: VariantSchema.Overrideable<DateTime.Utc, string>
499
+ readonly update: VariantSchema.Overrideable<DateTime.Utc, string>
500
+ readonly json: typeof Schema.DateTimeUtc
501
+ }>
502
+ {}
503
+
504
+ /**
505
+ * A field that represents a date-time value that is updated as the current
506
+ * `DateTime.Utc`. It is serialized as a string for the database.
507
+ *
508
+ * It is set to the current `DateTime.Utc` on updates and inserts and is
509
+ * available for selection.
510
+ *
511
+ * @since 1.0.0
512
+ * @category date & time
513
+ */
514
+ export const DateTimeUpdate: DateTimeUpdate = Field({
515
+ select: Schema.DateTimeUtc,
516
+ insert: DateTimeWithNow,
517
+ update: DateTimeWithNow,
518
+ json: Schema.DateTimeUtc
519
+ })
520
+
521
+ /**
522
+ * @since 1.0.0
523
+ * @category date & time
524
+ */
525
+ export interface DateTimeUpdateFromDate extends
526
+ VariantSchema.Field<{
527
+ readonly select: DateTimeFromDate
528
+ readonly insert: VariantSchema.Overrideable<DateTime.Utc, globalThis.Date>
529
+ readonly update: VariantSchema.Overrideable<DateTime.Utc, globalThis.Date>
530
+ readonly json: typeof Schema.DateTimeUtc
531
+ }>
532
+ {}
533
+
534
+ /**
535
+ * A field that represents a date-time value that is updated as the current
536
+ * `DateTime.Utc`. It is serialized as a `Date` for the database.
537
+ *
538
+ * It is set to the current `DateTime.Utc` on updates and inserts and is
539
+ * available for selection.
540
+ *
541
+ * @since 1.0.0
542
+ * @category date & time
543
+ */
544
+ export const DateTimeUpdateFromDate: DateTimeUpdateFromDate = Field({
545
+ select: DateTimeFromDate,
546
+ insert: DateTimeFromDateWithNow,
547
+ update: DateTimeFromDateWithNow,
548
+ json: Schema.DateTimeUtc
549
+ })
550
+
551
+ /**
552
+ * @since 1.0.0
553
+ * @category date & time
554
+ */
555
+ export interface DateTimeUpdateFromNumber extends
556
+ VariantSchema.Field<{
557
+ readonly select: typeof Schema.DateTimeUtcFromNumber
558
+ readonly insert: VariantSchema.Overrideable<DateTime.Utc, number>
559
+ readonly update: VariantSchema.Overrideable<DateTime.Utc, number>
560
+ readonly json: typeof Schema.DateTimeUtcFromNumber
561
+ }>
562
+ {}
563
+
564
+ /**
565
+ * A field that represents a date-time value that is updated as the current
566
+ * `DateTime.Utc`. It is serialized as a `number`.
567
+ *
568
+ * It is set to the current `DateTime.Utc` on updates and inserts and is
569
+ * available for selection.
570
+ *
571
+ * @since 1.0.0
572
+ * @category date & time
573
+ */
574
+ export const DateTimeUpdateFromNumber: DateTimeUpdateFromNumber = Field({
575
+ select: Schema.DateTimeUtcFromNumber,
576
+ insert: DateTimeFromNumberWithNow,
577
+ update: DateTimeFromNumberWithNow,
578
+ json: Schema.DateTimeUtcFromNumber
579
+ })
580
+
581
+ /**
582
+ * @since 1.0.0
583
+ * @category json
584
+ */
585
+ export interface JsonFromString<S extends Schema.Schema.All | Schema.PropertySignature.All>
586
+ extends
587
+ VariantSchema.Field<{
588
+ readonly select: Schema.Schema<Schema.Schema.Type<S>, string, Schema.Schema.Context<S>>
589
+ readonly insert: Schema.Schema<Schema.Schema.Type<S>, string, Schema.Schema.Context<S>>
590
+ readonly update: Schema.Schema<Schema.Schema.Type<S>, string, Schema.Schema.Context<S>>
591
+ readonly json: S
592
+ readonly jsonCreate: S
593
+ readonly jsonUpdate: S
594
+ }>
595
+ {}
596
+
597
+ /**
598
+ * A field that represents a JSON value stored as text in the database.
599
+ *
600
+ * The "json" variants will use the object schema directly.
601
+ *
602
+ * @since 1.0.0
603
+ * @category json
604
+ */
605
+ export const JsonFromString = <S extends Schema.Schema.All | Schema.PropertySignature.All>(
606
+ schema: S
607
+ ): JsonFromString<S> => {
608
+ const parsed = Schema.parseJson(schema as any)
609
+ return Field({
610
+ select: parsed,
611
+ insert: parsed,
612
+ update: parsed,
613
+ json: schema,
614
+ jsonCreate: schema,
615
+ jsonUpdate: schema
616
+ }) as any
617
+ }
618
+
619
+ /**
620
+ * Create a simple CRUD repository from a model.
621
+ *
622
+ * @since 1.0.0
623
+ * @category repository
624
+ */
625
+ export const makeRepository = <
626
+ S extends Any,
627
+ Id extends (keyof S["Type"]) & (keyof S["update"]["Type"]) & (keyof S["fields"])
628
+ >(Model: S, options: {
629
+ readonly tableName: string
630
+ readonly spanPrefix: string
631
+ readonly idColumn: Id
632
+ readonly versionColumn?: string | undefined
633
+ }): Effect.Effect<
634
+ {
635
+ readonly insert: (
636
+ insert: S["insert"]["Type"]
637
+ ) => Effect.Effect<S["Type"], never, S["Context"] | S["insert"]["Context"]>
638
+ readonly insertVoid: (
639
+ insert: S["insert"]["Type"]
640
+ ) => Effect.Effect<void, never, S["Context"] | S["insert"]["Context"]>
641
+ readonly update: (
642
+ update: S["update"]["Type"]
643
+ ) => Effect.Effect<S["Type"], never, S["Context"] | S["update"]["Context"]>
644
+ readonly updateVoid: (
645
+ update: S["update"]["Type"]
646
+ ) => Effect.Effect<void, never, S["Context"] | S["update"]["Context"]>
647
+ readonly findById: (
648
+ id: Schema.Schema.Type<S["fields"][Id]>
649
+ ) => Effect.Effect<Option.Option<S["Type"]>, never, S["Context"] | Schema.Schema.Context<S["fields"][Id]>>
650
+ readonly delete: (
651
+ id: Schema.Schema.Type<S["fields"][Id]>
652
+ ) => Effect.Effect<void, never, Schema.Schema.Context<S["fields"][Id]>>
653
+ },
654
+ never,
655
+ SqlClient
656
+ > =>
657
+ Effect.gen(function*() {
658
+ const sql = yield* SqlClient
659
+ const idSchema = Model.fields[options.idColumn] as Schema.Schema.Any
660
+ const idColumn = options.idColumn as string
661
+ const versionColumn = options.versionColumn
662
+
663
+ // TODO: insert version automatically...
664
+ // I guess we should hide the versionColumn and insert it in the schema instead
665
+ const insertSchema = SqlSchema.single({
666
+ Request: Model.insert,
667
+ Result: Model,
668
+ execute: (request) =>
669
+ sql.onDialectOrElse({
670
+ mysql: () =>
671
+ sql`insert into ${sql(options.tableName)} ${sql.insert(request)};
672
+ select * from ${sql(options.tableName)} where ${sql(idColumn)} = LAST_INSERT_ID();`
673
+ .unprepared
674
+ .pipe(
675
+ Effect.map(([, results]) => results as any)
676
+ ),
677
+ orElse: () => sql`insert into ${sql(options.tableName)} ${sql.insert(request).returning("*")}`
678
+ })
679
+ })
680
+ const insert = (
681
+ insert: S["insert"]["Type"]
682
+ ): Effect.Effect<S["Type"], never, S["Context"] | S["insert"]["Context"]> =>
683
+ insertSchema(insert).pipe(
684
+ Effect.orDie,
685
+ Effect.withSpan(`${options.spanPrefix}.insert`, {
686
+ captureStackTrace: false,
687
+ attributes: { insert }
688
+ })
689
+ ) as any
690
+
691
+ const insertVoidSchema = SqlSchema.void({
692
+ Request: Model.insert,
693
+ execute: (request) => sql`insert into ${sql(options.tableName)} ${sql.insert(request)}`
694
+ })
695
+ const insertVoid = (
696
+ insert: S["insert"]["Type"]
697
+ ): Effect.Effect<void, never, S["Context"] | S["insert"]["Context"]> =>
698
+ insertVoidSchema(insert).pipe(
699
+ Effect.orDie,
700
+ Effect.withSpan(`${options.spanPrefix}.insertVoid`, {
701
+ captureStackTrace: false,
702
+ attributes: { insert }
703
+ })
704
+ ) as any
705
+
706
+ const updateSchema = SqlSchema.single({
707
+ Request: Model.update,
708
+ Result: Model,
709
+ execute: versionColumn
710
+ ? (request) =>
711
+ sql.onDialectOrElse({
712
+ mysql: () =>
713
+ sql`update ${sql(options.tableName)} set ${
714
+ sql.update({ ...request, [versionColumn]: randomUUID() }, [idColumn])
715
+ } where ${sql(idColumn)} = ${request[idColumn]} and ${sql(versionColumn)} = ${request[versionColumn]};
716
+ select * from ${sql(options.tableName)} where ${sql(idColumn)} = ${request[idColumn]};`
717
+ .unprepared
718
+ .pipe(
719
+ Effect.map(([, results]) => results as any)
720
+ ),
721
+ orElse: () =>
722
+ sql`update ${sql(options.tableName)} set ${
723
+ sql.update({ ...request, [versionColumn]: randomUUID() }, [idColumn])
724
+ } where ${sql(idColumn)} = ${request[idColumn]} and ${sql(versionColumn)} = ${
725
+ request[versionColumn]
726
+ } returning *`
727
+ })
728
+ : (request) =>
729
+ sql.onDialectOrElse({
730
+ mysql: () =>
731
+ sql`update ${sql(options.tableName)} set ${sql.update(request, [idColumn])} where ${sql(idColumn)} = ${
732
+ request[idColumn]
733
+ };
734
+ select * from ${sql(options.tableName)} where ${sql(idColumn)} = ${request[idColumn]};`
735
+ .unprepared
736
+ .pipe(
737
+ Effect.map(([, results]) => results as any)
738
+ ),
739
+ orElse: () =>
740
+ sql`update ${sql(options.tableName)} set ${sql.update(request, [idColumn])} where ${sql(idColumn)} = ${
741
+ request[idColumn]
742
+ } returning *`
743
+ })
744
+ })
745
+ const update = (
746
+ update: S["update"]["Type"]
747
+ ): Effect.Effect<S["Type"], never, S["Context"] | S["update"]["Context"]> =>
748
+ updateSchema(update).pipe(
749
+ Effect.orDie,
750
+ Effect.withSpan(`${options.spanPrefix}.update`, {
751
+ captureStackTrace: false,
752
+ attributes: { update }
753
+ })
754
+ ) as any
755
+
756
+ const updateVoidSchema = SqlSchema.void({
757
+ Request: Model.update,
758
+ execute: versionColumn
759
+ ? (request) =>
760
+ sql`update ${sql(options.tableName)} set ${
761
+ sql.update({ ...request, [versionColumn]: randomUUID() }, [idColumn])
762
+ } where ${sql(idColumn)} = ${request[idColumn]} and ${sql(versionColumn)} = ${request[versionColumn]}`
763
+ : (request) =>
764
+ sql`update ${sql(options.tableName)} set ${sql.update(request, [idColumn])} where ${sql(idColumn)} = ${
765
+ request[idColumn]
766
+ }`
767
+ })
768
+ const updateVoid = (
769
+ update: S["update"]["Type"]
770
+ ): Effect.Effect<void, never, S["Context"] | S["update"]["Context"]> =>
771
+ updateVoidSchema(update).pipe(
772
+ Effect.orDie,
773
+ Effect.withSpan(`${options.spanPrefix}.updateVoid`, {
774
+ captureStackTrace: false,
775
+ attributes: { update }
776
+ })
777
+ ) as any
778
+
779
+ const findByIdSchema = SqlSchema.findOne({
780
+ Request: idSchema,
781
+ Result: Model,
782
+ execute: (id) => sql`select * from ${sql(options.tableName)} where ${sql(idColumn)} = ${id}`
783
+ })
784
+ const findById = (
785
+ id: Schema.Schema.Type<S["fields"][Id]>
786
+ ): Effect.Effect<Option.Option<S["Type"]>, never, S["Context"] | Schema.Schema.Context<S["fields"][Id]>> =>
787
+ findByIdSchema(id).pipe(
788
+ Effect.orDie,
789
+ Effect.withSpan(`${options.spanPrefix}.findById`, {
790
+ captureStackTrace: false,
791
+ attributes: { id }
792
+ })
793
+ ) as any
794
+
795
+ const deleteSchema = SqlSchema.void({
796
+ Request: idSchema,
797
+ execute: (id) => sql`delete from ${sql(options.tableName)} where ${sql(idColumn)} = ${id}`
798
+ })
799
+ const delete_ = (
800
+ id: Schema.Schema.Type<S["fields"][Id]>
801
+ ): Effect.Effect<void, never, Schema.Schema.Context<S["fields"][Id]>> =>
802
+ deleteSchema(id).pipe(
803
+ Effect.orDie,
804
+ Effect.withSpan(`${options.spanPrefix}.delete`, {
805
+ captureStackTrace: false,
806
+ attributes: { id }
807
+ })
808
+ ) as any
809
+
810
+ return { insert, insertVoid, update, updateVoid, findById, delete: delete_ } as const
811
+ })
812
+
813
+ /**
814
+ * Create some simple data loaders from a model.
815
+ *
816
+ * @since 1.0.0
817
+ * @category repository
818
+ */
819
+ export const makeDataLoaders = <
820
+ S extends AnyNoContext,
821
+ Id extends (keyof S["Type"]) & (keyof S["update"]["Type"]) & (keyof S["fields"])
822
+ >(
823
+ Model: S,
824
+ options: {
825
+ readonly tableName: string
826
+ readonly spanPrefix: string
827
+ readonly idColumn: Id
828
+ readonly window: DurationInput
829
+ readonly maxBatchSize?: number | undefined
830
+ }
831
+ ): Effect.Effect<
832
+ {
833
+ readonly insert: (insert: S["insert"]["Type"]) => Effect.Effect<S["Type"]>
834
+ readonly insertVoid: (insert: S["insert"]["Type"]) => Effect.Effect<void>
835
+ readonly findById: (id: Schema.Schema.Type<S["fields"][Id]>) => Effect.Effect<Option.Option<S["Type"]>>
836
+ readonly delete: (id: Schema.Schema.Type<S["fields"][Id]>) => Effect.Effect<void>
837
+ },
838
+ never,
839
+ SqlClient | Scope
840
+ > =>
841
+ Effect.gen(function*() {
842
+ const sql = yield* SqlClient
843
+ const idSchema = Model.fields[options.idColumn] as Schema.Schema.Any
844
+ const idColumn = options.idColumn as string
845
+
846
+ const insertResolver = yield* SqlResolver.ordered(`${options.spanPrefix}/insert`, {
847
+ Request: Model.insert,
848
+ Result: Model,
849
+ execute: (request) =>
850
+ sql.onDialectOrElse({
851
+ mysql: () =>
852
+ Effect.forEach(request, (request) =>
853
+ sql`insert into ${sql(options.tableName)} ${sql.insert(request)};
854
+ select * from ${sql(options.tableName)} where ${sql(idColumn)} = LAST_INSERT_ID();`
855
+ .unprepared
856
+ .pipe(
857
+ Effect.map(([, results]) => results as any)
858
+ ), { concurrency: 10 }),
859
+ orElse: () => sql`insert into ${sql(options.tableName)} ${sql.insert(request).returning("*")}`
860
+ })
861
+ })
862
+ const insertLoader = yield* RRX.dataLoader(insertResolver, {
863
+ window: options.window,
864
+ maxBatchSize: options.maxBatchSize!
865
+ })
866
+ const insertExecute = insertResolver.makeExecute(insertLoader)
867
+ const insert = (
868
+ insert: S["insert"]["Type"]
869
+ ): Effect.Effect<S["Type"], never, S["Context"] | S["insert"]["Context"]> =>
870
+ insertExecute(insert).pipe(
871
+ Effect.orDie,
872
+ Effect.withSpan(`${options.spanPrefix}.insert`, {
873
+ captureStackTrace: false,
874
+ attributes: { insert }
875
+ })
876
+ )
877
+
878
+ const insertVoidResolver = yield* SqlResolver.void(`${options.spanPrefix}/insertVoid`, {
879
+ Request: Model.insert,
880
+ execute: (request) => sql`insert into ${sql(options.tableName)} ${sql.insert(request)}`
881
+ })
882
+ const insertVoidLoader = yield* RRX.dataLoader(insertVoidResolver, {
883
+ window: options.window,
884
+ maxBatchSize: options.maxBatchSize!
885
+ })
886
+ const insertVoidExecute = insertVoidResolver.makeExecute(insertVoidLoader)
887
+ const insertVoid = (
888
+ insert: S["insert"]["Type"]
889
+ ): Effect.Effect<void, never, S["Context"] | S["insert"]["Context"]> =>
890
+ insertVoidExecute(insert).pipe(
891
+ Effect.orDie,
892
+ Effect.withSpan(`${options.spanPrefix}.insertVoid`, {
893
+ captureStackTrace: false,
894
+ attributes: { insert }
895
+ })
896
+ )
897
+
898
+ const findByIdResolver = yield* SqlResolver.findById(`${options.spanPrefix}/findById`, {
899
+ Id: idSchema,
900
+ Result: Model,
901
+ ResultId(request) {
902
+ return request[idColumn]
903
+ },
904
+ execute: (ids) => sql`select * from ${sql(options.tableName)} where ${sql.in(idColumn, ids)}`
905
+ })
906
+ const findByIdLoader = yield* RRX.dataLoader(findByIdResolver, {
907
+ window: options.window,
908
+ maxBatchSize: options.maxBatchSize!
909
+ })
910
+ const findByIdExecute = findByIdResolver.makeExecute(findByIdLoader)
911
+ const findById = (id: Schema.Schema.Type<S["fields"][Id]>): Effect.Effect<Option.Option<S["Type"]>> =>
912
+ findByIdExecute(id).pipe(
913
+ Effect.orDie,
914
+ Effect.withSpan(`${options.spanPrefix}.findById`, {
915
+ captureStackTrace: false,
916
+ attributes: { id }
917
+ })
918
+ ) as any
919
+
920
+ const deleteResolver = yield* SqlResolver.void(`${options.spanPrefix}/delete`, {
921
+ Request: idSchema,
922
+ execute: (ids) => sql`delete from ${sql(options.tableName)} where ${sql.in(idColumn, ids)}`
923
+ })
924
+ const deleteLoader = yield* RRX.dataLoader(deleteResolver, {
925
+ window: options.window,
926
+ maxBatchSize: options.maxBatchSize!
927
+ })
928
+ const deleteExecute = deleteResolver.makeExecute(deleteLoader)
929
+ const delete_ = (id: Schema.Schema.Type<S["fields"][Id]>): Effect.Effect<void> =>
930
+ deleteExecute(id).pipe(
931
+ Effect.orDie,
932
+ Effect.withSpan(`${options.spanPrefix}.delete`, {
933
+ captureStackTrace: false,
934
+ attributes: { id }
935
+ })
936
+ ) as any
937
+
938
+ return { insert, insertVoid, findById, delete: delete_ } as const
939
+ })