effect-app 4.0.0-beta.99 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2028 -0
- package/dist/Array.d.ts +2 -1
- package/dist/Array.d.ts.map +1 -1
- package/dist/Array.js +4 -4
- package/dist/Chunk.d.ts +2 -2
- package/dist/Chunk.d.ts.map +1 -1
- 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/Effect.d.ts +3 -3
- package/dist/Effect.d.ts.map +1 -1
- package/dist/Effect.js +6 -9
- 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.map +1 -1
- 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 +611 -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 +33 -0
- package/dist/Model/dsl.d.ts.map +1 -0
- package/dist/Model/dsl.js +43 -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 +370 -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 -0
- package/dist/Option.d.ts.map +1 -1
- package/dist/Option.js +3 -1
- package/dist/Pure.d.ts +5 -3
- package/dist/Pure.d.ts.map +1 -1
- package/dist/Pure.js +5 -2
- 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 +297 -72
- package/dist/Schema/ext.d.ts.map +1 -1
- package/dist/Schema/ext.js +331 -67
- 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 +223 -6
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +189 -23
- 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 +3 -2
- 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/misc.d.ts +4 -1
- package/dist/_ext/misc.d.ts.map +1 -1
- package/dist/_ext/misc.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 +148 -29
- 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/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 -39
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +33 -37
- 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 +26 -266
- package/src/Array.ts +5 -5
- package/src/Chunk.ts +3 -3
- 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/Effect.ts +9 -12
- package/src/Emailer.ts +51 -0
- 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 +988 -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 +129 -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 +532 -0
- package/src/Model/query.ts +13 -0
- package/src/Model.ts +5 -0
- package/src/NonEmptySet.ts +6 -4
- package/src/Option.ts +2 -0
- package/src/Pure.ts +6 -3
- 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 +408 -91
- 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 +6 -2
- package/src/Store.ts +325 -0
- package/src/_ext/Array.ts +4 -2
- package/src/_ext/misc.ts +4 -1
- 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 +291 -126
- 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/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 -158
- package/src/validation.ts +2 -2
- package/src/withToast.ts +133 -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/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/eslint.config.mjs +0 -26
- package/src/Operations.ts +0 -55
- package/tsconfig.json.bak +0 -12
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import type { NonEmptyReadonlyArray } from "effect/Array"
|
|
2
|
+
import * as Ref from "effect/Ref"
|
|
3
|
+
import * as Stream from "effect/Stream"
|
|
4
|
+
import * as Context from "./Context.ts"
|
|
5
|
+
import * as Effect from "./Effect.ts"
|
|
6
|
+
import * as RequestScopedDependencies from "./RequestScopedDependencies.ts"
|
|
7
|
+
import * as S from "./Schema.ts"
|
|
8
|
+
|
|
9
|
+
export const DataDependency = S.Union([
|
|
10
|
+
S.Struct({
|
|
11
|
+
type: S.Literal("repo"),
|
|
12
|
+
name: S.String,
|
|
13
|
+
ids: S.optional(S.NonEmptyArray(S.String))
|
|
14
|
+
}),
|
|
15
|
+
S.Struct({
|
|
16
|
+
type: S.Literal("signal"),
|
|
17
|
+
name: S.String,
|
|
18
|
+
ids: S.optional(S.NonEmptyArray(S.String))
|
|
19
|
+
})
|
|
20
|
+
])
|
|
21
|
+
export type DataDependency = S.Schema.Type<typeof DataDependency>
|
|
22
|
+
|
|
23
|
+
export const DataDependencies = S.ReadonlySet(DataDependency)
|
|
24
|
+
export type DataDependencies = S.Schema.Type<typeof DataDependencies>
|
|
25
|
+
|
|
26
|
+
export const empty = (): DataDependencies => new Set()
|
|
27
|
+
|
|
28
|
+
export const DataDependencySet = S.Struct({
|
|
29
|
+
reads: DataDependencies,
|
|
30
|
+
writes: DataDependencies
|
|
31
|
+
})
|
|
32
|
+
export type DataDependencySet = S.Schema.Type<typeof DataDependencySet>
|
|
33
|
+
|
|
34
|
+
export interface DataDependencyRecorderService {
|
|
35
|
+
readonly read: (dependency: DataDependency) => Effect.Effect<void>
|
|
36
|
+
readonly write: (dependency: DataDependency) => Effect.Effect<void>
|
|
37
|
+
readonly get: Effect.Effect<DataDependencySet>
|
|
38
|
+
readonly drain: Effect.Effect<DataDependencySet>
|
|
39
|
+
readonly drainWrites: Effect.Effect<DataDependencies>
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const sameDomain = (a: DataDependency, b: DataDependency) => a.type === b.type && a.name === b.name
|
|
43
|
+
|
|
44
|
+
const dependenciesOverlap = (a: DataDependency, b: DataDependency) => {
|
|
45
|
+
if (!sameDomain(a, b)) return false
|
|
46
|
+
if (a.ids === undefined || b.ids === undefined) return true
|
|
47
|
+
return a.ids.some((id) => b.ids?.includes(id))
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const containsDependency = (dependencies: ReadonlySet<DataDependency>, dependency: DataDependency) => {
|
|
51
|
+
for (const _ of dependencies) {
|
|
52
|
+
if (dependenciesOverlap(_, dependency)) return true
|
|
53
|
+
}
|
|
54
|
+
return false
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const appendDependency = (dependency: DataDependency) => (dependencies: DataDependencies): DataDependencies => {
|
|
58
|
+
const existing = [...dependencies].find((_) => sameDomain(_, dependency))
|
|
59
|
+
if (existing === undefined) return new Set([...dependencies, dependency])
|
|
60
|
+
if (existing.ids === undefined) return dependencies
|
|
61
|
+
if (dependency.ids === undefined) {
|
|
62
|
+
return new Set([...dependencies].filter((_) => _ !== existing).concat(dependency))
|
|
63
|
+
}
|
|
64
|
+
const first = existing.ids[0]
|
|
65
|
+
const ids: NonEmptyReadonlyArray<string> = [
|
|
66
|
+
first,
|
|
67
|
+
...new Set([...existing.ids.slice(1), ...dependency.ids].filter((id) => id !== first))
|
|
68
|
+
]
|
|
69
|
+
if (ids.length === existing.ids.length) return dependencies
|
|
70
|
+
const merged = existing.type === "repo" ? repo(existing.name, ids) : signal(existing.name, ids)
|
|
71
|
+
return new Set([...dependencies].filter((_) => _ !== existing).concat(merged))
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export const merge = (...groups: ReadonlyArray<DataDependencies>): DataDependencies => {
|
|
75
|
+
let merged = empty()
|
|
76
|
+
for (const group of groups) {
|
|
77
|
+
for (const dependency of group) merged = appendDependency(dependency)(merged)
|
|
78
|
+
}
|
|
79
|
+
return merged
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export const DataDependencyRecorder = RequestScopedDependencies.make(
|
|
83
|
+
"effect-app/DataDependencyRecorder",
|
|
84
|
+
Effect.gen(function*() {
|
|
85
|
+
const readsRef = yield* Ref.make(empty())
|
|
86
|
+
const writesRef = yield* Ref.make(empty())
|
|
87
|
+
return makeDataDependencyRecorder(readsRef, writesRef)
|
|
88
|
+
})
|
|
89
|
+
)
|
|
90
|
+
export type DataDependencyRecorder = typeof DataDependencyRecorder
|
|
91
|
+
|
|
92
|
+
export type DataDependencyRecorderNotStartedError = RequestScopedDependencies.RequestScopedDependencyNotStartedError
|
|
93
|
+
|
|
94
|
+
export const getDataDependencyRecorder = DataDependencyRecorder.current
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Guarantee a recorder is in scope for `self`, providing a fresh one per request when none is
|
|
98
|
+
* (e.g. a top-level client call with no parent query/command tracking its dependencies). An
|
|
99
|
+
* existing recorder is inherited, so nested calls forward into their parent rather than shadow it.
|
|
100
|
+
*/
|
|
101
|
+
export const ensureDataDependencyRecorder = <A, E, R>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>
|
|
102
|
+
Effect.flatMap(
|
|
103
|
+
DataDependencyRecorder,
|
|
104
|
+
(current) => current === "root" ? Effect.provide(self, DataDependencyRecorder.layer) : self
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
/** Stream variant of {@link ensureDataDependencyRecorder}. */
|
|
108
|
+
export const ensureDataDependencyRecorderStream = <A, E, R>(self: Stream.Stream<A, E, R>): Stream.Stream<A, E, R> =>
|
|
109
|
+
Stream.unwrap(
|
|
110
|
+
Effect.map(
|
|
111
|
+
DataDependencyRecorder,
|
|
112
|
+
(current) => current === "root" ? Stream.provide(self, DataDependencyRecorder.layer) : self
|
|
113
|
+
)
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
export const makeDataDependencyRecorder = (
|
|
117
|
+
readsRef: Ref.Ref<DataDependencies>,
|
|
118
|
+
writesRef: Ref.Ref<DataDependencies>
|
|
119
|
+
): DataDependencyRecorderService => ({
|
|
120
|
+
read: (dependency) => Ref.update(readsRef, appendDependency(dependency)),
|
|
121
|
+
write: (dependency) => Ref.update(writesRef, appendDependency(dependency)),
|
|
122
|
+
get: Effect.all({
|
|
123
|
+
reads: Ref.get(readsRef),
|
|
124
|
+
writes: Ref.get(writesRef)
|
|
125
|
+
}),
|
|
126
|
+
drain: Effect.all({
|
|
127
|
+
reads: Ref.getAndSet(readsRef, empty()),
|
|
128
|
+
writes: Ref.getAndSet(writesRef, empty())
|
|
129
|
+
}),
|
|
130
|
+
drainWrites: Ref.getAndSet(writesRef, empty())
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
export const repo = (name: string, ids?: NonEmptyReadonlyArray<string>): DataDependency =>
|
|
134
|
+
ids === undefined ? { type: "repo", name } : { type: "repo", name, ids }
|
|
135
|
+
export const signal = (name: string, ids?: NonEmptyReadonlyArray<string>): DataDependency =>
|
|
136
|
+
ids === undefined ? { type: "signal", name } : { type: "signal", name, ids }
|
|
137
|
+
|
|
138
|
+
const RepoReadScope = Context.Reference<DataDependency | undefined>("effect-app/DataDependencies/RepoReadScope", {
|
|
139
|
+
defaultValue: () => undefined
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
export const readRepo = (name: string) =>
|
|
143
|
+
Effect.flatMap(
|
|
144
|
+
RepoReadScope,
|
|
145
|
+
(scope) => read(scope?.type === "repo" && scope.name === name ? scope : repo(name))
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
export const withRepoReadScope =
|
|
149
|
+
(name: string, ids: NonEmptyReadonlyArray<string>) =>
|
|
150
|
+
<A, E, R>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>
|
|
151
|
+
Effect.provideService(self, RepoReadScope, repo(name, ids))
|
|
152
|
+
|
|
153
|
+
export const QueryReadDependenciesMetaKey = "effect-app.query.readDependencies"
|
|
154
|
+
|
|
155
|
+
export const read = (dependency: DataDependency) =>
|
|
156
|
+
getDataDependencyRecorder.pipe(Effect.flatMap((_) => _.read(dependency)))
|
|
157
|
+
|
|
158
|
+
export const write = (dependency: DataDependency) =>
|
|
159
|
+
getDataDependencyRecorder.pipe(Effect.flatMap((_) => _.write(dependency)))
|
|
160
|
+
|
|
161
|
+
export const intersects = (
|
|
162
|
+
a: ReadonlySet<DataDependency>,
|
|
163
|
+
b: ReadonlySet<DataDependency>
|
|
164
|
+
) => {
|
|
165
|
+
for (const dependency of a) {
|
|
166
|
+
if (containsDependency(b, dependency)) return true
|
|
167
|
+
}
|
|
168
|
+
return false
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export const isNonEmpty = (dependencies: DataDependencies) => dependencies.size > 0
|
package/src/Effect.ts
CHANGED
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
/* eslint-disable prefer-destructuring */
|
|
3
3
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
4
4
|
|
|
5
|
-
import { Effect, Option, Ref } from "effect"
|
|
6
5
|
import * as Def from "effect/Deferred"
|
|
7
|
-
import * as
|
|
6
|
+
import * as Effect from "effect/Effect"
|
|
7
|
+
import * as Option from "effect/Option"
|
|
8
|
+
import * as Ref from "effect/Ref"
|
|
8
9
|
import type { Scope } from "effect/Scope"
|
|
9
10
|
import type { Semaphore } from "effect/Semaphore"
|
|
10
|
-
import type * as Context from "./Context.
|
|
11
|
-
import { curry } from "./Function.
|
|
12
|
-
import { typedKeysOf } from "./utils.
|
|
11
|
+
import type * as Context from "./Context.ts"
|
|
12
|
+
import { curry } from "./Function.ts"
|
|
13
|
+
import { typedKeysOf } from "./utils.ts"
|
|
13
14
|
|
|
14
15
|
export * from "effect/Effect"
|
|
15
16
|
// v4: Effect interface not re-exported by `export *` due to local binding collision
|
|
@@ -112,10 +113,6 @@ export function modifyWithPermitWithEffect<A>(ref: Ref.Ref<A>, semaphore: Semaph
|
|
|
112
113
|
)
|
|
113
114
|
}
|
|
114
115
|
|
|
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
116
|
type ServiceA<T> = T extends Effect.Effect<infer S, any, any> ? S
|
|
120
117
|
: T extends Context.Service<any, infer S> ? S
|
|
121
118
|
: never
|
|
@@ -141,6 +138,7 @@ type ValuesE<T> = T extends { [s: string]: infer S } ? ServiceE<S> : never
|
|
|
141
138
|
* const b = a({ str: "" }) // valid, but shouldn't be!
|
|
142
139
|
* ```
|
|
143
140
|
*/
|
|
141
|
+
// eslint-disable-next-line import/namespace
|
|
144
142
|
export interface EffectUnunified<R, E, A> extends Effect.Effect<R, E, A> {}
|
|
145
143
|
|
|
146
144
|
export type LowerFirst<S extends PropertyKey> = S extends `${infer First}${infer Rest}` ? `${Lowercase<First>}${Rest}`
|
|
@@ -155,11 +153,10 @@ export function allLower<T extends Record<string, Context.Service<any, any> | Ef
|
|
|
155
153
|
return Effect.all(
|
|
156
154
|
typedKeysOf(services).reduce((prev, cur) => {
|
|
157
155
|
const svc = services[cur]!
|
|
158
|
-
prev[((cur as string)[0]!.toLowerCase() + (cur as string).slice(1)) as unknown as LowerFirst<typeof cur>] =
|
|
159
|
-
"asEffect" in svc ? svc.asEffect() : svc
|
|
156
|
+
prev[((cur as string)[0]!.toLowerCase() + (cur as string).slice(1)) as unknown as LowerFirst<typeof cur>] = svc
|
|
160
157
|
return prev
|
|
161
158
|
}, {} as any),
|
|
162
|
-
{ concurrency: "
|
|
159
|
+
{ concurrency: "unbounded" }
|
|
163
160
|
) as any as Effect.Effect<LowerServices<T>, ValuesE<T>, ValuesR<T>>
|
|
164
161
|
}
|
|
165
162
|
|
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/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
|
+
}
|