@effect-app/infra 4.0.0-beta.327 → 4.0.0-beta.329
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 +19 -0
- package/dist/Store/Cosmos.d.ts.map +1 -1
- package/dist/Store/Cosmos.js +7 -6
- package/dist/Store/Disk.d.ts.map +1 -1
- package/dist/Store/Disk.js +4 -4
- package/dist/Store/Memory.d.ts.map +1 -1
- package/dist/Store/Memory.js +11 -10
- package/dist/Store/SQL/Pg.js +17 -16
- package/dist/Store/SQL.d.ts +9 -2
- package/dist/Store/SQL.d.ts.map +1 -1
- package/dist/Store/SQL.js +28 -29
- package/dist/Store/jsonDocument.d.ts +34 -11
- package/dist/Store/jsonDocument.d.ts.map +1 -1
- package/dist/Store/jsonDocument.js +53 -19
- package/dist/arbs.d.ts.map +1 -1
- package/dist/arbs.js +5 -4
- package/package.json +2 -2
- package/src/Store/Cosmos.ts +11 -6
- package/src/Store/Disk.ts +4 -3
- package/src/Store/Memory.ts +14 -9
- package/src/Store/SQL/Pg.ts +21 -21
- package/src/Store/SQL.ts +37 -37
- package/src/Store/jsonDocument.ts +73 -18
- package/src/arbs.ts +4 -3
- package/test/arbs-magnitude.test.ts +12 -0
- package/test/arbs.test.ts +20 -0
- package/test/repository-legacy-document.test.ts +96 -31
|
@@ -1,10 +1,14 @@
|
|
|
1
|
+
import * as Option from "effect-app/Option";
|
|
1
2
|
import * as S from "effect-app/Schema";
|
|
3
|
+
import * as Result from "effect/Result";
|
|
2
4
|
import { toJsonQueryValue } from "./utils.js";
|
|
3
5
|
const splitEtag = (doc) => {
|
|
4
6
|
const { _etag, ...rest } = doc;
|
|
5
7
|
return { rest: rest, _etag };
|
|
6
8
|
};
|
|
7
9
|
const joinEtag = (rest, _etag) => (_etag === undefined ? rest : { ...rest, _etag });
|
|
10
|
+
/** JSON document ↔ Encoded document codec for a store's domain schema. */
|
|
11
|
+
const jsonCodec = (schema) => S.toCodecJson(S.toEncoded(schema));
|
|
8
12
|
/**
|
|
9
13
|
* Encoded document ↔ JSON document. Prefer `Schema.toCodecJson(toEncoded(schema))`
|
|
10
14
|
* when the store has a schema; otherwise lower Date/Map/Set structurally.
|
|
@@ -13,18 +17,17 @@ const joinEtag = (rest, _etag) => (_etag === undefined ? rest : { ...rest, _etag
|
|
|
13
17
|
* document that does not match the Encoded shape fails loudly rather than being
|
|
14
18
|
* read back half-decoded.
|
|
15
19
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* failure and throws here.
|
|
20
|
+
* This is the **write** path codec: `encode` on the way to the database, and
|
|
21
|
+
* `decode` to hand the just-written document back. The read path uses
|
|
22
|
+
* {@link makeStoredDecode}, which applies `jitM` first and reports a document it
|
|
23
|
+
* does not repair as a typed `S.SchemaError` instead of throwing.
|
|
21
24
|
*
|
|
22
25
|
* `_etag` is infrastructure metadata rather than part of the domain document,
|
|
23
26
|
* so it is split off before the codec runs and re-attached afterwards.
|
|
24
27
|
*/
|
|
25
28
|
export const makeJsonDocumentCodec = (schema) => {
|
|
26
29
|
if (schema) {
|
|
27
|
-
const codec =
|
|
30
|
+
const codec = jsonCodec(schema);
|
|
28
31
|
return {
|
|
29
32
|
encode: (doc) => {
|
|
30
33
|
const { rest, _etag } = splitEtag(doc);
|
|
@@ -44,30 +47,61 @@ export const makeJsonDocumentCodec = (schema) => {
|
|
|
44
47
|
/**
|
|
45
48
|
* The store *read* boundary: apply `jitM` to the raw JSON document - after the
|
|
46
49
|
* adapter merged `defaultValues` into it - and only then decode JSON→Encoded
|
|
47
|
-
* with the strict
|
|
50
|
+
* with the strict `Schema.toCodecJson(Schema.toEncoded(schema))` codec.
|
|
48
51
|
*
|
|
49
52
|
* This is the one place a legacy-shaped document can be repaired, and why
|
|
50
53
|
* `jitM` can fix things no schema decode could - an explicit `null` where a
|
|
51
54
|
* `Date` is expected, a key added after the document was written. It runs
|
|
52
|
-
* before any schema decode sees the document
|
|
53
|
-
*
|
|
55
|
+
* before any schema decode sees the document.
|
|
56
|
+
*
|
|
57
|
+
* Nothing here is lenient: a document `jitM` does not repair still fails. It
|
|
58
|
+
* fails as a typed `S.SchemaError` in a `Result` (via `S.decodeUnknownResult`)
|
|
59
|
+
* rather than by throwing, so adapters can turn it into `Effect.fail` and
|
|
60
|
+
* callers - `Repository.validateSample` in particular - can inspect it per
|
|
61
|
+
* document instead of losing the whole run to a defect.
|
|
54
62
|
*
|
|
55
63
|
* `_etag` is infrastructure metadata rather than part of the domain document,
|
|
56
64
|
* so it is split off first and `jitM` never sees it.
|
|
57
|
-
*
|
|
58
|
-
* Returns `codec.decode` unchanged when there is no `jitM`; the write path,
|
|
59
|
-
* which must not run migrations, keeps using `codec.decode` directly.
|
|
60
65
|
*/
|
|
61
|
-
export const makeStoredDecode = (
|
|
62
|
-
if (
|
|
63
|
-
|
|
66
|
+
export const makeStoredDecode = (schema, jitM) => {
|
|
67
|
+
if (schema === undefined) {
|
|
68
|
+
// schemaless store: the stored document is already the Encoded document,
|
|
69
|
+
// so there is nothing to decode - only `jitM` to apply.
|
|
70
|
+
if (jitM === undefined)
|
|
71
|
+
return Result.succeed;
|
|
72
|
+
return (doc) => {
|
|
73
|
+
const { rest, _etag } = splitEtag(doc);
|
|
74
|
+
return Result.succeed(joinEtag(jitM(rest), _etag));
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const decode = S.decodeUnknownResult(jsonCodec(schema));
|
|
64
78
|
return (doc) => {
|
|
65
79
|
const { rest, _etag } = splitEtag(doc);
|
|
66
80
|
// adapters type the stored document as the Encoded persistence model, but
|
|
67
81
|
// at this point it is still the raw JSON document that `jitM` is written
|
|
68
|
-
// against - and `jitM` returns JSON, which is what
|
|
69
|
-
const
|
|
70
|
-
return
|
|
82
|
+
// against - and `jitM` returns JSON, which is what the codec consumes.
|
|
83
|
+
const json = jitM === undefined ? rest : jitM(rest);
|
|
84
|
+
return Result.map(decode(json), (decoded) => joinEtag(decoded, _etag));
|
|
71
85
|
};
|
|
72
86
|
};
|
|
73
|
-
|
|
87
|
+
/**
|
|
88
|
+
* Decode a whole result set, failing fast on the first bad document.
|
|
89
|
+
*
|
|
90
|
+
* One synchronous loop and one `Result` for the batch: no Effect per document,
|
|
91
|
+
* and no allocation beyond the decoded array (plus a `Result` wrapper) on the
|
|
92
|
+
* happy path. Adapters lift the returned `Result` with `Effect.fromResult`
|
|
93
|
+
* once, at the end.
|
|
94
|
+
*/
|
|
95
|
+
export const decodeStoredMany = (docs, decode) => {
|
|
96
|
+
const decoded = [];
|
|
97
|
+
for (const doc of docs) {
|
|
98
|
+
const result = decode(doc);
|
|
99
|
+
if (Result.isFailure(result))
|
|
100
|
+
return Result.fail(result.failure);
|
|
101
|
+
decoded.push(result.success);
|
|
102
|
+
}
|
|
103
|
+
return Result.succeed(decoded);
|
|
104
|
+
};
|
|
105
|
+
/** Decode an optional stored document; `None` short-circuits without decoding. */
|
|
106
|
+
export const decodeStoredOption = (doc, decode) => Option.isNone(doc) ? Result.succeed(Option.none()) : Result.map(decode(doc.value), Option.some);
|
|
107
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoianNvbkRvY3VtZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL1N0b3JlL2pzb25Eb2N1bWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEtBQUssTUFBTSxNQUFNLG1CQUFtQixDQUFBO0FBQzNDLE9BQU8sS0FBSyxDQUFDLE1BQU0sbUJBQW1CLENBQUE7QUFFdEMsT0FBTyxLQUFLLE1BQU0sTUFBTSxlQUFlLENBQUE7QUFDdkMsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sWUFBWSxDQUFBO0FBbUI3QyxNQUFNLFNBQVMsR0FBRyxDQUF3QixHQUE0QixFQUFFLEVBQUU7SUFDeEUsTUFBTSxFQUFFLEtBQUssRUFBRSxHQUFHLElBQUksRUFBRSxHQUFHLEdBQUcsQ0FBQTtJQUM5QixPQUFPLEVBQUUsSUFBSSxFQUFFLElBQVMsRUFBRSxLQUFLLEVBQUUsQ0FBQTtBQUNuQyxDQUFDLENBQUE7QUFFRCxNQUFNLFFBQVEsR0FBRyxDQUNmLElBQU8sRUFDUCxLQUF5QixFQUNBLEVBQUUsQ0FBQyxDQUFDLEtBQUssS0FBSyxTQUFTLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxHQUFHLElBQUksRUFBRSxLQUFLLEVBQUUsQ0FBQyxDQUFBO0FBRS9FLDBFQUEwRTtBQUMxRSxNQUFNLFNBQVMsR0FBRyxDQUF3QixNQUFhLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxXQUFXLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUMsQ0FBdUIsQ0FBQTtBQUVwSDs7Ozs7Ozs7Ozs7Ozs7O0dBZUc7QUFDSCxNQUFNLENBQUMsTUFBTSxxQkFBcUIsR0FBRyxDQUF3QixNQUFjLEVBQXdCLEVBQUU7SUFDbkcsSUFBSSxNQUFNLEVBQUUsQ0FBQztRQUNYLE1BQU0sS0FBSyxHQUFHLFNBQVMsQ0FBSSxNQUFNLENBQUMsQ0FBQTtRQUNsQyxPQUFPO1lBQ0wsTUFBTSxFQUFFLENBQUMsR0FB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
|
package/dist/arbs.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"arbs.d.ts","sourceRoot":"","sources":["../src/arbs.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,KAAK,CAAC,MAAM,mBAAmB,CAAA;AAG3C,OAAO,KAAK,SAAS,MAAM,YAAY,CAAA;AAUvC,wBAAgB,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,sBAEtD;AAED,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,sBAE3D;
|
|
1
|
+
{"version":3,"file":"arbs.d.ts","sourceRoot":"","sources":["../src/arbs.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,KAAK,CAAC,MAAM,mBAAmB,CAAA;AAG3C,OAAO,KAAK,SAAS,MAAM,YAAY,CAAA;AAUvC,wBAAgB,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,sBAEtD;AAED,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,sBAE3D;AAOD,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,CAAC,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC;;EASnE"}
|
package/dist/arbs.js
CHANGED
|
@@ -16,15 +16,16 @@ export function generate(arb) {
|
|
|
16
16
|
export function generateFromArbitrary(arb) {
|
|
17
17
|
return generate(arb(FastCheck));
|
|
18
18
|
}
|
|
19
|
-
//
|
|
20
|
-
//
|
|
19
|
+
// Native `sampleEffect({ count: 1 })` always uses attempt 0 of the master seed (edge-biased).
|
|
20
|
+
// Integer `seed + n` stayed in that bucket and collided for StringId. Mix a per-call suffix
|
|
21
|
+
// so each call is a jump-before-toss, like successive `generate(rnd)` draws.
|
|
21
22
|
let schemaSamples = 0;
|
|
22
23
|
export function generateFromSchema(schema) {
|
|
23
|
-
const samples = Effect.runSync(Arbitrary.sampleEffect(Arbitrary.schema(schema), { count: 1, seed: seed
|
|
24
|
+
const samples = Effect.runSync(Arbitrary.sampleEffect(Arbitrary.schema(schema), { count: 1, seed: `${seed}:${schemaSamples++}` }));
|
|
24
25
|
const value = samples[0];
|
|
25
26
|
if (value === undefined) {
|
|
26
27
|
throw new Error("failed to sample schema");
|
|
27
28
|
}
|
|
28
29
|
return { value };
|
|
29
30
|
}
|
|
30
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
31
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXJicy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9hcmJzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLDRCQUE0QjtBQUU1QixPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0saUJBQWlCLENBQUE7QUFDdkMsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLGtCQUFrQixDQUFBO0FBRTNDLE9BQU8sS0FBSyxNQUFNLE1BQU0sZUFBZSxDQUFBO0FBQ3ZDLE9BQU8sS0FBSyxTQUFTLE1BQU0scUNBQXFDLENBQUE7QUFDaEUsT0FBTyxLQUFLLFNBQVMsTUFBTSxZQUFZLENBQUE7QUFDdkMsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLFlBQVksQ0FBQTtBQUNuQyxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sb0NBQW9DLENBQUE7QUFFbkUsTUFBTSxJQUFJLEdBQUcsQ0FBQyxDQUFBO0FBQ2QsTUFBTSxHQUFHLEdBQUcsY0FBYyxDQUFDLElBQUksQ0FBQyxDQUFBO0FBQ2hDLE1BQU0sR0FBRyxHQUFHLElBQUksTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFBO0FBRTNCLFFBQVEsQ0FBQyxLQUFLLENBQUMsQ0FBQTtBQUVmLE1BQU0sVUFBVSxRQUFRLENBQUksR0FBMkI7SUFDckQsT0FBTyxHQUFHLENBQUMsUUFBUSxDQUFDLEdBQUcsRUFBRSxTQUFTLENBQUMsQ0FBQTtBQUNyQyxDQUFDO0FBRUQsTUFBTSxVQUFVLHFCQUFxQixDQUFJLEdBQW1CO0lBQzFELE9BQU8sUUFBUSxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFBO0FBQ2pDLENBQUM7QUFFRCw4RkFBOEY7QUFDOUYsNEZBQTRGO0FBQzVGLDZFQUE2RTtBQUM3RSxJQUFJLGFBQWEsR0FBRyxDQUFDLENBQUE7QUFFckIsTUFBTSxVQUFVLGtCQUFrQixDQUF5QixNQUFTO0lBQ2xFLE1BQU0sT0FBTyxHQUFHLE1BQU0sQ0FBQyxPQUFPLENBQzVCLFNBQVMsQ0FBQyxZQUFZLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsRUFBRSxFQUFFLEtBQUssRUFBRSxDQUFDLEVBQUUsSUFBSSxFQUFFLEdBQUcsSUFBSSxJQUFJLGFBQWEsRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUNuRyxDQUFBO0lBQ0QsTUFBTSxLQUFLLEdBQUcsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFBO0lBQ3hCLElBQUksS0FBSyxLQUFLLFNBQVMsRUFBRSxDQUFDO1FBQ3hCLE1BQU0sSUFBSSxLQUFLLENBQUMseUJBQXlCLENBQUMsQ0FBQTtJQUM1QyxDQUFDO0lBQ0QsT0FBTyxFQUFFLEtBQUssRUFBRSxDQUFBO0FBQ2xCLENBQUMifQ==
|
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.329",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"proper-lockfile": "^4.1.2",
|
|
17
17
|
"pure-rand": "8.4.0",
|
|
18
18
|
"query-string": "^9.4.0",
|
|
19
|
-
"effect-app": "4.0.0-beta.
|
|
19
|
+
"effect-app": "4.0.0-beta.329"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@azure/cosmos": "^4.9.3",
|
package/src/Store/Cosmos.ts
CHANGED
|
@@ -19,7 +19,7 @@ import { DatabaseError, OptimisticConcurrencyException } from "../errors.ts"
|
|
|
19
19
|
import { InfraLogger } from "../logger.ts"
|
|
20
20
|
import { annotateCosmosResponse, annotateDb } from "../otel.ts"
|
|
21
21
|
import { buildWhereCosmosQuery3, logQuery } from "./Cosmos/query.ts"
|
|
22
|
-
import { makeJsonDocumentCodec, makeStoredDecode } from "./jsonDocument.ts"
|
|
22
|
+
import { decodeStoredMany, decodeStoredOption, makeJsonDocumentCodec, makeStoredDecode } from "./jsonDocument.ts"
|
|
23
23
|
import { makeJsonLower } from "./utils.ts"
|
|
24
24
|
|
|
25
25
|
const makeMapId =
|
|
@@ -99,7 +99,7 @@ const makeCosmosStore = Effect.fnUntraced(function*({ autoscaleMaxThroughput, pr
|
|
|
99
99
|
const mapId = makeMapId<IdKey, Encoded>(idKey)
|
|
100
100
|
const mapReverseId = makeReverseMapId<IdKey, Encoded>(idKey)
|
|
101
101
|
const codec = makeJsonDocumentCodec<Encoded>(config?.schema)
|
|
102
|
-
const decodeStored = makeStoredDecode<Encoded>(
|
|
102
|
+
const decodeStored = makeStoredDecode<Encoded>(config?.schema, config?.jitM)
|
|
103
103
|
const json = makeJsonLower(config)
|
|
104
104
|
const defaultValues = json.toJson(config?.defaultValues ?? {}) as Partial<Encoded>
|
|
105
105
|
// stored JSON -> defaultValues -> jitM -> JSON→Encoded decode
|
|
@@ -458,7 +458,9 @@ const makeCosmosStore = Effect.fnUntraced(function*({ autoscaleMaxThroughput, pr
|
|
|
458
458
|
container.items.query<PMCosmos>(q, { partitionKey: nsBasePartitionKey(ns) }).fetchAll()
|
|
459
459
|
)
|
|
460
460
|
yield* annotateFeed(response)
|
|
461
|
-
return
|
|
461
|
+
return yield* Effect.fromResult(
|
|
462
|
+
decodeStoredMany(response.resources, (_) => fromStored(_ as unknown as Encoded))
|
|
463
|
+
)
|
|
462
464
|
})
|
|
463
465
|
.pipe(
|
|
464
466
|
annotateDb({
|
|
@@ -532,7 +534,10 @@ const makeCosmosStore = Effect.fnUntraced(function*({ autoscaleMaxThroughput, pr
|
|
|
532
534
|
container.items.query<{ f: M }>(q, { partitionKey: nsBasePartitionKey(ns) }).fetchAll()
|
|
533
535
|
)
|
|
534
536
|
yield* annotateFeed(response)
|
|
535
|
-
|
|
537
|
+
const decoded = yield* Effect.fromResult(
|
|
538
|
+
decodeStoredMany(response.resources, ({ f }) => fromStored(f as Encoded))
|
|
539
|
+
)
|
|
540
|
+
return decoded as any
|
|
536
541
|
})
|
|
537
542
|
.pipe(
|
|
538
543
|
annotateDb({
|
|
@@ -557,8 +562,8 @@ const makeCosmosStore = Effect.fnUntraced(function*({ autoscaleMaxThroughput, pr
|
|
|
557
562
|
.read<Encoded>()
|
|
558
563
|
)
|
|
559
564
|
yield* annotateItem(response)
|
|
560
|
-
return
|
|
561
|
-
Option.
|
|
565
|
+
return yield* Effect.fromResult(
|
|
566
|
+
decodeStoredOption(Option.fromNullishOr(response.resource), fromStored)
|
|
562
567
|
)
|
|
563
568
|
})
|
|
564
569
|
.pipe(annotateDb({
|
package/src/Store/Disk.ts
CHANGED
|
@@ -10,7 +10,7 @@ import * as Console from "effect/Console"
|
|
|
10
10
|
import { flow } from "effect/Function"
|
|
11
11
|
import * as Semaphore from "effect/Semaphore"
|
|
12
12
|
import { annotateDb } from "../otel.ts"
|
|
13
|
-
import { makeJsonDocumentCodec, makeStoredDecode } from "./jsonDocument.ts"
|
|
13
|
+
import { decodeStoredMany, makeJsonDocumentCodec, makeStoredDecode } from "./jsonDocument.ts"
|
|
14
14
|
import { makeMemoryStoreInt } from "./Memory.ts"
|
|
15
15
|
import { type JsonLower, makeJsonLower } from "./utils.ts"
|
|
16
16
|
|
|
@@ -30,7 +30,7 @@ function makeDiskStoreInt<IdKey extends keyof Encoded, Encoded extends FieldValu
|
|
|
30
30
|
const codec = makeJsonDocumentCodec<Encoded>(schema)
|
|
31
31
|
// the file is the raw JSON boundary for this adapter:
|
|
32
32
|
// stored JSON -> jitM -> JSON→Encoded decode
|
|
33
|
-
const decodeStored = makeStoredDecode<Encoded>(
|
|
33
|
+
const decodeStored = makeStoredDecode<Encoded>(schema, jitM)
|
|
34
34
|
return Effect.gen(function*() {
|
|
35
35
|
if (namespace !== "primary") {
|
|
36
36
|
dir = dir + "/" + namespace
|
|
@@ -53,7 +53,8 @@ function makeDiskStoreInt<IdKey extends keyof Encoded, Encoded extends FieldValu
|
|
|
53
53
|
extra: fileExtra
|
|
54
54
|
}),
|
|
55
55
|
Effect.flatMap((x) =>
|
|
56
|
-
Effect.sync(() =>
|
|
56
|
+
Effect.sync(() => JSON.parse(x) as PM[]).pipe(
|
|
57
|
+
Effect.flatMap((docs) => Effect.fromResult(decodeStoredMany(docs, decodeStored))),
|
|
57
58
|
annotateDb({
|
|
58
59
|
operation: "read.parse",
|
|
59
60
|
system: "disk",
|
package/src/Store/Memory.ts
CHANGED
|
@@ -18,7 +18,7 @@ import * as Struct from "effect/Struct"
|
|
|
18
18
|
import { InfraLogger } from "../logger.ts"
|
|
19
19
|
import { annotateDb } from "../otel.ts"
|
|
20
20
|
import { codeFilter, codeFilter3_ } from "./codeFilter.ts"
|
|
21
|
-
import { makeJsonDocumentCodec, makeStoredDecode } from "./jsonDocument.ts"
|
|
21
|
+
import { decodeStoredMany, decodeStoredOption, makeJsonDocumentCodec, makeStoredDecode } from "./jsonDocument.ts"
|
|
22
22
|
import { get, jsonifyFilter, type JsonLower, makeJsonLower, makeUpdateETag, toJsonQueryValue } from "./utils.ts"
|
|
23
23
|
|
|
24
24
|
export { get } from "./utils.ts"
|
|
@@ -344,7 +344,7 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
|
|
|
344
344
|
const codec = makeJsonDocumentCodec<Encoded>(schema)
|
|
345
345
|
const encodeDoc = (e: Encoded | PM): PM => codec.encode({ _etag: undefined, ...e })
|
|
346
346
|
// read path: stored JSON -> jitM -> JSON→Encoded decode
|
|
347
|
-
const decodeDoc = makeStoredDecode<Encoded>(
|
|
347
|
+
const decodeDoc = makeStoredDecode<Encoded>(schema, jitM)
|
|
348
348
|
const items_ = yield* seed ?? Effect.sync(() => [])
|
|
349
349
|
const toJson = json?.toJson ?? toJsonQueryValue
|
|
350
350
|
const lowerFilter = json?.jsonifyFilter ?? jsonifyFilter
|
|
@@ -362,7 +362,7 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
|
|
|
362
362
|
const values = Effect.map(Ref.get(store), (s) => s.values())
|
|
363
363
|
|
|
364
364
|
const allStored = Effect.map(values, Array.fromIterable)
|
|
365
|
-
const all = Effect.
|
|
365
|
+
const all = Effect.flatMap(allStored, (rows) => Effect.fromResult(decodeStoredMany(rows, decodeDoc)))
|
|
366
366
|
|
|
367
367
|
const batchSet = (items: NonEmptyReadonlyArray<PM>) =>
|
|
368
368
|
Effect
|
|
@@ -384,7 +384,9 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
|
|
|
384
384
|
)
|
|
385
385
|
),
|
|
386
386
|
Effect
|
|
387
|
-
.
|
|
387
|
+
.flatMap((items) => Effect.fromResult(decodeStoredMany(items, decodeDoc))),
|
|
388
|
+
Effect
|
|
389
|
+
.map((items) => items as unknown as NonEmptyReadonlyArray<PM>),
|
|
388
390
|
withPermit
|
|
389
391
|
)
|
|
390
392
|
|
|
@@ -430,7 +432,7 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
|
|
|
430
432
|
Ref
|
|
431
433
|
.get(store)
|
|
432
434
|
.pipe(
|
|
433
|
-
Effect.
|
|
435
|
+
Effect.flatMap((_) => Effect.fromResult(decodeStoredOption(Option.fromNullishOr(_.get(id)), decodeDoc))),
|
|
434
436
|
annotateDb({
|
|
435
437
|
operation: "find",
|
|
436
438
|
system: "memory",
|
|
@@ -445,10 +447,13 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
|
|
|
445
447
|
.pipe(
|
|
446
448
|
Effect.tap(() => logQuery(f, encodedDefaults)),
|
|
447
449
|
Effect.map(memFilter({ ...f, filter: f.filter ? lowerFilter(f.filter) : f.filter })),
|
|
448
|
-
Effect.
|
|
450
|
+
Effect.flatMap((rows) =>
|
|
449
451
|
f.select
|
|
450
|
-
? rows as (U extends undefined ? Encoded : Pick<Encoded, U>)[]
|
|
451
|
-
:
|
|
452
|
+
? Effect.succeed(rows as (U extends undefined ? Encoded : Pick<Encoded, U>)[])
|
|
453
|
+
: Effect.map(
|
|
454
|
+
Effect.fromResult(decodeStoredMany(rows, decodeDoc)),
|
|
455
|
+
(decoded) => decoded as (U extends undefined ? Encoded : Pick<Encoded, U>)[]
|
|
456
|
+
)
|
|
452
457
|
),
|
|
453
458
|
annotateDb({
|
|
454
459
|
operation: "filter",
|
|
@@ -470,7 +475,7 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
|
|
|
470
475
|
Effect.flatMap((_) => Ref.set(store, _))
|
|
471
476
|
)
|
|
472
477
|
),
|
|
473
|
-
Effect.
|
|
478
|
+
Effect.flatMap((stored) => Effect.fromResult(decodeDoc(stored))),
|
|
474
479
|
withPermit,
|
|
475
480
|
annotateDb({
|
|
476
481
|
operation: "set",
|
package/src/Store/SQL/Pg.ts
CHANGED
|
@@ -12,7 +12,7 @@ import { SqlClient } from "effect/unstable/sql"
|
|
|
12
12
|
import { DatabaseError, OptimisticConcurrencyException } from "../../errors.ts"
|
|
13
13
|
import { InfraLogger } from "../../logger.ts"
|
|
14
14
|
import { annotateDb } from "../../otel.ts"
|
|
15
|
-
import { makeJsonDocumentCodec, makeStoredDecode } from "../jsonDocument.ts"
|
|
15
|
+
import { decodeStoredMany, decodeStoredOption, makeJsonDocumentCodec, makeStoredDecode } from "../jsonDocument.ts"
|
|
16
16
|
import { makeETag, makeJsonLower, toJsonQueryValue } from "../utils.ts"
|
|
17
17
|
import { buildWhereSQLQuery, logQuery, pgDialect } from "./query.ts"
|
|
18
18
|
|
|
@@ -34,17 +34,19 @@ const preserveStoreError = (e: unknown): DatabaseError | OptimisticConcurrencyEx
|
|
|
34
34
|
return toDatabaseError(e)
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Row -> raw stored JSON document. Decoding it is a separate step
|
|
39
|
+
* (`makeStoredDecode`), so a document `jitM` does not repair fails as a typed
|
|
40
|
+
* `S.SchemaError` rather than throwing out of here.
|
|
41
|
+
*/
|
|
37
42
|
const parseRow = <Encoded extends FieldValues>(
|
|
38
43
|
row: { id: string; _etag: string | null; data: unknown },
|
|
39
44
|
idKey: PropertyKey,
|
|
40
|
-
defaultValues: Partial<Encoded
|
|
41
|
-
decode: (doc: PersistenceModelType<Encoded>) => PersistenceModelType<Encoded> = (doc) => doc
|
|
45
|
+
defaultValues: Partial<Encoded>
|
|
42
46
|
): PersistenceModelType<Encoded> => {
|
|
43
47
|
const data = (typeof row.data === "string" ? JSON.parse(row.data) : row.data) as object
|
|
44
48
|
const jsonDefaults = toJsonQueryValue(defaultValues) as Partial<Encoded>
|
|
45
|
-
return
|
|
46
|
-
{ ...jsonDefaults, ...data, [idKey]: row.id, _etag: row._etag ?? undefined } as PersistenceModelType<Encoded>
|
|
47
|
-
)
|
|
49
|
+
return { ...jsonDefaults, ...data, [idKey]: row.id, _etag: row._etag ?? undefined } as PersistenceModelType<Encoded>
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
const parseSelectRow = (
|
|
@@ -79,7 +81,9 @@ const makePgStore = Effect.fnUntraced(function*({ prefix }: StorageConfig) {
|
|
|
79
81
|
const defaultValues = json.toJson(config?.defaultValues ?? {}) as Partial<Encoded>
|
|
80
82
|
const codec = makeJsonDocumentCodec<Encoded>(config?.schema)
|
|
81
83
|
// read path: stored JSON -> defaultValues -> jitM -> JSON→Encoded decode
|
|
82
|
-
const decodeStored = makeStoredDecode<Encoded>(
|
|
84
|
+
const decodeStored = makeStoredDecode<Encoded>(config?.schema, config?.jitM)
|
|
85
|
+
const decodeRow = (row: { id: string; _etag: string | null; data: unknown }) =>
|
|
86
|
+
decodeStored(parseRow<Encoded>(row, idKey, defaultValues))
|
|
83
87
|
|
|
84
88
|
const resolveNamespace = !config?.allowNamespace
|
|
85
89
|
? Effect.succeed("primary")
|
|
@@ -202,9 +206,7 @@ const makePgStore = Effect.fnUntraced(function*({ prefix }: StorageConfig) {
|
|
|
202
206
|
const sqlText = `SELECT id, _etag, data FROM "${tableName}" WHERE _namespace = $1`
|
|
203
207
|
return exec(sqlText, [ns])
|
|
204
208
|
.pipe(
|
|
205
|
-
Effect.
|
|
206
|
-
(rows as any[]).map((r) => parseRow<Encoded>(r, idKey, defaultValues, decodeStored))
|
|
207
|
-
),
|
|
209
|
+
Effect.flatMap((rows) => Effect.fromResult(decodeStoredMany(rows as any[], decodeRow))),
|
|
208
210
|
annotateDb({
|
|
209
211
|
operation: "all",
|
|
210
212
|
system: "postgresql",
|
|
@@ -223,12 +225,9 @@ const makePgStore = Effect.fnUntraced(function*({ prefix }: StorageConfig) {
|
|
|
223
225
|
const sqlText = `SELECT id, _etag, data FROM "${tableName}" WHERE id = $1 AND _namespace = $2`
|
|
224
226
|
return exec(sqlText, [id, ns])
|
|
225
227
|
.pipe(
|
|
226
|
-
Effect.
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
? Option.some(parseRow<Encoded>(row, idKey, defaultValues, decodeStored))
|
|
230
|
-
: Option.none()
|
|
231
|
-
}),
|
|
228
|
+
Effect.flatMap((rows) =>
|
|
229
|
+
Effect.fromResult(decodeStoredOption(Option.fromNullishOr((rows as any[])[0]), decodeRow))
|
|
230
|
+
),
|
|
232
231
|
annotateDb({
|
|
233
232
|
operation: "find",
|
|
234
233
|
system: "postgresql",
|
|
@@ -286,9 +285,9 @@ const makePgStore = Effect.fnUntraced(function*({ prefix }: StorageConfig) {
|
|
|
286
285
|
Effect.tap((q) => Effect.annotateCurrentSpan({ "db.query.text": q.sql })),
|
|
287
286
|
Effect.flatMap((q) =>
|
|
288
287
|
exec(q.sql, q.params).pipe(
|
|
289
|
-
Effect.
|
|
288
|
+
Effect.flatMap((rows) => {
|
|
290
289
|
if (f.select) {
|
|
291
|
-
return (rows as any[]).map((r) => {
|
|
290
|
+
return Effect.succeed((rows as any[]).map((r) => {
|
|
292
291
|
const selected = parseSelectRow(r, idKey, {})
|
|
293
292
|
return {
|
|
294
293
|
...Struct.pick(
|
|
@@ -297,10 +296,11 @@ const makePgStore = Effect.fnUntraced(function*({ prefix }: StorageConfig) {
|
|
|
297
296
|
),
|
|
298
297
|
...selected
|
|
299
298
|
} as M
|
|
300
|
-
})
|
|
299
|
+
}))
|
|
301
300
|
}
|
|
302
|
-
return
|
|
303
|
-
|
|
301
|
+
return Effect.map(
|
|
302
|
+
Effect.fromResult(decodeStoredMany(rows as any[], decodeRow)),
|
|
303
|
+
(decoded) => decoded as any as M[]
|
|
304
304
|
)
|
|
305
305
|
})
|
|
306
306
|
)
|
package/src/Store/SQL.ts
CHANGED
|
@@ -15,7 +15,7 @@ import { SqlClient } from "effect/unstable/sql"
|
|
|
15
15
|
import { DatabaseError, OptimisticConcurrencyException } from "../errors.ts"
|
|
16
16
|
import { InfraLogger } from "../logger.ts"
|
|
17
17
|
import { annotateDb, type DbSystem } from "../otel.ts"
|
|
18
|
-
import { makeJsonDocumentCodec, makeStoredDecode } from "./jsonDocument.ts"
|
|
18
|
+
import { decodeStoredMany, decodeStoredOption, makeJsonDocumentCodec, makeStoredDecode } from "./jsonDocument.ts"
|
|
19
19
|
import { buildWhereSQLQuery, logQuery, type SQLDialect, sqliteDialect } from "./SQL/query.ts"
|
|
20
20
|
import { makeETag, makeJsonLower, toJsonQueryValue } from "./utils.ts"
|
|
21
21
|
|
|
@@ -43,18 +43,22 @@ export class WithNsTransaction
|
|
|
43
43
|
extends Context.Service<WithNsTransaction, WithNsTransactionFn>()("effect-app/WithNsTransaction")
|
|
44
44
|
{}
|
|
45
45
|
|
|
46
|
-
/**
|
|
46
|
+
/**
|
|
47
|
+
* Row -> raw stored JSON document: `data` parsed, `defaultValues` merged, `id`
|
|
48
|
+
* and `_etag` re-injected from their columns. Decoding it is a separate step
|
|
49
|
+
* (`makeStoredDecode`), so a document `jitM` does not repair fails as a typed
|
|
50
|
+
* `S.SchemaError` rather than throwing out of here.
|
|
51
|
+
*
|
|
52
|
+
* @internal
|
|
53
|
+
*/
|
|
47
54
|
export const parseRow = <Encoded extends FieldValues>(
|
|
48
55
|
row: { id: string; _etag: string | null; data: string },
|
|
49
56
|
idKey: PropertyKey,
|
|
50
|
-
defaultValues: Partial<Encoded
|
|
51
|
-
decode: (doc: PersistenceModelType<Encoded>) => PersistenceModelType<Encoded> = (doc) => doc
|
|
57
|
+
defaultValues: Partial<Encoded>
|
|
52
58
|
): PersistenceModelType<Encoded> => {
|
|
53
59
|
const data = (typeof row.data === "string" ? JSON.parse(row.data) : row.data) as object
|
|
54
60
|
const jsonDefaults = toJsonQueryValue(defaultValues) as Partial<Encoded>
|
|
55
|
-
return
|
|
56
|
-
{ ...jsonDefaults, ...data, [idKey]: row.id, _etag: row._etag ?? undefined } as PersistenceModelType<Encoded>
|
|
57
|
-
)
|
|
61
|
+
return { ...jsonDefaults, ...data, [idKey]: row.id, _etag: row._etag ?? undefined } as PersistenceModelType<Encoded>
|
|
58
62
|
}
|
|
59
63
|
|
|
60
64
|
const parseSelectRow = (
|
|
@@ -95,7 +99,9 @@ function makeSQLStoreInt(system: DbSystem, dialect: SQLDialect, jsonColumnType:
|
|
|
95
99
|
const defaultValues = json.toJson(config?.defaultValues ?? {}) as Partial<Encoded>
|
|
96
100
|
const codec = makeJsonDocumentCodec<Encoded>(config?.schema)
|
|
97
101
|
// read path: stored JSON -> defaultValues -> jitM -> JSON→Encoded decode
|
|
98
|
-
const decodeStored = makeStoredDecode<Encoded>(
|
|
102
|
+
const decodeStored = makeStoredDecode<Encoded>(config?.schema, config?.jitM)
|
|
103
|
+
const decodeRow = (row: { id: string; _etag: string | null; data: string }) =>
|
|
104
|
+
decodeStored(parseRow<Encoded>(row, idKey, defaultValues))
|
|
99
105
|
|
|
100
106
|
const resolveNamespace = !config?.allowNamespace
|
|
101
107
|
? Effect.succeed("primary")
|
|
@@ -218,9 +224,7 @@ function makeSQLStoreInt(system: DbSystem, dialect: SQLDialect, jsonColumnType:
|
|
|
218
224
|
const sqlText = `SELECT id, _etag, data FROM "${tableName}" WHERE _namespace = ?`
|
|
219
225
|
return exec(sqlText, [ns])
|
|
220
226
|
.pipe(
|
|
221
|
-
Effect.
|
|
222
|
-
(rows as any[]).map((r) => parseRow<Encoded>(r, idKey, defaultValues, decodeStored))
|
|
223
|
-
),
|
|
227
|
+
Effect.flatMap((rows) => Effect.fromResult(decodeStoredMany(rows as any[], decodeRow))),
|
|
224
228
|
annotateDb({
|
|
225
229
|
operation: "all",
|
|
226
230
|
system,
|
|
@@ -239,12 +243,9 @@ function makeSQLStoreInt(system: DbSystem, dialect: SQLDialect, jsonColumnType:
|
|
|
239
243
|
const sqlText = `SELECT id, _etag, data FROM "${tableName}" WHERE id = ? AND _namespace = ?`
|
|
240
244
|
return exec(sqlText, [id, ns])
|
|
241
245
|
.pipe(
|
|
242
|
-
Effect.
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
? Option.some(parseRow<Encoded>(row, idKey, defaultValues, decodeStored))
|
|
246
|
-
: Option.none()
|
|
247
|
-
}),
|
|
246
|
+
Effect.flatMap((rows) =>
|
|
247
|
+
Effect.fromResult(decodeStoredOption(Option.fromNullishOr((rows as any[])[0]), decodeRow))
|
|
248
|
+
),
|
|
248
249
|
annotateDb({
|
|
249
250
|
operation: "find",
|
|
250
251
|
system,
|
|
@@ -302,9 +303,9 @@ function makeSQLStoreInt(system: DbSystem, dialect: SQLDialect, jsonColumnType:
|
|
|
302
303
|
Effect.tap((q) => Effect.annotateCurrentSpan({ "db.query.text": q.sql })),
|
|
303
304
|
Effect.flatMap((q) =>
|
|
304
305
|
exec(q.sql, q.params).pipe(
|
|
305
|
-
Effect.
|
|
306
|
+
Effect.flatMap((rows) => {
|
|
306
307
|
if (f.select) {
|
|
307
|
-
return (rows as any[]).map((r) => {
|
|
308
|
+
return Effect.succeed((rows as any[]).map((r) => {
|
|
308
309
|
const selected = parseSelectRow(r, idKey)
|
|
309
310
|
return {
|
|
310
311
|
...Struct.pick(
|
|
@@ -313,10 +314,11 @@ function makeSQLStoreInt(system: DbSystem, dialect: SQLDialect, jsonColumnType:
|
|
|
313
314
|
),
|
|
314
315
|
...selected
|
|
315
316
|
} as M
|
|
316
|
-
})
|
|
317
|
+
}))
|
|
317
318
|
}
|
|
318
|
-
return
|
|
319
|
-
|
|
319
|
+
return Effect.map(
|
|
320
|
+
Effect.fromResult(decodeStoredMany(rows as any[], decodeRow)),
|
|
321
|
+
(decoded) => decoded as any as M[]
|
|
320
322
|
)
|
|
321
323
|
})
|
|
322
324
|
)
|
|
@@ -436,7 +438,9 @@ function makeSQLiteStorePerNs(
|
|
|
436
438
|
const defaultValues = json.toJson(config?.defaultValues ?? {}) as Partial<Encoded>
|
|
437
439
|
const codec = makeJsonDocumentCodec<Encoded>(config?.schema)
|
|
438
440
|
// read path: stored JSON -> defaultValues -> jitM -> JSON→Encoded decode
|
|
439
|
-
const decodeStored = makeStoredDecode<Encoded>(
|
|
441
|
+
const decodeStored = makeStoredDecode<Encoded>(config?.schema, config?.jitM)
|
|
442
|
+
const decodeRow = (row: { id: string; _etag: string | null; data: string }) =>
|
|
443
|
+
decodeStored(parseRow<Encoded>(row, idKey, defaultValues))
|
|
440
444
|
|
|
441
445
|
const resolveNamespace = !config?.allowNamespace
|
|
442
446
|
? Effect.succeed("primary")
|
|
@@ -567,9 +571,7 @@ function makeSQLiteStorePerNs(
|
|
|
567
571
|
const sqlText = `SELECT id, _etag, data FROM "${tableName}"`
|
|
568
572
|
return exec(ns, sqlText)
|
|
569
573
|
.pipe(
|
|
570
|
-
Effect.
|
|
571
|
-
(rows as any[]).map((r) => parseRow<Encoded>(r, idKey, defaultValues, decodeStored))
|
|
572
|
-
),
|
|
574
|
+
Effect.flatMap((rows) => Effect.fromResult(decodeStoredMany(rows as any[], decodeRow))),
|
|
573
575
|
annotateDb({
|
|
574
576
|
operation: "all",
|
|
575
577
|
system: "sqlite",
|
|
@@ -587,12 +589,9 @@ function makeSQLiteStorePerNs(
|
|
|
587
589
|
const sqlText = `SELECT id, _etag, data FROM "${tableName}" WHERE id = ?`
|
|
588
590
|
return exec(ns, sqlText, [id])
|
|
589
591
|
.pipe(
|
|
590
|
-
Effect.
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
? Option.some(parseRow<Encoded>(row, idKey, defaultValues, decodeStored))
|
|
594
|
-
: Option.none()
|
|
595
|
-
}),
|
|
592
|
+
Effect.flatMap((rows) =>
|
|
593
|
+
Effect.fromResult(decodeStoredOption(Option.fromNullishOr((rows as any[])[0]), decodeRow))
|
|
594
|
+
),
|
|
596
595
|
annotateDb({
|
|
597
596
|
operation: "find",
|
|
598
597
|
system: "sqlite",
|
|
@@ -650,9 +649,9 @@ function makeSQLiteStorePerNs(
|
|
|
650
649
|
Effect.tap((q) => Effect.annotateCurrentSpan({ "db.query.text": q.sql })),
|
|
651
650
|
Effect.flatMap((q) =>
|
|
652
651
|
exec(ns, q.sql, q.params).pipe(
|
|
653
|
-
Effect.
|
|
652
|
+
Effect.flatMap((rows) => {
|
|
654
653
|
if (f.select) {
|
|
655
|
-
return (rows as any[]).map((r) => {
|
|
654
|
+
return Effect.succeed((rows as any[]).map((r) => {
|
|
656
655
|
const selected = parseSelectRow(r, idKey)
|
|
657
656
|
return {
|
|
658
657
|
...Struct.pick(
|
|
@@ -661,10 +660,11 @@ function makeSQLiteStorePerNs(
|
|
|
661
660
|
),
|
|
662
661
|
...selected
|
|
663
662
|
} as M
|
|
664
|
-
})
|
|
663
|
+
}))
|
|
665
664
|
}
|
|
666
|
-
return
|
|
667
|
-
|
|
665
|
+
return Effect.map(
|
|
666
|
+
Effect.fromResult(decodeStoredMany(rows as any[], decodeRow)),
|
|
667
|
+
(decoded) => decoded as any as M[]
|
|
668
668
|
)
|
|
669
669
|
})
|
|
670
670
|
)
|