@effect-app/infra 4.0.0-beta.121 → 4.0.0-beta.123

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/CUPS.d.ts.map +1 -1
  3. package/dist/CUPS.js +8 -10
  4. package/dist/Model/Repository/ext.d.ts +15 -3
  5. package/dist/Model/Repository/ext.d.ts.map +1 -1
  6. package/dist/Model/Repository/ext.js +25 -2
  7. package/dist/Model/Repository/internal/internal.d.ts +1 -1
  8. package/dist/Model/Repository/internal/internal.d.ts.map +1 -1
  9. package/dist/Model/Repository/internal/internal.js +9 -8
  10. package/dist/Model/Repository/makeRepo.d.ts +3 -3
  11. package/dist/Model/Repository/makeRepo.d.ts.map +1 -1
  12. package/dist/Model/Repository/service.d.ts +21 -21
  13. package/dist/Model/Repository/service.d.ts.map +1 -1
  14. package/dist/Model/query/new-kid-interpreter.d.ts +2 -2
  15. package/dist/Operations.d.ts +2 -2
  16. package/dist/Operations.d.ts.map +1 -1
  17. package/dist/Operations.js +54 -57
  18. package/dist/OperationsRepo.d.ts +2 -2
  19. package/dist/QueueMaker/SQLQueue.d.ts +2 -3
  20. package/dist/QueueMaker/SQLQueue.d.ts.map +1 -1
  21. package/dist/QueueMaker/SQLQueue.js +104 -115
  22. package/dist/QueueMaker/memQueue.d.ts +2 -2
  23. package/dist/QueueMaker/memQueue.d.ts.map +1 -1
  24. package/dist/QueueMaker/memQueue.js +51 -62
  25. package/dist/QueueMaker/sbqueue.d.ts.map +1 -1
  26. package/dist/QueueMaker/sbqueue.js +34 -50
  27. package/dist/Store/ContextMapContainer.d.ts +1 -1
  28. package/dist/Store/Cosmos.d.ts.map +1 -1
  29. package/dist/Store/Cosmos.js +304 -306
  30. package/dist/Store/Disk.d.ts +1 -1
  31. package/dist/Store/Disk.d.ts.map +1 -1
  32. package/dist/Store/Disk.js +2 -2
  33. package/dist/Store/Memory.d.ts +1 -1
  34. package/dist/Store/Memory.d.ts.map +1 -1
  35. package/dist/Store/Memory.js +2 -2
  36. package/dist/Store/SQL/Pg.d.ts.map +1 -1
  37. package/dist/Store/SQL/Pg.js +147 -149
  38. package/dist/Store/SQL.d.ts.map +1 -1
  39. package/dist/Store/SQL.js +6 -6
  40. package/dist/Store/utils.d.ts.map +1 -1
  41. package/dist/Store/utils.js +3 -4
  42. package/dist/adapters/ServiceBus.d.ts.map +1 -1
  43. package/dist/adapters/ServiceBus.js +7 -9
  44. package/dist/api/internal/auth.d.ts.map +1 -1
  45. package/dist/api/internal/auth.js +1 -1
  46. package/dist/api/routing/middleware/middleware.d.ts.map +1 -1
  47. package/dist/api/routing/middleware/middleware.js +2 -2
  48. package/dist/errorReporter.d.ts +3 -3
  49. package/dist/errorReporter.d.ts.map +1 -1
  50. package/dist/errorReporter.js +16 -23
  51. package/package.json +14 -14
  52. package/src/CUPS.ts +7 -9
  53. package/src/Model/Repository/ext.ts +71 -6
  54. package/src/Model/Repository/internal/internal.ts +13 -25
  55. package/src/Model/Repository/makeRepo.ts +4 -4
  56. package/src/Model/Repository/service.ts +22 -21
  57. package/src/Operations.ts +76 -111
  58. package/src/QueueMaker/SQLQueue.ts +119 -150
  59. package/src/QueueMaker/memQueue.ts +81 -102
  60. package/src/QueueMaker/sbqueue.ts +51 -81
  61. package/src/Store/Cosmos.ts +481 -484
  62. package/src/Store/Disk.ts +52 -53
  63. package/src/Store/Memory.ts +49 -50
  64. package/src/Store/SQL/Pg.ts +247 -250
  65. package/src/Store/SQL.ts +420 -426
  66. package/src/Store/utils.ts +23 -22
  67. package/src/adapters/ServiceBus.ts +106 -110
  68. package/src/api/internal/auth.ts +8 -6
  69. package/src/api/routing/middleware/middleware.ts +10 -11
  70. package/src/errorReporter.ts +58 -72
  71. package/test/dist/auth.test.d.ts.map +1 -0
  72. package/test/dist/fixtures.d.ts +1 -1
  73. package/test/dist/repository-ext.test.d.ts.map +1 -0
  74. package/test/repository-ext.test.ts +58 -0
@@ -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
- (type: string) =>
17
- <IdKey extends keyof E, E extends PersistenceModelType<{}>>(e: E, idKey: IdKey, current: Option.Option<E>) =>
18
- Effect.gen(function*() {
19
- if (e._etag) {
20
- if (Option.isNone(current)) {
21
- return yield* new OptimisticConcurrencyException({
22
- type,
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: current.value[idKey] as string,
34
- current: current.value._etag,
25
+ id: e[idKey] as string,
26
+ current: "",
35
27
  found: e._etag,
36
- code: 412
28
+ code: 409
37
29
  })
38
30
  }
39
- const newE = makeETag(e)
40
- return newE
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
- function makeSender_(queueName: string) {
28
- return Effect.gen(function*() {
29
- const serviceBusClient = yield* ServiceBusClientTag
30
-
31
- return yield* Effect.acquireRelease(
32
- Effect.sync(() => serviceBusClient.createSender(queueName)).pipe(
33
- withSpanAndLog(`ServiceBus.sender.create ${queueName}`)
34
- ),
35
- (sender) => Effect.promise(() => sender.close()).pipe(withSpanAndLog(`ServiceBus.sender.close ${queueName}`))
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
- return { name, sendMessages }
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
- Effect.gen(function*() {
76
- const serviceBusClient = yield* ServiceBusClientTag
77
-
78
- const makeReceiver = Effect.fnUntraced(
79
- function*(queueName: string, waitTillEmpty: Effect.Effect<void>, sessionId?: string) {
80
- return yield* Effect.acquireRelease(
81
- (sessionId
82
- ? Effect.promise(() => serviceBusClient.acceptSession(queueName, sessionId))
83
- : Effect.sync(() => serviceBusClient.createReceiver(queueName)))
84
- .pipe(withSpanAndLog(`ServiceBus.receiver.create ${queueName}.${sessionId}`)),
85
- (r) =>
86
- waitTillEmpty.pipe(
87
- withSpanAndLog(`ServiceBus.receiver.waitTillEmpty ${queueName}.${sessionId}`),
88
- Effect.andThen(
89
- Effect.promise(() => r.close()).pipe(
90
- withSpanAndLog(`ServiceBus.receiver.close ${queueName}.${sessionId}`)
91
- )
92
- ),
93
- withSpanAndLog(`ServiceBus.receiver.release ${queueName}.${sessionId}`)
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
- const make = (waitTillEmpty: Effect.Effect<void>) => makeReceiver(name, waitTillEmpty)
100
-
101
- const makeSession = (sessionId: string, waitTillEmpty: Effect.Effect<void>) =>
102
- makeReceiver(name, waitTillEmpty, sessionId)
103
-
104
- return {
105
- name,
106
- make,
107
- makeSession,
108
- subscribe: Effect.fnUntraced(function*<RMsg, RErr>(hndlr: MessageHandlers<RMsg, RErr>, sessionId?: string) {
109
- const fs = yield* FiberSet.make()
110
- const fr = yield* FiberSet.runtime(fs)<RMsg | RErr>()
111
- const wait = Effect
112
- .gen(function*() {
113
- if ((yield* FiberSet.size(fs)) > 0) {
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
- yield* Effect.acquireRelease(
139
- Effect
140
- .sync(() => {
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
@@ -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(Effect.catch((e) =>
49
- HttpServerResponse.json({ message: e.message }, {
50
- status: e.status,
51
- headers: HttpHeaders.fromInput(e.headers)
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
- Effect.gen(function*() {
164
- const withTx = yield* WithNsTransaction
165
- const mw: RpcMiddleware.RpcMiddlewareV4<never, never, never> = (effect, { rpc }) => {
166
- const { requiresTransaction } = rcm.getConfig(rpc)
167
- if (requiresTransaction !== true) return effect
168
- return withTx(effect)
169
- }
170
- return mw
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
+ })
@@ -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
- name: string
18
- ) {
19
- return (
20
- cause: Cause.Cause<unknown>,
21
- extras?: Record<string, unknown>,
22
- level: LogLevel.Severity = "Error"
23
- ) =>
24
- Effect
25
- .gen(function*() {
26
- if (Cause.hasInterruptsOnly(cause)) {
27
- yield* InfraLogger.logDebug("Interrupted").pipe(Effect.annotateLogs("extras", JSON.stringify(extras ?? {})))
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
- return error
49
- })
50
- .pipe(
51
- Effect.tapCause((cause) =>
52
- InfraLogger.logError("Failed to report error", cause).pipe(
53
- Effect.tapCause(() => InfraLogger.logFatal("Failed to log error cause"))
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
- name: string
77
- ) {
78
- return (cause: Cause.Cause<E>, extras?: Record<string, unknown>) =>
79
- Effect
80
- .gen(function*() {
81
- if (Cause.hasInterruptsOnly(cause)) {
82
- yield* InfraLogger.logDebug("Interrupted").pipe(Effect.annotateLogs(dropUndefined({ extras })))
83
- return
84
- }
85
- yield* InfraLogger
86
- .logWarning("Logging error", cause)
87
- .pipe(
88
- Effect.annotateLogs(dropUndefined({
89
- extras,
90
- cause: tryToJson(cause),
91
- __error_name__: name
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 function reportMessage(message: string, extras?: Record<string, unknown>) {
101
- return Effect.gen(function*() {
102
- const context = yield* getRC
103
- const scope = new Sentry.Scope()
104
- if (context) scope.setContext("context", { ...context })
105
- if (extras) scope.setContext("extras", extras)
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
- console.warn(message, extras)
109
- })
110
- }
95
+ console.warn(message, extras)
96
+ })
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.test.d.ts","sourceRoot":"","sources":["../auth.test.ts"],"names":[],"mappings":""}
@@ -67,7 +67,7 @@ declare const RequestContextMap_base: (new () => {
67
67
  readonly requireRoles: RpcContextMap.RpcContextMap.Custom<never, typeof UnauthorizedError, string[]>;
68
68
  readonly test: RpcContextMap.RpcContextMap<never, S.Never>;
69
69
  }>;
70
- get: <Key extends "test" | "allowAnonymous" | "requireRoles">(key: Key) => RpcX.RpcMiddleware.RpcDynamic<Key, {
70
+ get: <Key extends "allowAnonymous" | "requireRoles" | "test">(key: Key) => RpcX.RpcMiddleware.RpcDynamic<Key, {
71
71
  readonly allowAnonymous: RpcContextMap.RpcContextMap.Inverted<UserProfile, typeof NotLoggedInError>;
72
72
  readonly requireRoles: RpcContextMap.RpcContextMap.Custom<never, typeof UnauthorizedError, string[]>;
73
73
  readonly test: RpcContextMap.RpcContextMap<never, S.Never>;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repository-ext.test.d.ts","sourceRoot":"","sources":["../repository-ext.test.ts"],"names":[],"mappings":""}
@@ -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
+ })