effect-app 4.0.0-beta.99 → 4.0.1
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 +2042 -0
- package/dist/Array.d.ts +1 -17
- package/dist/Array.d.ts.map +1 -1
- package/dist/Array.js +3 -27
- package/dist/Chunk.d.ts +3 -27
- package/dist/Chunk.d.ts.map +1 -1
- package/dist/Chunk.js +4 -39
- package/dist/Config/SecretURL.d.ts +3 -1
- package/dist/Config/SecretURL.d.ts.map +1 -1
- package/dist/Config/SecretURL.js +3 -6
- package/dist/Config/internal/configSecretURL.d.ts.map +1 -1
- package/dist/Config/internal/configSecretURL.js +2 -2
- package/dist/Config.d.ts +0 -5
- package/dist/Config.d.ts.map +1 -1
- package/dist/Config.js +7 -5
- package/dist/ConfigProvider.d.ts +0 -37
- package/dist/ConfigProvider.d.ts.map +1 -1
- package/dist/ConfigProvider.js +5 -41
- package/dist/Context.d.ts +6 -4
- package/dist/Context.d.ts.map +1 -1
- package/dist/Context.js +4 -3
- package/dist/DataDependencies.d.ts +136 -0
- package/dist/DataDependencies.d.ts.map +1 -0
- package/dist/DataDependencies.js +112 -0
- package/dist/DateTime.d.ts +33 -0
- package/dist/DateTime.d.ts.map +1 -0
- package/dist/DateTime.js +18 -0
- package/dist/Effect.d.ts +3 -14
- package/dist/Effect.d.ts.map +1 -1
- package/dist/Effect.js +8 -26
- package/dist/Emailer.d.ts +51 -0
- package/dist/Emailer.d.ts.map +1 -0
- package/dist/Emailer.js +7 -0
- package/dist/Function.d.ts +0 -4
- package/dist/Function.d.ts.map +1 -1
- package/dist/Function.js +1 -7
- package/dist/Layer.d.ts +6 -3
- package/dist/Layer.d.ts.map +1 -1
- package/dist/Layer.js +3 -2
- package/dist/Model/Repository/Registry.d.ts +22 -0
- package/dist/Model/Repository/Registry.d.ts.map +1 -0
- package/dist/Model/Repository/Registry.js +18 -0
- package/dist/Model/Repository/dependency.d.ts +6 -0
- package/dist/Model/Repository/dependency.d.ts.map +1 -0
- package/dist/Model/Repository/dependency.js +29 -0
- package/dist/Model/Repository/ext.d.ts +60 -0
- package/dist/Model/Repository/ext.d.ts.map +1 -0
- package/dist/Model/Repository/ext.js +124 -0
- package/dist/Model/Repository/internal/internal.d.ts +68 -0
- package/dist/Model/Repository/internal/internal.d.ts.map +1 -0
- package/dist/Model/Repository/internal/internal.js +610 -0
- package/dist/Model/Repository/legacy.d.ts +21 -0
- package/dist/Model/Repository/legacy.d.ts.map +1 -0
- package/dist/Model/Repository/legacy.js +2 -0
- package/dist/Model/Repository/makeRepo.d.ts +71 -0
- package/dist/Model/Repository/makeRepo.d.ts.map +1 -0
- package/dist/Model/Repository/makeRepo.js +30 -0
- package/dist/Model/Repository/service.d.ts +121 -0
- package/dist/Model/Repository/service.d.ts.map +1 -0
- package/dist/Model/Repository/service.js +2 -0
- package/dist/Model/Repository/validation.d.ts +72 -0
- package/dist/Model/Repository/validation.d.ts.map +1 -0
- package/dist/Model/Repository/validation.js +39 -0
- package/dist/Model/Repository.d.ts +8 -0
- package/dist/Model/Repository.d.ts.map +1 -0
- package/dist/Model/Repository.js +8 -0
- package/dist/Model/dsl.d.ts +34 -0
- package/dist/Model/dsl.d.ts.map +1 -0
- package/dist/Model/dsl.js +45 -0
- package/dist/Model/filter/filterApi.d.ts +30 -0
- package/dist/Model/filter/filterApi.d.ts.map +1 -0
- package/dist/Model/filter/filterApi.js +2 -0
- package/dist/Model/filter/types/errors.d.ts +29 -0
- package/dist/Model/filter/types/errors.d.ts.map +1 -0
- package/dist/Model/filter/types/errors.js +2 -0
- package/dist/Model/filter/types/fields.d.ts +15 -0
- package/dist/Model/filter/types/fields.d.ts.map +1 -0
- package/dist/Model/filter/types/fields.js +2 -0
- package/dist/Model/filter/types/path/common.d.ts +316 -0
- package/dist/Model/filter/types/path/common.d.ts.map +1 -0
- package/dist/Model/filter/types/path/common.js +2 -0
- package/dist/Model/filter/types/path/eager.d.ts +94 -0
- package/dist/Model/filter/types/path/eager.d.ts.map +1 -0
- package/dist/Model/filter/types/path/eager.js +36 -0
- package/dist/Model/filter/types/path/index.d.ts +4 -0
- package/dist/Model/filter/types/path/index.d.ts.map +1 -0
- package/dist/Model/filter/types/path/index.js +3 -0
- package/dist/Model/filter/types/utils.d.ts +79 -0
- package/dist/Model/filter/types/utils.d.ts.map +1 -0
- package/dist/Model/filter/types/utils.js +2 -0
- package/dist/Model/filter/types/validator.d.ts +30 -0
- package/dist/Model/filter/types/validator.d.ts.map +1 -0
- package/dist/Model/filter/types/validator.js +2 -0
- package/dist/Model/filter/types.d.ts +5 -0
- package/dist/Model/filter/types.d.ts.map +1 -0
- package/dist/Model/filter/types.js +7 -0
- package/dist/Model/query/dsl.d.ts +630 -0
- package/dist/Model/query/dsl.d.ts.map +1 -0
- package/dist/Model/query/dsl.js +376 -0
- package/dist/Model/query/new-kid-interpreter.d.ts +132 -0
- package/dist/Model/query/new-kid-interpreter.d.ts.map +1 -0
- package/dist/Model/query/new-kid-interpreter.js +369 -0
- package/dist/Model/query.d.ts +15 -0
- package/dist/Model/query.d.ts.map +1 -0
- package/dist/Model/query.js +3 -0
- package/dist/Model.d.ts +6 -0
- package/dist/Model.d.ts.map +1 -0
- package/dist/Model.js +6 -0
- package/dist/NonEmptySet.d.ts +3 -1
- package/dist/NonEmptySet.d.ts.map +1 -1
- package/dist/NonEmptySet.js +2 -2
- package/dist/Option.d.ts +1 -2
- package/dist/Option.d.ts.map +1 -1
- package/dist/Option.js +3 -7
- package/dist/Pure.d.ts +9 -7
- package/dist/Pure.d.ts.map +1 -1
- package/dist/Pure.js +20 -17
- package/dist/QueueMaker.d.ts +11 -0
- package/dist/QueueMaker.d.ts.map +1 -0
- package/dist/QueueMaker.js +11 -0
- package/dist/RequestContext.d.ts +87 -0
- package/dist/RequestContext.d.ts.map +1 -0
- package/dist/RequestContext.js +45 -0
- package/dist/RequestScopedDependencies.d.ts +21 -0
- package/dist/RequestScopedDependencies.d.ts.map +1 -0
- package/dist/RequestScopedDependencies.js +16 -0
- package/dist/Schema/Class.d.ts +163 -16
- package/dist/Schema/Class.d.ts.map +1 -1
- package/dist/Schema/Class.js +193 -21
- package/dist/Schema/FastCheck.d.ts.map +1 -1
- package/dist/Schema/SchemaParser.d.ts +5 -0
- package/dist/Schema/SchemaParser.d.ts.map +1 -0
- package/dist/Schema/SchemaParser.js +6 -0
- package/dist/Schema/SpecialJsonSchema.d.ts +15 -2
- package/dist/Schema/SpecialJsonSchema.d.ts.map +1 -1
- package/dist/Schema/SpecialJsonSchema.js +70 -12
- package/dist/Schema/SpecialOpenApi.d.ts +1 -1
- package/dist/Schema/SpecialOpenApi.js +3 -3
- package/dist/Schema/brand.d.ts +5 -5
- package/dist/Schema/brand.d.ts.map +1 -1
- package/dist/Schema/brand.js +6 -4
- package/dist/Schema/email.d.ts.map +1 -1
- package/dist/Schema/email.js +8 -5
- package/dist/Schema/ext.d.ts +279 -75
- package/dist/Schema/ext.d.ts.map +1 -1
- package/dist/Schema/ext.js +330 -72
- package/dist/Schema/moreStrings.d.ts +82 -48
- package/dist/Schema/moreStrings.d.ts.map +1 -1
- package/dist/Schema/moreStrings.js +85 -51
- package/dist/Schema/numbers.d.ts +33 -21
- package/dist/Schema/numbers.d.ts.map +1 -1
- package/dist/Schema/numbers.js +64 -12
- package/dist/Schema/phoneNumber.d.ts.map +1 -1
- package/dist/Schema/phoneNumber.js +6 -6
- package/dist/Schema/schema.d.ts +1 -1
- package/dist/Schema/schema.d.ts.map +1 -1
- package/dist/Schema/schema.js +2 -2
- package/dist/Schema/strings.d.ts +18 -4
- package/dist/Schema/strings.d.ts.map +1 -1
- package/dist/Schema/strings.js +1 -5
- package/dist/Schema.d.ts +224 -7
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +190 -24
- package/dist/SchemaAST.d.ts +2 -0
- package/dist/SchemaAST.d.ts.map +1 -0
- package/dist/SchemaAST.js +2 -0
- package/dist/Set.d.ts +4 -1
- package/dist/Set.d.ts.map +1 -1
- package/dist/Set.js +8 -6
- package/dist/Store.d.ts +215 -0
- package/dist/Store.d.ts.map +1 -0
- package/dist/Store.js +113 -0
- package/dist/_ext/Array.d.ts +1 -1
- package/dist/_ext/Array.d.ts.map +1 -1
- package/dist/_ext/Array.js +4 -2
- package/dist/_ext/ord.ext.d.ts +2 -1
- package/dist/_ext/ord.ext.d.ts.map +1 -1
- package/dist/_ext/ord.ext.js +2 -2
- package/dist/builtin.d.ts +2 -14
- package/dist/builtin.d.ts.map +1 -1
- package/dist/builtin.js +1 -1
- package/dist/client/InvalidationKeys.d.ts +30 -0
- package/dist/client/InvalidationKeys.d.ts.map +1 -0
- package/dist/client/InvalidationKeys.js +37 -0
- package/dist/client/apiClientFactory.d.ts +13 -8
- package/dist/client/apiClientFactory.d.ts.map +1 -1
- package/dist/client/apiClientFactory.js +154 -34
- package/dist/client/clientFor.d.ts +48 -13
- package/dist/client/clientFor.d.ts.map +1 -1
- package/dist/client/clientFor.js +9 -1
- package/dist/client/errors.d.ts +74 -28
- package/dist/client/errors.d.ts.map +1 -1
- package/dist/client/errors.js +41 -10
- package/dist/client/makeClient.d.ts +487 -32
- package/dist/client/makeClient.d.ts.map +1 -1
- package/dist/client/makeClient.js +47 -21
- package/dist/client.d.ts +2 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +3 -1
- package/dist/faker.d.ts.map +1 -1
- package/dist/http/Request.d.ts +2 -2
- package/dist/http/Request.d.ts.map +1 -1
- package/dist/http/Request.js +2 -2
- package/dist/http/internal/lib.d.ts +12 -12
- package/dist/http/internal/lib.d.ts.map +1 -1
- package/dist/http/internal/lib.js +13 -13
- package/dist/ids.d.ts +32 -14
- package/dist/ids.d.ts.map +1 -1
- package/dist/ids.js +38 -7
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/middleware.d.ts +12 -6
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +13 -7
- package/dist/migration/nativeEffect.d.ts +21 -0
- package/dist/migration/nativeEffect.d.ts.map +1 -0
- package/dist/migration/nativeEffect.js +598 -0
- package/dist/rpc/Invalidation.d.ts +1124 -0
- package/dist/rpc/Invalidation.d.ts.map +1 -0
- package/dist/rpc/Invalidation.js +178 -0
- package/dist/rpc/MiddlewareMaker.d.ts +10 -7
- package/dist/rpc/MiddlewareMaker.d.ts.map +1 -1
- package/dist/rpc/MiddlewareMaker.js +57 -37
- package/dist/rpc/RpcContextMap.d.ts +2 -2
- package/dist/rpc/RpcContextMap.d.ts.map +1 -1
- package/dist/rpc/RpcMiddleware.d.ts +13 -10
- package/dist/rpc/RpcMiddleware.d.ts.map +1 -1
- package/dist/rpc/RpcMiddleware.js +2 -2
- package/dist/rpc.d.ts +1 -1
- package/dist/rpc.d.ts.map +1 -1
- package/dist/rpc.js +2 -2
- package/dist/runtime.d.ts +19 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +40 -0
- package/dist/setupRequest.d.ts +20 -0
- package/dist/setupRequest.d.ts.map +1 -0
- package/dist/setupRequest.js +72 -0
- package/dist/toast.d.ts +51 -0
- package/dist/toast.d.ts.map +1 -0
- package/dist/toast.js +34 -0
- package/dist/transform.d.ts +1 -1
- package/dist/transform.d.ts.map +1 -1
- package/dist/transform.js +4 -5
- package/dist/utils/effectify.d.ts +1 -1
- package/dist/utils/effectify.d.ts.map +1 -1
- package/dist/utils/effectify.js +2 -2
- package/dist/utils/extend.d.ts +6 -1
- package/dist/utils/extend.d.ts.map +1 -1
- package/dist/utils/extend.js +24 -6
- package/dist/utils/gen.d.ts +4 -4
- package/dist/utils/gen.d.ts.map +1 -1
- package/dist/utils/logLevel.d.ts +2 -2
- package/dist/utils/logLevel.d.ts.map +1 -1
- package/dist/utils/logger.d.ts +2 -1
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +2 -2
- package/dist/utils.d.ts +34 -45
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +33 -49
- package/dist/validation/validators.d.ts.map +1 -1
- package/dist/validation.d.ts.map +1 -1
- package/dist/withToast.d.ts +30 -0
- package/dist/withToast.d.ts.map +1 -0
- package/dist/withToast.js +64 -0
- package/package.json +25 -266
- package/src/Array.ts +7 -39
- package/src/Chunk.ts +6 -55
- package/src/Config/SecretURL.ts +6 -3
- package/src/Config/internal/configSecretURL.ts +2 -2
- package/src/Config.ts +6 -13
- package/src/ConfigProvider.ts +4 -47
- package/src/Context.ts +9 -8
- package/src/DataDependencies.ts +171 -0
- package/src/DateTime.ts +473 -0
- package/src/Effect.ts +10 -32
- package/src/Emailer.ts +51 -0
- package/src/Function.ts +0 -7
- package/src/Inputify.type.ts +1 -1
- package/src/Layer.ts +8 -5
- package/src/Model/Repository/Registry.ts +35 -0
- package/src/Model/Repository/dependency.ts +32 -0
- package/src/Model/Repository/ext.ts +374 -0
- package/src/Model/Repository/internal/internal.ts +987 -0
- package/src/Model/Repository/legacy.ts +29 -0
- package/src/Model/Repository/makeRepo.ts +168 -0
- package/src/Model/Repository/service.ts +676 -0
- package/src/Model/Repository/validation.ts +38 -0
- package/src/Model/Repository.ts +7 -0
- package/src/Model/dsl.ts +130 -0
- package/src/Model/filter/filterApi.ts +78 -0
- package/src/Model/filter/types/errors.ts +47 -0
- package/src/Model/filter/types/fields.ts +50 -0
- package/src/Model/filter/types/path/common.ts +404 -0
- package/src/Model/filter/types/path/eager.ts +329 -0
- package/src/Model/filter/types/path/index.ts +4 -0
- package/src/Model/filter/types/utils.ts +128 -0
- package/src/Model/filter/types/validator.ts +46 -0
- package/src/Model/filter/types.ts +6 -0
- package/src/Model/query/dsl.ts +3092 -0
- package/src/Model/query/new-kid-interpreter.ts +531 -0
- package/src/Model/query.ts +13 -0
- package/src/Model.ts +5 -0
- package/src/NonEmptySet.ts +6 -4
- package/src/Option.ts +3 -8
- package/src/Pure.ts +20 -17
- package/src/QueueMaker.ts +11 -0
- package/src/RequestContext.ts +48 -0
- package/src/RequestScopedDependencies.ts +45 -0
- package/src/Schema/Class.ts +550 -56
- package/src/Schema/SchemaParser.ts +12 -0
- package/src/Schema/SpecialJsonSchema.ts +75 -7
- package/src/Schema/SpecialOpenApi.ts +5 -5
- package/src/Schema/brand.ts +27 -12
- package/src/Schema/email.ts +9 -7
- package/src/Schema/ext.ts +405 -95
- package/src/Schema/moreStrings.ts +213 -88
- package/src/Schema/numbers.ts +107 -29
- package/src/Schema/phoneNumber.ts +8 -9
- package/src/Schema/schema.ts +1 -1
- package/src/Schema/strings.ts +23 -14
- package/src/Schema.ts +403 -46
- package/src/SchemaAST.ts +1 -0
- package/src/Set.ts +10 -5
- package/src/Store.ts +325 -0
- package/src/_ext/Array.ts +4 -2
- package/src/_ext/ord.ext.ts +2 -1
- package/src/builtin-json.d.ts +24 -0
- package/src/builtin.ts +16 -21
- package/src/client/InvalidationKeys.ts +55 -0
- package/src/client/apiClientFactory.ts +295 -129
- package/src/client/clientFor.ts +101 -33
- package/src/client/errors.ts +66 -20
- package/src/client/makeClient.ts +519 -60
- package/src/client.ts +6 -4
- package/src/http/Request.ts +4 -4
- package/src/http/internal/lib.ts +12 -12
- package/src/http.ts +1 -1
- package/src/ids.ts +45 -8
- package/src/index.ts +20 -16
- package/src/middleware.ts +12 -8
- package/src/migration/nativeEffect.ts +645 -0
- package/src/rpc/Invalidation.ts +278 -0
- package/src/rpc/MiddlewareMaker.ts +86 -79
- package/src/rpc/README.md +2 -2
- package/src/rpc/RpcContextMap.ts +4 -4
- package/src/rpc/RpcMiddleware.ts +18 -13
- package/src/rpc.ts +4 -4
- package/src/runtime.ts +57 -0
- package/src/setupRequest.ts +138 -0
- package/src/toast.ts +54 -0
- package/src/transform.ts +4 -4
- package/src/utils/effectify.ts +1 -1
- package/src/utils/extend.ts +24 -5
- package/src/utils/gen.ts +7 -7
- package/src/utils/logLevel.ts +1 -1
- package/src/utils/logger.ts +3 -2
- package/src/utils.ts +85 -175
- package/src/validation.ts +2 -2
- package/src/withToast.ts +133 -0
- package/test/dateTimeLocal.test.ts +55 -0
- package/test/dist/dateTimeLocal.test.d.ts.map +1 -0
- package/test/dist/nativeEffectMigration.test.d.ts.map +1 -0
- package/test/dist/rpc.test.d.ts.map +1 -1
- package/test/dist/rpcMiddlewareContext.test.d.ts.map +1 -0
- package/test/moreStrings.test.ts +55 -10
- package/test/nativeEffectMigration.test.ts +77 -0
- package/test/rpc.test.ts +28 -11
- package/test/rpcMiddlewareContext.test.ts +63 -0
- package/test/schema.test.ts +318 -55
- package/test/secretURL.test.ts +4 -1
- package/test/special.test.ts +663 -131
- package/test/utils.test.ts +5 -5
- package/tsconfig.base.json +6 -4
- package/tsconfig.json +9 -1
- package/tsconfig.src.json +29 -29
- package/tsconfig.test.json +2 -2
- package/dist/Operations.d.ts +0 -124
- package/dist/Operations.d.ts.map +0 -1
- package/dist/Operations.js +0 -29
- package/dist/_ext/date.d.ts +0 -15
- package/dist/_ext/date.d.ts.map +0 -1
- package/dist/_ext/date.js +0 -16
- package/dist/_ext/misc.d.ts +0 -14
- package/dist/_ext/misc.d.ts.map +0 -1
- package/dist/_ext/misc.js +0 -17
- package/eslint.config.mjs +0 -26
- package/src/Operations.ts +0 -55
- package/src/_ext/date.ts +0 -27
- package/src/_ext/misc.ts +0 -46
- package/tsconfig.json.bak +0 -12
package/src/Effect.ts
CHANGED
|
@@ -2,14 +2,13 @@
|
|
|
2
2
|
/* eslint-disable prefer-destructuring */
|
|
3
3
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
import * as
|
|
7
|
-
import * as
|
|
8
|
-
import type { Scope } from "effect/Scope"
|
|
5
|
+
import * as Effect from "effect/Effect"
|
|
6
|
+
import * as Option from "effect/Option"
|
|
7
|
+
import * as Ref from "effect/Ref"
|
|
9
8
|
import type { Semaphore } from "effect/Semaphore"
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import {
|
|
9
|
+
import * as Struct from "effect/Struct"
|
|
10
|
+
import type * as Context from "./Context.ts"
|
|
11
|
+
import { curry } from "./Function.ts"
|
|
13
12
|
|
|
14
13
|
export * from "effect/Effect"
|
|
15
14
|
// v4: Effect interface not re-exported by `export *` due to local binding collision
|
|
@@ -93,9 +92,6 @@ export function ifDiff<I, R, E, A>(n: I, orig: I) {
|
|
|
93
92
|
return (f: (i: I) => Effect.Effect<A, E, R>) => ifDiff_(n, orig, f)
|
|
94
93
|
}
|
|
95
94
|
|
|
96
|
-
// NOTE: await extension doesnt work via tsplus somehow
|
|
97
|
-
export const await_ = Def.await
|
|
98
|
-
|
|
99
95
|
/**
|
|
100
96
|
* Ref has atomic modify support if synchronous, for Effect we need a Semaphore.
|
|
101
97
|
*/
|
|
@@ -112,10 +108,6 @@ export function modifyWithPermitWithEffect<A>(ref: Ref.Ref<A>, semaphore: Semaph
|
|
|
112
108
|
)
|
|
113
109
|
}
|
|
114
110
|
|
|
115
|
-
export function joinAll<E, A>(fibers: Iterable<Fiber.Fiber<A, E>>): Effect.Effect<readonly A[], E> {
|
|
116
|
-
return Fiber.joinAll(fibers) as any
|
|
117
|
-
}
|
|
118
|
-
|
|
119
111
|
type ServiceA<T> = T extends Effect.Effect<infer S, any, any> ? S
|
|
120
112
|
: T extends Context.Service<any, infer S> ? S
|
|
121
113
|
: never
|
|
@@ -141,6 +133,7 @@ type ValuesE<T> = T extends { [s: string]: infer S } ? ServiceE<S> : never
|
|
|
141
133
|
* const b = a({ str: "" }) // valid, but shouldn't be!
|
|
142
134
|
* ```
|
|
143
135
|
*/
|
|
136
|
+
// eslint-disable-next-line import/namespace
|
|
144
137
|
export interface EffectUnunified<R, E, A> extends Effect.Effect<R, E, A> {}
|
|
145
138
|
|
|
146
139
|
export type LowerFirst<S extends PropertyKey> = S extends `${infer First}${infer Rest}` ? `${Lowercase<First>}${Rest}`
|
|
@@ -153,13 +146,12 @@ export function allLower<T extends Record<string, Context.Service<any, any> | Ef
|
|
|
153
146
|
services: T
|
|
154
147
|
) {
|
|
155
148
|
return Effect.all(
|
|
156
|
-
|
|
149
|
+
Struct.keys(services).reduce((prev, cur) => {
|
|
157
150
|
const svc = services[cur]!
|
|
158
|
-
prev[(
|
|
159
|
-
"asEffect" in svc ? svc.asEffect() : svc
|
|
151
|
+
prev[(cur[0]!.toLowerCase() + cur.slice(1)) as unknown as LowerFirst<typeof cur>] = svc
|
|
160
152
|
return prev
|
|
161
153
|
}, {} as any),
|
|
162
|
-
{ concurrency: "
|
|
154
|
+
{ concurrency: "unbounded" }
|
|
163
155
|
) as any as Effect.Effect<LowerServices<T>, ValuesE<T>, ValuesR<T>>
|
|
164
156
|
}
|
|
165
157
|
|
|
@@ -189,17 +181,3 @@ export function catchAllMap<E, A2>(f: (e: E) => A2) {
|
|
|
189
181
|
return <R, A>(self: Effect.Effect<A, E, R>): Effect.Effect<A2 | A, never, R> =>
|
|
190
182
|
Effect.catch(self, (err: E) => Effect.sync(() => f(err)))
|
|
191
183
|
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Annotates each log in this scope with the specified log annotation.
|
|
195
|
-
*/
|
|
196
|
-
export function annotateLogscoped(key: string, value: string): Effect.Effect<void, never, Scope> {
|
|
197
|
-
return Effect.annotateLogsScoped(key, value)
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Annotates each log in this scope with the specified log annotations.
|
|
202
|
-
*/
|
|
203
|
-
export function annotateLogsScoped(kvps: Record<string, string>): Effect.Effect<void, never, Scope> {
|
|
204
|
-
return Effect.annotateLogsScoped(kvps)
|
|
205
|
-
}
|
package/src/Emailer.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import * as Data from "effect/Data"
|
|
2
|
+
import type { NonEmptyReadonlyArray } from "./Array.ts"
|
|
3
|
+
import * as Context from "./Context.ts"
|
|
4
|
+
import type * as Effect from "./Effect.ts"
|
|
5
|
+
import type { Email } from "./Schema.ts"
|
|
6
|
+
|
|
7
|
+
export class SendMailError extends Data.TaggedError("SendMailError")<{
|
|
8
|
+
readonly raw: Error
|
|
9
|
+
}> {}
|
|
10
|
+
|
|
11
|
+
export class Emailer extends Context.Opaque<Emailer, {
|
|
12
|
+
sendMail: (msg: EmailMsgOptionalFrom) => Effect.Effect<void, SendMailError>
|
|
13
|
+
}>()("effect-app/Emailer") {}
|
|
14
|
+
|
|
15
|
+
export type EmailData = Email | {
|
|
16
|
+
name?: string
|
|
17
|
+
email: Email
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface EmailContentPart {
|
|
21
|
+
type: string
|
|
22
|
+
value: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type EmailRecipients = EmailData | NonEmptyReadonlyArray<EmailData>
|
|
26
|
+
|
|
27
|
+
export interface EmailMsgBase {
|
|
28
|
+
readonly to: EmailRecipients
|
|
29
|
+
readonly cc?: EmailRecipients
|
|
30
|
+
readonly bcc?: EmailRecipients
|
|
31
|
+
readonly from: EmailData
|
|
32
|
+
readonly replyTo?: EmailData
|
|
33
|
+
readonly subject?: string
|
|
34
|
+
/**
|
|
35
|
+
* should multiple `to` addresess be considered multiple emails?
|
|
36
|
+
* defaults to `true`, not to leak email addresses
|
|
37
|
+
*/
|
|
38
|
+
readonly isMultiple?: boolean
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export type EmailContent =
|
|
42
|
+
| { text: string }
|
|
43
|
+
| { html: string }
|
|
44
|
+
| { templateId: string }
|
|
45
|
+
| { content: NonEmptyReadonlyArray<EmailContentPart> }
|
|
46
|
+
|
|
47
|
+
export type EmailMsg =
|
|
48
|
+
& EmailMsgBase
|
|
49
|
+
& EmailContent
|
|
50
|
+
|
|
51
|
+
export type EmailMsgOptionalFrom = Omit<EmailMsgBase, "from"> & Partial<Pick<EmailMsg, "from">> & EmailContent
|
package/src/Function.ts
CHANGED
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
export * from "effect/Function"
|
|
2
2
|
export type { Predicate, Refinement } from "effect/Predicate"
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
* Construct tuples
|
|
6
|
-
*/
|
|
7
|
-
export function tuple<T extends ReadonlyArray<any>>(...t: T): Readonly<T> {
|
|
8
|
-
return t
|
|
9
|
-
}
|
|
10
|
-
|
|
11
4
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
12
5
|
|
|
13
6
|
export function tupledCurry<A, B, C>(f: (b: B) => (a: A) => C) {
|
package/src/Inputify.type.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { BuiltInObject } from "effect-app/utils"
|
|
2
2
|
|
|
3
|
-
import type { Primitive } from "./Widen.type.
|
|
3
|
+
import type { Primitive } from "./Widen.type.ts"
|
|
4
4
|
|
|
5
5
|
// Get rid of Date | string, and replace with Date | null
|
|
6
6
|
export type Inputify<T> = Date extends T ? string extends T ? Date | null
|
package/src/Layer.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import type * as Array from "effect/Array"
|
|
2
|
+
import * as Effect from "effect/Effect"
|
|
3
3
|
import { dual } from "effect/Function"
|
|
4
|
-
import
|
|
5
|
-
import
|
|
4
|
+
import * as Layer from "effect/Layer"
|
|
5
|
+
import type * as Scope from "effect/Scope"
|
|
6
|
+
import type * as Types from "effect/Types"
|
|
7
|
+
import type * as Context from "./Context.ts"
|
|
8
|
+
import { type EffectGenUtils } from "./utils/gen.ts"
|
|
6
9
|
|
|
7
10
|
export * from "effect/Layer"
|
|
8
11
|
|
|
@@ -12,7 +15,7 @@ type MakeEff<S, E, R> = {
|
|
|
12
15
|
readonly make: Effect.Effect<S, E, R>
|
|
13
16
|
}
|
|
14
17
|
type MakeGen<S, E = never, R = never> = {
|
|
15
|
-
readonly make: () => Generator<
|
|
18
|
+
readonly make: () => Generator<Effect.Effect<any, E, R>, S>
|
|
16
19
|
}
|
|
17
20
|
type MakeGenNo<S> = {
|
|
18
21
|
readonly make: () => Generator<unknown, S>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { DatabaseError } from "../../client/errors.ts"
|
|
2
|
+
import * as Context from "../../Context.ts"
|
|
3
|
+
import * as Effect from "../../Effect.ts"
|
|
4
|
+
|
|
5
|
+
export interface RegisteredRepository {
|
|
6
|
+
readonly seedNamespace: (namespace: string) => Effect.Effect<void, DatabaseError>
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const make = Effect.sync(() => {
|
|
10
|
+
const repos = new Map<string, RegisteredRepository>()
|
|
11
|
+
return {
|
|
12
|
+
register(modelName: string, repo: RegisteredRepository) {
|
|
13
|
+
repos.set(modelName, repo)
|
|
14
|
+
},
|
|
15
|
+
seedNamespace: (namespace: string) =>
|
|
16
|
+
Effect.suspend(() =>
|
|
17
|
+
Effect.forEach(
|
|
18
|
+
repos.values(),
|
|
19
|
+
(r) => r.seedNamespace(namespace),
|
|
20
|
+
{ concurrency: "unbounded", discard: true }
|
|
21
|
+
)
|
|
22
|
+
),
|
|
23
|
+
get entries(): ReadonlyMap<string, RegisteredRepository> {
|
|
24
|
+
return repos
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
export class RepositoryRegistry extends Context.Opaque<RepositoryRegistry, {
|
|
30
|
+
readonly register: (modelName: string, repo: RegisteredRepository) => void
|
|
31
|
+
readonly seedNamespace: (namespace: string) => Effect.Effect<void, DatabaseError>
|
|
32
|
+
readonly entries: ReadonlyMap<string, RegisteredRepository>
|
|
33
|
+
}>()("effect-app/RepositoryRegistry", { make }) {}
|
|
34
|
+
|
|
35
|
+
export const RepositoryRegistryLive = RepositoryRegistry.toLayer(RepositoryRegistry.make)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as S from "../../Schema.ts"
|
|
2
|
+
import * as SchemaAST from "../../SchemaAST.ts"
|
|
3
|
+
|
|
4
|
+
const RepositoryDependencyAnnotation = "effect-app/Repository/dependency"
|
|
5
|
+
|
|
6
|
+
/** Marks an encoded model field as an identity alias for repository invalidation. */
|
|
7
|
+
export const repositoryDependency = <Schema extends S.Top>(schema: Schema): Schema["Rebuild"] =>
|
|
8
|
+
S.annotateEncoded({ [RepositoryDependencyAnnotation]: true })(schema)
|
|
9
|
+
|
|
10
|
+
/** @internal */
|
|
11
|
+
export const repositoryDependencyPaths = (schema: S.Schema<unknown>): readonly string[] => {
|
|
12
|
+
const visit = (ast: SchemaAST.AST, path: readonly string[]): readonly string[] => {
|
|
13
|
+
const annotations = ast.checks?.at(-1)?.annotations ?? ast.annotations
|
|
14
|
+
if (annotations?.[RepositoryDependencyAnnotation] === true) return [path.join(".")]
|
|
15
|
+
if (SchemaAST.isDeclaration(ast)) return ast.typeParameters.flatMap((parameter) => visit(parameter, path))
|
|
16
|
+
if (SchemaAST.isUnion(ast)) return ast.types.flatMap((member) => visit(member, path))
|
|
17
|
+
if (SchemaAST.isObjects(ast)) {
|
|
18
|
+
return ast.propertySignatures.flatMap((property) =>
|
|
19
|
+
typeof property.name === "string" ? visit(property.type, [...path, property.name]) : []
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
if (SchemaAST.isArrays(ast)) {
|
|
23
|
+
return [
|
|
24
|
+
...ast.elements.flatMap((element, index) => visit(element, [...path, String(index)])),
|
|
25
|
+
...ast.rest.flatMap((element) => visit(element, [...path, "-1"]))
|
|
26
|
+
]
|
|
27
|
+
}
|
|
28
|
+
return []
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return [...new Set(visit(SchemaAST.toEncoded(schema.ast), []))]
|
|
32
|
+
}
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
|
3
|
+
import * as Exit from "effect/Exit"
|
|
4
|
+
import * as Request from "effect/Request"
|
|
5
|
+
import * as RequestResolver from "effect/RequestResolver"
|
|
6
|
+
import * as Array from "../../Array.ts"
|
|
7
|
+
import type { NonEmptyArray } from "../../Array.ts"
|
|
8
|
+
import { type DatabaseError, type InvalidStateError, NotFoundError, type OptimisticConcurrencyException } from "../../client/errors.ts"
|
|
9
|
+
import * as Effect from "../../Effect.ts"
|
|
10
|
+
import * as Option from "../../Option.ts"
|
|
11
|
+
import { type FixEnv, type PureEnv, runTerm } from "../../Pure.ts"
|
|
12
|
+
import { AnyPureDSL } from "../dsl.ts"
|
|
13
|
+
import type { FieldValues } from "../filter/types.ts"
|
|
14
|
+
import type { Query, QueryEnd, QueryWhere } from "../query.ts"
|
|
15
|
+
import * as Q from "../query.ts"
|
|
16
|
+
import type { Repository } from "./service.ts"
|
|
17
|
+
|
|
18
|
+
interface BatchOptions {
|
|
19
|
+
readonly batch?: true | number
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const asReadonlyArray = <T>(itemOrItems: T | ReadonlyArray<T>): ReadonlyArray<T> =>
|
|
23
|
+
globalThis.Array.isArray(itemOrItems)
|
|
24
|
+
? itemOrItems as ReadonlyArray<T>
|
|
25
|
+
: [itemOrItems as T]
|
|
26
|
+
|
|
27
|
+
const getBatchSize = (batch?: true | number) =>
|
|
28
|
+
batch === true
|
|
29
|
+
? 100
|
|
30
|
+
: typeof batch === "number" && Number.isFinite(batch) && batch > 0
|
|
31
|
+
? Math.floor(batch)
|
|
32
|
+
: undefined
|
|
33
|
+
|
|
34
|
+
export const extendRepo = <
|
|
35
|
+
T,
|
|
36
|
+
Encoded extends FieldValues,
|
|
37
|
+
Evt,
|
|
38
|
+
ItemType extends string,
|
|
39
|
+
IdKey extends keyof T & keyof Encoded,
|
|
40
|
+
RSchema,
|
|
41
|
+
RPublish,
|
|
42
|
+
RProvided = never
|
|
43
|
+
>(
|
|
44
|
+
repo: Repository<T, Encoded, Evt, ItemType, IdKey, RSchema, RPublish, RProvided>
|
|
45
|
+
) => {
|
|
46
|
+
const get = (id: T[IdKey]) =>
|
|
47
|
+
repo.find(id).pipe(
|
|
48
|
+
Effect.flatMap(Option.match({
|
|
49
|
+
onNone: () => Effect.fail(new NotFoundError<ItemType>({ type: repo.itemType, id })),
|
|
50
|
+
onSome: Effect.succeed
|
|
51
|
+
}))
|
|
52
|
+
)
|
|
53
|
+
function saveManyWithPure_<
|
|
54
|
+
R,
|
|
55
|
+
A,
|
|
56
|
+
E,
|
|
57
|
+
S1 extends T,
|
|
58
|
+
S2 extends T
|
|
59
|
+
>(
|
|
60
|
+
items: Iterable<S1>,
|
|
61
|
+
pure: Effect.Effect<A, E, FixEnv<R, Evt, readonly S1[], readonly S2[]>>
|
|
62
|
+
) {
|
|
63
|
+
return saveAllWithEffectInt(
|
|
64
|
+
runTerm(pure, [...items])
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function saveWithPure_<
|
|
69
|
+
R,
|
|
70
|
+
A,
|
|
71
|
+
E,
|
|
72
|
+
S1 extends T,
|
|
73
|
+
S2 extends T
|
|
74
|
+
>(
|
|
75
|
+
item: S1,
|
|
76
|
+
pure: Effect.Effect<A, E, FixEnv<R, Evt, S1, S2>>
|
|
77
|
+
) {
|
|
78
|
+
return saveAllWithEffectInt(
|
|
79
|
+
runTerm(pure, item)
|
|
80
|
+
.pipe(Effect
|
|
81
|
+
.map(([item, events, a]) => [[item], events, a]))
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function saveAllWithEffectInt<
|
|
86
|
+
P extends T,
|
|
87
|
+
R,
|
|
88
|
+
E,
|
|
89
|
+
A
|
|
90
|
+
>(
|
|
91
|
+
gen: Effect.Effect<readonly [Iterable<P>, Iterable<Evt>, A], E, R>
|
|
92
|
+
): Effect.Effect<
|
|
93
|
+
A,
|
|
94
|
+
E | InvalidStateError | OptimisticConcurrencyException | DatabaseError,
|
|
95
|
+
R | RSchema | RPublish
|
|
96
|
+
> {
|
|
97
|
+
return Effect.flatMap(gen, ([items, events, a]) => repo.saveAndPublish(items, events).pipe(Effect.map(() => a)))
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function saveManyWithPureBatched_<
|
|
101
|
+
R,
|
|
102
|
+
A,
|
|
103
|
+
E,
|
|
104
|
+
S1 extends T,
|
|
105
|
+
S2 extends T
|
|
106
|
+
>(
|
|
107
|
+
items: Iterable<S1>,
|
|
108
|
+
pure: Effect.Effect<A, E, FixEnv<R, Evt, readonly S1[], readonly S2[]>>,
|
|
109
|
+
batchSize = 100
|
|
110
|
+
) {
|
|
111
|
+
return Effect.forEach(
|
|
112
|
+
Array.chunksOf(items, batchSize),
|
|
113
|
+
(batch) =>
|
|
114
|
+
saveAllWithEffectInt(
|
|
115
|
+
runTerm(pure, batch)
|
|
116
|
+
)
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
type PureStoreError = InvalidStateError | OptimisticConcurrencyException | DatabaseError
|
|
121
|
+
|
|
122
|
+
const queryAndSavePure: {
|
|
123
|
+
<A, E2, R2, T2 extends T>(
|
|
124
|
+
q: (
|
|
125
|
+
q: Query<Encoded>
|
|
126
|
+
) => QueryEnd<Encoded, "one">,
|
|
127
|
+
pure: Effect.Effect<A, E2, FixEnv<R2, Evt, T, T2>>
|
|
128
|
+
): Effect.Effect<
|
|
129
|
+
A,
|
|
130
|
+
PureStoreError | NotFoundError<ItemType> | E2,
|
|
131
|
+
| RSchema
|
|
132
|
+
| RPublish
|
|
133
|
+
| Exclude<R2, {
|
|
134
|
+
env: PureEnv<Evt, T, T2>
|
|
135
|
+
}>
|
|
136
|
+
>
|
|
137
|
+
<A, E2, R2, T2 extends T>(
|
|
138
|
+
q: (
|
|
139
|
+
q: Query<Encoded>
|
|
140
|
+
) =>
|
|
141
|
+
| Query<Encoded>
|
|
142
|
+
| QueryWhere<Encoded>
|
|
143
|
+
| QueryEnd<Encoded>,
|
|
144
|
+
pure: Effect.Effect<A, E2, FixEnv<R2, Evt, readonly T[], readonly T2[]>>
|
|
145
|
+
): Effect.Effect<
|
|
146
|
+
A,
|
|
147
|
+
PureStoreError | E2,
|
|
148
|
+
| RSchema
|
|
149
|
+
| RPublish
|
|
150
|
+
| Exclude<R2, {
|
|
151
|
+
env: PureEnv<Evt, readonly T[], readonly T2[]>
|
|
152
|
+
}>
|
|
153
|
+
>
|
|
154
|
+
<A, E2, R2, T2 extends T>(
|
|
155
|
+
q: (
|
|
156
|
+
q: Query<Encoded>
|
|
157
|
+
) =>
|
|
158
|
+
| Query<Encoded>
|
|
159
|
+
| QueryWhere<Encoded>
|
|
160
|
+
| QueryEnd<Encoded>,
|
|
161
|
+
pure: Effect.Effect<A, E2, FixEnv<R2, Evt, readonly T[], readonly T2[]>>,
|
|
162
|
+
batch: "batched" | number
|
|
163
|
+
): Effect.Effect<
|
|
164
|
+
A[],
|
|
165
|
+
PureStoreError | E2,
|
|
166
|
+
| RSchema
|
|
167
|
+
| RPublish
|
|
168
|
+
| Exclude<R2, {
|
|
169
|
+
env: PureEnv<Evt, readonly T[], readonly T2[]>
|
|
170
|
+
}>
|
|
171
|
+
>
|
|
172
|
+
} = ((q: any, pure: any, batch?: "batched" | number) =>
|
|
173
|
+
// Overload dispatch: query returns T | T[]; pure helpers are generic on item shape.
|
|
174
|
+
// Runtime path is query → pure term → saveAndPublish (which raises DatabaseError).
|
|
175
|
+
repo.query(q).pipe(
|
|
176
|
+
Effect.andThen((_) =>
|
|
177
|
+
Array.isArray(_)
|
|
178
|
+
? batch === undefined
|
|
179
|
+
? saveManyWithPure_(_ as T[], pure)
|
|
180
|
+
: saveManyWithPureBatched_(_ as T[], pure, batch === "batched" ? 100 : batch)
|
|
181
|
+
: saveWithPure_(_ as T, pure)
|
|
182
|
+
)
|
|
183
|
+
)) as typeof queryAndSavePure
|
|
184
|
+
|
|
185
|
+
const saveManyWithPure: {
|
|
186
|
+
<R, A, E, S1 extends T, S2 extends T>(
|
|
187
|
+
items: Iterable<S1>,
|
|
188
|
+
pure: Effect.Effect<A, E, FixEnv<R, Evt, readonly S1[], readonly S2[]>>
|
|
189
|
+
): Effect.Effect<
|
|
190
|
+
A,
|
|
191
|
+
PureStoreError | E,
|
|
192
|
+
| RSchema
|
|
193
|
+
| RPublish
|
|
194
|
+
| Exclude<R, {
|
|
195
|
+
env: PureEnv<Evt, readonly S1[], readonly S2[]>
|
|
196
|
+
}>
|
|
197
|
+
>
|
|
198
|
+
<R, A, E, S1 extends T, S2 extends T>(
|
|
199
|
+
items: Iterable<S1>,
|
|
200
|
+
pure: Effect.Effect<A, E, FixEnv<R, Evt, readonly S1[], readonly S2[]>>,
|
|
201
|
+
batch: "batched" | number
|
|
202
|
+
): Effect.Effect<
|
|
203
|
+
A[],
|
|
204
|
+
PureStoreError | E,
|
|
205
|
+
| RSchema
|
|
206
|
+
| RPublish
|
|
207
|
+
| Exclude<R, {
|
|
208
|
+
env: PureEnv<Evt, readonly S1[], readonly S2[]>
|
|
209
|
+
}>
|
|
210
|
+
>
|
|
211
|
+
} = ((items: Iterable<T>, pure: any, batch?: "batched" | number) =>
|
|
212
|
+
batch
|
|
213
|
+
? Effect.forEach(
|
|
214
|
+
Array.chunksOf([...items], batch === "batched" ? 100 : batch),
|
|
215
|
+
(batchItems) =>
|
|
216
|
+
saveAllWithEffectInt(
|
|
217
|
+
runTerm(pure, batchItems as any)
|
|
218
|
+
)
|
|
219
|
+
)
|
|
220
|
+
: saveAllWithEffectInt(
|
|
221
|
+
runTerm(pure, [...items] as any)
|
|
222
|
+
)) as typeof saveManyWithPure
|
|
223
|
+
|
|
224
|
+
const byIdAndSaveWithPure: {
|
|
225
|
+
<R, A, E, S2 extends T>(
|
|
226
|
+
id: T[IdKey],
|
|
227
|
+
pure: Effect.Effect<A, E, FixEnv<R, Evt, T, S2>>
|
|
228
|
+
): Effect.Effect<
|
|
229
|
+
A,
|
|
230
|
+
PureStoreError | NotFoundError<ItemType> | E,
|
|
231
|
+
| RSchema
|
|
232
|
+
| RPublish
|
|
233
|
+
| Exclude<R, {
|
|
234
|
+
env: PureEnv<Evt, T, S2>
|
|
235
|
+
}>
|
|
236
|
+
>
|
|
237
|
+
} = (id, pure) => get(id).pipe(Effect.flatMap((item) => saveWithPure_(item, pure)))
|
|
238
|
+
|
|
239
|
+
// query can raise DatabaseError; NotFound is completed per-entry. Dual package
|
|
240
|
+
// round-trips may still surface other store errors via failCause — keep channel honest.
|
|
241
|
+
type Req =
|
|
242
|
+
& Request.Request<T, NotFoundError<ItemType> | DatabaseError>
|
|
243
|
+
& { _tag: `Get${ItemType}`; id: T[IdKey] }
|
|
244
|
+
const _request = Request.tagged<Req>(`Get${repo.itemType}`)
|
|
245
|
+
|
|
246
|
+
const requestResolver = RequestResolver
|
|
247
|
+
.make((
|
|
248
|
+
entries: NonEmptyArray<Request.Entry<Req>>,
|
|
249
|
+
_key: unknown
|
|
250
|
+
) =>
|
|
251
|
+
(repo.query(Q.where(repo.idKey as any, "in" as any, entries.map((_) => _.request.id)) as any) as Effect.Effect<
|
|
252
|
+
readonly T[],
|
|
253
|
+
DatabaseError
|
|
254
|
+
>)
|
|
255
|
+
.pipe(
|
|
256
|
+
Effect.andThen((items) =>
|
|
257
|
+
Effect.forEach(entries, (entry) =>
|
|
258
|
+
Request.complete(
|
|
259
|
+
Array
|
|
260
|
+
.findFirst(items, (_) => _[repo.idKey] === entry.request.id)
|
|
261
|
+
.pipe(Option.match({
|
|
262
|
+
onNone: () => Exit.fail(new NotFoundError({ type: repo.itemType, id: entry.request.id })),
|
|
263
|
+
onSome: Exit.succeed
|
|
264
|
+
}))
|
|
265
|
+
)(entry), { discard: true })
|
|
266
|
+
),
|
|
267
|
+
Effect
|
|
268
|
+
.catchCause((cause) =>
|
|
269
|
+
Effect.forEach(entries, (entry) => Request.complete(Exit.failCause(cause))(entry), { discard: true })
|
|
270
|
+
)
|
|
271
|
+
)
|
|
272
|
+
)
|
|
273
|
+
.pipe(
|
|
274
|
+
RequestResolver.batchN(20)
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
const exts = {
|
|
278
|
+
request: (id: T[IdKey]) => Effect.request(_request({ id }), requestResolver),
|
|
279
|
+
get,
|
|
280
|
+
log: (evt: Evt) => AnyPureDSL.log(evt),
|
|
281
|
+
/**
|
|
282
|
+
* Enables chunked writes for large batches via `options.batch`.
|
|
283
|
+
* Note: batching breaks transactional properties because chunks are saved independently.
|
|
284
|
+
*/
|
|
285
|
+
save: (itemOrItems: T | ReadonlyArray<T>, options?: BatchOptions) => {
|
|
286
|
+
const items = asReadonlyArray(itemOrItems)
|
|
287
|
+
if (!Array.isReadonlyArrayNonEmpty(items)) {
|
|
288
|
+
return Effect.void
|
|
289
|
+
}
|
|
290
|
+
const batchSize = getBatchSize(options?.batch)
|
|
291
|
+
if (batchSize === undefined) {
|
|
292
|
+
return repo.saveAndPublish(items)
|
|
293
|
+
}
|
|
294
|
+
return Effect.forEach(
|
|
295
|
+
Array.chunksOf(items, batchSize),
|
|
296
|
+
(batch) => repo.saveAndPublish(batch),
|
|
297
|
+
{ discard: true }
|
|
298
|
+
)
|
|
299
|
+
},
|
|
300
|
+
saveWithEvents: (events: Iterable<Evt>) => (...items: NonEmptyArray<T>) => repo.saveAndPublish(items, events),
|
|
301
|
+
/**
|
|
302
|
+
* Enables chunked deletes for large batches via `options.batch`.
|
|
303
|
+
* Note: batching breaks transactional properties because chunks are removed independently.
|
|
304
|
+
*/
|
|
305
|
+
remove: (itemOrItems: T | ReadonlyArray<T>, options?: BatchOptions) => {
|
|
306
|
+
const items = asReadonlyArray(itemOrItems)
|
|
307
|
+
if (!Array.isReadonlyArrayNonEmpty(items)) {
|
|
308
|
+
return Effect.void as Effect.Effect<void, DatabaseError, RSchema | RPublish>
|
|
309
|
+
}
|
|
310
|
+
const batchSize = getBatchSize(options?.batch)
|
|
311
|
+
if (batchSize === undefined) {
|
|
312
|
+
return repo.removeAndPublish(items)
|
|
313
|
+
}
|
|
314
|
+
return Effect.forEach(
|
|
315
|
+
Array.chunksOf(items, batchSize),
|
|
316
|
+
(batch) => repo.removeAndPublish(batch),
|
|
317
|
+
{ discard: true }
|
|
318
|
+
)
|
|
319
|
+
},
|
|
320
|
+
/**
|
|
321
|
+
* Enables chunked deletes for large batches via `options.batch`.
|
|
322
|
+
* Note: batching breaks transactional properties because chunks are removed independently.
|
|
323
|
+
*/
|
|
324
|
+
removeById: (idOrIds: T[IdKey] | ReadonlyArray<T[IdKey]>, options?: BatchOptions) => {
|
|
325
|
+
const ids = asReadonlyArray(idOrIds)
|
|
326
|
+
if (!Array.isReadonlyArrayNonEmpty(ids)) {
|
|
327
|
+
return Effect.void as Effect.Effect<void, DatabaseError, RSchema>
|
|
328
|
+
}
|
|
329
|
+
const batchSize = getBatchSize(options?.batch)
|
|
330
|
+
if (batchSize === undefined) {
|
|
331
|
+
return repo.removeById(ids)
|
|
332
|
+
}
|
|
333
|
+
return Effect.forEach(
|
|
334
|
+
Array.chunksOf(ids, batchSize),
|
|
335
|
+
(batch) => repo.removeById(batch),
|
|
336
|
+
{ discard: true }
|
|
337
|
+
)
|
|
338
|
+
},
|
|
339
|
+
queryAndSavePure,
|
|
340
|
+
saveManyWithPure,
|
|
341
|
+
byIdAndSaveWithPure,
|
|
342
|
+
saveWithPure: <
|
|
343
|
+
R,
|
|
344
|
+
A,
|
|
345
|
+
E,
|
|
346
|
+
S1 extends T,
|
|
347
|
+
S2 extends T
|
|
348
|
+
>(
|
|
349
|
+
item: S1,
|
|
350
|
+
pure: Effect.Effect<A, E, FixEnv<R, Evt, S1, S2>>
|
|
351
|
+
) =>
|
|
352
|
+
saveAllWithEffectInt(
|
|
353
|
+
runTerm(pure, item)
|
|
354
|
+
.pipe(Effect.map(([item, events, a]) => [[item], events, a]))
|
|
355
|
+
)
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return {
|
|
359
|
+
...repo,
|
|
360
|
+
...exts
|
|
361
|
+
} as Repository<T, Encoded, Evt, ItemType, IdKey, RSchema, RPublish, RProvided> & typeof exts
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
365
|
+
export interface ExtendedRepository<
|
|
366
|
+
T,
|
|
367
|
+
Encoded extends FieldValues,
|
|
368
|
+
Evt,
|
|
369
|
+
ItemType extends string,
|
|
370
|
+
IdKey extends keyof T & keyof Encoded,
|
|
371
|
+
RSchema,
|
|
372
|
+
RPublish,
|
|
373
|
+
RProvided = never
|
|
374
|
+
> extends ReturnType<typeof extendRepo<T, Encoded, Evt, ItemType, IdKey, RSchema, RPublish, RProvided>> {}
|