@effect-app/infra 4.0.0-beta.15 → 4.0.0-beta.151
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 +992 -0
- package/dist/CUPS.d.ts +15 -7
- package/dist/CUPS.d.ts.map +1 -1
- package/dist/CUPS.js +10 -12
- package/dist/Emailer/Sendgrid.d.ts +14 -14
- package/dist/Emailer/Sendgrid.d.ts.map +1 -1
- package/dist/Emailer/Sendgrid.js +16 -15
- package/dist/Emailer/fake.d.ts +1 -1
- package/dist/Emailer/service.d.ts +9 -3
- package/dist/Emailer/service.d.ts.map +1 -1
- package/dist/Emailer/service.js +3 -3
- package/dist/Emailer.d.ts +1 -1
- package/dist/MainFiberSet.d.ts +5 -5
- package/dist/MainFiberSet.d.ts.map +1 -1
- package/dist/MainFiberSet.js +3 -3
- package/dist/Model/Repository/Registry.d.ts +20 -0
- package/dist/Model/Repository/Registry.d.ts.map +1 -0
- package/dist/Model/Repository/Registry.js +17 -0
- package/dist/Model/Repository/ext.d.ts +33 -15
- package/dist/Model/Repository/ext.d.ts.map +1 -1
- package/dist/Model/Repository/ext.js +54 -2
- package/dist/Model/Repository/internal/internal.d.ts +6 -6
- package/dist/Model/Repository/internal/internal.d.ts.map +1 -1
- package/dist/Model/Repository/internal/internal.js +35 -24
- package/dist/Model/Repository/legacy.d.ts +1 -1
- package/dist/Model/Repository/makeRepo.d.ts +7 -6
- package/dist/Model/Repository/makeRepo.d.ts.map +1 -1
- package/dist/Model/Repository/makeRepo.js +5 -1
- package/dist/Model/Repository/service.d.ts +28 -23
- package/dist/Model/Repository/service.d.ts.map +1 -1
- package/dist/Model/Repository/validation.d.ts +142 -17
- package/dist/Model/Repository/validation.d.ts.map +1 -1
- package/dist/Model/Repository/validation.js +5 -5
- package/dist/Model/Repository.d.ts +2 -1
- package/dist/Model/Repository.d.ts.map +1 -1
- package/dist/Model/Repository.js +2 -1
- package/dist/Model/dsl.d.ts +4 -4
- package/dist/Model/dsl.d.ts.map +1 -1
- package/dist/Model/filter/filterApi.d.ts +5 -5
- package/dist/Model/filter/filterApi.d.ts.map +1 -1
- package/dist/Model/filter/types/errors.d.ts +1 -1
- package/dist/Model/filter/types/fields.d.ts +1 -1
- package/dist/Model/filter/types/path/common.d.ts +1 -1
- package/dist/Model/filter/types/path/eager.d.ts +1 -1
- package/dist/Model/filter/types/path/eager.d.ts.map +1 -1
- package/dist/Model/filter/types/path/index.d.ts +1 -1
- package/dist/Model/filter/types/utils.d.ts +1 -1
- package/dist/Model/filter/types/validator.d.ts +1 -1
- package/dist/Model/filter/types.d.ts +1 -1
- package/dist/Model/query/dsl.d.ts +1 -1
- package/dist/Model/query/dsl.d.ts.map +1 -1
- package/dist/Model/query/new-kid-interpreter.d.ts +6 -6
- package/dist/Model/query/new-kid-interpreter.d.ts.map +1 -1
- package/dist/Model/query/new-kid-interpreter.js +3 -3
- package/dist/Model/query.d.ts +1 -1
- package/dist/Model.d.ts +2 -1
- package/dist/Model.d.ts.map +1 -1
- package/dist/Model.js +2 -1
- package/dist/Operations.d.ts +6 -6
- package/dist/Operations.d.ts.map +1 -1
- package/dist/Operations.js +56 -59
- package/dist/OperationsRepo.d.ts +11 -29
- package/dist/OperationsRepo.d.ts.map +1 -1
- package/dist/OperationsRepo.js +3 -3
- package/dist/QueueMaker/SQLQueue.d.ts +5 -7
- package/dist/QueueMaker/SQLQueue.d.ts.map +1 -1
- package/dist/QueueMaker/SQLQueue.js +105 -114
- package/dist/QueueMaker/errors.d.ts +2 -2
- package/dist/QueueMaker/errors.d.ts.map +1 -1
- package/dist/QueueMaker/memQueue.d.ts +7 -4
- package/dist/QueueMaker/memQueue.d.ts.map +1 -1
- package/dist/QueueMaker/memQueue.js +51 -62
- package/dist/QueueMaker/sbqueue.d.ts +6 -3
- package/dist/QueueMaker/sbqueue.d.ts.map +1 -1
- package/dist/QueueMaker/sbqueue.js +36 -52
- package/dist/QueueMaker/service.d.ts +1 -1
- package/dist/RequestContext.d.ts +114 -26
- package/dist/RequestContext.d.ts.map +1 -1
- package/dist/RequestContext.js +7 -7
- package/dist/RequestFiberSet.d.ts +7 -7
- package/dist/RequestFiberSet.d.ts.map +1 -1
- package/dist/RequestFiberSet.js +5 -5
- package/dist/Store/ContextMapContainer.d.ts +19 -3
- package/dist/Store/ContextMapContainer.d.ts.map +1 -1
- package/dist/Store/ContextMapContainer.js +13 -3
- package/dist/Store/Cosmos/query.d.ts +1 -1
- package/dist/Store/Cosmos/query.d.ts.map +1 -1
- package/dist/Store/Cosmos/query.js +8 -10
- package/dist/Store/Cosmos.d.ts +1 -1
- package/dist/Store/Cosmos.d.ts.map +1 -1
- package/dist/Store/Cosmos.js +308 -242
- package/dist/Store/Disk.d.ts +2 -2
- package/dist/Store/Disk.d.ts.map +1 -1
- package/dist/Store/Disk.js +25 -22
- package/dist/Store/Memory.d.ts +4 -4
- package/dist/Store/Memory.d.ts.map +1 -1
- package/dist/Store/Memory.js +27 -22
- package/dist/Store/SQL/Pg.d.ts +4 -0
- package/dist/Store/SQL/Pg.d.ts.map +1 -0
- package/dist/Store/SQL/Pg.js +189 -0
- package/dist/Store/SQL/query.d.ts +38 -0
- package/dist/Store/SQL/query.d.ts.map +1 -0
- package/dist/Store/SQL/query.js +367 -0
- package/dist/Store/SQL.d.ts +20 -0
- package/dist/Store/SQL.d.ts.map +1 -0
- package/dist/Store/SQL.js +381 -0
- package/dist/Store/codeFilter.d.ts +1 -1
- package/dist/Store/codeFilter.d.ts.map +1 -1
- package/dist/Store/codeFilter.js +2 -1
- package/dist/Store/index.d.ts +5 -2
- package/dist/Store/index.d.ts.map +1 -1
- package/dist/Store/index.js +15 -3
- package/dist/Store/service.d.ts +17 -6
- package/dist/Store/service.d.ts.map +1 -1
- package/dist/Store/service.js +24 -6
- package/dist/Store/utils.d.ts +1 -1
- package/dist/Store/utils.d.ts.map +1 -1
- package/dist/Store/utils.js +3 -4
- package/dist/Store.d.ts +1 -1
- package/dist/adapters/SQL/Model.d.ts +28 -42
- package/dist/adapters/SQL/Model.d.ts.map +1 -1
- package/dist/adapters/SQL/Model.js +2 -2
- package/dist/adapters/SQL.d.ts +1 -1
- package/dist/adapters/ServiceBus.d.ts +9 -9
- package/dist/adapters/ServiceBus.d.ts.map +1 -1
- package/dist/adapters/ServiceBus.js +13 -15
- package/dist/adapters/cosmos-client.d.ts +3 -3
- package/dist/adapters/cosmos-client.d.ts.map +1 -1
- package/dist/adapters/cosmos-client.js +3 -3
- package/dist/adapters/index.d.ts +8 -2
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +8 -2
- package/dist/adapters/logger.d.ts +1 -1
- package/dist/adapters/logger.d.ts.map +1 -1
- package/dist/adapters/memQueue.d.ts +3 -3
- package/dist/adapters/memQueue.d.ts.map +1 -1
- package/dist/adapters/memQueue.js +3 -3
- package/dist/adapters/mongo-client.d.ts +3 -3
- package/dist/adapters/mongo-client.d.ts.map +1 -1
- package/dist/adapters/mongo-client.js +3 -3
- package/dist/adapters/redis-client.d.ts +3 -3
- package/dist/adapters/redis-client.d.ts.map +1 -1
- package/dist/adapters/redis-client.js +3 -3
- package/dist/api/ContextProvider.d.ts +7 -7
- package/dist/api/ContextProvider.d.ts.map +1 -1
- package/dist/api/ContextProvider.js +6 -6
- package/dist/api/codec.d.ts +1 -1
- package/dist/api/internal/RequestContextMiddleware.d.ts +2 -2
- package/dist/api/internal/RequestContextMiddleware.d.ts.map +1 -1
- package/dist/api/internal/RequestContextMiddleware.js +2 -2
- package/dist/api/internal/auth.d.ts +44 -6
- package/dist/api/internal/auth.d.ts.map +1 -1
- package/dist/api/internal/auth.js +160 -29
- package/dist/api/internal/events.d.ts +3 -3
- package/dist/api/internal/events.d.ts.map +1 -1
- package/dist/api/internal/events.js +9 -7
- package/dist/api/internal/health.d.ts +1 -1
- package/dist/api/layerUtils.d.ts +6 -6
- package/dist/api/layerUtils.d.ts.map +1 -1
- package/dist/api/layerUtils.js +5 -5
- package/dist/api/middlewares.d.ts +1 -1
- package/dist/api/reportError.d.ts +1 -1
- package/dist/api/routing/middleware/RouterMiddleware.d.ts +4 -4
- package/dist/api/routing/middleware/RouterMiddleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/middleware.d.ts +39 -3
- package/dist/api/routing/middleware/middleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/middleware.js +45 -14
- package/dist/api/routing/middleware.d.ts +1 -2
- package/dist/api/routing/middleware.d.ts.map +1 -1
- package/dist/api/routing/middleware.js +1 -2
- package/dist/api/routing/schema/jwt.d.ts +1 -1
- package/dist/api/routing/schema/jwt.d.ts.map +1 -1
- package/dist/api/routing/tsort.d.ts +1 -1
- package/dist/api/routing/tsort.d.ts.map +1 -1
- package/dist/api/routing/utils.d.ts +3 -3
- package/dist/api/routing/utils.d.ts.map +1 -1
- package/dist/api/routing.d.ts +12 -14
- package/dist/api/routing.d.ts.map +1 -1
- package/dist/api/routing.js +17 -6
- package/dist/api/setupRequest.d.ts +8 -5
- package/dist/api/setupRequest.d.ts.map +1 -1
- package/dist/api/setupRequest.js +12 -7
- package/dist/api/util.d.ts +1 -1
- package/dist/arbs.d.ts +1 -1
- package/dist/arbs.d.ts.map +1 -1
- package/dist/arbs.js +5 -3
- package/dist/errorReporter.d.ts +4 -4
- package/dist/errorReporter.d.ts.map +1 -1
- package/dist/errorReporter.js +16 -23
- package/dist/errors.d.ts +1 -1
- package/dist/fileUtil.d.ts +1 -1
- package/dist/fileUtil.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/logger/jsonLogger.d.ts +1 -1
- package/dist/logger/logFmtLogger.d.ts +1 -1
- package/dist/logger/shared.d.ts +1 -1
- package/dist/logger/shared.js +2 -2
- package/dist/logger.d.ts +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/rateLimit.d.ts +9 -3
- package/dist/rateLimit.d.ts.map +1 -1
- package/dist/rateLimit.js +5 -11
- package/dist/test.d.ts +1 -1
- package/dist/test.d.ts.map +1 -1
- package/dist/vitest.d.ts +1 -1
- package/eslint.config.mjs +1 -1
- package/examples/query.ts +39 -35
- package/package.json +42 -28
- package/src/CUPS.ts +9 -11
- package/src/Emailer/Sendgrid.ts +17 -14
- package/src/Emailer/service.ts +8 -2
- package/src/MainFiberSet.ts +3 -3
- package/src/Model/Repository/Registry.ts +33 -0
- package/src/Model/Repository/ext.ts +93 -6
- package/src/Model/Repository/internal/internal.ts +87 -80
- package/src/Model/Repository/makeRepo.ts +12 -10
- package/src/Model/Repository/service.ts +31 -22
- package/src/Model/Repository/validation.ts +4 -4
- package/src/Model/Repository.ts +1 -0
- package/src/Model/dsl.ts +3 -3
- package/src/Model/query/new-kid-interpreter.ts +2 -2
- package/src/Model.ts +1 -0
- package/src/Operations.ts +78 -113
- package/src/OperationsRepo.ts +2 -2
- package/src/QueueMaker/SQLQueue.ts +121 -151
- package/src/QueueMaker/memQueue.ts +82 -103
- package/src/QueueMaker/sbqueue.ts +55 -85
- package/src/RequestContext.ts +7 -7
- package/src/RequestFiberSet.ts +4 -4
- package/src/Store/ContextMapContainer.ts +41 -2
- package/src/Store/Cosmos/query.ts +9 -11
- package/src/Store/Cosmos.ts +437 -343
- package/src/Store/Disk.ts +52 -49
- package/src/Store/Memory.ts +54 -48
- package/src/Store/SQL/Pg.ts +318 -0
- package/src/Store/SQL/query.ts +409 -0
- package/src/Store/SQL.ts +668 -0
- package/src/Store/codeFilter.ts +1 -0
- package/src/Store/index.ts +17 -2
- package/src/Store/service.ts +31 -7
- package/src/Store/utils.ts +23 -22
- package/src/adapters/SQL/Model.ts +10 -4
- package/src/adapters/ServiceBus.ts +111 -115
- package/src/adapters/cosmos-client.ts +2 -2
- package/src/adapters/index.ts +7 -0
- package/src/adapters/memQueue.ts +2 -2
- package/src/adapters/mongo-client.ts +2 -2
- package/src/adapters/redis-client.ts +2 -2
- package/src/api/ContextProvider.ts +11 -11
- package/src/api/internal/RequestContextMiddleware.ts +1 -1
- package/src/api/internal/auth.ts +246 -44
- package/src/api/internal/events.ts +12 -8
- package/src/api/layerUtils.ts +8 -8
- package/src/api/routing/middleware/RouterMiddleware.ts +4 -4
- package/src/api/routing/middleware/middleware.ts +52 -12
- package/src/api/routing/middleware.ts +0 -2
- package/src/api/routing.ts +21 -7
- package/src/api/setupRequest.ts +28 -8
- package/src/arbs.ts +4 -2
- package/src/errorReporter.ts +58 -72
- package/src/logger/shared.ts +1 -1
- package/src/rateLimit.ts +30 -22
- package/test/auth.test.ts +101 -0
- package/test/contextProvider.test.ts +11 -11
- package/test/controller.test.ts +18 -14
- package/test/dist/auth.test.d.ts.map +1 -0
- package/test/dist/contextProvider.test.d.ts.map +1 -1
- package/test/dist/controller.test.d.ts.map +1 -1
- package/test/dist/date-query.test.d.ts.map +1 -0
- package/test/dist/fixtures.d.ts +26 -12
- package/test/dist/fixtures.d.ts.map +1 -1
- package/test/dist/fixtures.js +12 -10
- package/test/dist/query.test.d.ts.map +1 -1
- package/test/dist/rawQuery.test.d.ts.map +1 -1
- package/test/dist/repository-ext.test.d.ts.map +1 -0
- package/test/dist/requires.test.d.ts.map +1 -1
- package/test/dist/router-generator.test.d.ts.map +1 -0
- package/test/dist/routing-interruptibility.test.d.ts.map +1 -0
- package/test/dist/rpc-multi-middleware.test.d.ts.map +1 -1
- package/test/dist/sql-store.test.d.ts.map +1 -0
- package/test/fixtures.ts +11 -9
- package/test/query.test.ts +212 -34
- package/test/rawQuery.test.ts +23 -19
- package/test/repository-ext.test.ts +58 -0
- package/test/requires.test.ts +6 -6
- package/test/router-generator.test.ts +180 -0
- package/test/routing-interruptibility.test.ts +63 -0
- package/test/rpc-multi-middleware.test.ts +78 -9
- package/test/sql-store.test.ts +1064 -0
- package/test/validateSample.test.ts +15 -12
- package/tsconfig.json +0 -1
package/src/CUPS.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type FileOptions, tempFile } from "@effect-app/infra/fileUtil"
|
|
2
2
|
import cp from "child_process"
|
|
3
|
-
import { Config, Effect, Layer, Option, Predicate, S
|
|
3
|
+
import { Config, Context, Effect, Layer, Option, Predicate, S } from "effect-app"
|
|
4
4
|
import { pretty } from "effect-app/utils"
|
|
5
5
|
import fs from "fs"
|
|
6
6
|
import os from "os"
|
|
@@ -74,15 +74,13 @@ function printBuffer(printer: PrinterConfig, options: string[]) {
|
|
|
74
74
|
)
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
})
|
|
85
|
-
}
|
|
77
|
+
const getAvailablePrinters = Effect.fnUntraced(function*(host?: string) {
|
|
78
|
+
const { stdout } = yield* exec(["lpstat", ...buildListArgs({ host }), "-s"].join(" "))
|
|
79
|
+
return [...stdout.matchAll(/device for (\w+):/g)]
|
|
80
|
+
.map((_) => _[1])
|
|
81
|
+
.filter(Predicate.isNotNullish)
|
|
82
|
+
.map((_) => S.NonEmptyString255(_))
|
|
83
|
+
})
|
|
86
84
|
|
|
87
85
|
function* buildListArgs(config?: { host?: string | undefined }) {
|
|
88
86
|
if (config?.host) {
|
|
@@ -100,7 +98,7 @@ export const CUPSConfig = Config.all({
|
|
|
100
98
|
)
|
|
101
99
|
})
|
|
102
100
|
|
|
103
|
-
export class CUPS extends
|
|
101
|
+
export class CUPS extends Context.Service<CUPS>()("effect-app/CUPS", {
|
|
104
102
|
make: Effect.gen(function*() {
|
|
105
103
|
const config = yield* CUPSConfig
|
|
106
104
|
const serverUrl = Option.getOrUndefined(config.server)
|
package/src/Emailer/Sendgrid.ts
CHANGED
|
@@ -7,7 +7,9 @@ import { inspect } from "util"
|
|
|
7
7
|
import { InfraLogger } from "../logger.js"
|
|
8
8
|
import { Emailer, type EmailMsg, type EmailMsgOptionalFrom, type SendgridConfig, SendMailError } from "./service.js"
|
|
9
9
|
|
|
10
|
-
const makeSendgrid = (
|
|
10
|
+
const makeSendgrid = (
|
|
11
|
+
{ apiKey, defaultFrom, defaultReplyTo, fakeMailAddress, realMail, subjectPrefix }: SendgridConfig
|
|
12
|
+
) =>
|
|
11
13
|
Effect.sync(() => {
|
|
12
14
|
sgMail.setApiKey(Redacted.value(apiKey))
|
|
13
15
|
|
|
@@ -18,7 +20,7 @@ const makeSendgrid = ({ apiKey, defaultFrom, defaultReplyTo, realMail, subjectPr
|
|
|
18
20
|
from: msg_.from ?? defaultFrom,
|
|
19
21
|
replyTo: msg_.replyTo ?? (msg_.from ? undefined : defaultReplyTo)
|
|
20
22
|
})
|
|
21
|
-
const render = renderMessage(!realMail)
|
|
23
|
+
const render = renderMessage(!realMail, fakeMailAddress)
|
|
22
24
|
|
|
23
25
|
const renderedMsg_ = render(msg)
|
|
24
26
|
const renderedMsg = {
|
|
@@ -68,23 +70,24 @@ export function Sendgrid(config: SendgridConfig) {
|
|
|
68
70
|
/**
|
|
69
71
|
* @hidden
|
|
70
72
|
*/
|
|
71
|
-
export function renderMessage(forceFake: boolean) {
|
|
73
|
+
export function renderMessage(forceFake: boolean, fakeMailAddress: string) {
|
|
72
74
|
let i = 0
|
|
73
75
|
const makeId = () => i++
|
|
76
|
+
const makeFakeEmail = () => fakeMailAddress.replace("{i}", String(makeId()))
|
|
74
77
|
return forceFake
|
|
75
78
|
? (msg: EmailMsg) =>
|
|
76
79
|
dropUndefinedT({
|
|
77
80
|
...msg,
|
|
78
|
-
to: msg.to && renderFake(msg.to,
|
|
79
|
-
cc: msg.cc && renderFake(msg.cc,
|
|
80
|
-
bcc: msg.bcc && renderFake(msg.bcc,
|
|
81
|
+
to: msg.to && renderFake(msg.to, makeFakeEmail),
|
|
82
|
+
cc: msg.cc && renderFake(msg.cc, makeFakeEmail),
|
|
83
|
+
bcc: msg.bcc && renderFake(msg.bcc, makeFakeEmail)
|
|
81
84
|
})
|
|
82
85
|
: (msg: EmailMsg) =>
|
|
83
86
|
dropUndefinedT({
|
|
84
87
|
...msg,
|
|
85
|
-
to: msg.to && renderFakeIfTest(msg.to,
|
|
86
|
-
cc: msg.cc && renderFakeIfTest(msg.cc,
|
|
87
|
-
bcc: msg.bcc && renderFakeIfTest(msg.bcc,
|
|
88
|
+
to: msg.to && renderFakeIfTest(msg.to, makeFakeEmail),
|
|
89
|
+
cc: msg.cc && renderFakeIfTest(msg.cc, makeFakeEmail),
|
|
90
|
+
bcc: msg.bcc && renderFakeIfTest(msg.bcc, makeFakeEmail)
|
|
88
91
|
})
|
|
89
92
|
}
|
|
90
93
|
|
|
@@ -100,10 +103,10 @@ export function isTestAddress(to: EmailData) {
|
|
|
100
103
|
)
|
|
101
104
|
}
|
|
102
105
|
|
|
103
|
-
function renderFake(addr: EmailData | readonly EmailData[],
|
|
106
|
+
function renderFake(addr: EmailData | readonly EmailData[], makeEmail: () => string) {
|
|
104
107
|
return {
|
|
105
108
|
name: renderMailData(addr),
|
|
106
|
-
email:
|
|
109
|
+
email: makeEmail()
|
|
107
110
|
}
|
|
108
111
|
}
|
|
109
112
|
const eq = Equivalence.mapInput(
|
|
@@ -117,14 +120,14 @@ function isEmailDataArray(md: EmailData | readonly EmailData[]): md is readonly
|
|
|
117
120
|
|
|
118
121
|
// TODO: should just not add any already added email address
|
|
119
122
|
// https://stackoverflow.com/a/53603076/11595834
|
|
120
|
-
function renderFakeIfTest(addr: EmailData | readonly EmailData[],
|
|
123
|
+
function renderFakeIfTest(addr: EmailData | readonly EmailData[], makeEmail: () => string) {
|
|
121
124
|
if (isEmailDataArray(addr)) {
|
|
122
125
|
return Array.dedupeWith(
|
|
123
|
-
addr.map((x) => (isTestAddress(x) ? renderFake(x,
|
|
126
|
+
addr.map((x) => (isTestAddress(x) ? renderFake(x, makeEmail) : x)),
|
|
124
127
|
eq
|
|
125
128
|
)
|
|
126
129
|
}
|
|
127
|
-
return isTestAddress(addr) ? renderFake(addr,
|
|
130
|
+
return isTestAddress(addr) ? renderFake(addr, makeEmail) : addr
|
|
128
131
|
}
|
|
129
132
|
|
|
130
133
|
function renderMailData(md: EmailData | readonly EmailData[]): string {
|
package/src/Emailer/service.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import type { MailContent, MailData } from "@sendgrid/helpers/classes/mail.js"
|
|
2
2
|
import type { ResponseError } from "@sendgrid/mail"
|
|
3
|
-
import { Data, type Effect, type NonEmptyReadonlyArray, type Redacted
|
|
3
|
+
import { Context, Data, type Effect, type NonEmptyReadonlyArray, type Redacted } from "effect-app"
|
|
4
4
|
import type { Email } from "effect-app/Schema"
|
|
5
5
|
|
|
6
6
|
export class SendMailError extends Data.TaggedError("SendMailError")<{
|
|
7
7
|
readonly raw: Error | ResponseError
|
|
8
8
|
}> {}
|
|
9
9
|
|
|
10
|
-
export class Emailer extends
|
|
10
|
+
export class Emailer extends Context.Opaque<Emailer, {
|
|
11
11
|
sendMail: (msg: EmailMsgOptionalFrom) => Effect.Effect<void, SendMailError>
|
|
12
12
|
}>()("effect-app/Emailer") {}
|
|
13
13
|
|
|
@@ -22,6 +22,12 @@ export interface SendgridConfig {
|
|
|
22
22
|
realMail: boolean
|
|
23
23
|
defaultFrom: EmailData
|
|
24
24
|
apiKey: Redacted.Redacted<string>
|
|
25
|
+
/**
|
|
26
|
+
* Email address used for fake/test recipients. Use `{i}` as a placeholder for an auto-incrementing index to ensure uniqueness.
|
|
27
|
+
*
|
|
28
|
+
* @example "test+{i}@example.com"
|
|
29
|
+
*/
|
|
30
|
+
fakeMailAddress: string
|
|
25
31
|
}
|
|
26
32
|
export type EmailTemplateMsg = MailData & { templateId: string }
|
|
27
33
|
|
package/src/MainFiberSet.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Effect, Fiber, FiberSet, Layer
|
|
2
|
-
|
|
1
|
+
import { Context, Effect, Fiber, FiberSet, Layer } from "effect-app"
|
|
2
|
+
|
|
3
3
|
import { InfraLogger } from "./logger.js"
|
|
4
4
|
import { reportNonInterruptedFailureCause } from "./QueueMaker/errors.js"
|
|
5
5
|
import { setRootParentSpan } from "./RequestFiberSet.js"
|
|
@@ -62,7 +62,7 @@ const make = Effect.gen(function*() {
|
|
|
62
62
|
* you should register these long running fibers in a FiberSet, and join them at the end of your main program.
|
|
63
63
|
* This way any errors will blow up the main program instead of fibers dying unknowingly.
|
|
64
64
|
*/
|
|
65
|
-
export class MainFiberSet extends
|
|
65
|
+
export class MainFiberSet extends Context.Service<MainFiberSet>()("MainFiberSet", { make }) {
|
|
66
66
|
static readonly Live = Layer.effect(this, this.make)
|
|
67
67
|
static readonly JoinLive = this.asEffect().pipe(
|
|
68
68
|
Effect.andThen((_) => _.join),
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Context, Effect } from "effect-app"
|
|
2
|
+
|
|
3
|
+
export interface RegisteredRepository {
|
|
4
|
+
readonly seedNamespace: (namespace: string) => Effect.Effect<void>
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const make = Effect.sync(() => {
|
|
8
|
+
const repos = new Map<string, RegisteredRepository>()
|
|
9
|
+
return {
|
|
10
|
+
register(modelName: string, repo: RegisteredRepository) {
|
|
11
|
+
repos.set(modelName, repo)
|
|
12
|
+
},
|
|
13
|
+
seedNamespace: (namespace: string) =>
|
|
14
|
+
Effect.suspend(() =>
|
|
15
|
+
Effect.forEach(
|
|
16
|
+
repos.values(),
|
|
17
|
+
(r) => r.seedNamespace(namespace),
|
|
18
|
+
{ concurrency: "unbounded", discard: true }
|
|
19
|
+
)
|
|
20
|
+
),
|
|
21
|
+
get entries(): ReadonlyMap<string, RegisteredRepository> {
|
|
22
|
+
return repos
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
export class RepositoryRegistry extends Context.Opaque<RepositoryRegistry, {
|
|
28
|
+
readonly register: (modelName: string, repo: RegisteredRepository) => void
|
|
29
|
+
readonly seedNamespace: (namespace: string) => Effect.Effect<void>
|
|
30
|
+
readonly entries: ReadonlyMap<string, RegisteredRepository>
|
|
31
|
+
}>()("effect-app/RepositoryRegistry", { make }) {}
|
|
32
|
+
|
|
33
|
+
export const RepositoryRegistryLive = RepositoryRegistry.toLayer(RepositoryRegistry.make)
|
|
@@ -9,6 +9,22 @@ import type { Query, QueryEnd, QueryWhere } from "../query.js"
|
|
|
9
9
|
import * as Q from "../query.js"
|
|
10
10
|
import type { Repository } from "./service.js"
|
|
11
11
|
|
|
12
|
+
interface BatchOptions {
|
|
13
|
+
readonly batch?: true | number
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const asReadonlyArray = <T>(itemOrItems: T | ReadonlyArray<T>): ReadonlyArray<T> =>
|
|
17
|
+
globalThis.Array.isArray(itemOrItems)
|
|
18
|
+
? itemOrItems as ReadonlyArray<T>
|
|
19
|
+
: [itemOrItems as T]
|
|
20
|
+
|
|
21
|
+
const getBatchSize = (batch?: true | number) =>
|
|
22
|
+
batch === true
|
|
23
|
+
? 100
|
|
24
|
+
: typeof batch === "number" && Number.isFinite(batch) && batch > 0
|
|
25
|
+
? Math.floor(batch)
|
|
26
|
+
: undefined
|
|
27
|
+
|
|
12
28
|
export const extendRepo = <
|
|
13
29
|
T,
|
|
14
30
|
Encoded extends FieldValues,
|
|
@@ -16,9 +32,10 @@ export const extendRepo = <
|
|
|
16
32
|
ItemType extends string,
|
|
17
33
|
IdKey extends keyof T & keyof Encoded,
|
|
18
34
|
RSchema,
|
|
19
|
-
RPublish
|
|
35
|
+
RPublish,
|
|
36
|
+
RProvided = never
|
|
20
37
|
>(
|
|
21
|
-
repo: Repository<T, Encoded, Evt, ItemType, IdKey, RSchema, RPublish>
|
|
38
|
+
repo: Repository<T, Encoded, Evt, ItemType, IdKey, RSchema, RPublish, RProvided>
|
|
22
39
|
) => {
|
|
23
40
|
const get = (id: T[IdKey]) =>
|
|
24
41
|
repo.find(id).pipe(
|
|
@@ -244,8 +261,77 @@ export const extendRepo = <
|
|
|
244
261
|
request: (id: T[IdKey]) => Effect.request(_request({ id }), requestResolver),
|
|
245
262
|
get,
|
|
246
263
|
log: (evt: Evt) => AnyPureDSL.log(evt),
|
|
247
|
-
|
|
264
|
+
/**
|
|
265
|
+
* Enables chunked writes for large batches via `options.batch`.
|
|
266
|
+
* Note: batching breaks transactional properties because chunks are saved independently.
|
|
267
|
+
*/
|
|
268
|
+
save: ((itemOrItems: T | ReadonlyArray<T>, options?: BatchOptions) => {
|
|
269
|
+
const items = asReadonlyArray(itemOrItems)
|
|
270
|
+
if (!Array.isReadonlyArrayNonEmpty(items)) {
|
|
271
|
+
return Effect.void
|
|
272
|
+
}
|
|
273
|
+
const batchSize = getBatchSize(options?.batch)
|
|
274
|
+
if (batchSize === undefined) {
|
|
275
|
+
return repo.saveAndPublish(items)
|
|
276
|
+
}
|
|
277
|
+
return Effect.forEach(
|
|
278
|
+
Array.chunksOf(items, batchSize),
|
|
279
|
+
(batch) => repo.saveAndPublish(batch),
|
|
280
|
+
{ discard: true }
|
|
281
|
+
)
|
|
282
|
+
}) as (
|
|
283
|
+
itemOrItems: T | ReadonlyArray<T>,
|
|
284
|
+
options?: BatchOptions
|
|
285
|
+
) => Effect.Effect<
|
|
286
|
+
void,
|
|
287
|
+
InvalidStateError | OptimisticConcurrencyException,
|
|
288
|
+
RSchema | RPublish
|
|
289
|
+
>,
|
|
248
290
|
saveWithEvents: (events: Iterable<Evt>) => (...items: NonEmptyArray<T>) => repo.saveAndPublish(items, events),
|
|
291
|
+
/**
|
|
292
|
+
* Enables chunked deletes for large batches via `options.batch`.
|
|
293
|
+
* Note: batching breaks transactional properties because chunks are removed independently.
|
|
294
|
+
*/
|
|
295
|
+
remove: ((itemOrItems: T | ReadonlyArray<T>, options?: BatchOptions) => {
|
|
296
|
+
const items = asReadonlyArray(itemOrItems)
|
|
297
|
+
if (!Array.isReadonlyArrayNonEmpty(items)) {
|
|
298
|
+
return Effect.void
|
|
299
|
+
}
|
|
300
|
+
const batchSize = getBatchSize(options?.batch)
|
|
301
|
+
if (batchSize === undefined) {
|
|
302
|
+
return repo.removeAndPublish(items)
|
|
303
|
+
}
|
|
304
|
+
return Effect.forEach(
|
|
305
|
+
Array.chunksOf(items, batchSize),
|
|
306
|
+
(batch) => repo.removeAndPublish(batch),
|
|
307
|
+
{ discard: true }
|
|
308
|
+
)
|
|
309
|
+
}) as (
|
|
310
|
+
itemOrItems: T | ReadonlyArray<T>,
|
|
311
|
+
options?: BatchOptions
|
|
312
|
+
) => Effect.Effect<void, never, RSchema | RPublish>,
|
|
313
|
+
/**
|
|
314
|
+
* Enables chunked deletes for large batches via `options.batch`.
|
|
315
|
+
* Note: batching breaks transactional properties because chunks are removed independently.
|
|
316
|
+
*/
|
|
317
|
+
removeById: ((idOrIds: T[IdKey] | ReadonlyArray<T[IdKey]>, options?: BatchOptions) => {
|
|
318
|
+
const ids = asReadonlyArray(idOrIds)
|
|
319
|
+
if (!Array.isReadonlyArrayNonEmpty(ids)) {
|
|
320
|
+
return Effect.void
|
|
321
|
+
}
|
|
322
|
+
const batchSize = getBatchSize(options?.batch)
|
|
323
|
+
if (batchSize === undefined) {
|
|
324
|
+
return repo.removeById(ids)
|
|
325
|
+
}
|
|
326
|
+
return Effect.forEach(
|
|
327
|
+
Array.chunksOf(ids, batchSize),
|
|
328
|
+
(batch) => repo.removeById(batch),
|
|
329
|
+
{ discard: true }
|
|
330
|
+
)
|
|
331
|
+
}) as (
|
|
332
|
+
idOrIds: T[IdKey] | ReadonlyArray<T[IdKey]>,
|
|
333
|
+
options?: BatchOptions
|
|
334
|
+
) => Effect.Effect<void, never, RSchema>,
|
|
249
335
|
queryAndSavePure,
|
|
250
336
|
saveManyWithPure,
|
|
251
337
|
byIdAndSaveWithPure,
|
|
@@ -268,7 +354,7 @@ export const extendRepo = <
|
|
|
268
354
|
return {
|
|
269
355
|
...repo,
|
|
270
356
|
...exts
|
|
271
|
-
} as Repository<T, Encoded, Evt, ItemType, IdKey, RSchema, RPublish> & typeof exts
|
|
357
|
+
} as Repository<T, Encoded, Evt, ItemType, IdKey, RSchema, RPublish, RProvided> & typeof exts
|
|
272
358
|
}
|
|
273
359
|
|
|
274
360
|
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
@@ -279,5 +365,6 @@ export interface ExtendedRepository<
|
|
|
279
365
|
ItemType extends string,
|
|
280
366
|
IdKey extends keyof T & keyof Encoded,
|
|
281
367
|
RSchema,
|
|
282
|
-
RPublish
|
|
283
|
-
|
|
368
|
+
RPublish,
|
|
369
|
+
RProvided = never
|
|
370
|
+
> extends ReturnType<typeof extendRepo<T, Encoded, Evt, ItemType, IdKey, RSchema, RPublish, RProvided>> {}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
|
|
3
|
-
import type
|
|
4
|
-
import { Array, Chunk, Effect, Equivalence, flow, type NonEmptyReadonlyArray, Option, pipe, Pipeable, PubSub, Result, S, SchemaAST, ServiceMap, Unify } from "effect-app"
|
|
2
|
+
|
|
3
|
+
import { Array, Chunk, Context, Effect, Equivalence, flow, type NonEmptyReadonlyArray, Option, pipe, Pipeable, PubSub, Result, S, SchemaAST, Unify } from "effect-app"
|
|
5
4
|
import { toNonEmptyArray } from "effect-app/Array"
|
|
6
5
|
import { NotFoundError } from "effect-app/client/errors"
|
|
7
6
|
import { flatMapOption } from "effect-app/Effect"
|
|
@@ -55,14 +54,14 @@ export function makeRepoInternal<
|
|
|
55
54
|
|
|
56
55
|
function make<RInitial = never, E = never, RPublish = never, RCtx = never>(
|
|
57
56
|
args: [Evt] extends [never] ? {
|
|
58
|
-
schemaContext?:
|
|
57
|
+
schemaContext?: Context.Context<RCtx>
|
|
59
58
|
makeInitial?: Effect.Effect<readonly T[], E, RInitial> | undefined
|
|
60
59
|
config?: Omit<StoreConfig<Encoded>, "partitionValue"> & {
|
|
61
60
|
partitionValue?: (e?: Encoded) => string
|
|
62
61
|
}
|
|
63
62
|
}
|
|
64
63
|
: {
|
|
65
|
-
schemaContext?:
|
|
64
|
+
schemaContext?: Context.Context<RCtx>
|
|
66
65
|
publishEvents: (evt: NonEmptyReadonlyArray<Evt>) => Effect.Effect<void, never, RPublish>
|
|
67
66
|
makeInitial?: Effect.Effect<readonly T[], E, RInitial> | undefined
|
|
68
67
|
config?: Omit<StoreConfig<Encoded>, "partitionValue"> & {
|
|
@@ -72,12 +71,12 @@ export function makeRepoInternal<
|
|
|
72
71
|
) {
|
|
73
72
|
return Effect
|
|
74
73
|
.gen(function*() {
|
|
75
|
-
const rctx:
|
|
74
|
+
const rctx: Context.Context<RCtx> = args.schemaContext ?? Context.empty() as any
|
|
76
75
|
const provideRctx = Effect.provide(rctx)
|
|
77
76
|
const encodeMany = flow(
|
|
78
77
|
S.encodeEffect(S.Array(schema)),
|
|
79
78
|
provideRctx,
|
|
80
|
-
Effect.withSpan("encodeMany", {}, { captureStackTrace: false })
|
|
79
|
+
Effect.withSpan("encodeMany", { attributes: { itemType: name } }, { captureStackTrace: false })
|
|
81
80
|
)
|
|
82
81
|
const decode = flow(S.decodeEffect(schema), provideRctx)
|
|
83
82
|
const decodeMany = flow(
|
|
@@ -113,11 +112,14 @@ export function makeRepoInternal<
|
|
|
113
112
|
let ast = _.ast
|
|
114
113
|
if (ast._tag === "Declaration") ast = ast.typeParameters[0]!
|
|
115
114
|
|
|
116
|
-
// In v4, to get the encoded (from) side of a schema, use SchemaAST.toEncoded
|
|
117
115
|
const pickIdFromAst = (a: SchemaAST.AST) => {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
116
|
+
// Unwrap Declaration (e.g. TaggedClass) to get the underlying Objects AST
|
|
117
|
+
let inner = a
|
|
118
|
+
if (inner._tag === "Declaration") inner = inner.typeParameters[0]!
|
|
119
|
+
// Pick from the original AST to preserve the full encoding chain (e.g. decodeTo transformations).
|
|
120
|
+
// Using toEncoded would lose transformation info needed to encode Type -> Encoded.
|
|
121
|
+
if (SchemaAST.isObjects(inner)) {
|
|
122
|
+
const field = inner.propertySignatures.find((_) => _.name === idKey)
|
|
121
123
|
if (field) {
|
|
122
124
|
return S.Struct({ [idKey]: S.make(field.type) }) as unknown as Codec<T, Encoded>
|
|
123
125
|
}
|
|
@@ -161,7 +163,7 @@ export function makeRepoInternal<
|
|
|
161
163
|
)
|
|
162
164
|
})
|
|
163
165
|
|
|
164
|
-
const find = Effect.fn("find")(function*(id: T[IdKey]) {
|
|
166
|
+
const find = Effect.fn("find", { attributes: { itemType: name } })(function*(id: T[IdKey]) {
|
|
165
167
|
yield* Effect.annotateCurrentSpan({ itemId: id })
|
|
166
168
|
|
|
167
169
|
return yield* flatMapOption(findE(id), (_) => Effect.orDie(decode(_)))
|
|
@@ -188,73 +190,76 @@ export function makeRepoInternal<
|
|
|
188
190
|
Effect.andThen(saveAllE)
|
|
189
191
|
)
|
|
190
192
|
|
|
191
|
-
const saveAndPublish = Effect.fn("saveAndPublish"
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
193
|
+
const saveAndPublish = Effect.fn("saveAndPublish", { attributes: { itemType: name } })(
|
|
194
|
+
function*(items: Iterable<T>, events: Iterable<Evt> = []) {
|
|
195
|
+
const it = Chunk.fromIterable(items)
|
|
196
|
+
const evts = [...events]
|
|
197
|
+
yield* Effect.annotateCurrentSpan({ itemIds: Chunk.map(it, (_) => _[idKey]), events: evts.length })
|
|
198
|
+
return yield* saveAll(it)
|
|
199
|
+
.pipe(
|
|
200
|
+
Effect.andThen(Effect.sync(() => toNonEmptyArray(evts))),
|
|
201
|
+
// TODO: for full consistency the events should be stored within the same database transaction, and then picked up.
|
|
202
|
+
(_) => flatMapOption(_, pub),
|
|
203
|
+
Effect.andThen(PubSub.publish(changeFeed, [Chunk.toArray(it), "save"] as [T[], "save" | "remove"])),
|
|
204
|
+
Effect.asVoid
|
|
205
|
+
)
|
|
206
|
+
}
|
|
207
|
+
)
|
|
204
208
|
|
|
205
|
-
const removeAndPublish = Effect.fn("removeAndPublish"
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
209
|
+
const removeAndPublish = Effect.fn("removeAndPublish", { attributes: { itemType: name } })(
|
|
210
|
+
function*(a: Iterable<T>, events: Iterable<Evt> = []) {
|
|
211
|
+
const { set } = yield* cms
|
|
212
|
+
const it = [...a]
|
|
213
|
+
const evts = [...events]
|
|
214
|
+
yield* Effect.annotateCurrentSpan({ itemIds: it.map((_) => _[idKey]), eventCount: evts.length })
|
|
215
|
+
const items = yield* encodeMany(it).pipe(Effect.orDie)
|
|
216
|
+
if (Array.isReadonlyArrayNonEmpty(items)) {
|
|
217
|
+
yield* store.batchRemove(
|
|
218
|
+
items.map((_) => (_[idKey])),
|
|
219
|
+
args.config?.partitionValue?.(items[0])
|
|
220
|
+
)
|
|
221
|
+
for (const e of items) {
|
|
222
|
+
set(e[idKey], undefined)
|
|
223
|
+
}
|
|
224
|
+
yield* Effect
|
|
225
|
+
.sync(() => toNonEmptyArray(evts))
|
|
226
|
+
// TODO: for full consistency the events should be stored within the same database transaction, and then picked up.
|
|
227
|
+
.pipe((_) => flatMapOption(_, pub))
|
|
223
228
|
|
|
224
|
-
|
|
229
|
+
yield* PubSub.publish(changeFeed, [it, "remove"] as [T[], "save" | "remove"])
|
|
230
|
+
}
|
|
225
231
|
}
|
|
226
|
-
|
|
232
|
+
)
|
|
227
233
|
|
|
228
|
-
const removeById = Effect.fn("removeById"
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
234
|
+
const removeById = Effect.fn("removeById", { attributes: { itemType: name } })(
|
|
235
|
+
function*(idOrIds: T[IdKey] | ReadonlyArray<T[IdKey]>) {
|
|
236
|
+
const ids = globalThis.Array.isArray(idOrIds)
|
|
237
|
+
? idOrIds as readonly T[IdKey][]
|
|
238
|
+
: [idOrIds as T[IdKey]]
|
|
239
|
+
if (!Array.isReadonlyArrayNonEmpty(ids)) {
|
|
240
|
+
return
|
|
241
|
+
}
|
|
242
|
+
const { set } = yield* cms
|
|
243
|
+
const eids = yield* Effect.forEach(ids, (_) => encodeIdOnly(_ as any)).pipe(Effect.orDie)
|
|
244
|
+
yield* Effect.annotateCurrentSpan({ itemIds: eids })
|
|
245
|
+
yield* store.batchRemove(eids)
|
|
246
|
+
for (const id of eids) {
|
|
247
|
+
set(id, undefined)
|
|
248
|
+
}
|
|
249
|
+
yield* PubSub.publish(changeFeed, [[], "remove"] as [T[], "save" | "remove"])
|
|
238
250
|
}
|
|
239
|
-
|
|
240
|
-
})
|
|
251
|
+
)
|
|
241
252
|
|
|
242
|
-
const parseMany = (items: readonly PM[])
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
.flatMap(cms, (cm) =>
|
|
253
|
-
S
|
|
254
|
-
.decodeEffect(S.Array(schema))(
|
|
255
|
-
items.map((_) => mapReverse(_, cm.set))
|
|
256
|
-
)
|
|
257
|
-
.pipe(Effect.orDie, Effect.withSpan("parseMany2", {}, { captureStackTrace: false })))
|
|
253
|
+
const parseMany = Effect.fn("parseMany", { attributes: { itemType: name } })(function*(items: readonly PM[]) {
|
|
254
|
+
const cm = yield* cms
|
|
255
|
+
return yield* decodeMany(items.map((_) => mapReverse(_, cm.set))).pipe(Effect.orDie)
|
|
256
|
+
})
|
|
257
|
+
const parseMany2 = Effect.fn("parseMany2", { attributes: { itemType: name } })(
|
|
258
|
+
function*<A, R>(items: readonly PM[], schema: S.Codec<A, Encoded, R>) {
|
|
259
|
+
const cm = yield* cms
|
|
260
|
+
return yield* S.decodeEffect(S.Array(schema))(items.map((_) => mapReverse(_, cm.set))).pipe(Effect.orDie)
|
|
261
|
+
}
|
|
262
|
+
)
|
|
258
263
|
const filter = <U extends keyof Encoded = keyof Encoded>(args: FilterArgs<Encoded, U>) =>
|
|
259
264
|
store
|
|
260
265
|
.filter(
|
|
@@ -276,10 +281,10 @@ export function makeRepoInternal<
|
|
|
276
281
|
const query: {
|
|
277
282
|
<A, R, From extends FieldValues>(
|
|
278
283
|
q: Q.QueryProjection<Encoded extends From ? From : never, A, R>
|
|
279
|
-
): Effect.Effect<readonly A[], S.SchemaError, R
|
|
284
|
+
): Effect.Effect<readonly A[], S.SchemaError, Exclude<R, RCtx>>
|
|
280
285
|
<A, R, EncodedRefined extends Encoded = Encoded>(
|
|
281
286
|
q: Q.QAll<NoInfer<Encoded>, NoInfer<EncodedRefined>, A, R>
|
|
282
|
-
): Effect.Effect<readonly A[], never, R
|
|
287
|
+
): Effect.Effect<readonly A[], never, Exclude<R, RCtx>>
|
|
283
288
|
} = (<A, R, EncodedRefined extends Encoded = Encoded>(q: Q.QAll<Encoded, EncodedRefined, A, R>) => {
|
|
284
289
|
const a = Q.toFilter(q)
|
|
285
290
|
const eff = a.mode === "project"
|
|
@@ -327,6 +332,7 @@ export function makeRepoInternal<
|
|
|
327
332
|
: eff,
|
|
328
333
|
Effect.withSpan("Repository.query [effect-app/infra]", {
|
|
329
334
|
attributes: {
|
|
335
|
+
itemType: name,
|
|
330
336
|
"repository.model_name": name,
|
|
331
337
|
query: { ...a, schema: a.schema ? "__SCHEMA__" : a.schema, filter: a.filter }
|
|
332
338
|
}
|
|
@@ -334,7 +340,7 @@ export function makeRepoInternal<
|
|
|
334
340
|
)
|
|
335
341
|
}) as any
|
|
336
342
|
|
|
337
|
-
const validateSample = Effect.fn("validateSample")(function*(options?: {
|
|
343
|
+
const validateSample = Effect.fn("validateSample", { attributes: { itemType: name } })(function*(options?: {
|
|
338
344
|
percentage?: number
|
|
339
345
|
maxItems?: number
|
|
340
346
|
}) {
|
|
@@ -374,7 +380,7 @@ export function makeRepoInternal<
|
|
|
374
380
|
|
|
375
381
|
if (Result.isFailure(decodeResult)) {
|
|
376
382
|
errors.push(
|
|
377
|
-
|
|
383
|
+
ValidationError.make({
|
|
378
384
|
id,
|
|
379
385
|
rawData,
|
|
380
386
|
jitMResult,
|
|
@@ -384,7 +390,7 @@ export function makeRepoInternal<
|
|
|
384
390
|
}
|
|
385
391
|
}
|
|
386
392
|
|
|
387
|
-
return
|
|
393
|
+
return ValidationResult.make({
|
|
388
394
|
total: NonNegativeInt(allIds.length),
|
|
389
395
|
sampled: NonNegativeInt(sample.length),
|
|
390
396
|
valid: NonNegativeInt(sample.length - errors.length),
|
|
@@ -401,6 +407,7 @@ export function makeRepoInternal<
|
|
|
401
407
|
saveAndPublish,
|
|
402
408
|
removeAndPublish,
|
|
403
409
|
removeById,
|
|
410
|
+
seedNamespace: (namespace: string) => store.seedNamespace(namespace),
|
|
404
411
|
validateSample,
|
|
405
412
|
queryRaw<A, Out, QR>(schema: S.Codec<A, Out, QR>, q: Q.RawQuery<Encoded, Out>) {
|
|
406
413
|
const dec = S.decodeEffect(S.Array(schema))
|
|
@@ -441,12 +448,12 @@ export function makeRepoInternal<
|
|
|
441
448
|
// },
|
|
442
449
|
save: (...xes: any[]) =>
|
|
443
450
|
Effect.flatMap(encMany(xes), (_) => saveAllE(_)).pipe(
|
|
444
|
-
Effect.withSpan("mapped.save", {}, { captureStackTrace: false })
|
|
451
|
+
Effect.withSpan("mapped.save", { attributes: { itemType: name } }, { captureStackTrace: false })
|
|
445
452
|
)
|
|
446
453
|
}
|
|
447
454
|
}
|
|
448
455
|
}
|
|
449
|
-
return r as Repository<T, Encoded, Evt, ItemType, IdKey, Exclude<R, RCtx>, RPublish>
|
|
456
|
+
return r as Repository<T, Encoded, Evt, ItemType, IdKey, Exclude<R, RCtx>, RPublish, RCtx>
|
|
450
457
|
})
|
|
451
458
|
.pipe(Effect
|
|
452
459
|
// .withSpan("Repository.make [effect-app/infra]", { attributes: { "repository.model_name": name } })
|