@effect-app/infra 4.0.0-beta.83 → 4.0.0-beta.85
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 +14 -0
- package/dist/Store/Cosmos.d.ts.map +1 -1
- package/dist/Store/Cosmos.js +41 -28
- package/dist/Store/SQL/Pg.d.ts.map +1 -1
- package/dist/Store/SQL/Pg.js +29 -15
- package/dist/Store/SQL.d.ts +8 -1
- package/dist/Store/SQL.d.ts.map +1 -1
- package/dist/Store/SQL.js +35 -24
- package/package.json +2 -2
- package/src/Store/Cosmos.ts +64 -38
- package/src/Store/SQL/Pg.ts +46 -20
- package/src/Store/SQL.ts +50 -30
- package/test/sql-store.test.ts +259 -36
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effect-app/infra",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.85",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"dependencies": {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"proper-lockfile": "^4.1.2",
|
|
14
14
|
"pure-rand": "7.0.1",
|
|
15
15
|
"query-string": "^9.3.1",
|
|
16
|
-
"effect-app": "4.0.0-beta.
|
|
16
|
+
"effect-app": "4.0.0-beta.85"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"@azure/cosmos": "^4.9.2",
|
package/src/Store/Cosmos.ts
CHANGED
|
@@ -75,10 +75,65 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
75
75
|
// then need to clean up the actual data.. perhaps first do with a config toggle to prescribe to it.
|
|
76
76
|
const importedMarkerId = containerId
|
|
77
77
|
|
|
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
|
+
s.bulkSet(a).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
|
+
|
|
78
133
|
const bulkSet = (items: NonEmptyReadonlyArray<PM>) =>
|
|
79
134
|
Effect
|
|
80
135
|
.gen(function*() {
|
|
81
|
-
const ns = yield*
|
|
136
|
+
const ns = yield* resolveAndSeed
|
|
82
137
|
// TODO: disable batching if need atomicity
|
|
83
138
|
// we delay and batch to keep low amount of RUs
|
|
84
139
|
const b = [...items]
|
|
@@ -182,7 +237,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
182
237
|
}, { captureStackTrace: false }))
|
|
183
238
|
|
|
184
239
|
const batchSet = (items: NonEmptyReadonlyArray<PM>) => {
|
|
185
|
-
return
|
|
240
|
+
return resolveAndSeed
|
|
186
241
|
.pipe(Effect.flatMap((ns) =>
|
|
187
242
|
Effect
|
|
188
243
|
.suspend(() => {
|
|
@@ -253,7 +308,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
253
308
|
const s: Store<IdKey, Encoded> = {
|
|
254
309
|
queryRaw: <Out>(query: RawQuery<Encoded, Out>) =>
|
|
255
310
|
Effect
|
|
256
|
-
.all({ q: Effect.sync(() => query.cosmos({ name })), pk:
|
|
311
|
+
.all({ q: Effect.sync(() => query.cosmos({ name })), pk: resolvePartitionKeyAndSeed })
|
|
257
312
|
.pipe(
|
|
258
313
|
Effect.tap(({ q }) => logQuery(q)),
|
|
259
314
|
Effect.flatMap(({ pk, q }) =>
|
|
@@ -275,7 +330,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
275
330
|
}, { captureStackTrace: false })
|
|
276
331
|
),
|
|
277
332
|
batchRemove: (ids, partitionKey?: string) =>
|
|
278
|
-
|
|
333
|
+
resolvePartitionKeyAndSeed.pipe(Effect.flatMap((pk) =>
|
|
279
334
|
Effect.promise(() =>
|
|
280
335
|
execBatch(
|
|
281
336
|
mutable(ids.map((id) =>
|
|
@@ -296,7 +351,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
296
351
|
query: `SELECT * FROM ${name}`,
|
|
297
352
|
parameters: []
|
|
298
353
|
})),
|
|
299
|
-
pk:
|
|
354
|
+
pk: resolvePartitionKeyAndSeed
|
|
300
355
|
})
|
|
301
356
|
.pipe(
|
|
302
357
|
Effect.tap(({ q }) => logQuery(q)),
|
|
@@ -382,7 +437,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
382
437
|
)
|
|
383
438
|
},
|
|
384
439
|
find: (id) =>
|
|
385
|
-
|
|
440
|
+
resolveAndSeed.pipe(Effect.flatMap((ns) =>
|
|
386
441
|
Effect
|
|
387
442
|
.promise(() =>
|
|
388
443
|
container
|
|
@@ -405,7 +460,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
405
460
|
}, { captureStackTrace: false }))
|
|
406
461
|
)),
|
|
407
462
|
set: (e) =>
|
|
408
|
-
|
|
463
|
+
resolveAndSeed.pipe(Effect.flatMap((ns) =>
|
|
409
464
|
Option
|
|
410
465
|
.match(
|
|
411
466
|
Option
|
|
@@ -466,38 +521,9 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
466
521
|
bulkSet
|
|
467
522
|
}
|
|
468
523
|
|
|
469
|
-
//
|
|
470
|
-
|
|
471
|
-
container
|
|
472
|
-
.item(importedMarkerId, importedMarkerId)
|
|
473
|
-
.read<{ id: string }>()
|
|
474
|
-
.then(({ resource }) => Option.fromNullishOr(resource))
|
|
475
|
-
)
|
|
524
|
+
// Eagerly seed primary namespace on initialization
|
|
525
|
+
yield* seedNamespace("primary")
|
|
476
526
|
|
|
477
|
-
if (!Option.isSome(marker)) {
|
|
478
|
-
yield* InfraLogger.logInfo("Creating mock data for " + name)
|
|
479
|
-
if (seed) {
|
|
480
|
-
const m = yield* seed
|
|
481
|
-
yield* Effect.flatMapOption(
|
|
482
|
-
Effect.succeed(toNonEmptyArray([...m])),
|
|
483
|
-
(a) =>
|
|
484
|
-
s.bulkSet(a).pipe(
|
|
485
|
-
Effect.orDie,
|
|
486
|
-
Effect
|
|
487
|
-
// we delay extra here, so that initial creation between Companies/POs also have an interval between them.
|
|
488
|
-
.delay(Duration.millis(1100))
|
|
489
|
-
)
|
|
490
|
-
)
|
|
491
|
-
}
|
|
492
|
-
// Mark as imported
|
|
493
|
-
yield* Effect.promise(() =>
|
|
494
|
-
container.items.create({
|
|
495
|
-
_partitionKey: importedMarkerId,
|
|
496
|
-
id: importedMarkerId,
|
|
497
|
-
ttl: -1
|
|
498
|
-
})
|
|
499
|
-
)
|
|
500
|
-
}
|
|
501
527
|
return s
|
|
502
528
|
})
|
|
503
529
|
}
|
package/src/Store/SQL/Pg.ts
CHANGED
|
@@ -78,8 +78,47 @@ function makePgStore({ prefix }: StorageConfig) {
|
|
|
78
78
|
const exec = (query: string, params?: readonly unknown[]) =>
|
|
79
79
|
sql.unsafe(query, params as any).pipe(Effect.orDie)
|
|
80
80
|
|
|
81
|
+
const ctx = yield* Effect.context<R>()
|
|
82
|
+
const seedCache = new Map<string, Effect.Effect<void>>()
|
|
83
|
+
const makeSeedEffect = (ns: string) =>
|
|
84
|
+
exec(
|
|
85
|
+
`SELECT COUNT(*) as cnt FROM "${tableName}" WHERE _namespace = $1`,
|
|
86
|
+
[ns]
|
|
87
|
+
)
|
|
88
|
+
.pipe(
|
|
89
|
+
Effect.flatMap((existing) => {
|
|
90
|
+
const count = Number((existing as any[])[0]?.cnt ?? 0)
|
|
91
|
+
if (count === 0) {
|
|
92
|
+
return InfraLogger.logInfo(`Seeding data for ${name} (namespace: ${ns})`).pipe(
|
|
93
|
+
Effect.andThen(seed!),
|
|
94
|
+
Effect.flatMap((items) =>
|
|
95
|
+
Effect.flatMapOption(
|
|
96
|
+
Effect.succeed(toNonEmptyArray([...items])),
|
|
97
|
+
(a) => s.bulkSet(a).pipe(Effect.orDie)
|
|
98
|
+
)
|
|
99
|
+
),
|
|
100
|
+
Effect.provide(ctx),
|
|
101
|
+
Effect.orDie
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
return Effect.void
|
|
105
|
+
})
|
|
106
|
+
)
|
|
107
|
+
const seedNamespace = Effect.fn("seedNamespace")(function*(ns: string) {
|
|
108
|
+
if (!seed) return
|
|
109
|
+
let cached = seedCache.get(ns)
|
|
110
|
+
if (!cached) {
|
|
111
|
+
cached = yield* Effect.cached(makeSeedEffect(ns))
|
|
112
|
+
seedCache.set(ns, cached)
|
|
113
|
+
}
|
|
114
|
+
yield* cached
|
|
115
|
+
})
|
|
116
|
+
const resolveAndSeed = resolveNamespace.pipe(
|
|
117
|
+
Effect.tap((ns) => seedNamespace(ns))
|
|
118
|
+
)
|
|
119
|
+
|
|
81
120
|
const s: Store<IdKey, Encoded> = {
|
|
82
|
-
all:
|
|
121
|
+
all: resolveAndSeed.pipe(Effect.flatMap((ns) =>
|
|
83
122
|
exec(`SELECT id, _etag, data FROM "${tableName}" WHERE _namespace = $1`, [ns])
|
|
84
123
|
.pipe(
|
|
85
124
|
Effect.map((rows) => (rows as any[]).map((r) => parseRow<Encoded>(r, idKey, defaultValues))),
|
|
@@ -94,7 +133,7 @@ function makePgStore({ prefix }: StorageConfig) {
|
|
|
94
133
|
)),
|
|
95
134
|
|
|
96
135
|
find: (id) =>
|
|
97
|
-
|
|
136
|
+
resolveAndSeed.pipe(Effect
|
|
98
137
|
.flatMap((ns) =>
|
|
99
138
|
exec(`SELECT id, _etag, data FROM "${tableName}" WHERE id = $1 AND _namespace = $2`, [id, ns])
|
|
100
139
|
.pipe(
|
|
@@ -114,7 +153,7 @@ function makePgStore({ prefix }: StorageConfig) {
|
|
|
114
153
|
const filter = f
|
|
115
154
|
.filter
|
|
116
155
|
type M = U extends undefined ? Encoded : Pick<Encoded, U>
|
|
117
|
-
return
|
|
156
|
+
return resolveAndSeed.pipe(Effect.flatMap((ns) =>
|
|
118
157
|
Effect
|
|
119
158
|
.sync(() => {
|
|
120
159
|
const q = buildWhereSQLQuery(
|
|
@@ -169,7 +208,7 @@ function makePgStore({ prefix }: StorageConfig) {
|
|
|
169
208
|
},
|
|
170
209
|
|
|
171
210
|
set: (e) =>
|
|
172
|
-
|
|
211
|
+
resolveAndSeed.pipe(Effect.flatMap((ns) =>
|
|
173
212
|
Effect
|
|
174
213
|
.gen(function*() {
|
|
175
214
|
const row = toRow(e)
|
|
@@ -245,7 +284,7 @@ function makePgStore({ prefix }: StorageConfig) {
|
|
|
245
284
|
batchRemove: (ids) => {
|
|
246
285
|
const placeholders = ids.map((_, i) => `$${i + 1}`).join(", ")
|
|
247
286
|
const nsPlaceholder = `$${ids.length + 1}`
|
|
248
|
-
return
|
|
287
|
+
return resolveAndSeed.pipe(Effect.flatMap((ns) =>
|
|
249
288
|
exec(
|
|
250
289
|
`DELETE FROM "${tableName}" WHERE id IN (${placeholders}) AND _namespace = ${nsPlaceholder}`,
|
|
251
290
|
[...ids, ns]
|
|
@@ -268,21 +307,8 @@ function makePgStore({ prefix }: StorageConfig) {
|
|
|
268
307
|
)
|
|
269
308
|
}
|
|
270
309
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
`SELECT COUNT(*) as cnt FROM "${tableName}" WHERE _namespace = $1`,
|
|
274
|
-
["primary"]
|
|
275
|
-
)
|
|
276
|
-
const count = Number((existing as any[])[0]?.cnt ?? 0)
|
|
277
|
-
if (count === 0) {
|
|
278
|
-
yield* InfraLogger.logInfo("Seeding data for " + name)
|
|
279
|
-
const items = yield* seed
|
|
280
|
-
yield* Effect.flatMapOption(
|
|
281
|
-
Effect.succeed(toNonEmptyArray([...items])),
|
|
282
|
-
(a) => s.bulkSet(a).pipe(Effect.orDie)
|
|
283
|
-
)
|
|
284
|
-
}
|
|
285
|
-
}
|
|
310
|
+
// Eagerly seed primary namespace on initialization
|
|
311
|
+
yield* seedNamespace("primary")
|
|
286
312
|
|
|
287
313
|
return s
|
|
288
314
|
})
|
package/src/Store/SQL.ts
CHANGED
|
@@ -86,8 +86,47 @@ function makeSQLStoreInt(dialect: SQLDialect, jsonColumnType: string) {
|
|
|
86
86
|
const exec = (query: string, params?: readonly unknown[]) =>
|
|
87
87
|
sql.unsafe(query, params as any).pipe(Effect.orDie)
|
|
88
88
|
|
|
89
|
+
const ctx = yield* Effect.context<R>()
|
|
90
|
+
const seedCache = new Map<string, Effect.Effect<void>>()
|
|
91
|
+
const makeSeedEffect = (ns: string) =>
|
|
92
|
+
exec(
|
|
93
|
+
`SELECT COUNT(*) as cnt FROM "${tableName}" WHERE _namespace = ?`,
|
|
94
|
+
[ns]
|
|
95
|
+
)
|
|
96
|
+
.pipe(
|
|
97
|
+
Effect.flatMap((existing) => {
|
|
98
|
+
const count = (existing as any[])[0]?.cnt ?? 0
|
|
99
|
+
if (count === 0) {
|
|
100
|
+
return InfraLogger.logInfo(`Seeding data for ${name} (namespace: ${ns})`).pipe(
|
|
101
|
+
Effect.andThen(seed!),
|
|
102
|
+
Effect.flatMap((items) =>
|
|
103
|
+
Effect.flatMapOption(
|
|
104
|
+
Effect.succeed(toNonEmptyArray([...items])),
|
|
105
|
+
(a) => s.bulkSet(a).pipe(Effect.orDie)
|
|
106
|
+
)
|
|
107
|
+
),
|
|
108
|
+
Effect.provide(ctx),
|
|
109
|
+
Effect.orDie
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
return Effect.void
|
|
113
|
+
})
|
|
114
|
+
)
|
|
115
|
+
const seedNamespace = Effect.fn("seedNamespace")(function*(ns: string) {
|
|
116
|
+
if (!seed) return
|
|
117
|
+
let cached = seedCache.get(ns)
|
|
118
|
+
if (!cached) {
|
|
119
|
+
cached = yield* Effect.cached(makeSeedEffect(ns))
|
|
120
|
+
seedCache.set(ns, cached)
|
|
121
|
+
}
|
|
122
|
+
yield* cached
|
|
123
|
+
})
|
|
124
|
+
const resolveAndSeed = resolveNamespace.pipe(
|
|
125
|
+
Effect.tap((ns) => seedNamespace(ns))
|
|
126
|
+
)
|
|
127
|
+
|
|
89
128
|
const s: Store<IdKey, Encoded> = {
|
|
90
|
-
all:
|
|
129
|
+
all: resolveAndSeed.pipe(Effect.flatMap((ns) =>
|
|
91
130
|
exec(`SELECT id, _etag, data FROM "${tableName}" WHERE _namespace = ?`, [ns])
|
|
92
131
|
.pipe(
|
|
93
132
|
Effect.map((rows) => (rows as any[]).map((r) => parseRow<Encoded>(r, idKey, defaultValues))),
|
|
@@ -102,7 +141,7 @@ function makeSQLStoreInt(dialect: SQLDialect, jsonColumnType: string) {
|
|
|
102
141
|
)),
|
|
103
142
|
|
|
104
143
|
find: (id) =>
|
|
105
|
-
|
|
144
|
+
resolveAndSeed.pipe(Effect.flatMap((ns) =>
|
|
106
145
|
exec(`SELECT id, _etag, data FROM "${tableName}" WHERE id = ? AND _namespace = ?`, [id, ns])
|
|
107
146
|
.pipe(
|
|
108
147
|
Effect.map((rows) => {
|
|
@@ -122,7 +161,7 @@ function makeSQLStoreInt(dialect: SQLDialect, jsonColumnType: string) {
|
|
|
122
161
|
.filter
|
|
123
162
|
type M = U extends undefined ? Encoded
|
|
124
163
|
: Pick<Encoded, U>
|
|
125
|
-
return
|
|
164
|
+
return resolveAndSeed
|
|
126
165
|
.pipe(Effect
|
|
127
166
|
.flatMap((ns) =>
|
|
128
167
|
Effect
|
|
@@ -144,31 +183,25 @@ function makeSQLStoreInt(dialect: SQLDialect, jsonColumnType: string) {
|
|
|
144
183
|
f
|
|
145
184
|
.limit
|
|
146
185
|
)
|
|
147
|
-
const nsPlaceholder = dialect
|
|
148
|
-
.placeholder(
|
|
149
|
-
q
|
|
150
|
-
.params
|
|
151
|
-
.length + 1
|
|
152
|
-
)
|
|
153
186
|
const hasWhere = q
|
|
154
187
|
.sql
|
|
155
188
|
.includes("WHERE")
|
|
156
189
|
const nsSql = hasWhere
|
|
157
190
|
? q
|
|
158
191
|
.sql
|
|
159
|
-
.replace("WHERE", `WHERE _namespace =
|
|
192
|
+
.replace("WHERE", `WHERE _namespace = ? AND`)
|
|
160
193
|
: q
|
|
161
194
|
.sql
|
|
162
195
|
.replace(
|
|
163
196
|
`FROM "${tableName}"`,
|
|
164
|
-
`FROM "${tableName}" WHERE _namespace =
|
|
197
|
+
`FROM "${tableName}" WHERE _namespace = ?`
|
|
165
198
|
)
|
|
166
199
|
return {
|
|
167
200
|
sql: nsSql,
|
|
168
201
|
params: [
|
|
202
|
+
ns,
|
|
169
203
|
...q
|
|
170
|
-
.params
|
|
171
|
-
ns
|
|
204
|
+
.params
|
|
172
205
|
]
|
|
173
206
|
}
|
|
174
207
|
})
|
|
@@ -206,7 +239,7 @@ function makeSQLStoreInt(dialect: SQLDialect, jsonColumnType: string) {
|
|
|
206
239
|
},
|
|
207
240
|
|
|
208
241
|
set: (e) =>
|
|
209
|
-
|
|
242
|
+
resolveAndSeed.pipe(Effect.flatMap((ns) =>
|
|
210
243
|
Effect
|
|
211
244
|
.gen(function*() {
|
|
212
245
|
const row = toRow(e)
|
|
@@ -281,7 +314,7 @@ function makeSQLStoreInt(dialect: SQLDialect, jsonColumnType: string) {
|
|
|
281
314
|
|
|
282
315
|
batchRemove: (ids) => {
|
|
283
316
|
const placeholders = ids.map(() => "?").join(", ")
|
|
284
|
-
return
|
|
317
|
+
return resolveAndSeed.pipe(Effect.flatMap((ns) =>
|
|
285
318
|
exec(
|
|
286
319
|
`DELETE FROM "${tableName}" WHERE id IN (${placeholders}) AND _namespace = ?`,
|
|
287
320
|
[...ids, ns]
|
|
@@ -304,21 +337,8 @@ function makeSQLStoreInt(dialect: SQLDialect, jsonColumnType: string) {
|
|
|
304
337
|
)
|
|
305
338
|
}
|
|
306
339
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
`SELECT COUNT(*) as cnt FROM "${tableName}" WHERE _namespace = ?`,
|
|
310
|
-
["primary"]
|
|
311
|
-
)
|
|
312
|
-
const count = (existing as any[])[0]?.cnt ?? 0
|
|
313
|
-
if (count === 0) {
|
|
314
|
-
yield* InfraLogger.logInfo("Seeding data for " + name)
|
|
315
|
-
const items = yield* seed
|
|
316
|
-
yield* Effect.flatMapOption(
|
|
317
|
-
Effect.succeed(toNonEmptyArray([...items])),
|
|
318
|
-
(a) => s.bulkSet(a).pipe(Effect.orDie)
|
|
319
|
-
)
|
|
320
|
-
}
|
|
321
|
-
}
|
|
340
|
+
// Eagerly seed primary namespace on initialization
|
|
341
|
+
yield* seedNamespace("primary")
|
|
322
342
|
|
|
323
343
|
return s
|
|
324
344
|
})
|