@effect-app/infra 4.0.0-beta.9 → 4.0.0-beta.91
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 +596 -0
- package/dist/CUPS.d.ts +3 -3
- package/dist/CUPS.d.ts.map +1 -1
- package/dist/CUPS.js +3 -3
- package/dist/Emailer/Sendgrid.js +1 -1
- package/dist/Emailer/service.d.ts +3 -3
- package/dist/Emailer/service.d.ts.map +1 -1
- package/dist/Emailer/service.js +3 -3
- package/dist/MainFiberSet.d.ts +2 -2
- package/dist/MainFiberSet.d.ts.map +1 -1
- package/dist/MainFiberSet.js +3 -3
- package/dist/Model/Repository/internal/internal.d.ts +3 -3
- package/dist/Model/Repository/internal/internal.d.ts.map +1 -1
- package/dist/Model/Repository/internal/internal.js +11 -7
- package/dist/Model/Repository/makeRepo.d.ts +2 -2
- package/dist/Model/Repository/makeRepo.d.ts.map +1 -1
- package/dist/Model/Repository/makeRepo.js +1 -1
- package/dist/Model/Repository/validation.d.ts +7 -6
- package/dist/Model/Repository/validation.d.ts.map +1 -1
- package/dist/Model/query/dsl.d.ts +9 -9
- package/dist/Operations.d.ts +2 -2
- package/dist/Operations.d.ts.map +1 -1
- package/dist/Operations.js +3 -3
- package/dist/OperationsRepo.d.ts +2 -2
- package/dist/OperationsRepo.d.ts.map +1 -1
- package/dist/OperationsRepo.js +3 -3
- package/dist/QueueMaker/SQLQueue.d.ts +3 -5
- package/dist/QueueMaker/SQLQueue.d.ts.map +1 -1
- package/dist/QueueMaker/SQLQueue.js +9 -7
- package/dist/QueueMaker/errors.d.ts +1 -1
- package/dist/QueueMaker/errors.d.ts.map +1 -1
- package/dist/QueueMaker/memQueue.d.ts.map +1 -1
- package/dist/QueueMaker/memQueue.js +10 -9
- package/dist/QueueMaker/sbqueue.d.ts.map +1 -1
- package/dist/QueueMaker/sbqueue.js +11 -9
- package/dist/RequestContext.d.ts +41 -21
- package/dist/RequestContext.d.ts.map +1 -1
- package/dist/RequestContext.js +5 -5
- package/dist/RequestFiberSet.d.ts +2 -2
- package/dist/RequestFiberSet.d.ts.map +1 -1
- package/dist/RequestFiberSet.js +5 -5
- package/dist/Store/ContextMapContainer.d.ts +14 -3
- package/dist/Store/ContextMapContainer.d.ts.map +1 -1
- package/dist/Store/ContextMapContainer.js +64 -3
- package/dist/Store/Cosmos.d.ts.map +1 -1
- package/dist/Store/Cosmos.js +91 -56
- package/dist/Store/Disk.d.ts.map +1 -1
- package/dist/Store/Disk.js +3 -4
- package/dist/Store/Memory.d.ts +2 -2
- package/dist/Store/Memory.d.ts.map +1 -1
- package/dist/Store/Memory.js +4 -4
- 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 +186 -0
- package/dist/Store/SQL/query.d.ts +36 -0
- package/dist/Store/SQL/query.d.ts.map +1 -0
- package/dist/Store/SQL/query.js +385 -0
- package/dist/Store/SQL.d.ts +11 -0
- package/dist/Store/SQL.d.ts.map +1 -0
- package/dist/Store/SQL.js +212 -0
- package/dist/Store/index.d.ts +1 -1
- package/dist/Store/index.d.ts.map +1 -1
- package/dist/Store/index.js +11 -1
- package/dist/Store/service.d.ts +8 -5
- package/dist/Store/service.d.ts.map +1 -1
- package/dist/Store/service.js +14 -6
- package/dist/adapters/SQL/Model.d.ts +2 -5
- package/dist/adapters/SQL/Model.d.ts.map +1 -1
- package/dist/adapters/SQL/Model.js +21 -13
- package/dist/adapters/ServiceBus.d.ts +6 -6
- package/dist/adapters/ServiceBus.d.ts.map +1 -1
- package/dist/adapters/ServiceBus.js +9 -9
- package/dist/adapters/cosmos-client.d.ts +2 -2
- package/dist/adapters/cosmos-client.d.ts.map +1 -1
- package/dist/adapters/cosmos-client.js +3 -3
- package/dist/adapters/logger.d.ts.map +1 -1
- package/dist/adapters/memQueue.d.ts +2 -2
- package/dist/adapters/memQueue.d.ts.map +1 -1
- package/dist/adapters/memQueue.js +3 -3
- package/dist/adapters/mongo-client.d.ts +2 -2
- 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 +6 -6
- package/dist/api/ContextProvider.d.ts.map +1 -1
- package/dist/api/ContextProvider.js +6 -6
- package/dist/api/internal/auth.d.ts +1 -1
- package/dist/api/internal/events.d.ts +2 -2
- package/dist/api/internal/events.d.ts.map +1 -1
- package/dist/api/internal/events.js +7 -5
- package/dist/api/layerUtils.d.ts +5 -5
- package/dist/api/layerUtils.d.ts.map +1 -1
- package/dist/api/layerUtils.js +5 -5
- package/dist/api/routing/middleware/RouterMiddleware.d.ts +3 -3
- package/dist/api/routing/middleware/RouterMiddleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/middleware.d.ts +35 -1
- package/dist/api/routing/middleware/middleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/middleware.js +39 -1
- 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/schema/jwt.js +1 -1
- package/dist/api/routing.d.ts +1 -5
- package/dist/api/routing.d.ts.map +1 -1
- package/dist/api/routing.js +3 -2
- package/dist/api/setupRequest.d.ts +6 -3
- package/dist/api/setupRequest.d.ts.map +1 -1
- package/dist/api/setupRequest.js +11 -6
- package/dist/errorReporter.d.ts +1 -1
- package/dist/errorReporter.d.ts.map +1 -1
- package/dist/errorReporter.js +1 -1
- package/dist/fileUtil.js +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/rateLimit.js +1 -1
- package/examples/query.ts +29 -25
- package/package.json +32 -18
- package/src/CUPS.ts +2 -2
- package/src/Emailer/Sendgrid.ts +1 -1
- package/src/Emailer/service.ts +2 -2
- package/src/MainFiberSet.ts +2 -2
- package/src/Model/Repository/internal/internal.ts +11 -8
- package/src/Model/Repository/makeRepo.ts +2 -2
- package/src/Operations.ts +2 -2
- package/src/OperationsRepo.ts +2 -2
- package/src/QueueMaker/SQLQueue.ts +10 -10
- package/src/QueueMaker/memQueue.ts +41 -42
- package/src/QueueMaker/sbqueue.ts +65 -62
- package/src/RequestContext.ts +4 -4
- package/src/RequestFiberSet.ts +4 -4
- package/src/Store/ContextMapContainer.ts +98 -2
- package/src/Store/Cosmos.ts +273 -207
- package/src/Store/Disk.ts +2 -3
- package/src/Store/Memory.ts +4 -6
- package/src/Store/SQL/Pg.ts +328 -0
- package/src/Store/SQL/query.ts +430 -0
- package/src/Store/SQL.ts +357 -0
- package/src/Store/index.ts +10 -0
- package/src/Store/service.ts +16 -7
- package/src/adapters/SQL/Model.ts +76 -71
- package/src/adapters/ServiceBus.ts +8 -8
- package/src/adapters/cosmos-client.ts +2 -2
- 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/events.ts +7 -6
- package/src/api/layerUtils.ts +8 -8
- package/src/api/routing/middleware/RouterMiddleware.ts +4 -4
- package/src/api/routing/middleware/middleware.ts +43 -0
- package/src/api/routing/schema/jwt.ts +2 -3
- package/src/api/routing.ts +7 -6
- package/src/api/setupRequest.ts +27 -7
- package/src/errorReporter.ts +1 -1
- package/src/fileUtil.ts +1 -1
- package/src/rateLimit.ts +2 -2
- package/test/contextProvider.test.ts +5 -5
- package/test/controller.test.ts +12 -9
- package/test/dist/contextProvider.test.d.ts.map +1 -1
- package/test/dist/controller.test.d.ts.map +1 -1
- package/test/dist/fixtures.d.ts +19 -9
- package/test/dist/fixtures.d.ts.map +1 -1
- package/test/dist/fixtures.js +11 -9
- package/test/dist/query.test.d.ts.map +1 -1
- package/test/dist/rawQuery.test.d.ts.map +1 -1
- package/test/dist/requires.test.d.ts.map +1 -1
- 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 +10 -8
- package/test/query.test.ts +160 -14
- package/test/rawQuery.test.ts +19 -17
- package/test/requires.test.ts +6 -5
- package/test/rpc-multi-middleware.test.ts +73 -4
- package/test/sql-store.test.ts +776 -0
- package/test/validateSample.test.ts +1 -1
- package/tsconfig.json +0 -1
package/src/Store/Cosmos.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { InfraLogger } from "../logger.js"
|
|
|
9
9
|
import type { FieldValues } from "../Model/filter/types.js"
|
|
10
10
|
import { type RawQuery } from "../Model/query.js"
|
|
11
11
|
import { buildWhereCosmosQuery3, logQuery } from "./Cosmos/query.js"
|
|
12
|
+
import { storeId } from "./Memory.js"
|
|
12
13
|
import { type FilterArgs, type PersistenceModelType, type StorageConfig, type Store, type StoreConfig, StoreMaker } from "./service.js"
|
|
13
14
|
|
|
14
15
|
const makeMapId =
|
|
@@ -50,7 +51,21 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
50
51
|
}))
|
|
51
52
|
)
|
|
52
53
|
|
|
53
|
-
const
|
|
54
|
+
const basePartitionKey = config?.partitionValue() ?? "primary"
|
|
55
|
+
const nsPrefix = (ns: string) => ns === "primary" ? "" : `${ns}::`
|
|
56
|
+
const nsPartitionValue = (ns: string, e?: Encoded) => {
|
|
57
|
+
const base = config?.partitionValue(e) ?? "primary"
|
|
58
|
+
return `${nsPrefix(ns)}${base}`
|
|
59
|
+
}
|
|
60
|
+
const resolveNamespace = !config?.allowNamespace
|
|
61
|
+
? Effect.succeed("primary")
|
|
62
|
+
: storeId.asEffect().pipe(Effect.map((namespace) => {
|
|
63
|
+
if (namespace !== "primary" && !config.allowNamespace!(namespace)) {
|
|
64
|
+
throw new Error(`Namespace ${namespace} not allowed!`)
|
|
65
|
+
}
|
|
66
|
+
return namespace
|
|
67
|
+
}))
|
|
68
|
+
const resolvePartitionKey = Effect.map(resolveNamespace, (ns) => `${nsPrefix(ns)}${basePartitionKey}`)
|
|
54
69
|
|
|
55
70
|
const defaultValues = config?.defaultValues ?? {}
|
|
56
71
|
const container = db.container(containerId)
|
|
@@ -60,7 +75,62 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
60
75
|
// then need to clean up the actual data.. perhaps first do with a config toggle to prescribe to it.
|
|
61
76
|
const importedMarkerId = containerId
|
|
62
77
|
|
|
63
|
-
const
|
|
78
|
+
const ctx = yield* Effect.context<R>()
|
|
79
|
+
const seedCache = new Map<string, Effect.Effect<void>>()
|
|
80
|
+
const makeSeedEffect = (ns: string) => {
|
|
81
|
+
const markerId = ns === "primary" ? importedMarkerId : `${importedMarkerId}::${ns}`
|
|
82
|
+
return Effect
|
|
83
|
+
.promise(() =>
|
|
84
|
+
container
|
|
85
|
+
.item(markerId, markerId)
|
|
86
|
+
.read<{ id: string }>()
|
|
87
|
+
.then(({ resource }) => Option.fromNullishOr(resource))
|
|
88
|
+
)
|
|
89
|
+
.pipe(
|
|
90
|
+
Effect.flatMap((marker) => {
|
|
91
|
+
if (Option.isSome(marker)) return Effect.void
|
|
92
|
+
return InfraLogger.logInfo(`Creating mock data for ${name} (namespace: ${ns})`).pipe(
|
|
93
|
+
Effect.andThen(seed!),
|
|
94
|
+
Effect.flatMap((m) =>
|
|
95
|
+
Effect.flatMapOption(
|
|
96
|
+
Effect.succeed(toNonEmptyArray([...m])),
|
|
97
|
+
(a) =>
|
|
98
|
+
bulkSetInternal(a, ns).pipe(
|
|
99
|
+
Effect.orDie,
|
|
100
|
+
Effect.delay(Duration.millis(1100))
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
),
|
|
104
|
+
Effect.andThen(
|
|
105
|
+
Effect.promise(() =>
|
|
106
|
+
container.items.create({
|
|
107
|
+
_partitionKey: markerId,
|
|
108
|
+
id: markerId,
|
|
109
|
+
ttl: -1
|
|
110
|
+
})
|
|
111
|
+
)
|
|
112
|
+
),
|
|
113
|
+
Effect.provide(ctx),
|
|
114
|
+
Effect.orDie
|
|
115
|
+
)
|
|
116
|
+
})
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
const seedNamespace = Effect.fn("seedNamespace")(function*(ns: string) {
|
|
120
|
+
if (!seed) return
|
|
121
|
+
let cached = seedCache.get(ns)
|
|
122
|
+
if (!cached) {
|
|
123
|
+
cached = yield* Effect.cached(makeSeedEffect(ns))
|
|
124
|
+
seedCache.set(ns, cached)
|
|
125
|
+
}
|
|
126
|
+
yield* cached
|
|
127
|
+
})
|
|
128
|
+
const resolveAndSeed = resolveNamespace.pipe(
|
|
129
|
+
Effect.tap((ns) => seedNamespace(ns))
|
|
130
|
+
)
|
|
131
|
+
const resolvePartitionKeyAndSeed = Effect.map(resolveAndSeed, (ns) => `${nsPrefix(ns)}${basePartitionKey}`)
|
|
132
|
+
|
|
133
|
+
const bulkSetInternal = (items: NonEmptyReadonlyArray<PM>, ns: string) =>
|
|
64
134
|
Effect
|
|
65
135
|
.gen(function*() {
|
|
66
136
|
// TODO: disable batching if need atomicity
|
|
@@ -77,7 +147,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
77
147
|
resourceBody: {
|
|
78
148
|
...Struct.omit(x, ["_etag", idKey]),
|
|
79
149
|
id: x[idKey],
|
|
80
|
-
_partitionKey:
|
|
150
|
+
_partitionKey: nsPartitionValue(ns, x)
|
|
81
151
|
}
|
|
82
152
|
// don't use this or we get an error that the request and some item partition key dont match - makese no sense
|
|
83
153
|
// partitionKey: config?.partitionValue(x)
|
|
@@ -89,7 +159,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
89
159
|
resourceBody: {
|
|
90
160
|
...Struct.omit(x, ["_etag", idKey]),
|
|
91
161
|
id: x[idKey],
|
|
92
|
-
_partitionKey:
|
|
162
|
+
_partitionKey: nsPartitionValue(ns, x)
|
|
93
163
|
},
|
|
94
164
|
ifMatch: eTag
|
|
95
165
|
// don't use this or we get an error that the request and some item partition key dont match - makese no sense
|
|
@@ -161,70 +231,79 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
161
231
|
|
|
162
232
|
return batchResult.flat() as unknown as NonEmptyReadonlyArray<Encoded>
|
|
163
233
|
})
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
234
|
+
|
|
235
|
+
const bulkSet = (items: NonEmptyReadonlyArray<PM>) =>
|
|
236
|
+
resolveAndSeed.pipe(Effect.flatMap((ns) =>
|
|
237
|
+
bulkSetInternal(items, ns).pipe(
|
|
238
|
+
Effect.withSpan("Cosmos.bulkSet [effect-app/infra/Store]", {
|
|
239
|
+
attributes: { "repository.container_id": containerId, "repository.model_name": name }
|
|
240
|
+
}, { captureStackTrace: false })
|
|
241
|
+
)
|
|
242
|
+
))
|
|
167
243
|
|
|
168
244
|
const batchSet = (items: NonEmptyReadonlyArray<PM>) => {
|
|
169
|
-
return
|
|
170
|
-
.
|
|
171
|
-
|
|
172
|
-
(
|
|
173
|
-
[
|
|
174
|
-
x
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
245
|
+
return resolveAndSeed
|
|
246
|
+
.pipe(Effect.flatMap((ns) =>
|
|
247
|
+
Effect
|
|
248
|
+
.suspend(() => {
|
|
249
|
+
const batch = [...items].map(
|
|
250
|
+
(x) =>
|
|
251
|
+
[
|
|
252
|
+
x,
|
|
253
|
+
Option.match(Option.fromNullishOr(x._etag), {
|
|
254
|
+
onNone: () => ({
|
|
255
|
+
operationType: "Create" as const,
|
|
256
|
+
resourceBody: {
|
|
257
|
+
...Struct.omit(x, ["_etag", idKey]),
|
|
258
|
+
id: x[idKey],
|
|
259
|
+
_partitionKey: nsPartitionValue(ns, x)
|
|
260
|
+
}
|
|
261
|
+
// don't use this or we get an error that the request and some item partition key dont match - makese no sense
|
|
262
|
+
// partitionKey: config?.partitionValue(x)
|
|
263
|
+
}),
|
|
264
|
+
onSome: (eTag) => ({
|
|
265
|
+
operationType: "Replace" as const,
|
|
266
|
+
id: x[idKey],
|
|
267
|
+
resourceBody: {
|
|
268
|
+
...Struct.omit(x, ["_etag", idKey]),
|
|
269
|
+
id: x[idKey],
|
|
270
|
+
_partitionKey: nsPartitionValue(ns, x)
|
|
271
|
+
},
|
|
272
|
+
// don't use this or we get an error that the request and some item partition key dont match - makese no sense
|
|
273
|
+
// partitionKey: config?.partitionValue(x)
|
|
274
|
+
ifMatch: eTag
|
|
275
|
+
})
|
|
276
|
+
})
|
|
277
|
+
] as const
|
|
278
|
+
)
|
|
201
279
|
|
|
202
|
-
|
|
280
|
+
const ex = batch.map(([, c]) => c)
|
|
203
281
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
282
|
+
return Effect
|
|
283
|
+
.promise(() => execBatch(ex, ex[0]?.resourceBody._partitionKey))
|
|
284
|
+
.pipe(Effect.flatMap(Effect.fnUntraced(function*(x) {
|
|
285
|
+
const result = x.result ?? []
|
|
286
|
+
const firstFailed = result.find(
|
|
287
|
+
(x: any) => x.statusCode > 299 || x.statusCode < 200
|
|
288
|
+
)
|
|
289
|
+
if (firstFailed) {
|
|
290
|
+
const code = firstFailed.statusCode ?? 0
|
|
291
|
+
if (code === 412 || code === 404 || code === 409) {
|
|
292
|
+
return yield* new OptimisticConcurrencyException({ type: name, id: "batch", code })
|
|
293
|
+
}
|
|
216
294
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
295
|
+
return yield* Effect.die(
|
|
296
|
+
new CosmosDbOperationError("not able to update record: " + code)
|
|
297
|
+
)
|
|
298
|
+
}
|
|
221
299
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
300
|
+
return batch.map(([e], i) => ({
|
|
301
|
+
...e,
|
|
302
|
+
_etag: result[i]?.eTag
|
|
303
|
+
})) as unknown as NonEmptyReadonlyArray<Encoded>
|
|
304
|
+
})))
|
|
305
|
+
})
|
|
306
|
+
))
|
|
228
307
|
.pipe(Effect
|
|
229
308
|
.withSpan("Cosmos.batchSet [effect-app/infra/Store]", {
|
|
230
309
|
attributes: { "repository.container_id": containerId, "repository.model_name": name }
|
|
@@ -234,14 +313,14 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
234
313
|
const s: Store<IdKey, Encoded> = {
|
|
235
314
|
queryRaw: <Out>(query: RawQuery<Encoded, Out>) =>
|
|
236
315
|
Effect
|
|
237
|
-
.sync(() => query.cosmos({ name }))
|
|
316
|
+
.all({ q: Effect.sync(() => query.cosmos({ name })), pk: resolvePartitionKeyAndSeed })
|
|
238
317
|
.pipe(
|
|
239
|
-
Effect.tap((q) => logQuery(q)),
|
|
240
|
-
Effect.flatMap((q) =>
|
|
318
|
+
Effect.tap(({ q }) => logQuery(q)),
|
|
319
|
+
Effect.flatMap(({ pk, q }) =>
|
|
241
320
|
Effect.promise(() =>
|
|
242
321
|
container
|
|
243
322
|
.items
|
|
244
|
-
.query<Out>(q, { partitionKey:
|
|
323
|
+
.query<Out>(q, { partitionKey: pk })
|
|
245
324
|
.fetchAll()
|
|
246
325
|
.then(({ resources }) =>
|
|
247
326
|
resources.map(
|
|
@@ -256,31 +335,36 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
256
335
|
}, { captureStackTrace: false })
|
|
257
336
|
),
|
|
258
337
|
batchRemove: (ids, partitionKey?: string) =>
|
|
259
|
-
Effect.
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
338
|
+
resolvePartitionKeyAndSeed.pipe(Effect.flatMap((pk) =>
|
|
339
|
+
Effect.promise(() =>
|
|
340
|
+
execBatch(
|
|
341
|
+
mutable(ids.map((id) =>
|
|
342
|
+
dropUndefinedT({
|
|
343
|
+
operationType: "Delete" as const,
|
|
344
|
+
id
|
|
345
|
+
// don't use this or we get an error that the request and some item partition key dont match - makese no sense
|
|
346
|
+
// partitionKey: config?.partitionValue({ [idKey]: id } as Encoded)
|
|
347
|
+
})
|
|
348
|
+
)),
|
|
349
|
+
partitionKey ?? pk
|
|
350
|
+
)
|
|
270
351
|
)
|
|
271
|
-
),
|
|
352
|
+
)),
|
|
272
353
|
all: Effect
|
|
273
|
-
.
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
354
|
+
.all({
|
|
355
|
+
q: Effect.sync(() => ({
|
|
356
|
+
query: `SELECT * FROM ${name}`,
|
|
357
|
+
parameters: []
|
|
358
|
+
})),
|
|
359
|
+
pk: resolvePartitionKeyAndSeed
|
|
360
|
+
})
|
|
277
361
|
.pipe(
|
|
278
|
-
Effect.tap((q) => logQuery(q)),
|
|
279
|
-
Effect.flatMap((q) =>
|
|
362
|
+
Effect.tap(({ q }) => logQuery(q)),
|
|
363
|
+
Effect.flatMap(({ pk, q }) =>
|
|
280
364
|
Effect.promise(() =>
|
|
281
365
|
container
|
|
282
366
|
.items
|
|
283
|
-
.query<PMCosmos>(q, { partitionKey:
|
|
367
|
+
.query<PMCosmos>(q, { partitionKey: pk })
|
|
284
368
|
.fetchAll()
|
|
285
369
|
.then(({ resources }) =>
|
|
286
370
|
resources.map(
|
|
@@ -305,42 +389,45 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
305
389
|
const filter = f.filter
|
|
306
390
|
type M = U extends undefined ? Encoded : Pick<Encoded, U>
|
|
307
391
|
return Effect
|
|
308
|
-
.
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
392
|
+
.all({
|
|
393
|
+
q: Effect.sync(() =>
|
|
394
|
+
buildWhereCosmosQuery3(
|
|
395
|
+
idKey,
|
|
396
|
+
filter ? [{ t: "where-scope", result: filter, relation: "some" }] : [],
|
|
397
|
+
name,
|
|
398
|
+
defaultValues,
|
|
399
|
+
f.select as
|
|
400
|
+
| NonEmptyReadonlyArray<string | { key: string; subKeys: readonly string[] }>
|
|
401
|
+
| undefined,
|
|
402
|
+
f.order as NonEmptyReadonlyArray<{ key: string; direction: "ASC" | "DESC" }> | undefined,
|
|
403
|
+
skip,
|
|
404
|
+
limit
|
|
405
|
+
)
|
|
406
|
+
),
|
|
407
|
+
pk: resolvePartitionKey
|
|
408
|
+
})
|
|
320
409
|
.pipe(
|
|
321
|
-
Effect.tap((q) => logQuery(q)),
|
|
410
|
+
Effect.tap(({ q }) => logQuery(q)),
|
|
322
411
|
Effect
|
|
323
|
-
.flatMap((q) =>
|
|
412
|
+
.flatMap(({ pk, q }) =>
|
|
324
413
|
Effect.promise(() =>
|
|
325
414
|
f.select
|
|
326
415
|
? container
|
|
327
416
|
.items
|
|
328
|
-
.query<M>(q, { partitionKey:
|
|
417
|
+
.query<M>(q, { partitionKey: pk })
|
|
329
418
|
.fetchAll()
|
|
330
419
|
.then(({ resources }) =>
|
|
331
|
-
resources.map((_) =>
|
|
332
|
-
(
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
}) as any
|
|
339
|
-
)
|
|
420
|
+
resources.map((_) => ({
|
|
421
|
+
...pipe(
|
|
422
|
+
defaultValues,
|
|
423
|
+
Struct.pick(f.select!.filter((_) => typeof _ === "string") as never[])
|
|
424
|
+
),
|
|
425
|
+
...mapReverseId(_ as any)
|
|
426
|
+
}))
|
|
340
427
|
)
|
|
341
428
|
: container
|
|
342
429
|
.items
|
|
343
|
-
.query<{ f: M }>(q, { partitionKey:
|
|
430
|
+
.query<{ f: M }>(q, { partitionKey: pk })
|
|
344
431
|
.fetchAll()
|
|
345
432
|
.then(({ resources }) =>
|
|
346
433
|
resources.map(({ f }) => ({ ...defaultValues, ...mapReverseId(f as any) }) as any)
|
|
@@ -355,114 +442,93 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
355
442
|
)
|
|
356
443
|
},
|
|
357
444
|
find: (id) =>
|
|
358
|
-
Effect
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
.pipe(Effect
|
|
368
|
-
.withSpan("Cosmos.find [effect-app/infra/Store]", {
|
|
369
|
-
attributes: {
|
|
370
|
-
"repository.container_id": containerId,
|
|
371
|
-
"repository.model_name": name,
|
|
372
|
-
partitionValue: config?.partitionValue({ [idKey]: id } as Encoded),
|
|
373
|
-
id
|
|
374
|
-
}
|
|
375
|
-
}, { captureStackTrace: false })),
|
|
376
|
-
set: (e) =>
|
|
377
|
-
Option
|
|
378
|
-
.match(
|
|
379
|
-
Option
|
|
380
|
-
.fromNullishOr(e._etag),
|
|
381
|
-
{
|
|
382
|
-
onNone: () =>
|
|
383
|
-
Effect.promise(() =>
|
|
384
|
-
container.items.create({
|
|
385
|
-
...mapId(e),
|
|
386
|
-
_partitionKey: config?.partitionValue(e)
|
|
387
|
-
})
|
|
388
|
-
),
|
|
389
|
-
onSome: (eTag) =>
|
|
390
|
-
Effect.promise(() =>
|
|
391
|
-
container.item(e[idKey], config?.partitionValue(e)).replace(
|
|
392
|
-
{ ...mapId(e), _partitionKey: config?.partitionValue(e) },
|
|
393
|
-
{
|
|
394
|
-
accessCondition: {
|
|
395
|
-
type: "IfMatch",
|
|
396
|
-
condition: eTag
|
|
397
|
-
}
|
|
398
|
-
}
|
|
445
|
+
resolveAndSeed.pipe(Effect.flatMap((ns) =>
|
|
446
|
+
Effect
|
|
447
|
+
.promise(() =>
|
|
448
|
+
container
|
|
449
|
+
.item(id, nsPartitionValue(ns, { [idKey]: id } as Encoded))
|
|
450
|
+
.read<Encoded>()
|
|
451
|
+
.then(({ resource }) =>
|
|
452
|
+
Option.fromNullishOr(resource).pipe(
|
|
453
|
+
Option.map((_) => ({ ...defaultValues, ...mapReverseId(_) }))
|
|
399
454
|
)
|
|
400
455
|
)
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
Effect
|
|
405
|
-
.flatMap((x) => {
|
|
406
|
-
if (x.statusCode === 412 || x.statusCode === 404 || x.statusCode === 409) {
|
|
407
|
-
return Effect.fail(new OptimisticConcurrencyException({ type: name, id: e[idKey], code: x.statusCode }))
|
|
408
|
-
}
|
|
409
|
-
if (x.statusCode > 299 || x.statusCode < 200) {
|
|
410
|
-
return Effect.die(
|
|
411
|
-
new CosmosDbOperationError(
|
|
412
|
-
"not able to update record: " + x.statusCode
|
|
413
|
-
)
|
|
414
|
-
)
|
|
415
|
-
}
|
|
416
|
-
return Effect.sync(() => ({
|
|
417
|
-
...e,
|
|
418
|
-
_etag: x.etag
|
|
419
|
-
}))
|
|
420
|
-
}),
|
|
421
|
-
Effect
|
|
422
|
-
.withSpan("Cosmos.set [effect-app/infra/Store]", {
|
|
456
|
+
)
|
|
457
|
+
.pipe(Effect
|
|
458
|
+
.withSpan("Cosmos.find [effect-app/infra/Store]", {
|
|
423
459
|
attributes: {
|
|
424
460
|
"repository.container_id": containerId,
|
|
425
461
|
"repository.model_name": name,
|
|
426
|
-
|
|
462
|
+
partitionValue: nsPartitionValue(ns, { [idKey]: id } as Encoded),
|
|
463
|
+
id
|
|
427
464
|
}
|
|
428
|
-
}, { captureStackTrace: false })
|
|
429
|
-
|
|
465
|
+
}, { captureStackTrace: false }))
|
|
466
|
+
)),
|
|
467
|
+
set: (e) =>
|
|
468
|
+
resolveAndSeed.pipe(Effect.flatMap((ns) =>
|
|
469
|
+
Option
|
|
470
|
+
.match(
|
|
471
|
+
Option
|
|
472
|
+
.fromNullishOr(e._etag),
|
|
473
|
+
{
|
|
474
|
+
onNone: () =>
|
|
475
|
+
Effect.promise(() =>
|
|
476
|
+
container.items.create({
|
|
477
|
+
...mapId(e),
|
|
478
|
+
_partitionKey: nsPartitionValue(ns, e)
|
|
479
|
+
})
|
|
480
|
+
),
|
|
481
|
+
onSome: (eTag) =>
|
|
482
|
+
Effect.promise(() =>
|
|
483
|
+
container.item(e[idKey], nsPartitionValue(ns, e)).replace(
|
|
484
|
+
{ ...mapId(e), _partitionKey: nsPartitionValue(ns, e) },
|
|
485
|
+
{
|
|
486
|
+
accessCondition: {
|
|
487
|
+
type: "IfMatch",
|
|
488
|
+
condition: eTag
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
)
|
|
492
|
+
)
|
|
493
|
+
}
|
|
494
|
+
)
|
|
495
|
+
.pipe(
|
|
496
|
+
Effect
|
|
497
|
+
.flatMap((x) => {
|
|
498
|
+
if (x.statusCode === 412 || x.statusCode === 404 || x.statusCode === 409) {
|
|
499
|
+
return Effect.fail(
|
|
500
|
+
new OptimisticConcurrencyException({ type: name, id: e[idKey], code: x.statusCode })
|
|
501
|
+
)
|
|
502
|
+
}
|
|
503
|
+
if (x.statusCode > 299 || x.statusCode < 200) {
|
|
504
|
+
return Effect.die(
|
|
505
|
+
new CosmosDbOperationError(
|
|
506
|
+
"not able to update record: " + x.statusCode
|
|
507
|
+
)
|
|
508
|
+
)
|
|
509
|
+
}
|
|
510
|
+
return Effect.sync(() => ({
|
|
511
|
+
...e,
|
|
512
|
+
_etag: x.etag
|
|
513
|
+
}))
|
|
514
|
+
}),
|
|
515
|
+
Effect
|
|
516
|
+
.withSpan("Cosmos.set [effect-app/infra/Store]", {
|
|
517
|
+
attributes: {
|
|
518
|
+
"repository.container_id": containerId,
|
|
519
|
+
"repository.model_name": name,
|
|
520
|
+
id: e[idKey]
|
|
521
|
+
}
|
|
522
|
+
}, { captureStackTrace: false })
|
|
523
|
+
)
|
|
524
|
+
)),
|
|
430
525
|
batchSet,
|
|
431
526
|
bulkSet
|
|
432
527
|
}
|
|
433
528
|
|
|
434
|
-
//
|
|
435
|
-
|
|
436
|
-
container
|
|
437
|
-
.item(importedMarkerId, importedMarkerId)
|
|
438
|
-
.read<{ id: string }>()
|
|
439
|
-
.then(({ resource }) => Option.fromNullishOr(resource))
|
|
440
|
-
)
|
|
529
|
+
// Eagerly seed primary namespace on initialization
|
|
530
|
+
yield* seedNamespace("primary")
|
|
441
531
|
|
|
442
|
-
if (!Option.isSome(marker)) {
|
|
443
|
-
yield* InfraLogger.logInfo("Creating mock data for " + name)
|
|
444
|
-
if (seed) {
|
|
445
|
-
const m = yield* seed
|
|
446
|
-
yield* Effect.flatMapOption(
|
|
447
|
-
Effect.succeed(toNonEmptyArray([...m])),
|
|
448
|
-
(a) =>
|
|
449
|
-
s.bulkSet(a).pipe(
|
|
450
|
-
Effect.orDie,
|
|
451
|
-
Effect
|
|
452
|
-
// we delay extra here, so that initial creation between Companies/POs also have an interval between them.
|
|
453
|
-
.delay(Duration.millis(1100))
|
|
454
|
-
)
|
|
455
|
-
)
|
|
456
|
-
}
|
|
457
|
-
// Mark as imported
|
|
458
|
-
yield* Effect.promise(() =>
|
|
459
|
-
container.items.create({
|
|
460
|
-
_partitionKey: importedMarkerId,
|
|
461
|
-
id: importedMarkerId,
|
|
462
|
-
ttl: -1
|
|
463
|
-
})
|
|
464
|
-
)
|
|
465
|
-
}
|
|
466
532
|
return s
|
|
467
533
|
})
|
|
468
534
|
}
|
package/src/Store/Disk.ts
CHANGED
|
@@ -66,11 +66,10 @@ function makeDiskStoreInt<IdKey extends keyof Encoded, Encoded extends FieldValu
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
// lock file for cross-process coordination during initialization
|
|
69
|
-
const lockFile = file + ".lock"
|
|
70
69
|
|
|
71
70
|
// wrap initialization in file lock to prevent race conditions in multi-worker setups
|
|
72
71
|
const store = yield* fu.withFileLock(
|
|
73
|
-
|
|
72
|
+
file,
|
|
74
73
|
Effect.gen(function*() {
|
|
75
74
|
const shouldSeed = !(fs.existsSync(file))
|
|
76
75
|
|
|
@@ -143,7 +142,7 @@ export function makeDiskStore({ prefix }: StorageConfig, dir: string) {
|
|
|
143
142
|
const storesSem = Semaphore.makeUnsafe(1)
|
|
144
143
|
const primary = yield* makeDiskStoreInt(prefix, idKey, "primary", dir, name, seed, config?.defaultValues)
|
|
145
144
|
const stores = new Map<string, Store<IdKey, Encoded>>([["primary", primary]])
|
|
146
|
-
const ctx = yield* Effect.
|
|
145
|
+
const ctx = yield* Effect.context<R>()
|
|
147
146
|
const getStore = !config?.allowNamespace
|
|
148
147
|
? Effect.succeed(primary)
|
|
149
148
|
: storeId.asEffect().pipe(Effect.flatMap((namespace) => {
|
package/src/Store/Memory.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
2
|
|
|
3
|
-
import { Array, Effect, flow, type NonEmptyReadonlyArray, Option, Order, pipe, Ref, Result, Semaphore,
|
|
3
|
+
import { Array, Context, Effect, flow, type NonEmptyReadonlyArray, Option, Order, pipe, Ref, Result, Semaphore, Struct } from "effect-app"
|
|
4
4
|
import { NonEmptyString255 } from "effect-app/Schema"
|
|
5
5
|
import { get } from "effect-app/utils"
|
|
6
6
|
import { InfraLogger } from "../logger.js"
|
|
@@ -24,7 +24,7 @@ export function memFilter<T extends FieldValues, U extends keyof T = never>(f: F
|
|
|
24
24
|
)
|
|
25
25
|
const n = Struct.pick(i, keys)
|
|
26
26
|
subKeys.forEach((subKey) => {
|
|
27
|
-
n[subKey.key] = i[subKey.key]!.map(Struct.pick(subKey.subKeys))
|
|
27
|
+
n[subKey.key] = i[subKey.key]!.map(Struct.pick(subKey.subKeys as never[]))
|
|
28
28
|
})
|
|
29
29
|
return n as M
|
|
30
30
|
}) as any
|
|
@@ -72,9 +72,7 @@ export function memFilter<T extends FieldValues, U extends keyof T = never>(f: F
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
const defaultNs: NonEmptyString255 = NonEmptyString255("primary")
|
|
75
|
-
export class storeId
|
|
76
|
-
extends ServiceMap.Reference("StoreId", { defaultValue: (): NonEmptyString255 => defaultNs })
|
|
77
|
-
{}
|
|
75
|
+
export class storeId extends Context.Reference("StoreId", { defaultValue: (): NonEmptyString255 => defaultNs }) {}
|
|
78
76
|
|
|
79
77
|
function logQuery(f: FilterArgs<any, any>, defaultValues?: any) {
|
|
80
78
|
return InfraLogger
|
|
@@ -267,7 +265,7 @@ export const makeMemoryStore = () => ({
|
|
|
267
265
|
seed,
|
|
268
266
|
config?.defaultValues
|
|
269
267
|
)
|
|
270
|
-
const ctx = yield* Effect.
|
|
268
|
+
const ctx = yield* Effect.context<R>()
|
|
271
269
|
const stores = new Map([["primary", primary]])
|
|
272
270
|
const getStore = !config?.allowNamespace
|
|
273
271
|
? Effect.succeed(primary)
|