@prisma-next/mongo-codec 0.5.0-dev.6 → 0.5.0-dev.8
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/README.md +29 -3
- package/dist/index.d.mts +46 -11
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +37 -7
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -3
- package/src/codecs.ts +89 -27
- package/src/exports/index.ts +6 -1
package/README.md
CHANGED
|
@@ -4,10 +4,36 @@ Codec interface and registry for MongoDB value serialization.
|
|
|
4
4
|
|
|
5
5
|
## Responsibilities
|
|
6
6
|
|
|
7
|
-
- **Codec interface**: `MongoCodec<Id, TTraits, TWire,
|
|
8
|
-
- **Codec factory**: `mongoCodec()` — creates frozen codec instances from a config object
|
|
7
|
+
- **Codec interface**: `MongoCodec<Id, TTraits, TWire, TInput>` — declares how a JS value translates to and from the BSON-shaped wire format the Mongo driver exchanges, plus the JSON-safe form stored in contract artifacts. Carries trait annotations (`equality`, `order`, `boolean`, `numeric`, `textual`, `vector`) for operator gating. Same four generics as the framework `Codec` base.
|
|
8
|
+
- **Codec factory**: `mongoCodec()` — creates frozen codec instances from a config object. `encode` is optional (identity default when omitted); `encode`/`decode` may be authored as sync or async functions and are lifted to Promise-returning query-time methods automatically. Build-time methods (`encodeJson`, `decodeJson`) are synchronous and default to identity when omitted.
|
|
9
9
|
- **Codec registry**: `MongoCodecRegistry` and `createMongoCodecRegistry()` — a map-based container that stores and retrieves codecs by ID, with duplicate-ID protection
|
|
10
|
-
- **Type-level helpers**: `
|
|
10
|
+
- **Type-level helpers**: `MongoCodecInput<T>` and `MongoCodecTraits<T>` for extracting the input JS type and traits from codec types
|
|
11
|
+
|
|
12
|
+
## Examples
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
// Sync authoring:
|
|
16
|
+
const intCodec = mongoCodec({
|
|
17
|
+
typeId: 'mongo/int@1',
|
|
18
|
+
targetTypes: ['int'],
|
|
19
|
+
encode: (v: number) => v,
|
|
20
|
+
decode: (w: number) => w,
|
|
21
|
+
encodeJson: (v: number) => v,
|
|
22
|
+
decodeJson: (j: number) => j,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Async authoring (e.g. KMS-backed encryption): same factory, same shape.
|
|
26
|
+
const secretCodec = mongoCodec({
|
|
27
|
+
typeId: 'mongo/secret@1',
|
|
28
|
+
targetTypes: ['string'],
|
|
29
|
+
encode: async (v: string) => encrypt(v, await getKey()),
|
|
30
|
+
decode: async (w: string) => decrypt(w, await getKey()),
|
|
31
|
+
encodeJson: (v: string) => v,
|
|
32
|
+
decodeJson: (j: string) => j,
|
|
33
|
+
});
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
See [ADR 204 — Single-Path Async Codec Runtime](../../../../docs/architecture%20docs/adrs/ADR%20204%20-%20Single-Path%20Async%20Codec%20Runtime.md) for the codec runtime's async boundary contract.
|
|
11
37
|
|
|
12
38
|
## Dependencies
|
|
13
39
|
|
package/dist/index.d.mts
CHANGED
|
@@ -3,20 +3,55 @@ import { Codec, CodecTrait } from "@prisma-next/framework-components/codec";
|
|
|
3
3
|
|
|
4
4
|
//#region src/codecs.d.ts
|
|
5
5
|
type MongoCodecTrait = CodecTrait;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
/**
|
|
7
|
+
* A codec for the Mongo target. Translates between an application value
|
|
8
|
+
* and the BSON-shaped wire form the Mongo driver exchanges, and between
|
|
9
|
+
* an application value and the JSON form stored in contract artifacts.
|
|
10
|
+
*
|
|
11
|
+
* Same shape as the framework codec base — see `Codec` in
|
|
12
|
+
* `@prisma-next/framework-components/codec` for the contract. The alias
|
|
13
|
+
* exists so Mongo-specific metadata can be added here in future without
|
|
14
|
+
* touching the framework base.
|
|
15
|
+
*/
|
|
16
|
+
type MongoCodec<Id extends string = string, TTraits$1 extends readonly MongoCodecTrait[] = readonly MongoCodecTrait[], TWire = unknown, TInput = unknown> = Codec<Id, TTraits$1, TWire, TInput>;
|
|
17
|
+
/**
|
|
18
|
+
* Conditional bundle for `encodeJson`/`decodeJson`: when `TInput` is
|
|
19
|
+
* structurally assignable to `JsonValue` the identity defaults are
|
|
20
|
+
* sound and both fields are optional; otherwise both fields are
|
|
21
|
+
* required so an author cannot silently produce a non-JSON-safe
|
|
22
|
+
* contract artifact.
|
|
23
|
+
*/
|
|
24
|
+
type JsonRoundTripConfig<TInput> = [TInput] extends [JsonValue] ? {
|
|
25
|
+
encodeJson?: (value: TInput) => JsonValue;
|
|
26
|
+
decodeJson?: (json: JsonValue) => TInput;
|
|
27
|
+
} : {
|
|
28
|
+
encodeJson: (value: TInput) => JsonValue;
|
|
29
|
+
decodeJson: (json: JsonValue) => TInput;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Construct a Mongo codec from author functions.
|
|
33
|
+
*
|
|
34
|
+
* Author `encode` and `decode` as sync or async functions; the factory
|
|
35
|
+
* produces a {@link MongoCodec} whose query-time methods follow the
|
|
36
|
+
* boundary contract documented on the framework {@link BaseCodec}.
|
|
37
|
+
*
|
|
38
|
+
* `encode` is optional — when omitted, an identity default is installed
|
|
39
|
+
* (declaring "the input value already is the wire value", so `TInput` and
|
|
40
|
+
* `TWire` are interchangeable for that codec). `decode` is always
|
|
41
|
+
* required. `encodeJson` and `decodeJson` default to identity **only when
|
|
42
|
+
* `TInput` is assignable to `JsonValue`**; otherwise both are required so
|
|
43
|
+
* the contract artifact stays JSON-safe.
|
|
44
|
+
*/
|
|
45
|
+
declare function mongoCodec<Id extends string, const TTraits$1 extends readonly MongoCodecTrait[] = readonly [], TWire = unknown, TInput = unknown>(config: {
|
|
10
46
|
typeId: Id;
|
|
11
47
|
targetTypes: readonly string[];
|
|
12
48
|
traits?: TTraits$1;
|
|
13
|
-
encode
|
|
14
|
-
decode: (wire: TWire) =>
|
|
15
|
-
encodeJson?: (value: TJs) => JsonValue;
|
|
16
|
-
decodeJson?: (json: JsonValue) => TJs;
|
|
49
|
+
encode?: (value: TInput) => TWire | Promise<TWire>;
|
|
50
|
+
decode: (wire: TWire) => TInput | Promise<TInput>;
|
|
17
51
|
renderOutputType?: (typeParams: Record<string, unknown>) => string | undefined;
|
|
18
|
-
}): MongoCodec<Id, TTraits$1, TWire,
|
|
19
|
-
type
|
|
52
|
+
} & JsonRoundTripConfig<TInput>): MongoCodec<Id, TTraits$1, TWire, TInput>;
|
|
53
|
+
/** Extract the JS application type carried by a Mongo codec — used both as `encode` input and as `decode` output. */
|
|
54
|
+
type MongoCodecInput<T> = T extends MongoCodec<string, readonly MongoCodecTrait[], unknown, infer TInput> ? TInput : never;
|
|
20
55
|
type MongoCodecTraits<T> = T extends MongoCodec<string, infer TTraits> ? TTraits[number] & MongoCodecTrait : never;
|
|
21
56
|
//#endregion
|
|
22
57
|
//#region src/codec-registry.d.ts
|
|
@@ -29,5 +64,5 @@ interface MongoCodecRegistry {
|
|
|
29
64
|
}
|
|
30
65
|
declare function createMongoCodecRegistry(): MongoCodecRegistry;
|
|
31
66
|
//#endregion
|
|
32
|
-
export { type MongoCodec, type
|
|
67
|
+
export { type MongoCodec, type MongoCodecInput, type MongoCodecRegistry, type MongoCodecTrait, type MongoCodecTraits, createMongoCodecRegistry, mongoCodec };
|
|
33
68
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/codecs.ts","../src/codec-registry.ts"],"sourcesContent":[],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/codecs.ts","../src/codec-registry.ts"],"sourcesContent":[],"mappings":";;;;KAIY,eAAA,GAAkB;;AAA9B;AAYA;;;;;;;;AAKa,KALD,UAKC,CAAA,WAAA,MAAA,GAAA,MAAA,EAAA,kBAAA,SAHc,eAGd,EAAA,GAAA,SAH2C,eAG3C,EAAA,EAAA,QAAA,OAAA,EAAA,SAAA,OAAA,CAAA,GAAT,KAAS,CAAC,EAAD,EAAK,SAAL,EAAc,KAAd,EAAqB,MAArB,CAAA;AAA6B;;;;;;;KASrC,mBAMqB,CAAA,MAAA,CAAA,GAAA,CANU,MAMV,CAAA,SAAA,CAN2B,SAM3B,CAAA,GAAA;EAAW,UAAA,CAAA,EAAA,CAAA,KAAA,EAJV,MAIU,EAAA,GAJC,SAID;EACZ,UAAA,CAAA,EAAA,CAAA,IAAA,EAJC,SAID,EAAA,GAJe,MAIf;CAAc,GAAA;EAAM,UAAA,EAAA,CAAA,KAAA,EADnB,MACmB,EAAA,GADR,SACQ;EAiB7B,UAAA,EAAA,CAAU,IAAA,EAjBD,SAiBC,EAAA,GAjBa,MAiBb;CAEO;;;;;;;;;;;;;;;AAYf,iBAdF,UAcE,CAAA,WAAA,MAAA,EAAA,wBAAA,SAZe,eAYf,EAAA,GAAA,SAAA,EAAA,EAAA,QAAA,OAAA,EAAA,SAAA,OAAA,CAAA,CAAA,MAAA,EAAA;EAAS,MAAA,EAPf,EAOe;EAAO,WAAA,EAAA,SAAA,MAAA,EAAA;EAA/B,MAAA,CAAA,EALU,SAKV;EAAU,MAAA,CAAA,EAAA,CAAA,KAAA,EAJQ,MAIR,EAAA,GAJmB,KAInB,GAJ2B,OAI3B,CAJmC,KAInC,CAAA;EAuCD,MAAA,EAAA,CAAA,IAAA,EA1CO,KA0CQ,EAAA,GA1CE,MA0CF,GA1CW,OA0CX,CA1CmB,MA0CnB,CAAA;EACzB,gBAAA,CAAA,EAAA,CAAA,UAAA,EA1CkC,MA0ClC,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,GAAA,MAAA,GAAA,SAAA;CAAsC,GAzClC,mBAyCkC,CAzCd,MAyCc,CAAA,CAAA,EAxCrC,UAwCqC,CAxC1B,EAwC0B,EAxCtB,SAwCsB,EAxCb,KAwCa,EAxCN,MAwCM,CAAA;;AAAlB,KADV,eACU,CAAA,CAAA,CAAA,GAApB,CAAoB,SAAV,UAAU,CAAA,MAAA,EAAA,SAAkB,eAAlB,EAAA,EAAA,OAAA,EAAA,KAAA,OAAA,CAAA,GAAA,MAAA,GAAA,KAAA;AAEV,KAAA,gBAAgB,CAAA,CAAA,CAAA,GAC1B,CAD0B,SAChB,UADgB,CAAA,MAAA,EAAA,KAAA,QAAA,CAAA,GACoB,OADpB,CAAA,MAAA,CAAA,GACsC,eADtC,GAAA,KAAA;;;UC5GX,kBAAA;mBACE;;EDCP,QAAA,CAAA,KAAA,ECCM,UDDS,CAAA,MAAG,CAAA,CAAA,EAAA,IAAU;EAY5B,CAAA,MAAA,CAAA,QAAU,GAAA,ECVC,QDUD,CCVU,UDUV,CAAA,MAAA,CAAA,CAAA;EAEK,MAAA,EAAA,ECXf,gBDWe,CCXE,UDWF,CAAA,MAAA,CAAA,CAAA;;AAGb,iBCgBE,wBAAA,CAAA,CDhBF,ECgB8B,kBDhB9B"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { ifDefined } from "@prisma-next/utils/defined";
|
|
2
|
+
|
|
1
3
|
//#region src/codec-registry.ts
|
|
2
4
|
var MongoCodecRegistryImpl = class {
|
|
3
5
|
#byId = /* @__PURE__ */ new Map();
|
|
@@ -24,18 +26,46 @@ function createMongoCodecRegistry() {
|
|
|
24
26
|
|
|
25
27
|
//#endregion
|
|
26
28
|
//#region src/codecs.ts
|
|
29
|
+
/**
|
|
30
|
+
* Construct a Mongo codec from author functions.
|
|
31
|
+
*
|
|
32
|
+
* Author `encode` and `decode` as sync or async functions; the factory
|
|
33
|
+
* produces a {@link MongoCodec} whose query-time methods follow the
|
|
34
|
+
* boundary contract documented on the framework {@link BaseCodec}.
|
|
35
|
+
*
|
|
36
|
+
* `encode` is optional — when omitted, an identity default is installed
|
|
37
|
+
* (declaring "the input value already is the wire value", so `TInput` and
|
|
38
|
+
* `TWire` are interchangeable for that codec). `decode` is always
|
|
39
|
+
* required. `encodeJson` and `decodeJson` default to identity **only when
|
|
40
|
+
* `TInput` is assignable to `JsonValue`**; otherwise both are required so
|
|
41
|
+
* the contract artifact stays JSON-safe.
|
|
42
|
+
*/
|
|
27
43
|
function mongoCodec(config) {
|
|
28
|
-
const traits = config.traits ? Object.freeze([...config.traits]) : Object.freeze([]);
|
|
29
44
|
const identity = (v) => v;
|
|
45
|
+
const userEncode = config.encode ?? ((value) => value);
|
|
46
|
+
const userDecode = config.decode;
|
|
47
|
+
const widenedConfig = config;
|
|
30
48
|
return {
|
|
31
49
|
id: config.typeId,
|
|
32
50
|
targetTypes: config.targetTypes,
|
|
33
|
-
traits,
|
|
34
|
-
|
|
35
|
-
encode:
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
51
|
+
...ifDefined("traits", config.traits ? Object.freeze([...config.traits]) : void 0),
|
|
52
|
+
...ifDefined("renderOutputType", config.renderOutputType),
|
|
53
|
+
encode: (value) => {
|
|
54
|
+
try {
|
|
55
|
+
return Promise.resolve(userEncode(value));
|
|
56
|
+
} catch (error) {
|
|
57
|
+
return Promise.reject(error);
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
decode: (wire) => {
|
|
61
|
+
try {
|
|
62
|
+
return Promise.resolve(userDecode(wire));
|
|
63
|
+
} catch (error) {
|
|
64
|
+
return Promise.reject(error);
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
encodeJson: widenedConfig.encodeJson ?? identity,
|
|
68
|
+
decodeJson: widenedConfig.decodeJson ?? identity
|
|
39
69
|
};
|
|
40
70
|
}
|
|
41
71
|
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["#byId"],"sources":["../src/codec-registry.ts","../src/codecs.ts"],"sourcesContent":["import type { MongoCodec } from './codecs';\n\nexport interface MongoCodecRegistry {\n get(id: string): MongoCodec<string> | undefined;\n has(id: string): boolean;\n register(codec: MongoCodec<string>): void;\n [Symbol.iterator](): Iterator<MongoCodec<string>>;\n values(): IterableIterator<MongoCodec<string>>;\n}\n\nclass MongoCodecRegistryImpl implements MongoCodecRegistry {\n readonly #byId = new Map<string, MongoCodec<string>>();\n\n get(id: string): MongoCodec<string> | undefined {\n return this.#byId.get(id);\n }\n\n has(id: string): boolean {\n return this.#byId.has(id);\n }\n\n register(codec: MongoCodec<string>): void {\n if (this.#byId.has(codec.id)) {\n throw new Error(`Codec with ID '${codec.id}' is already registered`);\n }\n this.#byId.set(codec.id, codec);\n }\n\n *[Symbol.iterator](): Iterator<MongoCodec<string>> {\n yield* this.#byId.values();\n }\n\n values(): IterableIterator<MongoCodec<string>> {\n return this.#byId.values();\n }\n}\n\nexport function createMongoCodecRegistry(): MongoCodecRegistry {\n return new MongoCodecRegistryImpl();\n}\n","import type { JsonValue } from '@prisma-next/contract/types';\nimport type { Codec as BaseCodec, CodecTrait } from '@prisma-next/framework-components/codec';\n\nexport type MongoCodecTrait = CodecTrait;\n\nexport
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["#byId"],"sources":["../src/codec-registry.ts","../src/codecs.ts"],"sourcesContent":["import type { MongoCodec } from './codecs';\n\nexport interface MongoCodecRegistry {\n get(id: string): MongoCodec<string> | undefined;\n has(id: string): boolean;\n register(codec: MongoCodec<string>): void;\n [Symbol.iterator](): Iterator<MongoCodec<string>>;\n values(): IterableIterator<MongoCodec<string>>;\n}\n\nclass MongoCodecRegistryImpl implements MongoCodecRegistry {\n readonly #byId = new Map<string, MongoCodec<string>>();\n\n get(id: string): MongoCodec<string> | undefined {\n return this.#byId.get(id);\n }\n\n has(id: string): boolean {\n return this.#byId.has(id);\n }\n\n register(codec: MongoCodec<string>): void {\n if (this.#byId.has(codec.id)) {\n throw new Error(`Codec with ID '${codec.id}' is already registered`);\n }\n this.#byId.set(codec.id, codec);\n }\n\n *[Symbol.iterator](): Iterator<MongoCodec<string>> {\n yield* this.#byId.values();\n }\n\n values(): IterableIterator<MongoCodec<string>> {\n return this.#byId.values();\n }\n}\n\nexport function createMongoCodecRegistry(): MongoCodecRegistry {\n return new MongoCodecRegistryImpl();\n}\n","import type { JsonValue } from '@prisma-next/contract/types';\nimport type { Codec as BaseCodec, CodecTrait } from '@prisma-next/framework-components/codec';\nimport { ifDefined } from '@prisma-next/utils/defined';\n\nexport type MongoCodecTrait = CodecTrait;\n\n/**\n * A codec for the Mongo target. Translates between an application value\n * and the BSON-shaped wire form the Mongo driver exchanges, and between\n * an application value and the JSON form stored in contract artifacts.\n *\n * Same shape as the framework codec base — see `Codec` in\n * `@prisma-next/framework-components/codec` for the contract. The alias\n * exists so Mongo-specific metadata can be added here in future without\n * touching the framework base.\n */\nexport type MongoCodec<\n Id extends string = string,\n TTraits extends readonly MongoCodecTrait[] = readonly MongoCodecTrait[],\n TWire = unknown,\n TInput = unknown,\n> = BaseCodec<Id, TTraits, TWire, TInput>;\n\n/**\n * Conditional bundle for `encodeJson`/`decodeJson`: when `TInput` is\n * structurally assignable to `JsonValue` the identity defaults are\n * sound and both fields are optional; otherwise both fields are\n * required so an author cannot silently produce a non-JSON-safe\n * contract artifact.\n */\ntype JsonRoundTripConfig<TInput> = [TInput] extends [JsonValue]\n ? {\n encodeJson?: (value: TInput) => JsonValue;\n decodeJson?: (json: JsonValue) => TInput;\n }\n : {\n encodeJson: (value: TInput) => JsonValue;\n decodeJson: (json: JsonValue) => TInput;\n };\n\n/**\n * Construct a Mongo codec from author functions.\n *\n * Author `encode` and `decode` as sync or async functions; the factory\n * produces a {@link MongoCodec} whose query-time methods follow the\n * boundary contract documented on the framework {@link BaseCodec}.\n *\n * `encode` is optional — when omitted, an identity default is installed\n * (declaring \"the input value already is the wire value\", so `TInput` and\n * `TWire` are interchangeable for that codec). `decode` is always\n * required. `encodeJson` and `decodeJson` default to identity **only when\n * `TInput` is assignable to `JsonValue`**; otherwise both are required so\n * the contract artifact stays JSON-safe.\n */\nexport function mongoCodec<\n Id extends string,\n const TTraits extends readonly MongoCodecTrait[] = readonly [],\n TWire = unknown,\n TInput = unknown,\n>(\n config: {\n typeId: Id;\n targetTypes: readonly string[];\n traits?: TTraits;\n encode?: (value: TInput) => TWire | Promise<TWire>;\n decode: (wire: TWire) => TInput | Promise<TInput>;\n renderOutputType?: (typeParams: Record<string, unknown>) => string | undefined;\n } & JsonRoundTripConfig<TInput>,\n): MongoCodec<Id, TTraits, TWire, TInput> {\n const identity = (v: unknown) => v;\n // The synchronous identity default is only safe when the author has\n // declared \"the input is already the wire value\" (i.e. TInput == TWire);\n // it returns the value directly, never a Promise.\n const userEncode = config.encode ?? ((value: TInput) => value as unknown as TWire);\n const userDecode = config.decode;\n const widenedConfig = config as {\n encodeJson?: (value: TInput) => JsonValue;\n decodeJson?: (json: JsonValue) => TInput;\n };\n return {\n id: config.typeId,\n targetTypes: config.targetTypes,\n ...ifDefined(\n 'traits',\n config.traits ? (Object.freeze([...config.traits]) as TTraits) : undefined,\n ),\n ...ifDefined('renderOutputType', config.renderOutputType),\n encode: (value) => {\n try {\n return Promise.resolve(userEncode(value));\n } catch (error) {\n return Promise.reject(error);\n }\n },\n decode: (wire) => {\n try {\n return Promise.resolve(userDecode(wire));\n } catch (error) {\n return Promise.reject(error);\n }\n },\n encodeJson: (widenedConfig.encodeJson ?? identity) as (value: TInput) => JsonValue,\n decodeJson: (widenedConfig.decodeJson ?? identity) as (json: JsonValue) => TInput,\n };\n}\n\n/** Extract the JS application type carried by a Mongo codec — used both as `encode` input and as `decode` output. */\nexport type MongoCodecInput<T> =\n T extends MongoCodec<string, readonly MongoCodecTrait[], unknown, infer TInput> ? TInput : never;\n\nexport type MongoCodecTraits<T> =\n T extends MongoCodec<string, infer TTraits> ? TTraits[number] & MongoCodecTrait : never;\n"],"mappings":";;;AAUA,IAAM,yBAAN,MAA2D;CACzD,CAASA,uBAAQ,IAAI,KAAiC;CAEtD,IAAI,IAA4C;AAC9C,SAAO,MAAKA,KAAM,IAAI,GAAG;;CAG3B,IAAI,IAAqB;AACvB,SAAO,MAAKA,KAAM,IAAI,GAAG;;CAG3B,SAAS,OAAiC;AACxC,MAAI,MAAKA,KAAM,IAAI,MAAM,GAAG,CAC1B,OAAM,IAAI,MAAM,kBAAkB,MAAM,GAAG,yBAAyB;AAEtE,QAAKA,KAAM,IAAI,MAAM,IAAI,MAAM;;CAGjC,EAAE,OAAO,YAA0C;AACjD,SAAO,MAAKA,KAAM,QAAQ;;CAG5B,SAA+C;AAC7C,SAAO,MAAKA,KAAM,QAAQ;;;AAI9B,SAAgB,2BAA+C;AAC7D,QAAO,IAAI,wBAAwB;;;;;;;;;;;;;;;;;;;ACgBrC,SAAgB,WAMd,QAQwC;CACxC,MAAM,YAAY,MAAe;CAIjC,MAAM,aAAa,OAAO,YAAY,UAAkB;CACxD,MAAM,aAAa,OAAO;CAC1B,MAAM,gBAAgB;AAItB,QAAO;EACL,IAAI,OAAO;EACX,aAAa,OAAO;EACpB,GAAG,UACD,UACA,OAAO,SAAU,OAAO,OAAO,CAAC,GAAG,OAAO,OAAO,CAAC,GAAe,OAClE;EACD,GAAG,UAAU,oBAAoB,OAAO,iBAAiB;EACzD,SAAS,UAAU;AACjB,OAAI;AACF,WAAO,QAAQ,QAAQ,WAAW,MAAM,CAAC;YAClC,OAAO;AACd,WAAO,QAAQ,OAAO,MAAM;;;EAGhC,SAAS,SAAS;AAChB,OAAI;AACF,WAAO,QAAQ,QAAQ,WAAW,KAAK,CAAC;YACjC,OAAO;AACd,WAAO,QAAQ,OAAO,MAAM;;;EAGhC,YAAa,cAAc,cAAc;EACzC,YAAa,cAAc,cAAc;EAC1C"}
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma-next/mongo-codec",
|
|
3
|
-
"version": "0.5.0-dev.
|
|
3
|
+
"version": "0.5.0-dev.8",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"description": "Codec interface and registry for Prisma Next MongoDB support",
|
|
7
7
|
"dependencies": {
|
|
8
|
-
"@prisma-next/contract": "0.5.0-dev.
|
|
9
|
-
"@prisma-next/framework-components": "0.5.0-dev.
|
|
8
|
+
"@prisma-next/contract": "0.5.0-dev.8",
|
|
9
|
+
"@prisma-next/framework-components": "0.5.0-dev.8",
|
|
10
|
+
"@prisma-next/utils": "0.5.0-dev.8"
|
|
10
11
|
},
|
|
11
12
|
"devDependencies": {
|
|
12
13
|
"tsdown": "0.18.4",
|
package/src/codecs.ts
CHANGED
|
@@ -1,50 +1,112 @@
|
|
|
1
1
|
import type { JsonValue } from '@prisma-next/contract/types';
|
|
2
2
|
import type { Codec as BaseCodec, CodecTrait } from '@prisma-next/framework-components/codec';
|
|
3
|
+
import { ifDefined } from '@prisma-next/utils/defined';
|
|
3
4
|
|
|
4
5
|
export type MongoCodecTrait = CodecTrait;
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
/**
|
|
8
|
+
* A codec for the Mongo target. Translates between an application value
|
|
9
|
+
* and the BSON-shaped wire form the Mongo driver exchanges, and between
|
|
10
|
+
* an application value and the JSON form stored in contract artifacts.
|
|
11
|
+
*
|
|
12
|
+
* Same shape as the framework codec base — see `Codec` in
|
|
13
|
+
* `@prisma-next/framework-components/codec` for the contract. The alias
|
|
14
|
+
* exists so Mongo-specific metadata can be added here in future without
|
|
15
|
+
* touching the framework base.
|
|
16
|
+
*/
|
|
17
|
+
export type MongoCodec<
|
|
7
18
|
Id extends string = string,
|
|
8
19
|
TTraits extends readonly MongoCodecTrait[] = readonly MongoCodecTrait[],
|
|
9
20
|
TWire = unknown,
|
|
10
|
-
|
|
11
|
-
>
|
|
12
|
-
|
|
13
|
-
|
|
21
|
+
TInput = unknown,
|
|
22
|
+
> = BaseCodec<Id, TTraits, TWire, TInput>;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Conditional bundle for `encodeJson`/`decodeJson`: when `TInput` is
|
|
26
|
+
* structurally assignable to `JsonValue` the identity defaults are
|
|
27
|
+
* sound and both fields are optional; otherwise both fields are
|
|
28
|
+
* required so an author cannot silently produce a non-JSON-safe
|
|
29
|
+
* contract artifact.
|
|
30
|
+
*/
|
|
31
|
+
type JsonRoundTripConfig<TInput> = [TInput] extends [JsonValue]
|
|
32
|
+
? {
|
|
33
|
+
encodeJson?: (value: TInput) => JsonValue;
|
|
34
|
+
decodeJson?: (json: JsonValue) => TInput;
|
|
35
|
+
}
|
|
36
|
+
: {
|
|
37
|
+
encodeJson: (value: TInput) => JsonValue;
|
|
38
|
+
decodeJson: (json: JsonValue) => TInput;
|
|
39
|
+
};
|
|
14
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Construct a Mongo codec from author functions.
|
|
43
|
+
*
|
|
44
|
+
* Author `encode` and `decode` as sync or async functions; the factory
|
|
45
|
+
* produces a {@link MongoCodec} whose query-time methods follow the
|
|
46
|
+
* boundary contract documented on the framework {@link BaseCodec}.
|
|
47
|
+
*
|
|
48
|
+
* `encode` is optional — when omitted, an identity default is installed
|
|
49
|
+
* (declaring "the input value already is the wire value", so `TInput` and
|
|
50
|
+
* `TWire` are interchangeable for that codec). `decode` is always
|
|
51
|
+
* required. `encodeJson` and `decodeJson` default to identity **only when
|
|
52
|
+
* `TInput` is assignable to `JsonValue`**; otherwise both are required so
|
|
53
|
+
* the contract artifact stays JSON-safe.
|
|
54
|
+
*/
|
|
15
55
|
export function mongoCodec<
|
|
16
56
|
Id extends string,
|
|
17
57
|
const TTraits extends readonly MongoCodecTrait[] = readonly [],
|
|
18
58
|
TWire = unknown,
|
|
19
|
-
|
|
20
|
-
>(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const traits = config.traits
|
|
31
|
-
? (Object.freeze([...config.traits]) as TTraits)
|
|
32
|
-
: (Object.freeze([] as const) as unknown as TTraits);
|
|
59
|
+
TInput = unknown,
|
|
60
|
+
>(
|
|
61
|
+
config: {
|
|
62
|
+
typeId: Id;
|
|
63
|
+
targetTypes: readonly string[];
|
|
64
|
+
traits?: TTraits;
|
|
65
|
+
encode?: (value: TInput) => TWire | Promise<TWire>;
|
|
66
|
+
decode: (wire: TWire) => TInput | Promise<TInput>;
|
|
67
|
+
renderOutputType?: (typeParams: Record<string, unknown>) => string | undefined;
|
|
68
|
+
} & JsonRoundTripConfig<TInput>,
|
|
69
|
+
): MongoCodec<Id, TTraits, TWire, TInput> {
|
|
33
70
|
const identity = (v: unknown) => v;
|
|
71
|
+
// The synchronous identity default is only safe when the author has
|
|
72
|
+
// declared "the input is already the wire value" (i.e. TInput == TWire);
|
|
73
|
+
// it returns the value directly, never a Promise.
|
|
74
|
+
const userEncode = config.encode ?? ((value: TInput) => value as unknown as TWire);
|
|
75
|
+
const userDecode = config.decode;
|
|
76
|
+
const widenedConfig = config as {
|
|
77
|
+
encodeJson?: (value: TInput) => JsonValue;
|
|
78
|
+
decodeJson?: (json: JsonValue) => TInput;
|
|
79
|
+
};
|
|
34
80
|
return {
|
|
35
81
|
id: config.typeId,
|
|
36
82
|
targetTypes: config.targetTypes,
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
83
|
+
...ifDefined(
|
|
84
|
+
'traits',
|
|
85
|
+
config.traits ? (Object.freeze([...config.traits]) as TTraits) : undefined,
|
|
86
|
+
),
|
|
87
|
+
...ifDefined('renderOutputType', config.renderOutputType),
|
|
88
|
+
encode: (value) => {
|
|
89
|
+
try {
|
|
90
|
+
return Promise.resolve(userEncode(value));
|
|
91
|
+
} catch (error) {
|
|
92
|
+
return Promise.reject(error);
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
decode: (wire) => {
|
|
96
|
+
try {
|
|
97
|
+
return Promise.resolve(userDecode(wire));
|
|
98
|
+
} catch (error) {
|
|
99
|
+
return Promise.reject(error);
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
encodeJson: (widenedConfig.encodeJson ?? identity) as (value: TInput) => JsonValue,
|
|
103
|
+
decodeJson: (widenedConfig.decodeJson ?? identity) as (json: JsonValue) => TInput,
|
|
43
104
|
};
|
|
44
105
|
}
|
|
45
106
|
|
|
46
|
-
|
|
47
|
-
|
|
107
|
+
/** Extract the JS application type carried by a Mongo codec — used both as `encode` input and as `decode` output. */
|
|
108
|
+
export type MongoCodecInput<T> =
|
|
109
|
+
T extends MongoCodec<string, readonly MongoCodecTrait[], unknown, infer TInput> ? TInput : never;
|
|
48
110
|
|
|
49
111
|
export type MongoCodecTraits<T> =
|
|
50
112
|
T extends MongoCodec<string, infer TTraits> ? TTraits[number] & MongoCodecTrait : never;
|
package/src/exports/index.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
export type { MongoCodecRegistry } from '../codec-registry';
|
|
2
2
|
export { createMongoCodecRegistry } from '../codec-registry';
|
|
3
|
-
export type {
|
|
3
|
+
export type {
|
|
4
|
+
MongoCodec,
|
|
5
|
+
MongoCodecInput,
|
|
6
|
+
MongoCodecTrait,
|
|
7
|
+
MongoCodecTraits,
|
|
8
|
+
} from '../codecs';
|
|
4
9
|
export { mongoCodec } from '../codecs';
|