@effect-app/infra 4.0.0-beta.111 → 4.0.0-beta.113
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 +21 -0
- 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/internal/internal.d.ts.map +1 -1
- package/dist/Model/Repository/internal/internal.js +2 -1
- package/dist/Model/Repository/makeRepo.d.ts +3 -2
- package/dist/Model/Repository/makeRepo.d.ts.map +1 -1
- package/dist/Model/Repository/makeRepo.js +4 -1
- package/dist/Model/Repository/service.d.ts +5 -0
- package/dist/Model/Repository/service.d.ts.map +1 -1
- package/dist/Model/Repository.d.ts +1 -0
- package/dist/Model/Repository.d.ts.map +1 -1
- package/dist/Model/Repository.js +2 -1
- package/dist/Model.d.ts +1 -0
- package/dist/Model.d.ts.map +1 -1
- package/dist/Model.js +2 -1
- package/dist/OperationsRepo.d.ts +1 -1
- package/dist/Store/ContextMapContainer.d.ts +1 -1
- package/dist/Store/Cosmos.d.ts.map +1 -1
- package/dist/Store/Cosmos.js +10 -10
- package/dist/Store/Disk.d.ts.map +1 -1
- package/dist/Store/Disk.js +22 -18
- package/dist/Store/Memory.d.ts.map +1 -1
- package/dist/Store/Memory.js +23 -18
- package/dist/Store/SQL/Pg.d.ts.map +1 -1
- package/dist/Store/SQL/Pg.js +27 -21
- package/dist/Store/SQL.d.ts.map +1 -1
- package/dist/Store/SQL.js +53 -41
- package/dist/Store/index.d.ts +1 -1
- package/dist/Store/index.d.ts.map +1 -1
- package/dist/Store/index.js +4 -2
- package/dist/Store/service.d.ts +5 -0
- package/dist/Store/service.d.ts.map +1 -1
- package/dist/Store/service.js +1 -1
- package/dist/api/internal/RequestContextMiddleware.d.ts +1 -1
- package/package.json +6 -2
- package/src/Model/Repository/Registry.ts +33 -0
- package/src/Model/Repository/internal/internal.ts +1 -0
- package/src/Model/Repository/makeRepo.ts +5 -2
- package/src/Model/Repository/service.ts +6 -0
- package/src/Model/Repository.ts +1 -0
- package/src/Model.ts +1 -0
- package/src/Store/Cosmos.ts +10 -12
- package/src/Store/Disk.ts +35 -30
- package/src/Store/Memory.ts +25 -18
- package/src/Store/SQL/Pg.ts +29 -42
- package/src/Store/SQL.ts +59 -84
- package/src/Store/index.ts +2 -1
- package/src/Store/service.ts +5 -0
- package/test/contextProvider.test.ts +6 -6
- package/test/dist/query.test.d.ts.map +1 -1
- package/test/dist/rawQuery.test.d.ts.map +1 -1
- package/test/query.test.ts +22 -19
- package/test/rawQuery.test.ts +4 -2
- package/test/validateSample.test.ts +11 -8
package/src/Store/Cosmos.ts
CHANGED
|
@@ -127,10 +127,6 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
127
127
|
}
|
|
128
128
|
yield* cached
|
|
129
129
|
})
|
|
130
|
-
const resolveAndSeed = resolveNamespace.pipe(
|
|
131
|
-
Effect.tap((ns) => seedNamespace(ns))
|
|
132
|
-
)
|
|
133
|
-
|
|
134
130
|
const bulkSetInternal = (items: NonEmptyReadonlyArray<PM>, ns: string) =>
|
|
135
131
|
Effect
|
|
136
132
|
.gen(function*() {
|
|
@@ -239,10 +235,10 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
239
235
|
)
|
|
240
236
|
|
|
241
237
|
const bulkSet = (items: NonEmptyReadonlyArray<PM>) =>
|
|
242
|
-
|
|
238
|
+
resolveNamespace.pipe(Effect.flatMap((ns) => bulkSetInternal(items, ns)))
|
|
243
239
|
|
|
244
240
|
const batchSet = (items: NonEmptyReadonlyArray<PM>) => {
|
|
245
|
-
return
|
|
241
|
+
return resolveNamespace
|
|
246
242
|
.pipe(Effect.flatMap((ns) =>
|
|
247
243
|
Effect
|
|
248
244
|
.suspend(() => {
|
|
@@ -315,9 +311,11 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
315
311
|
}
|
|
316
312
|
|
|
317
313
|
const s: Store<IdKey, Encoded> = {
|
|
314
|
+
seedNamespace: (ns) => seedNamespace(ns),
|
|
315
|
+
|
|
318
316
|
queryRaw: <Out>(query: RawQuery<Encoded, Out>) =>
|
|
319
317
|
Effect
|
|
320
|
-
.all({ q: Effect.sync(() => query.cosmos({ name })), ns:
|
|
318
|
+
.all({ q: Effect.sync(() => query.cosmos({ name })), ns: resolveNamespace })
|
|
321
319
|
.pipe(
|
|
322
320
|
Effect.tap(({ q }) => logQuery(q)),
|
|
323
321
|
Effect.flatMap(({ ns, q }) =>
|
|
@@ -345,7 +343,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
345
343
|
)
|
|
346
344
|
),
|
|
347
345
|
batchRemove: (ids, partitionKey?: string) =>
|
|
348
|
-
|
|
346
|
+
resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
349
347
|
Effect
|
|
350
348
|
.promise(() =>
|
|
351
349
|
execBatch(
|
|
@@ -376,7 +374,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
376
374
|
query: `SELECT * FROM ${name}`,
|
|
377
375
|
parameters: []
|
|
378
376
|
})),
|
|
379
|
-
ns:
|
|
377
|
+
ns: resolveNamespace
|
|
380
378
|
})
|
|
381
379
|
.pipe(
|
|
382
380
|
Effect.tap(({ q }) => logQuery(q)),
|
|
@@ -430,7 +428,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
430
428
|
limit
|
|
431
429
|
)
|
|
432
430
|
),
|
|
433
|
-
ns:
|
|
431
|
+
ns: resolveNamespace
|
|
434
432
|
})
|
|
435
433
|
.pipe(
|
|
436
434
|
Effect.tap(({ q }) => logQuery(q)),
|
|
@@ -473,7 +471,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
473
471
|
)
|
|
474
472
|
},
|
|
475
473
|
find: (id) =>
|
|
476
|
-
|
|
474
|
+
resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
477
475
|
Effect
|
|
478
476
|
.promise(() =>
|
|
479
477
|
container
|
|
@@ -497,7 +495,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
497
495
|
}, { captureStackTrace: false }))
|
|
498
496
|
)),
|
|
499
497
|
set: (e) =>
|
|
500
|
-
|
|
498
|
+
resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
501
499
|
Option
|
|
502
500
|
.match(
|
|
503
501
|
Option
|
package/src/Store/Disk.ts
CHANGED
|
@@ -139,43 +139,48 @@ export function makeDiskStore({ prefix }: StorageConfig, dir: string) {
|
|
|
139
139
|
config?: StoreConfig<Encoded>
|
|
140
140
|
) =>
|
|
141
141
|
Effect.gen(function*() {
|
|
142
|
-
const storesSem = Semaphore.makeUnsafe(1)
|
|
143
142
|
const primary = yield* makeDiskStoreInt(prefix, idKey, "primary", dir, name, seed, config?.defaultValues)
|
|
144
143
|
const stores = new Map<string, Store<IdKey, Encoded>>([["primary", primary]])
|
|
145
144
|
const ctx = yield* Effect.context<R>()
|
|
145
|
+
const semaphores = new Map<string, Semaphore.Semaphore>()
|
|
146
|
+
const getSem = (ns: string) => {
|
|
147
|
+
let sem = semaphores.get(ns)
|
|
148
|
+
if (!sem) {
|
|
149
|
+
sem = Semaphore.makeUnsafe(1)
|
|
150
|
+
semaphores.set(ns, sem)
|
|
151
|
+
}
|
|
152
|
+
return sem
|
|
153
|
+
}
|
|
154
|
+
const ensureStore = (namespace: string) =>
|
|
155
|
+
getSem(namespace).withPermits(1)(
|
|
156
|
+
Effect.suspend(() => {
|
|
157
|
+
const existing = stores.get(namespace)
|
|
158
|
+
if (existing) return Effect.succeed(existing)
|
|
159
|
+
if (config?.allowNamespace && !config.allowNamespace(namespace)) {
|
|
160
|
+
throw new Error(`Namespace ${namespace} not allowed!`)
|
|
161
|
+
}
|
|
162
|
+
return makeDiskStoreInt<IdKey, Encoded, R, E>(
|
|
163
|
+
prefix,
|
|
164
|
+
idKey,
|
|
165
|
+
namespace,
|
|
166
|
+
dir,
|
|
167
|
+
name,
|
|
168
|
+
seed,
|
|
169
|
+
config?.defaultValues
|
|
170
|
+
)
|
|
171
|
+
.pipe(
|
|
172
|
+
Effect.orDie,
|
|
173
|
+
Effect.provide(ctx),
|
|
174
|
+
Effect.tap((store) => Effect.sync(() => stores.set(namespace, store)))
|
|
175
|
+
)
|
|
176
|
+
})
|
|
177
|
+
)
|
|
146
178
|
const getStore = !config?.allowNamespace
|
|
147
179
|
? Effect.succeed(primary)
|
|
148
|
-
: storeId.asEffect().pipe(Effect.flatMap((namespace) =>
|
|
149
|
-
const store = stores.get(namespace)
|
|
150
|
-
if (store) {
|
|
151
|
-
return Effect.succeed(store)
|
|
152
|
-
}
|
|
153
|
-
if (!config.allowNamespace!(namespace)) {
|
|
154
|
-
throw new Error(`Namespace ${namespace} not allowed!`)
|
|
155
|
-
}
|
|
156
|
-
return storesSem.withPermits(1)(
|
|
157
|
-
Effect.suspend(() => {
|
|
158
|
-
const existing = stores.get(namespace)
|
|
159
|
-
if (existing) return Effect.sync(() => existing)
|
|
160
|
-
return makeDiskStoreInt<IdKey, Encoded, R, E>(
|
|
161
|
-
prefix,
|
|
162
|
-
idKey,
|
|
163
|
-
namespace,
|
|
164
|
-
dir,
|
|
165
|
-
name,
|
|
166
|
-
seed,
|
|
167
|
-
config?.defaultValues
|
|
168
|
-
)
|
|
169
|
-
.pipe(
|
|
170
|
-
Effect.orDie,
|
|
171
|
-
Effect.provide(ctx),
|
|
172
|
-
Effect.tap((store) => Effect.sync(() => stores.set(namespace, store)))
|
|
173
|
-
)
|
|
174
|
-
})
|
|
175
|
-
)
|
|
176
|
-
}))
|
|
180
|
+
: storeId.asEffect().pipe(Effect.flatMap((namespace) => ensureStore(namespace)))
|
|
177
181
|
|
|
178
182
|
const s: Store<IdKey, Encoded> = {
|
|
183
|
+
seedNamespace: (namespace) => ensureStore(namespace).pipe(Effect.asVoid),
|
|
179
184
|
all: Effect.flatMap(getStore, (_) => _.all),
|
|
180
185
|
find: (...args) => Effect.flatMap(getStore, (_) => _.find(...args)),
|
|
181
186
|
filter: (...args) => Effect.flatMap(getStore, (_) => _.filter(...args)),
|
package/src/Store/Memory.ts
CHANGED
|
@@ -151,6 +151,8 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
|
|
|
151
151
|
withPermit
|
|
152
152
|
)
|
|
153
153
|
const s: Store<IdKey, Encoded> = {
|
|
154
|
+
seedNamespace: () => Effect.void,
|
|
155
|
+
|
|
154
156
|
queryRaw: (query) =>
|
|
155
157
|
all
|
|
156
158
|
.pipe(
|
|
@@ -257,7 +259,6 @@ export const makeMemoryStore = () => ({
|
|
|
257
259
|
config?: StoreConfig<Encoded>
|
|
258
260
|
) =>
|
|
259
261
|
Effect.gen(function*() {
|
|
260
|
-
const storesSem = Semaphore.makeUnsafe(1)
|
|
261
262
|
const primary = yield* makeMemoryStoreInt<IdKey, Encoded, R, E>(
|
|
262
263
|
modelName,
|
|
263
264
|
idKey,
|
|
@@ -267,28 +268,34 @@ export const makeMemoryStore = () => ({
|
|
|
267
268
|
)
|
|
268
269
|
const ctx = yield* Effect.context<R>()
|
|
269
270
|
const stores = new Map([["primary", primary]])
|
|
270
|
-
const
|
|
271
|
-
|
|
272
|
-
|
|
271
|
+
const semaphores = new Map<string, Semaphore.Semaphore>()
|
|
272
|
+
const getSem = (ns: string) => {
|
|
273
|
+
let sem = semaphores.get(ns)
|
|
274
|
+
if (!sem) {
|
|
275
|
+
sem = Semaphore.makeUnsafe(1)
|
|
276
|
+
semaphores.set(ns, sem)
|
|
277
|
+
}
|
|
278
|
+
return sem
|
|
279
|
+
}
|
|
280
|
+
const ensureStore = (namespace: string) =>
|
|
281
|
+
getSem(namespace).withPermits(1)(Effect.suspend(() => {
|
|
273
282
|
const store = stores.get(namespace)
|
|
274
|
-
if (store)
|
|
275
|
-
|
|
276
|
-
}
|
|
277
|
-
if (!config.allowNamespace!(namespace)) {
|
|
283
|
+
if (store) return Effect.succeed(store)
|
|
284
|
+
if (config?.allowNamespace && !config.allowNamespace(namespace)) {
|
|
278
285
|
throw new Error(`Namespace ${namespace} not allowed!`)
|
|
279
286
|
}
|
|
280
|
-
return
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
.
|
|
285
|
-
|
|
286
|
-
Effect.provide(ctx),
|
|
287
|
-
Effect.tap((store) => Effect.sync(() => stores.set(namespace, store)))
|
|
288
|
-
)
|
|
289
|
-
}))
|
|
287
|
+
return makeMemoryStoreInt(modelName, idKey, namespace, seed, config?.defaultValues)
|
|
288
|
+
.pipe(
|
|
289
|
+
Effect.orDie,
|
|
290
|
+
Effect.provide(ctx),
|
|
291
|
+
Effect.tap((store) => Effect.sync(() => stores.set(namespace, store)))
|
|
292
|
+
)
|
|
290
293
|
}))
|
|
294
|
+
const getStore = !config?.allowNamespace
|
|
295
|
+
? Effect.succeed(primary)
|
|
296
|
+
: storeId.asEffect().pipe(Effect.flatMap((namespace) => ensureStore(namespace)))
|
|
291
297
|
const s: Store<IdKey, Encoded> = {
|
|
298
|
+
seedNamespace: (namespace) => ensureStore(namespace).pipe(Effect.asVoid),
|
|
292
299
|
all: Effect.flatMap(getStore, (_) => _.all),
|
|
293
300
|
queryRaw: (...args) => Effect.flatMap(getStore, (_) => _.queryRaw(...args)),
|
|
294
301
|
find: (...args) => Effect.flatMap(getStore, (_) => _.find(...args)),
|
package/src/Store/SQL/Pg.ts
CHANGED
|
@@ -61,11 +61,11 @@ function makePgStore({ prefix }: StorageConfig) {
|
|
|
61
61
|
return namespace
|
|
62
62
|
}))
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
const ensureTable = sql
|
|
65
65
|
.unsafe(
|
|
66
66
|
`CREATE TABLE IF NOT EXISTS "${tableName}" (id TEXT NOT NULL, _namespace TEXT NOT NULL DEFAULT 'primary', _etag TEXT, data JSONB NOT NULL, PRIMARY KEY (id, _namespace))`
|
|
67
67
|
)
|
|
68
|
-
.pipe(Effect.orDie)
|
|
68
|
+
.pipe(Effect.orDie, Effect.asVoid)
|
|
69
69
|
|
|
70
70
|
const toRow = (e: PM) => {
|
|
71
71
|
const newE = makeETag(e)
|
|
@@ -130,48 +130,35 @@ function makePgStore({ prefix }: StorageConfig) {
|
|
|
130
130
|
|
|
131
131
|
const ctx = yield* Effect.context<R>()
|
|
132
132
|
const seedCache = new Map<string, Effect.Effect<void>>()
|
|
133
|
-
const makeSeedEffect = (ns: string)
|
|
134
|
-
|
|
133
|
+
const makeSeedEffect = Effect.fnUntraced(function*(ns: string) {
|
|
134
|
+
yield* ensureTable
|
|
135
|
+
if (!seed) return
|
|
136
|
+
const existing = yield* exec(
|
|
135
137
|
`SELECT id FROM "${tableName}" WHERE id = $1 AND _namespace = $2`,
|
|
136
138
|
[seedMarkerId, `__seed__::${ns}`]
|
|
137
139
|
)
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
Effect.andThen(
|
|
150
|
-
exec(
|
|
151
|
-
`INSERT INTO "${tableName}" (id, _namespace, _etag, data) VALUES ($1, $2, $3, $4)`,
|
|
152
|
-
[seedMarkerId, `__seed__::${ns}`, null, JSON.stringify({ _marker: true })]
|
|
153
|
-
)
|
|
154
|
-
),
|
|
155
|
-
Effect.provide(ctx),
|
|
156
|
-
Effect.orDie
|
|
157
|
-
)
|
|
158
|
-
})
|
|
159
|
-
)
|
|
160
|
-
const seedNamespace = Effect.fn("seedNamespace")(function*(ns: string) {
|
|
161
|
-
if (!seed) return
|
|
140
|
+
if ((existing as any[]).length > 0) return
|
|
141
|
+
yield* InfraLogger.logInfo(`Seeding data for ${name} (namespace: ${ns})`)
|
|
142
|
+
const items = yield* seed.pipe(Effect.provide(ctx), Effect.orDie)
|
|
143
|
+
const ne = toNonEmptyArray([...items])
|
|
144
|
+
if (Option.isSome(ne)) yield* bulkSetInternal(ne.value, ns)
|
|
145
|
+
yield* exec(
|
|
146
|
+
`INSERT INTO "${tableName}" (id, _namespace, _etag, data) VALUES ($1, $2, $3, $4)`,
|
|
147
|
+
[seedMarkerId, `__seed__::${ns}`, null, JSON.stringify({ _marker: true })]
|
|
148
|
+
)
|
|
149
|
+
})
|
|
150
|
+
const seedNamespace = (ns: string) => {
|
|
162
151
|
let cached = seedCache.get(ns)
|
|
163
152
|
if (!cached) {
|
|
164
|
-
cached =
|
|
153
|
+
cached = Effect.cached(makeSeedEffect(ns)).pipe(Effect.runSync)
|
|
165
154
|
seedCache.set(ns, cached)
|
|
166
155
|
}
|
|
167
|
-
|
|
168
|
-
}
|
|
169
|
-
const resolveAndSeed = resolveNamespace.pipe(
|
|
170
|
-
Effect.tap((ns) => seedNamespace(ns))
|
|
171
|
-
)
|
|
172
|
-
|
|
156
|
+
return cached
|
|
157
|
+
}
|
|
173
158
|
const s: Store<IdKey, Encoded> = {
|
|
174
|
-
|
|
159
|
+
seedNamespace: (ns) => seedNamespace(ns),
|
|
160
|
+
|
|
161
|
+
all: resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
175
162
|
exec(`SELECT id, _etag, data FROM "${tableName}" WHERE _namespace = $1`, [ns])
|
|
176
163
|
.pipe(
|
|
177
164
|
Effect.map((rows) => (rows as any[]).map((r) => parseRow<Encoded>(r, idKey, defaultValues))),
|
|
@@ -186,7 +173,7 @@ function makePgStore({ prefix }: StorageConfig) {
|
|
|
186
173
|
)),
|
|
187
174
|
|
|
188
175
|
find: (id) =>
|
|
189
|
-
|
|
176
|
+
resolveNamespace.pipe(Effect
|
|
190
177
|
.flatMap((ns) =>
|
|
191
178
|
exec(`SELECT id, _etag, data FROM "${tableName}" WHERE id = $1 AND _namespace = $2`, [id, ns])
|
|
192
179
|
.pipe(
|
|
@@ -206,7 +193,7 @@ function makePgStore({ prefix }: StorageConfig) {
|
|
|
206
193
|
const filter = f
|
|
207
194
|
.filter
|
|
208
195
|
type M = U extends undefined ? Encoded : Pick<Encoded, U>
|
|
209
|
-
return
|
|
196
|
+
return resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
210
197
|
Effect
|
|
211
198
|
.sync(() => {
|
|
212
199
|
const q = buildWhereSQLQuery(
|
|
@@ -261,7 +248,7 @@ function makePgStore({ prefix }: StorageConfig) {
|
|
|
261
248
|
},
|
|
262
249
|
|
|
263
250
|
set: (e) =>
|
|
264
|
-
|
|
251
|
+
resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
265
252
|
setInternal(e, ns).pipe(
|
|
266
253
|
Effect.withSpan("PgSQL.set [effect-app/infra/Store]", {
|
|
267
254
|
attributes: { "repository.table_name": tableName, "repository.model_name": name, id: e[idKey] }
|
|
@@ -270,7 +257,7 @@ function makePgStore({ prefix }: StorageConfig) {
|
|
|
270
257
|
)),
|
|
271
258
|
|
|
272
259
|
batchSet: (items) =>
|
|
273
|
-
|
|
260
|
+
resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
274
261
|
bulkSetInternal(items, ns).pipe(
|
|
275
262
|
Effect.withSpan("PgSQL.batchSet [effect-app/infra/Store]", {
|
|
276
263
|
attributes: { "repository.table_name": tableName, "repository.model_name": name }
|
|
@@ -279,7 +266,7 @@ function makePgStore({ prefix }: StorageConfig) {
|
|
|
279
266
|
)),
|
|
280
267
|
|
|
281
268
|
bulkSet: (items) =>
|
|
282
|
-
|
|
269
|
+
resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
283
270
|
bulkSetInternal(items, ns).pipe(
|
|
284
271
|
Effect.withSpan("PgSQL.bulkSet [effect-app/infra/Store]", {
|
|
285
272
|
attributes: { "repository.table_name": tableName, "repository.model_name": name }
|
|
@@ -290,7 +277,7 @@ function makePgStore({ prefix }: StorageConfig) {
|
|
|
290
277
|
batchRemove: (ids) => {
|
|
291
278
|
const placeholders = ids.map((_, i) => `$${i + 1}`).join(", ")
|
|
292
279
|
const nsPlaceholder = `$${ids.length + 1}`
|
|
293
|
-
return
|
|
280
|
+
return resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
294
281
|
exec(
|
|
295
282
|
`DELETE FROM "${tableName}" WHERE id IN (${placeholders}) AND _namespace = ${nsPlaceholder}`,
|
|
296
283
|
[...ids, ns]
|
package/src/Store/SQL.ts
CHANGED
|
@@ -75,11 +75,11 @@ function makeSQLStoreInt(dialect: SQLDialect, jsonColumnType: string) {
|
|
|
75
75
|
return namespace
|
|
76
76
|
}))
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
const ensureTable = sql
|
|
79
79
|
.unsafe(
|
|
80
80
|
`CREATE TABLE IF NOT EXISTS "${tableName}" (id TEXT NOT NULL, _namespace TEXT NOT NULL DEFAULT 'primary', _etag TEXT, data ${jsonColumnType} NOT NULL, PRIMARY KEY (id, _namespace))`
|
|
81
81
|
)
|
|
82
|
-
.pipe(Effect.orDie)
|
|
82
|
+
.pipe(Effect.orDie, Effect.asVoid)
|
|
83
83
|
|
|
84
84
|
const toRow = (e: PM) => {
|
|
85
85
|
const newE = makeETag(e)
|
|
@@ -144,48 +144,35 @@ function makeSQLStoreInt(dialect: SQLDialect, jsonColumnType: string) {
|
|
|
144
144
|
|
|
145
145
|
const ctx = yield* Effect.context<R>()
|
|
146
146
|
const seedCache = new Map<string, Effect.Effect<void>>()
|
|
147
|
-
const makeSeedEffect = (ns: string)
|
|
148
|
-
|
|
147
|
+
const makeSeedEffect = Effect.fnUntraced(function*(ns: string) {
|
|
148
|
+
yield* ensureTable
|
|
149
|
+
if (!seed) return
|
|
150
|
+
const existing = yield* exec(
|
|
149
151
|
`SELECT id FROM "${tableName}" WHERE id = ? AND _namespace = ?`,
|
|
150
152
|
[seedMarkerId, `__seed__::${ns}`]
|
|
151
153
|
)
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
Effect.andThen(
|
|
164
|
-
exec(
|
|
165
|
-
`INSERT INTO "${tableName}" (id, _namespace, _etag, data) VALUES (?, ?, ?, ?)`,
|
|
166
|
-
[seedMarkerId, `__seed__::${ns}`, null, JSON.stringify({ _marker: true })]
|
|
167
|
-
)
|
|
168
|
-
),
|
|
169
|
-
Effect.provide(ctx),
|
|
170
|
-
Effect.orDie
|
|
171
|
-
)
|
|
172
|
-
})
|
|
173
|
-
)
|
|
174
|
-
const seedNamespace = Effect.fn("seedNamespace")(function*(ns: string) {
|
|
175
|
-
if (!seed) return
|
|
154
|
+
if ((existing as any[]).length > 0) return
|
|
155
|
+
yield* InfraLogger.logInfo(`Seeding data for ${name} (namespace: ${ns})`)
|
|
156
|
+
const items = yield* seed.pipe(Effect.provide(ctx), Effect.orDie)
|
|
157
|
+
const ne = toNonEmptyArray([...items])
|
|
158
|
+
if (Option.isSome(ne)) yield* bulkSetInternal(ne.value, ns)
|
|
159
|
+
yield* exec(
|
|
160
|
+
`INSERT INTO "${tableName}" (id, _namespace, _etag, data) VALUES (?, ?, ?, ?)`,
|
|
161
|
+
[seedMarkerId, `__seed__::${ns}`, null, JSON.stringify({ _marker: true })]
|
|
162
|
+
)
|
|
163
|
+
})
|
|
164
|
+
const seedNamespace = (ns: string) => {
|
|
176
165
|
let cached = seedCache.get(ns)
|
|
177
166
|
if (!cached) {
|
|
178
|
-
cached =
|
|
167
|
+
cached = Effect.cached(makeSeedEffect(ns)).pipe(Effect.runSync)
|
|
179
168
|
seedCache.set(ns, cached)
|
|
180
169
|
}
|
|
181
|
-
|
|
182
|
-
}
|
|
183
|
-
const resolveAndSeed = resolveNamespace.pipe(
|
|
184
|
-
Effect.tap((ns) => seedNamespace(ns))
|
|
185
|
-
)
|
|
186
|
-
|
|
170
|
+
return cached
|
|
171
|
+
}
|
|
187
172
|
const s: Store<IdKey, Encoded> = {
|
|
188
|
-
|
|
173
|
+
seedNamespace: (ns) => seedNamespace(ns),
|
|
174
|
+
|
|
175
|
+
all: resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
189
176
|
exec(`SELECT id, _etag, data FROM "${tableName}" WHERE _namespace = ?`, [ns])
|
|
190
177
|
.pipe(
|
|
191
178
|
Effect.map((rows) => (rows as any[]).map((r) => parseRow<Encoded>(r, idKey, defaultValues))),
|
|
@@ -200,7 +187,7 @@ function makeSQLStoreInt(dialect: SQLDialect, jsonColumnType: string) {
|
|
|
200
187
|
)),
|
|
201
188
|
|
|
202
189
|
find: (id) =>
|
|
203
|
-
|
|
190
|
+
resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
204
191
|
exec(`SELECT id, _etag, data FROM "${tableName}" WHERE id = ? AND _namespace = ?`, [id, ns])
|
|
205
192
|
.pipe(
|
|
206
193
|
Effect.map((rows) => {
|
|
@@ -220,7 +207,7 @@ function makeSQLStoreInt(dialect: SQLDialect, jsonColumnType: string) {
|
|
|
220
207
|
.filter
|
|
221
208
|
type M = U extends undefined ? Encoded
|
|
222
209
|
: Pick<Encoded, U>
|
|
223
|
-
return
|
|
210
|
+
return resolveNamespace
|
|
224
211
|
.pipe(Effect
|
|
225
212
|
.flatMap((ns) =>
|
|
226
213
|
Effect
|
|
@@ -298,7 +285,7 @@ function makeSQLStoreInt(dialect: SQLDialect, jsonColumnType: string) {
|
|
|
298
285
|
},
|
|
299
286
|
|
|
300
287
|
set: (e) =>
|
|
301
|
-
|
|
288
|
+
resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
302
289
|
setInternal(e, ns).pipe(
|
|
303
290
|
Effect.withSpan("SQL.set [effect-app/infra/Store]", {
|
|
304
291
|
attributes: { "repository.table_name": tableName, "repository.model_name": name, id: e[idKey] }
|
|
@@ -307,7 +294,7 @@ function makeSQLStoreInt(dialect: SQLDialect, jsonColumnType: string) {
|
|
|
307
294
|
)),
|
|
308
295
|
|
|
309
296
|
batchSet: (items) =>
|
|
310
|
-
|
|
297
|
+
resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
311
298
|
bulkSetInternal(items, ns).pipe(
|
|
312
299
|
Effect.withSpan("SQL.batchSet [effect-app/infra/Store]", {
|
|
313
300
|
attributes: { "repository.table_name": tableName, "repository.model_name": name }
|
|
@@ -316,7 +303,7 @@ function makeSQLStoreInt(dialect: SQLDialect, jsonColumnType: string) {
|
|
|
316
303
|
)),
|
|
317
304
|
|
|
318
305
|
bulkSet: (items) =>
|
|
319
|
-
|
|
306
|
+
resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
320
307
|
bulkSetInternal(items, ns).pipe(
|
|
321
308
|
Effect.withSpan("SQL.bulkSet [effect-app/infra/Store]", {
|
|
322
309
|
attributes: { "repository.table_name": tableName, "repository.model_name": name }
|
|
@@ -326,7 +313,7 @@ function makeSQLStoreInt(dialect: SQLDialect, jsonColumnType: string) {
|
|
|
326
313
|
|
|
327
314
|
batchRemove: (ids) => {
|
|
328
315
|
const placeholders = ids.map(() => "?").join(", ")
|
|
329
|
-
return
|
|
316
|
+
return resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
330
317
|
exec(
|
|
331
318
|
`DELETE FROM "${tableName}" WHERE id IN (${placeholders}) AND _namespace = ?`,
|
|
332
319
|
[...ids, ns]
|
|
@@ -404,6 +391,7 @@ function makeSQLiteStorePerNs(
|
|
|
404
391
|
ns,
|
|
405
392
|
`CREATE TABLE IF NOT EXISTS "${tableName}" (id TEXT NOT NULL PRIMARY KEY, _etag TEXT, data JSON NOT NULL)`
|
|
406
393
|
)
|
|
394
|
+
.pipe(Effect.asVoid)
|
|
407
395
|
|
|
408
396
|
const seedMarkerId = `__seed_marker__`
|
|
409
397
|
|
|
@@ -461,51 +449,38 @@ function makeSQLiteStorePerNs(
|
|
|
461
449
|
|
|
462
450
|
const ctx = yield* Effect.context<R>()
|
|
463
451
|
const seedCache = new Map<string, Effect.Effect<void>>()
|
|
464
|
-
const makeSeedEffect = (ns: string)
|
|
465
|
-
|
|
452
|
+
const makeSeedEffect = Effect.fnUntraced(function*(ns: string) {
|
|
453
|
+
yield* ensureTable(ns)
|
|
454
|
+
if (!seed) return
|
|
455
|
+
const existing = yield* exec(
|
|
466
456
|
ns,
|
|
467
457
|
`SELECT id FROM "${tableName}" WHERE id = ?`,
|
|
468
458
|
[seedMarkerId]
|
|
469
459
|
)
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
exec(
|
|
483
|
-
ns,
|
|
484
|
-
`INSERT INTO "${tableName}" (id, _etag, data) VALUES (?, ?, ?)`,
|
|
485
|
-
[seedMarkerId, null, JSON.stringify({ _marker: true })]
|
|
486
|
-
)
|
|
487
|
-
),
|
|
488
|
-
Effect.provide(ctx),
|
|
489
|
-
Effect.orDie
|
|
490
|
-
)
|
|
491
|
-
})
|
|
492
|
-
)
|
|
493
|
-
const seedNamespace = Effect.fn("seedNamespace")(function*(ns: string) {
|
|
494
|
-
yield* ensureTable(ns)
|
|
495
|
-
if (!seed) return
|
|
460
|
+
if ((existing as any[]).length > 0) return
|
|
461
|
+
yield* InfraLogger.logInfo(`Seeding data for ${name} (namespace: ${ns})`)
|
|
462
|
+
const items = yield* seed.pipe(Effect.provide(ctx), Effect.orDie)
|
|
463
|
+
const ne = toNonEmptyArray([...items])
|
|
464
|
+
if (Option.isSome(ne)) yield* bulkSetInternal(ne.value, ns)
|
|
465
|
+
yield* exec(
|
|
466
|
+
ns,
|
|
467
|
+
`INSERT INTO "${tableName}" (id, _etag, data) VALUES (?, ?, ?)`,
|
|
468
|
+
[seedMarkerId, null, JSON.stringify({ _marker: true })]
|
|
469
|
+
)
|
|
470
|
+
})
|
|
471
|
+
const seedNamespace = (ns: string) => {
|
|
496
472
|
let cached = seedCache.get(ns)
|
|
497
473
|
if (!cached) {
|
|
498
|
-
cached =
|
|
474
|
+
cached = Effect.cached(makeSeedEffect(ns)).pipe(Effect.runSync)
|
|
499
475
|
seedCache.set(ns, cached)
|
|
500
476
|
}
|
|
501
|
-
|
|
502
|
-
}
|
|
503
|
-
const resolveAndSeed = resolveNamespace.pipe(
|
|
504
|
-
Effect.tap((ns) => seedNamespace(ns))
|
|
505
|
-
)
|
|
477
|
+
return cached
|
|
478
|
+
}
|
|
506
479
|
|
|
507
480
|
const s: Store<IdKey, Encoded> = {
|
|
508
|
-
|
|
481
|
+
seedNamespace: (ns) => seedNamespace(ns),
|
|
482
|
+
|
|
483
|
+
all: resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
509
484
|
exec(ns, `SELECT id, _etag, data FROM "${tableName}"`)
|
|
510
485
|
.pipe(
|
|
511
486
|
Effect.map((rows) => (rows as any[]).map((r) => parseRow<Encoded>(r, idKey, defaultValues))),
|
|
@@ -520,7 +495,7 @@ function makeSQLiteStorePerNs(
|
|
|
520
495
|
)),
|
|
521
496
|
|
|
522
497
|
find: (id) =>
|
|
523
|
-
|
|
498
|
+
resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
524
499
|
exec(ns, `SELECT id, _etag, data FROM "${tableName}" WHERE id = ?`, [id])
|
|
525
500
|
.pipe(
|
|
526
501
|
Effect.map((rows) => {
|
|
@@ -540,7 +515,7 @@ function makeSQLiteStorePerNs(
|
|
|
540
515
|
.filter
|
|
541
516
|
type M = U extends undefined ? Encoded
|
|
542
517
|
: Pick<Encoded, U>
|
|
543
|
-
return
|
|
518
|
+
return resolveNamespace
|
|
544
519
|
.pipe(Effect
|
|
545
520
|
.flatMap((ns) =>
|
|
546
521
|
Effect
|
|
@@ -595,7 +570,7 @@ function makeSQLiteStorePerNs(
|
|
|
595
570
|
},
|
|
596
571
|
|
|
597
572
|
set: (e) =>
|
|
598
|
-
|
|
573
|
+
resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
599
574
|
setInternal(e, ns).pipe(
|
|
600
575
|
Effect.withSpan("SQLite.set [effect-app/infra/Store]", {
|
|
601
576
|
attributes: { "repository.table_name": tableName, "repository.model_name": name, id: e[idKey] }
|
|
@@ -604,7 +579,7 @@ function makeSQLiteStorePerNs(
|
|
|
604
579
|
)),
|
|
605
580
|
|
|
606
581
|
batchSet: (items) =>
|
|
607
|
-
|
|
582
|
+
resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
608
583
|
bulkSetInternal(items, ns).pipe(
|
|
609
584
|
Effect.withSpan("SQLite.batchSet [effect-app/infra/Store]", {
|
|
610
585
|
attributes: { "repository.table_name": tableName, "repository.model_name": name }
|
|
@@ -613,7 +588,7 @@ function makeSQLiteStorePerNs(
|
|
|
613
588
|
)),
|
|
614
589
|
|
|
615
590
|
bulkSet: (items) =>
|
|
616
|
-
|
|
591
|
+
resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
617
592
|
bulkSetInternal(items, ns).pipe(
|
|
618
593
|
Effect.withSpan("SQLite.bulkSet [effect-app/infra/Store]", {
|
|
619
594
|
attributes: { "repository.table_name": tableName, "repository.model_name": name }
|
|
@@ -623,7 +598,7 @@ function makeSQLiteStorePerNs(
|
|
|
623
598
|
|
|
624
599
|
batchRemove: (ids) => {
|
|
625
600
|
const placeholders = ids.map(() => "?").join(", ")
|
|
626
|
-
return
|
|
601
|
+
return resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
627
602
|
exec(
|
|
628
603
|
ns,
|
|
629
604
|
`DELETE FROM "${tableName}" WHERE id IN (${placeholders})`,
|
|
@@ -675,7 +650,7 @@ export function SQLiteStoreLayer(
|
|
|
675
650
|
Effect.flatMap((ns) => withNsSql(ns, (sql) => sql.withTransaction(effect).pipe(Effect.orDie)))
|
|
676
651
|
) as any
|
|
677
652
|
|
|
678
|
-
return StoreMaker.
|
|
653
|
+
return StoreMaker.context(storeMaker).pipe(
|
|
679
654
|
Context.add(WithNsTransaction, withTransaction)
|
|
680
655
|
)
|
|
681
656
|
})
|
package/src/Store/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { CosmosStoreLayer } from "./Cosmos.js"
|
|
|
5
5
|
import { DiskStoreLayer } from "./Disk.js"
|
|
6
6
|
import { MemoryStoreLive } from "./Memory.js"
|
|
7
7
|
// import { RedisStoreLayer } from "./Redis.js"
|
|
8
|
+
import { RepositoryRegistryLive } from "../Model.js"
|
|
8
9
|
import type { StorageConfig } from "./service.js"
|
|
9
10
|
import { SQLiteStoreLayer } from "./SQL.js"
|
|
10
11
|
import { PgStoreLayer } from "./SQL/Pg.js"
|
|
@@ -41,7 +42,7 @@ export function StoreMakerLayer(
|
|
|
41
42
|
console.log("Using Cosmos DB store")
|
|
42
43
|
return CosmosStoreLayer(cfg)
|
|
43
44
|
})
|
|
44
|
-
.pipe(Layer.unwrap)
|
|
45
|
+
.pipe(Layer.unwrap, Layer.merge(RepositoryRegistryLive))
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
export * from "./service.js"
|