@effect-app/infra 4.0.0-beta.112 → 4.0.0-beta.114
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/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 +73 -91
- package/src/Store/index.ts +2 -1
- package/src/Store/service.ts +5 -0
- 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/test/dist/date-query.test.d.ts.map +0 -1
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]
|
|
@@ -400,12 +387,20 @@ function makeSQLiteStorePerNs(
|
|
|
400
387
|
withNsSql(ns, (sql) => sql.unsafe(query, params as any).pipe(Effect.orDie))
|
|
401
388
|
|
|
402
389
|
const ensureTable = (ns: string) =>
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
390
|
+
withNsSql(ns, (sql) =>
|
|
391
|
+
sql
|
|
392
|
+
.unsafe(
|
|
393
|
+
`CREATE TABLE IF NOT EXISTS "${tableName}" (id TEXT NOT NULL PRIMARY KEY, _etag TEXT, data JSON NOT NULL)`
|
|
394
|
+
)
|
|
395
|
+
.pipe(
|
|
396
|
+
Effect.andThen(
|
|
397
|
+
sql.unsafe(
|
|
398
|
+
`CREATE TABLE IF NOT EXISTS "_migrations" (id TEXT NOT NULL, version TEXT NOT NULL, PRIMARY KEY (id, version))`
|
|
399
|
+
)
|
|
400
|
+
),
|
|
401
|
+
Effect.orDie,
|
|
402
|
+
Effect.asVoid
|
|
403
|
+
))
|
|
409
404
|
|
|
410
405
|
const setInternal = (e: PM, ns: string) =>
|
|
411
406
|
Effect.gen(function*() {
|
|
@@ -461,51 +456,38 @@ function makeSQLiteStorePerNs(
|
|
|
461
456
|
|
|
462
457
|
const ctx = yield* Effect.context<R>()
|
|
463
458
|
const seedCache = new Map<string, Effect.Effect<void>>()
|
|
464
|
-
const makeSeedEffect = (ns: string)
|
|
465
|
-
exec(
|
|
466
|
-
ns,
|
|
467
|
-
`SELECT id FROM "${tableName}" WHERE id = ?`,
|
|
468
|
-
[seedMarkerId]
|
|
469
|
-
)
|
|
470
|
-
.pipe(
|
|
471
|
-
Effect.flatMap((existing) => {
|
|
472
|
-
if ((existing as any[]).length > 0) return Effect.void
|
|
473
|
-
return InfraLogger.logInfo(`Seeding data for ${name} (namespace: ${ns})`).pipe(
|
|
474
|
-
Effect.andThen(seed!),
|
|
475
|
-
Effect.flatMap((items) =>
|
|
476
|
-
Effect.flatMapOption(
|
|
477
|
-
Effect.succeed(toNonEmptyArray([...items])),
|
|
478
|
-
(a) => bulkSetInternal(a, ns)
|
|
479
|
-
)
|
|
480
|
-
),
|
|
481
|
-
Effect.andThen(
|
|
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) {
|
|
459
|
+
const makeSeedEffect = Effect.fnUntraced(function*(ns: string) {
|
|
494
460
|
yield* ensureTable(ns)
|
|
495
461
|
if (!seed) return
|
|
462
|
+
const existing = yield* exec(
|
|
463
|
+
ns,
|
|
464
|
+
`SELECT id FROM "_migrations" WHERE id = ? AND version = ?`,
|
|
465
|
+
[tableName, tableName]
|
|
466
|
+
)
|
|
467
|
+
if ((existing as any[]).length > 0) return
|
|
468
|
+
yield* InfraLogger.logInfo(`Seeding data for ${name} (namespace: ${ns})`)
|
|
469
|
+
const items = yield* seed.pipe(Effect.provide(ctx), Effect.orDie)
|
|
470
|
+
const ne = toNonEmptyArray([...items])
|
|
471
|
+
if (Option.isSome(ne)) yield* bulkSetInternal(ne.value, ns)
|
|
472
|
+
yield* exec(
|
|
473
|
+
ns,
|
|
474
|
+
`INSERT INTO "_migrations" (id, version) VALUES (?, ?)`,
|
|
475
|
+
[tableName, tableName]
|
|
476
|
+
)
|
|
477
|
+
})
|
|
478
|
+
const seedNamespace = (ns: string) => {
|
|
496
479
|
let cached = seedCache.get(ns)
|
|
497
480
|
if (!cached) {
|
|
498
|
-
cached =
|
|
481
|
+
cached = Effect.cached(makeSeedEffect(ns)).pipe(Effect.runSync)
|
|
499
482
|
seedCache.set(ns, cached)
|
|
500
483
|
}
|
|
501
|
-
|
|
502
|
-
}
|
|
503
|
-
const resolveAndSeed = resolveNamespace.pipe(
|
|
504
|
-
Effect.tap((ns) => seedNamespace(ns))
|
|
505
|
-
)
|
|
484
|
+
return cached
|
|
485
|
+
}
|
|
506
486
|
|
|
507
487
|
const s: Store<IdKey, Encoded> = {
|
|
508
|
-
|
|
488
|
+
seedNamespace: (ns) => seedNamespace(ns),
|
|
489
|
+
|
|
490
|
+
all: resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
509
491
|
exec(ns, `SELECT id, _etag, data FROM "${tableName}"`)
|
|
510
492
|
.pipe(
|
|
511
493
|
Effect.map((rows) => (rows as any[]).map((r) => parseRow<Encoded>(r, idKey, defaultValues))),
|
|
@@ -520,7 +502,7 @@ function makeSQLiteStorePerNs(
|
|
|
520
502
|
)),
|
|
521
503
|
|
|
522
504
|
find: (id) =>
|
|
523
|
-
|
|
505
|
+
resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
524
506
|
exec(ns, `SELECT id, _etag, data FROM "${tableName}" WHERE id = ?`, [id])
|
|
525
507
|
.pipe(
|
|
526
508
|
Effect.map((rows) => {
|
|
@@ -540,7 +522,7 @@ function makeSQLiteStorePerNs(
|
|
|
540
522
|
.filter
|
|
541
523
|
type M = U extends undefined ? Encoded
|
|
542
524
|
: Pick<Encoded, U>
|
|
543
|
-
return
|
|
525
|
+
return resolveNamespace
|
|
544
526
|
.pipe(Effect
|
|
545
527
|
.flatMap((ns) =>
|
|
546
528
|
Effect
|
|
@@ -595,7 +577,7 @@ function makeSQLiteStorePerNs(
|
|
|
595
577
|
},
|
|
596
578
|
|
|
597
579
|
set: (e) =>
|
|
598
|
-
|
|
580
|
+
resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
599
581
|
setInternal(e, ns).pipe(
|
|
600
582
|
Effect.withSpan("SQLite.set [effect-app/infra/Store]", {
|
|
601
583
|
attributes: { "repository.table_name": tableName, "repository.model_name": name, id: e[idKey] }
|
|
@@ -604,7 +586,7 @@ function makeSQLiteStorePerNs(
|
|
|
604
586
|
)),
|
|
605
587
|
|
|
606
588
|
batchSet: (items) =>
|
|
607
|
-
|
|
589
|
+
resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
608
590
|
bulkSetInternal(items, ns).pipe(
|
|
609
591
|
Effect.withSpan("SQLite.batchSet [effect-app/infra/Store]", {
|
|
610
592
|
attributes: { "repository.table_name": tableName, "repository.model_name": name }
|
|
@@ -613,7 +595,7 @@ function makeSQLiteStorePerNs(
|
|
|
613
595
|
)),
|
|
614
596
|
|
|
615
597
|
bulkSet: (items) =>
|
|
616
|
-
|
|
598
|
+
resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
617
599
|
bulkSetInternal(items, ns).pipe(
|
|
618
600
|
Effect.withSpan("SQLite.bulkSet [effect-app/infra/Store]", {
|
|
619
601
|
attributes: { "repository.table_name": tableName, "repository.model_name": name }
|
|
@@ -623,7 +605,7 @@ function makeSQLiteStorePerNs(
|
|
|
623
605
|
|
|
624
606
|
batchRemove: (ids) => {
|
|
625
607
|
const placeholders = ids.map(() => "?").join(", ")
|
|
626
|
-
return
|
|
608
|
+
return resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
627
609
|
exec(
|
|
628
610
|
ns,
|
|
629
611
|
`DELETE FROM "${tableName}" WHERE id IN (${placeholders})`,
|