@effect-app/infra 4.0.0-beta.120 → 4.0.0-beta.122
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 +18 -0
- package/dist/CUPS.d.ts.map +1 -1
- package/dist/CUPS.js +8 -10
- package/dist/Model/Repository/ext.d.ts +17 -5
- package/dist/Model/Repository/ext.d.ts.map +1 -1
- package/dist/Model/Repository/ext.js +25 -2
- package/dist/Model/Repository/internal/internal.d.ts +1 -1
- package/dist/Model/Repository/internal/internal.d.ts.map +1 -1
- package/dist/Model/Repository/internal/internal.js +9 -8
- package/dist/Model/Repository/makeRepo.d.ts +3 -3
- package/dist/Model/Repository/makeRepo.d.ts.map +1 -1
- package/dist/Model/Repository/service.d.ts +21 -21
- package/dist/Model/Repository/service.d.ts.map +1 -1
- package/dist/Model/query/new-kid-interpreter.d.ts.map +1 -1
- package/dist/Model/query/new-kid-interpreter.js +3 -3
- package/dist/Operations.d.ts +3 -3
- package/dist/Operations.d.ts.map +1 -1
- package/dist/Operations.js +54 -57
- package/dist/OperationsRepo.d.ts +2 -2
- package/dist/QueueMaker/SQLQueue.d.ts +2 -3
- package/dist/QueueMaker/SQLQueue.d.ts.map +1 -1
- package/dist/QueueMaker/SQLQueue.js +104 -115
- package/dist/QueueMaker/memQueue.d.ts +2 -2
- package/dist/QueueMaker/memQueue.d.ts.map +1 -1
- package/dist/QueueMaker/memQueue.js +51 -62
- package/dist/QueueMaker/sbqueue.d.ts.map +1 -1
- package/dist/QueueMaker/sbqueue.js +34 -50
- package/dist/Store/Cosmos.d.ts.map +1 -1
- package/dist/Store/Cosmos.js +304 -306
- package/dist/Store/Disk.d.ts +1 -1
- package/dist/Store/Disk.d.ts.map +1 -1
- package/dist/Store/Disk.js +2 -2
- package/dist/Store/Memory.d.ts +1 -1
- package/dist/Store/Memory.d.ts.map +1 -1
- package/dist/Store/Memory.js +2 -2
- package/dist/Store/SQL/Pg.d.ts.map +1 -1
- package/dist/Store/SQL/Pg.js +147 -149
- package/dist/Store/SQL.d.ts.map +1 -1
- package/dist/Store/SQL.js +6 -6
- package/dist/Store/utils.d.ts.map +1 -1
- package/dist/Store/utils.js +3 -4
- package/dist/adapters/ServiceBus.d.ts.map +1 -1
- package/dist/adapters/ServiceBus.js +7 -9
- package/dist/api/internal/auth.d.ts.map +1 -1
- package/dist/api/internal/auth.js +1 -1
- package/dist/api/routing/middleware/middleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/middleware.js +2 -2
- package/dist/errorReporter.d.ts +3 -3
- package/dist/errorReporter.d.ts.map +1 -1
- package/dist/errorReporter.js +16 -23
- package/package.json +14 -14
- package/src/CUPS.ts +7 -9
- package/src/Model/Repository/ext.ts +71 -6
- package/src/Model/Repository/internal/internal.ts +13 -25
- package/src/Model/Repository/makeRepo.ts +4 -4
- package/src/Model/Repository/service.ts +22 -21
- package/src/Model/query/new-kid-interpreter.ts +2 -2
- package/src/Operations.ts +76 -111
- package/src/QueueMaker/SQLQueue.ts +119 -150
- package/src/QueueMaker/memQueue.ts +81 -102
- package/src/QueueMaker/sbqueue.ts +51 -81
- package/src/Store/Cosmos.ts +481 -484
- package/src/Store/Disk.ts +52 -53
- package/src/Store/Memory.ts +49 -50
- package/src/Store/SQL/Pg.ts +247 -250
- package/src/Store/SQL.ts +420 -426
- package/src/Store/utils.ts +23 -22
- package/src/adapters/ServiceBus.ts +106 -110
- package/src/api/internal/auth.ts +8 -6
- package/src/api/routing/middleware/middleware.ts +10 -11
- package/src/errorReporter.ts +58 -72
- package/test/dist/repository-ext.test.d.ts.map +1 -0
- package/test/query.test.ts +27 -0
- package/test/repository-ext.test.ts +58 -0
package/src/Store/utils.ts
CHANGED
|
@@ -12,33 +12,34 @@ export const makeETag = <E extends PersistenceModelType<{}>>(
|
|
|
12
12
|
_etag: crypto.createHash("sha256").update(JSON.stringify(e)).digest("hex")
|
|
13
13
|
}) as any
|
|
14
14
|
|
|
15
|
-
export const makeUpdateETag =
|
|
16
|
-
(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
id: e[idKey] as string,
|
|
24
|
-
current: "",
|
|
25
|
-
found: e._etag,
|
|
26
|
-
code: 409
|
|
27
|
-
})
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
if (Option.isSome(current) && current.value._etag !== e._etag) {
|
|
15
|
+
export const makeUpdateETag = (type: string) =>
|
|
16
|
+
Effect.fnUntraced(function*<IdKey extends keyof E, E extends PersistenceModelType<{}>>(
|
|
17
|
+
e: E,
|
|
18
|
+
idKey: IdKey,
|
|
19
|
+
current: Option.Option<E>
|
|
20
|
+
) {
|
|
21
|
+
if (e._etag) {
|
|
22
|
+
if (Option.isNone(current)) {
|
|
31
23
|
return yield* new OptimisticConcurrencyException({
|
|
32
24
|
type,
|
|
33
|
-
id:
|
|
34
|
-
current:
|
|
25
|
+
id: e[idKey] as string,
|
|
26
|
+
current: "",
|
|
35
27
|
found: e._etag,
|
|
36
|
-
code:
|
|
28
|
+
code: 409
|
|
37
29
|
})
|
|
38
30
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
31
|
+
}
|
|
32
|
+
if (Option.isSome(current) && current.value._etag !== e._etag) {
|
|
33
|
+
return yield* new OptimisticConcurrencyException({
|
|
34
|
+
type,
|
|
35
|
+
id: current.value[idKey] as string,
|
|
36
|
+
current: current.value._etag,
|
|
37
|
+
found: e._etag,
|
|
38
|
+
code: 412
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
return makeETag(e)
|
|
42
|
+
})
|
|
42
43
|
|
|
43
44
|
export function lowercaseIfString<T>(val: T) {
|
|
44
45
|
if (typeof val === "string") {
|
|
@@ -24,32 +24,29 @@ export class ServiceBusClientTag
|
|
|
24
24
|
static readonly layer = (url: string) => this.toLayer(this.make(url))
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
})
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const makeSender = (name: string) =>
|
|
41
|
-
Effect.gen(function*() {
|
|
42
|
-
const sender = yield* makeSender_(name)
|
|
43
|
-
const sendMessages = Effect.fnUntraced(function*(
|
|
44
|
-
messages: ServiceBusMessage | ServiceBusMessage[] | ServiceBusMessageBatch,
|
|
45
|
-
options?: Omit<OperationOptionsBase, "abortSignal">
|
|
46
|
-
) {
|
|
47
|
-
return yield* Effect.promise((abortSignal) => sender.sendMessages(messages, { ...options, abortSignal }))
|
|
48
|
-
})
|
|
27
|
+
const makeSender_ = Effect.fnUntraced(function*(queueName: string) {
|
|
28
|
+
const serviceBusClient = yield* ServiceBusClientTag
|
|
29
|
+
|
|
30
|
+
return yield* Effect.acquireRelease(
|
|
31
|
+
Effect.sync(() => serviceBusClient.createSender(queueName)).pipe(
|
|
32
|
+
withSpanAndLog(`ServiceBus.sender.create ${queueName}`)
|
|
33
|
+
),
|
|
34
|
+
(sender) => Effect.promise(() => sender.close()).pipe(withSpanAndLog(`ServiceBus.sender.close ${queueName}`))
|
|
35
|
+
)
|
|
36
|
+
})
|
|
49
37
|
|
|
50
|
-
|
|
38
|
+
const makeSender = Effect.fnUntraced(function*(name: string) {
|
|
39
|
+
const sender = yield* makeSender_(name)
|
|
40
|
+
const sendMessages = Effect.fnUntraced(function*(
|
|
41
|
+
messages: ServiceBusMessage | ServiceBusMessage[] | ServiceBusMessageBatch,
|
|
42
|
+
options?: Omit<OperationOptionsBase, "abortSignal">
|
|
43
|
+
) {
|
|
44
|
+
return yield* Effect.promise((abortSignal) => sender.sendMessages(messages, { ...options, abortSignal }))
|
|
51
45
|
})
|
|
52
46
|
|
|
47
|
+
return { name, sendMessages }
|
|
48
|
+
})
|
|
49
|
+
|
|
53
50
|
export class Sender extends Context.Opaque<Sender, {
|
|
54
51
|
name: string
|
|
55
52
|
sendMessages: (
|
|
@@ -71,97 +68,96 @@ export const SenderTag = <Id>() => <Key extends string>(queueName: Key) => {
|
|
|
71
68
|
})
|
|
72
69
|
}
|
|
73
70
|
|
|
74
|
-
const makeReceiver = (name: string)
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
(sessionId
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
(
|
|
86
|
-
waitTillEmpty
|
|
87
|
-
|
|
88
|
-
Effect.
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
71
|
+
const makeReceiver = Effect.fnUntraced(function*(name: string) {
|
|
72
|
+
const serviceBusClient = yield* ServiceBusClientTag
|
|
73
|
+
|
|
74
|
+
const makeReceiver = Effect.fnUntraced(
|
|
75
|
+
function*(queueName: string, waitTillEmpty: Effect.Effect<void>, sessionId?: string) {
|
|
76
|
+
return yield* Effect.acquireRelease(
|
|
77
|
+
(sessionId
|
|
78
|
+
? Effect.promise(() => serviceBusClient.acceptSession(queueName, sessionId))
|
|
79
|
+
: Effect.sync(() => serviceBusClient.createReceiver(queueName)))
|
|
80
|
+
.pipe(withSpanAndLog(`ServiceBus.receiver.create ${queueName}.${sessionId}`)),
|
|
81
|
+
(r) =>
|
|
82
|
+
waitTillEmpty.pipe(
|
|
83
|
+
withSpanAndLog(`ServiceBus.receiver.waitTillEmpty ${queueName}.${sessionId}`),
|
|
84
|
+
Effect.andThen(
|
|
85
|
+
Effect.promise(() => r.close()).pipe(
|
|
86
|
+
withSpanAndLog(`ServiceBus.receiver.close ${queueName}.${sessionId}`)
|
|
87
|
+
)
|
|
88
|
+
),
|
|
89
|
+
withSpanAndLog(`ServiceBus.receiver.release ${queueName}.${sessionId}`)
|
|
90
|
+
)
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
const make = (waitTillEmpty: Effect.Effect<void>) => makeReceiver(name, waitTillEmpty)
|
|
96
|
+
|
|
97
|
+
const makeSession = (sessionId: string, waitTillEmpty: Effect.Effect<void>) =>
|
|
98
|
+
makeReceiver(name, waitTillEmpty, sessionId)
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
name,
|
|
102
|
+
make,
|
|
103
|
+
makeSession,
|
|
104
|
+
subscribe: Effect.fnUntraced(function*<RMsg, RErr>(hndlr: MessageHandlers<RMsg, RErr>, sessionId?: string) {
|
|
105
|
+
const fs = yield* FiberSet.make()
|
|
106
|
+
const fr = yield* FiberSet.runtime(fs)<RMsg | RErr>()
|
|
107
|
+
const wait = Effect
|
|
108
|
+
.gen(function*() {
|
|
109
|
+
if ((yield* FiberSet.size(fs)) > 0) {
|
|
110
|
+
yield* InfraLogger.logDebug("Waiting ServiceBusFiberSet to be empty: " + (yield* FiberSet.size(fs)))
|
|
111
|
+
}
|
|
112
|
+
while ((yield* FiberSet.size(fs)) > 0) yield* Effect.sleep("250 millis")
|
|
113
|
+
})
|
|
114
|
+
const r = yield* sessionId
|
|
115
|
+
? makeSession(
|
|
116
|
+
sessionId,
|
|
117
|
+
wait
|
|
118
|
+
)
|
|
119
|
+
: make(wait)
|
|
120
|
+
|
|
121
|
+
const runEffect = <E>(effect: Effect.Effect<void, E, RMsg | RErr>) =>
|
|
122
|
+
new Promise<void>((resolve, reject) =>
|
|
123
|
+
fr(effect)
|
|
124
|
+
.addObserver((exit) => {
|
|
125
|
+
if (Exit.isSuccess(exit)) {
|
|
126
|
+
resolve(exit.value)
|
|
127
|
+
} else {
|
|
128
|
+
// disable @typescript-eslint/prefer-promise-reject-errors
|
|
129
|
+
reject(Cause.pretty(exit.cause))
|
|
130
|
+
}
|
|
131
|
+
})
|
|
95
132
|
)
|
|
96
|
-
}
|
|
97
|
-
)
|
|
98
133
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
yield* InfraLogger.logDebug("Waiting ServiceBusFiberSet to be empty: " + (yield* FiberSet.size(fs)))
|
|
115
|
-
}
|
|
116
|
-
while ((yield* FiberSet.size(fs)) > 0) yield* Effect.sleep("250 millis")
|
|
117
|
-
})
|
|
118
|
-
const r = yield* sessionId
|
|
119
|
-
? makeSession(
|
|
120
|
-
sessionId,
|
|
121
|
-
wait
|
|
122
|
-
)
|
|
123
|
-
: make(wait)
|
|
124
|
-
|
|
125
|
-
const runEffect = <E>(effect: Effect.Effect<void, E, RMsg | RErr>) =>
|
|
126
|
-
new Promise<void>((resolve, reject) =>
|
|
127
|
-
fr(effect)
|
|
128
|
-
.addObserver((exit) => {
|
|
129
|
-
if (Exit.isSuccess(exit)) {
|
|
130
|
-
resolve(exit.value)
|
|
131
|
-
} else {
|
|
132
|
-
// disable @typescript-eslint/prefer-promise-reject-errors
|
|
133
|
-
reject(Cause.pretty(exit.cause))
|
|
134
|
-
}
|
|
134
|
+
yield* Effect.acquireRelease(
|
|
135
|
+
Effect
|
|
136
|
+
.sync(() => {
|
|
137
|
+
const s = r
|
|
138
|
+
.subscribe({
|
|
139
|
+
processError: (err) =>
|
|
140
|
+
runEffect(
|
|
141
|
+
hndlr
|
|
142
|
+
.processError(err)
|
|
143
|
+
.pipe(
|
|
144
|
+
Effect.catchCause((cause) => Effect.logError(`ServiceBus Error ${sessionId}`, cause))
|
|
145
|
+
)
|
|
146
|
+
),
|
|
147
|
+
processMessage: (msg) => runEffect(hndlr.processMessage(msg))
|
|
148
|
+
// DO NOT CATCH ERRORS here as they should return to the queue!
|
|
135
149
|
})
|
|
150
|
+
return { close: Effect.promise(() => s.close()) }
|
|
151
|
+
})
|
|
152
|
+
.pipe(withSpanAndLog(`ServiceBus.subscription.create ${sessionId}`)),
|
|
153
|
+
(subscription) =>
|
|
154
|
+
subscription.close.pipe(
|
|
155
|
+
withSpanAndLog(`ServiceBus.subscription.close ${sessionId}`)
|
|
136
156
|
)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
const s = r
|
|
142
|
-
.subscribe({
|
|
143
|
-
processError: (err) =>
|
|
144
|
-
runEffect(
|
|
145
|
-
hndlr
|
|
146
|
-
.processError(err)
|
|
147
|
-
.pipe(
|
|
148
|
-
Effect.catchCause((cause) => Effect.logError(`ServiceBus Error ${sessionId}`, cause))
|
|
149
|
-
)
|
|
150
|
-
),
|
|
151
|
-
processMessage: (msg) => runEffect(hndlr.processMessage(msg))
|
|
152
|
-
// DO NOT CATCH ERRORS here as they should return to the queue!
|
|
153
|
-
})
|
|
154
|
-
return { close: Effect.promise(() => s.close()) }
|
|
155
|
-
})
|
|
156
|
-
.pipe(withSpanAndLog(`ServiceBus.subscription.create ${sessionId}`)),
|
|
157
|
-
(subscription) =>
|
|
158
|
-
subscription.close.pipe(
|
|
159
|
-
withSpanAndLog(`ServiceBus.subscription.close ${sessionId}`)
|
|
160
|
-
)
|
|
161
|
-
) as Effect.Effect<void, never, Scope.Scope> // wth is going on here
|
|
162
|
-
})
|
|
163
|
-
}
|
|
164
|
-
})
|
|
157
|
+
) as Effect.Effect<void, never, Scope.Scope> // wth is going on here
|
|
158
|
+
})
|
|
159
|
+
}
|
|
160
|
+
})
|
|
165
161
|
|
|
166
162
|
export class Receiver extends Context.Opaque<Receiver, {
|
|
167
163
|
name: string
|
package/src/api/internal/auth.ts
CHANGED
|
@@ -45,12 +45,14 @@ export const checkJwt = (config: Config) => {
|
|
|
45
45
|
return HttpMiddleware.make((app) =>
|
|
46
46
|
Effect.gen(function*() {
|
|
47
47
|
const req = yield* HttpServerRequest.HttpServerRequest
|
|
48
|
-
const response = yield* check(req.headers).pipe(
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
48
|
+
const response = yield* check(req.headers).pipe(
|
|
49
|
+
Effect.catch((e) =>
|
|
50
|
+
HttpServerResponse.json({ message: e.message }, {
|
|
51
|
+
status: e.status,
|
|
52
|
+
headers: HttpHeaders.fromInput(e.headers)
|
|
53
|
+
})
|
|
54
|
+
)
|
|
55
|
+
)
|
|
54
56
|
if (response) {
|
|
55
57
|
return response
|
|
56
58
|
}
|
|
@@ -157,15 +157,14 @@ export const requiresTransactionConfig = RpcContextMap.makeCustom()(Schema.Never
|
|
|
157
157
|
* )
|
|
158
158
|
* ```
|
|
159
159
|
*/
|
|
160
|
-
export const makeSqlTransactionMiddleware = (
|
|
160
|
+
export const makeSqlTransactionMiddleware = Effect.fnUntraced(function*(
|
|
161
161
|
rcm: { getConfig: (rpc: Rpc.AnyWithProps) => { readonly requiresTransaction?: boolean } }
|
|
162
|
-
)
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
})
|
|
162
|
+
) {
|
|
163
|
+
const withTx = yield* WithNsTransaction
|
|
164
|
+
const mw: RpcMiddleware.RpcMiddlewareV4<never, never, never> = (effect, { rpc }) => {
|
|
165
|
+
const { requiresTransaction } = rcm.getConfig(rpc)
|
|
166
|
+
if (requiresTransaction !== true) return effect
|
|
167
|
+
return withTx(effect)
|
|
168
|
+
}
|
|
169
|
+
return mw
|
|
170
|
+
})
|
package/src/errorReporter.ts
CHANGED
|
@@ -13,47 +13,41 @@ const tryCauseException = <E>(cause: Cause.Cause<E>, name: string): CauseExcepti
|
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
export function reportError(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return
|
|
29
|
-
}
|
|
30
|
-
const error = tryCauseException(cause, name)
|
|
31
|
-
|
|
32
|
-
yield* reportSentry(error, extras, LogLevelToSentry(level))
|
|
33
|
-
yield* InfraLogger
|
|
34
|
-
.logWithLevel(level, "Reporting error", cause)
|
|
35
|
-
.pipe(
|
|
36
|
-
Effect.annotateLogs(dropUndefined({
|
|
37
|
-
extras,
|
|
38
|
-
error: tryToReport(error),
|
|
39
|
-
cause: tryToJson(cause),
|
|
40
|
-
__error_name__: name
|
|
41
|
-
}))
|
|
42
|
-
)
|
|
43
|
-
.pipe(
|
|
44
|
-
Effect.catchCause((cause) => InfraLogger.logWarning("Failed to log error", cause)),
|
|
45
|
-
Effect.catchCause(() => InfraLogger.logFatal("Failed to log error cause"))
|
|
46
|
-
)
|
|
16
|
+
export function reportError(name: string) {
|
|
17
|
+
return Effect.fnUntraced(
|
|
18
|
+
function*(
|
|
19
|
+
cause: Cause.Cause<unknown>,
|
|
20
|
+
extras?: Record<string, unknown>,
|
|
21
|
+
level: LogLevel.Severity = "Error"
|
|
22
|
+
) {
|
|
23
|
+
if (Cause.hasInterruptsOnly(cause)) {
|
|
24
|
+
yield* InfraLogger.logDebug("Interrupted").pipe(Effect.annotateLogs("extras", JSON.stringify(extras ?? {})))
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
const error = tryCauseException(cause, name)
|
|
47
28
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
29
|
+
yield* reportSentry(error, extras, LogLevelToSentry(level))
|
|
30
|
+
yield* InfraLogger
|
|
31
|
+
.logWithLevel(level, "Reporting error", cause)
|
|
32
|
+
.pipe(
|
|
33
|
+
Effect.annotateLogs(dropUndefined({
|
|
34
|
+
extras,
|
|
35
|
+
error: tryToReport(error),
|
|
36
|
+
cause: tryToJson(cause),
|
|
37
|
+
__error_name__: name
|
|
38
|
+
})),
|
|
39
|
+
Effect.catchCause((cause) => InfraLogger.logWarning("Failed to log error", cause)),
|
|
40
|
+
Effect.catchCause(() => InfraLogger.logFatal("Failed to log error cause"))
|
|
55
41
|
)
|
|
56
|
-
|
|
42
|
+
|
|
43
|
+
return error
|
|
44
|
+
},
|
|
45
|
+
(effect) =>
|
|
46
|
+
Effect.tapCause(effect, (cause) =>
|
|
47
|
+
InfraLogger.logError("Failed to report error", cause).pipe(
|
|
48
|
+
Effect.tapCause(() => InfraLogger.logFatal("Failed to log error cause"))
|
|
49
|
+
))
|
|
50
|
+
)
|
|
57
51
|
}
|
|
58
52
|
|
|
59
53
|
function reportSentry(
|
|
@@ -72,39 +66,31 @@ function reportSentry(
|
|
|
72
66
|
}))
|
|
73
67
|
}
|
|
74
68
|
|
|
75
|
-
export function logError<E>(
|
|
76
|
-
|
|
77
|
-
) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}))
|
|
93
|
-
)
|
|
94
|
-
})
|
|
95
|
-
.pipe(
|
|
96
|
-
Effect.tapCause(() => InfraLogger.logFatal("Failed to log error cause"))
|
|
97
|
-
)
|
|
69
|
+
export function logError<E>(name: string) {
|
|
70
|
+
return Effect.fnUntraced(
|
|
71
|
+
function*(cause: Cause.Cause<E>, extras?: Record<string, unknown>) {
|
|
72
|
+
if (Cause.hasInterruptsOnly(cause)) {
|
|
73
|
+
yield* InfraLogger.logDebug("Interrupted").pipe(Effect.annotateLogs(dropUndefined({ extras })))
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
yield* InfraLogger
|
|
77
|
+
.logWarning("Logging error", cause)
|
|
78
|
+
.pipe(Effect.annotateLogs(dropUndefined({
|
|
79
|
+
extras,
|
|
80
|
+
cause: tryToJson(cause),
|
|
81
|
+
__error_name__: name
|
|
82
|
+
})))
|
|
83
|
+
},
|
|
84
|
+
(effect) => Effect.tapCause(effect, () => InfraLogger.logFatal("Failed to log error cause"))
|
|
85
|
+
)
|
|
98
86
|
}
|
|
99
87
|
|
|
100
|
-
export
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
Sentry.captureMessage(message, scope)
|
|
88
|
+
export const reportMessage = Effect.fnUntraced(function*(message: string, extras?: Record<string, unknown>) {
|
|
89
|
+
const context = yield* getRC
|
|
90
|
+
const scope = new Sentry.Scope()
|
|
91
|
+
if (context) scope.setContext("context", { ...context })
|
|
92
|
+
if (extras) scope.setContext("extras", extras)
|
|
93
|
+
Sentry.captureMessage(message, scope)
|
|
107
94
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
95
|
+
console.warn(message, extras)
|
|
96
|
+
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repository-ext.test.d.ts","sourceRoot":"","sources":["../repository-ext.test.ts"],"names":[],"mappings":""}
|
package/test/query.test.ts
CHANGED
|
@@ -548,6 +548,33 @@ it(
|
|
|
548
548
|
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.runPromise)
|
|
549
549
|
)
|
|
550
550
|
|
|
551
|
+
it(
|
|
552
|
+
"project with encodeKeys in projection maps encoded keys",
|
|
553
|
+
() =>
|
|
554
|
+
Effect
|
|
555
|
+
.gen(function*() {
|
|
556
|
+
const schema = S.Struct({
|
|
557
|
+
id: S.String,
|
|
558
|
+
a: S.Number
|
|
559
|
+
})
|
|
560
|
+
|
|
561
|
+
const repo = yield* makeRepo(
|
|
562
|
+
"test",
|
|
563
|
+
schema,
|
|
564
|
+
{
|
|
565
|
+
makeInitial: Effect.sync(() => [{ id: "1", a: 1 }])
|
|
566
|
+
}
|
|
567
|
+
)
|
|
568
|
+
|
|
569
|
+
const outputSchema = S.Struct({ b: S.Number }).pipe(S.encodeKeys({ b: "a" }))
|
|
570
|
+
|
|
571
|
+
const result = yield* repo.query(project(outputSchema))
|
|
572
|
+
|
|
573
|
+
expect(result).toStrictEqual([{ b: 1 }])
|
|
574
|
+
})
|
|
575
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.runPromise)
|
|
576
|
+
)
|
|
577
|
+
|
|
551
578
|
it(
|
|
552
579
|
"doesn't mess when refining fields",
|
|
553
580
|
() =>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { describe, expect, it } from "@effect/vitest"
|
|
2
|
+
import { Effect, Layer, S } from "effect-app"
|
|
3
|
+
import { setupRequestContextFromCurrent } from "../src/api/setupRequest.js"
|
|
4
|
+
import { makeRepo } from "../src/Model/Repository.js"
|
|
5
|
+
import { RepositoryRegistryLive } from "../src/Model/Repository/Registry.js"
|
|
6
|
+
import { MemoryStoreLive } from "../src/Store/Memory.js"
|
|
7
|
+
|
|
8
|
+
class BatchItem extends S.Class<BatchItem>("BatchItem")({
|
|
9
|
+
id: S.String,
|
|
10
|
+
label: S.String
|
|
11
|
+
}) {}
|
|
12
|
+
|
|
13
|
+
const TestStoreLive = Layer.merge(MemoryStoreLive, RepositoryRegistryLive)
|
|
14
|
+
|
|
15
|
+
describe("repository ext save/remove batching", () => {
|
|
16
|
+
it.effect("supports save batching overload", () =>
|
|
17
|
+
Effect.gen(function*() {
|
|
18
|
+
const repo = yield* makeRepo("BatchItem", BatchItem, {})
|
|
19
|
+
const items = [
|
|
20
|
+
new BatchItem({ id: "1", label: "one" }),
|
|
21
|
+
new BatchItem({ id: "2", label: "two" }),
|
|
22
|
+
new BatchItem({ id: "3", label: "three" }),
|
|
23
|
+
new BatchItem({ id: "4", label: "four" })
|
|
24
|
+
] as const
|
|
25
|
+
|
|
26
|
+
yield* repo.save(items, { batch: 2 })
|
|
27
|
+
|
|
28
|
+
const all = yield* repo.all
|
|
29
|
+
expect(all).toHaveLength(4)
|
|
30
|
+
expect(all.map((_) => _.id).toSorted()).toEqual(["1", "2", "3", "4"])
|
|
31
|
+
}).pipe(
|
|
32
|
+
setupRequestContextFromCurrent(),
|
|
33
|
+
Effect.provide(TestStoreLive)
|
|
34
|
+
)
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
it.effect("supports remove batching overload", () =>
|
|
38
|
+
Effect.gen(function*() {
|
|
39
|
+
const repo = yield* makeRepo("BatchItem", BatchItem, {})
|
|
40
|
+
const items = [
|
|
41
|
+
new BatchItem({ id: "1", label: "one" }),
|
|
42
|
+
new BatchItem({ id: "2", label: "two" }),
|
|
43
|
+
new BatchItem({ id: "3", label: "three" }),
|
|
44
|
+
new BatchItem({ id: "4", label: "four" })
|
|
45
|
+
] as const
|
|
46
|
+
|
|
47
|
+
yield* repo.save(items)
|
|
48
|
+
yield* repo.remove([items[0], items[1], items[2]], { batch: true })
|
|
49
|
+
|
|
50
|
+
const all = yield* repo.all
|
|
51
|
+
expect(all).toHaveLength(1)
|
|
52
|
+
expect(all[0]?.id).toBe("4")
|
|
53
|
+
}).pipe(
|
|
54
|
+
setupRequestContextFromCurrent(),
|
|
55
|
+
Effect.provide(TestStoreLive)
|
|
56
|
+
)
|
|
57
|
+
)
|
|
58
|
+
})
|