@prisma-next/contract 0.14.0-dev.5 → 0.14.0-dev.51

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.
@@ -22,29 +22,12 @@ interface EnumAccessor {
22
22
  ordinalOf(v: JsonValue): number;
23
23
  }
24
24
  declare function createEnumAccessor(contractEnum: ContractEnum): EnumAccessor;
25
- /**
26
- * Build the enum-accessor map for a single namespace, keyed by enum name.
27
- * Each namespace facet exposes only its own enums — the IR keys enums under
28
- * `domain.namespaces[ns].enum`, so the same name in two namespaces resolves
29
- * independently rather than colliding in one flat map.
30
- */
31
25
  declare function buildEnumsMapForNamespace(domain: {
32
26
  readonly namespaces: Readonly<Record<string, {
33
27
  readonly enum?: Readonly<Record<string, ContractEnum>>;
34
28
  }>>;
35
29
  }, namespaceId: string): Record<string, EnumAccessor>;
36
- /**
37
- * Build the enum-accessor map for every namespace of a domain, keyed by
38
- * namespace id then enum name. This is the lane-agnostic enum surface the
39
- * `db.enums` facade member exposes: enums are contract metadata, the same
40
- * whether reached through the sql lane or the orm lane, so the facade builds
41
- * this once and projects it per target.
42
- */
43
- declare function buildNamespacedEnums(domain: {
44
- readonly namespaces: Readonly<Record<string, {
45
- readonly enum?: Readonly<Record<string, ContractEnum>>;
46
- }>>;
47
- }): Record<string, Record<string, EnumAccessor>>;
30
+ declare function buildNamespacedEnums<TContract extends Contract>(domain: TContract['domain']): NamespacedEnums<TContract>;
48
31
  type Present<T> = Exclude<T, undefined>;
49
32
  type EnumMemberEntry = {
50
33
  readonly name: string;
@@ -53,8 +36,10 @@ type EnumMemberEntry = {
53
36
  type EnumEntry = {
54
37
  readonly members: readonly EnumMemberEntry[];
55
38
  };
56
- type EnumEntryValues<Entry extends EnumEntry> = { readonly [I in keyof Entry['members']]: Entry['members'][I] extends EnumMemberEntry ? Entry['members'][I]['value'] : never };
57
- type EnumEntryNames<Entry extends EnumEntry> = { readonly [I in keyof Entry['members']]: Entry['members'][I] extends EnumMemberEntry ? Entry['members'][I]['name'] : never };
39
+ type MemberValues<Members> = { readonly [I in keyof Members]: Members[I] extends EnumMemberEntry ? Members[I]['value'] : never };
40
+ type MemberNames<Members> = { readonly [I in keyof Members]: Members[I] extends EnumMemberEntry ? Members[I]['name'] : never };
41
+ type EnumEntryValues<Entry extends EnumEntry> = MemberValues<Entry['members']>;
42
+ type EnumEntryNames<Entry extends EnumEntry> = MemberNames<Entry['members']>;
58
43
  type EnumEntryMembers<Entry extends EnumEntry> = { readonly [M in Entry['members'][number] as M['name']]: M['value'] };
59
44
  type ContractEnumAccessor<Entry extends EnumEntry> = {
60
45
  readonly values: EnumEntryValues<Entry>;
@@ -64,6 +49,11 @@ type ContractEnumAccessor<Entry extends EnumEntry> = {
64
49
  hasName(name: string): name is Extract<EnumEntryNames<Entry>[number], string>;
65
50
  nameOf(v: EnumEntryValues<Entry>[number]): string | undefined;
66
51
  ordinalOf(v: EnumEntryValues<Entry>[number]): number;
52
+ /**
53
+ * Type-only: the enum's value union. Absent at runtime — use `typeof X.Value`
54
+ * to derive the type; never read `X.Value` as a value.
55
+ */
56
+ readonly Value: EnumEntryValues<Entry>[number];
67
57
  };
68
58
  /**
69
59
  * The value union for a `ContractEnumAccessor`.
@@ -81,13 +71,13 @@ type EnumMemberNames<A> = A extends {
81
71
  } ? N : never;
82
72
  type EnumEntriesToAccessors<Enums> = { readonly [K in keyof Enums]: Enums[K] extends EnumEntry ? ContractEnumAccessor<Enums[K]> : never };
83
73
  type BuiltEnumAccessorsOf<TContract> = TContract extends {
84
- readonly enumAccessors: infer A;
85
- } ? A : Record<never, never>;
74
+ readonly enumAccessors?: infer A;
75
+ } ? Exclude<A, undefined> : Record<never, never>;
86
76
  type NamespaceEnumEntries<TNamespace> = TNamespace extends {
87
77
  readonly enum?: infer E;
88
78
  } ? unknown extends E ? Record<never, never> : Present<E> : Record<never, never>;
89
- type NamespaceEnumAccessors<TContract extends Contract, NsId extends keyof TContract['domain']['namespaces']> = EnumEntriesToAccessors<NamespaceEnumEntries<TContract['domain']['namespaces'][NsId]>> & BuiltEnumAccessorsOf<TContract>;
79
+ type NamespaceEnumAccessors<TContract extends Contract, NsId extends keyof TContract['domain']['namespaces']> = keyof BuiltEnumAccessorsOf<TContract> extends never ? EnumEntriesToAccessors<NamespaceEnumEntries<TContract['domain']['namespaces'][NsId]>> : BuiltEnumAccessorsOf<TContract>;
90
80
  type NamespacedEnums<TContract extends Contract> = { readonly [Ns in keyof TContract['domain']['namespaces']]: NamespaceEnumAccessors<TContract, Ns> };
91
81
  //#endregion
92
- export { type ContractEnumAccessor, type EnumAccessor, type EnumMemberNames, type EnumValues, type NamespaceEnumAccessors, type NamespacedEnums, buildEnumsMapForNamespace, buildNamespacedEnums, createEnumAccessor };
82
+ export { type ContractEnumAccessor, type EnumAccessor, type EnumEntriesToAccessors, type EnumMemberNames, type EnumValues, type NamespaceEnumAccessors, type NamespacedEnums, buildEnumsMapForNamespace, buildNamespacedEnums, createEnumAccessor };
93
83
  //# sourceMappingURL=enum-accessor.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"enum-accessor.d.mts","names":[],"sources":["../src/enum-accessor.ts"],"mappings":";;;;;AAcA;;;;;;;;;UAAiB,YAAA;EAAA,SACN,MAAA,WAAiB,SAAA;EAAA,SACjB,KAAA;EAAA,SACA,OAAA,EAAS,QAAA,CAAS,MAAA,SAAe,SAAA;EAC1C,GAAA,CAAI,CAAA,EAAG,SAAA;EACP,OAAA,CAAQ,IAAA;EACR,MAAA,CAAO,CAAA,EAAG,SAAA;EACV,SAAA,CAAU,CAAA,EAAG,SAAA;AAAA;AAAA,iBAGC,kBAAA,CAAmB,YAAA,EAAc,YAAA,GAAe,YAAY;;;;;;;iBA6B5D,yBAAA,CACd,MAAA;EAAA,SACW,UAAA,EAAY,QAAA,CACnB,MAAA;IAAA,SAA0B,IAAA,GAAO,QAAA,CAAS,MAAA,SAAe,YAAA;EAAA;AAAA,GAG7D,WAAA,WACC,MAAA,SAAe,YAAA;;AAvCM;AAGxB;;;;;iBAsDgB,oBAAA,CAAqB,MAAA;EAAA,SAC1B,UAAA,EAAY,QAAA,CACnB,MAAA;IAAA,SAA0B,IAAA,GAAO,QAAA,CAAS,MAAA,SAAe,YAAA;EAAA;AAAA,IAEzD,MAAA,SAAe,MAAA,SAAe,YAAA;AAAA,KA0B7B,OAAA,MAAa,OAAO,CAAC,CAAA;AAAA,KAKrB,eAAA;EAAA,SAA6B,IAAA;EAAA,SAAuB,KAAA,EAAO,SAAS;AAAA;AAAA,KACpE,SAAA;EAAA,SAAuB,OAAA,WAAkB,eAAe;AAAA;AAAA,KAExD,eAAA,eAA8B,SAAA,2BACZ,KAAA,cAAmB,KAAA,YAAiB,CAAA,UAAW,eAAA,GAChE,KAAA,YAAiB,CAAA;AAAA,KAIlB,cAAA,eAA6B,SAAA,2BACX,KAAA,cAAmB,KAAA,YAAiB,CAAA,UAAW,eAAA,GAChE,KAAA,YAAiB,CAAA;AAAA,KAIlB,gBAAA,eAA+B,SAAA,qBACnB,KAAA,uBAA4B,CAAA,WAAY,CAAA;AAAA,KAM7C,oBAAA,eAAmC,SAAA;EAAA,SACpC,MAAA,EAAQ,eAAA,CAAgB,KAAA;EAAA,SACxB,KAAA,EAAO,cAAA,CAAe,KAAA;EAAA,SACtB,OAAA,EAAS,gBAAA,CAAiB,KAAA,GA9EnB;EAgFhB,GAAA,CAAI,CAAA,EAAG,SAAA,GAAY,CAAA,IAAK,eAAA,CAAgB,KAAA,WAhFZ;EAkF5B,OAAA,CAAQ,IAAA,WAAe,IAAA,IAAQ,OAAA,CAAQ,cAAA,CAAe,KAAA;EACtD,MAAA,CAAO,CAAA,EAAG,eAAA,CAAgB,KAAA;EAC1B,SAAA,CAAU,CAAA,EAAG,eAAA,CAAgB,KAAA;AAAA;;;;;;KAQnB,UAAA,MAAgB,CAAA;EAAA,SAAqB,MAAA,EAAQ,aAAa;AAAA,IAAc,CAAA;;;;KAKxE,eAAA,MAAqB,CAAA;EAAA,SAAqB,KAAA,EAAO,aAAa;AAAA,IAAc,CAAA;AAAA,KAEnF,sBAAA,iCACkB,KAAA,GAAQ,KAAA,CAAM,CAAA,UAAW,SAAA,GAAY,oBAAA,CAAqB,KAAA,CAAM,CAAA;AAAA,KAGlF,oBAAA,cAAkC,SAAA;EAAA,SAC5B,aAAA;AAAA,IAEP,CAAA,GACA,MAAM;AAAA,KAEL,oBAAA,eAAmC,UAAA;EAAA,SAC7B,IAAA;AAAA,oBAES,CAAA,GACd,MAAA,iBACA,OAAA,CAAQ,CAAA,IACV,MAAA;AAAA,KAKQ,sBAAA,mBACQ,QAAA,qBACC,SAAA,4BACjB,sBAAA,CAAuB,oBAAA,CAAqB,SAAA,yBAAkC,IAAA,MAChF,oBAAA,CAAqB,SAAA;AAAA,KAIX,eAAA,mBAAkC,QAAA,4BACtB,SAAA,2BAAoC,sBAAA,CAAuB,SAAA,EAAW,EAAA"}
1
+ {"version":3,"file":"enum-accessor.d.mts","names":[],"sources":["../src/enum-accessor.ts"],"mappings":";;;;;AAeA;;;;;;;;;UAAiB,YAAA;EAAA,SACN,MAAA,WAAiB,SAAA;EAAA,SACjB,KAAA;EAAA,SACA,OAAA,EAAS,QAAA,CAAS,MAAA,SAAe,SAAA;EAC1C,GAAA,CAAI,CAAA,EAAG,SAAA;EACP,OAAA,CAAQ,IAAA;EACR,MAAA,CAAO,CAAA,EAAG,SAAA;EACV,SAAA,CAAU,CAAA,EAAG,SAAA;AAAA;AAAA,iBAGC,kBAAA,CAAmB,YAAA,EAAc,YAAA,GAAe,YAAY;AAAA,iBAuB5D,yBAAA,CACd,MAAA;EAAA,SACW,UAAA,EAAY,QAAA,CACnB,MAAA;IAAA,SAA0B,IAAA,GAAO,QAAA,CAAS,MAAA,SAAe,YAAA;EAAA;AAAA,GAG7D,WAAA,WACC,MAAA,SAAe,YAAA;AAAA,iBAWF,oBAAA,mBAAuC,QAAA,EACrD,MAAA,EAAQ,SAAA,aACP,eAAA,CAAgB,SAAA;AAAA,KAWd,OAAA,MAAa,OAAO,CAAC,CAAA;AAAA,KAErB,eAAA;EAAA,SAA6B,IAAA;EAAA,SAAuB,KAAA,EAAO,SAAS;AAAA;AAAA,KACpE,SAAA;EAAA,SAAuB,OAAA,WAAkB,eAAe;AAAA;AAAA,KAKxD,YAAA,mCACkB,OAAA,GAAU,OAAA,CAAQ,CAAA,UAAW,eAAA,GAAkB,OAAA,CAAQ,CAAA;AAAA,KAGzE,WAAA,mCACkB,OAAA,GAAU,OAAA,CAAQ,CAAA,UAAW,eAAA,GAAkB,OAAA,CAAQ,CAAA;AAAA,KAGzE,eAAA,eAA8B,SAAA,IAAa,YAAA,CAAa,KAAA;AAAA,KAExD,cAAA,eAA6B,SAAA,IAAa,WAAA,CAAY,KAAA;AAAA,KAEtD,gBAAA,eAA+B,SAAA,qBACnB,KAAA,uBAA4B,CAAA,WAAY,CAAA;AAAA,KAG7C,oBAAA,eAAmC,SAAA;EAAA,SACpC,MAAA,EAAQ,eAAA,CAAgB,KAAA;EAAA,SACxB,KAAA,EAAO,cAAA,CAAe,KAAA;EAAA,SACtB,OAAA,EAAS,gBAAA,CAAiB,KAAA,GAxDZ;EA0DvB,GAAA,CAAI,CAAA,EAAG,SAAA,GAAY,CAAA,IAAK,eAAA,CAAgB,KAAA,WArDvC;EAuDD,OAAA,CAAQ,IAAA,WAAe,IAAA,IAAQ,OAAA,CAAQ,cAAA,CAAe,KAAA;EACtD,MAAA,CAAO,CAAA,EAAG,eAAA,CAAgB,KAAA;EAC1B,SAAA,CAAU,CAAA,EAAG,eAAA,CAAgB,KAAA;EA9DN;;;;EAAA,SAmEd,KAAA,EAAO,eAAA,CAAgB,KAAA;AAAA;;;;;;KAQtB,UAAA,MAAgB,CAAA;EAAA,SAAqB,MAAA,EAAQ,aAAa;AAAA,IAAc,CAAA;;;;KAKxE,eAAA,MAAqB,CAAA;EAAA,SAAqB,KAAA,EAAO,aAAa;AAAA,IAAc,CAAA;AAAA,KAE5E,sBAAA,iCACW,KAAA,GAAQ,KAAA,CAAM,CAAA,UAAW,SAAA,GAAY,oBAAA,CAAqB,KAAA,CAAM,CAAA;AAAA,KAGlF,oBAAA,cAAkC,SAAA;EAAA,SAC5B,aAAA;AAAA,IAEP,OAAA,CAAQ,CAAA,eACR,MAAA;AAAA,KAEC,oBAAA,eAAmC,UAAA;EAAA,SAC7B,IAAA;AAAA,oBAES,CAAA,GACd,MAAA,iBACA,OAAA,CAAQ,CAAA,IACV,MAAA;AAAA,KAIQ,sBAAA,mBACQ,QAAA,qBACC,SAAA,kCACX,oBAAA,CAAqB,SAAA,kBAC3B,sBAAA,CAAuB,oBAAA,CAAqB,SAAA,yBAAkC,IAAA,MAC9E,oBAAA,CAAqB,SAAA;AAAA,KAEb,eAAA,mBAAkC,QAAA,4BACtB,SAAA,2BAAoC,sBAAA,CAAuB,SAAA,EAAW,EAAA"}
@@ -1,3 +1,4 @@
1
+ import { blindCast } from "@prisma-next/utils/casts";
1
2
  //#region src/enum-accessor.ts
2
3
  function createEnumAccessor(contractEnum) {
3
4
  const values = Object.freeze(contractEnum.members.map((m) => m.value));
@@ -17,29 +18,16 @@ function createEnumAccessor(contractEnum) {
17
18
  ordinalOf: (v) => valueToOrdinal.get(v) ?? -1
18
19
  };
19
20
  }
20
- /**
21
- * Build the enum-accessor map for a single namespace, keyed by enum name.
22
- * Each namespace facet exposes only its own enums — the IR keys enums under
23
- * `domain.namespaces[ns].enum`, so the same name in two namespaces resolves
24
- * independently rather than colliding in one flat map.
25
- */
26
21
  function buildEnumsMapForNamespace(domain, namespaceId) {
27
22
  const result = {};
28
23
  const namespace = domain.namespaces[namespaceId];
29
24
  if (namespace?.enum) for (const [name, contractEnum] of Object.entries(namespace.enum)) result[name] = createEnumAccessor(contractEnum);
30
25
  return result;
31
26
  }
32
- /**
33
- * Build the enum-accessor map for every namespace of a domain, keyed by
34
- * namespace id then enum name. This is the lane-agnostic enum surface the
35
- * `db.enums` facade member exposes: enums are contract metadata, the same
36
- * whether reached through the sql lane or the orm lane, so the facade builds
37
- * this once and projects it per target.
38
- */
39
27
  function buildNamespacedEnums(domain) {
40
28
  const result = {};
41
29
  for (const namespaceId of Object.keys(domain.namespaces)) result[namespaceId] = buildEnumsMapForNamespace(domain, namespaceId);
42
- return result;
30
+ return blindCast(result);
43
31
  }
44
32
  //#endregion
45
33
  export { buildEnumsMapForNamespace, buildNamespacedEnums, createEnumAccessor };
@@ -1 +1 @@
1
- {"version":3,"file":"enum-accessor.mjs","names":[],"sources":["../src/enum-accessor.ts"],"sourcesContent":["import type { Contract } from './contract-types';\nimport type { ContractEnum } from './domain-types';\nimport type { JsonValue } from './types';\n\n/**\n * Runtime view of a domain enum, built at the client from the emitted\n * `ContractEnum` JSON (codec-encoded `JsonValue` members, literal types erased).\n *\n * This deliberately mirrors the accessor shape of the authoring-time\n * `EnumTypeHandle` (in `contract-ts`) rather than reusing it: that handle carries\n * the literal value generics and lives in the authoring layer, which the\n * foundation layer cannot depend on. The two are the same surface seen from the\n * two planes — authoring (typed) and runtime (validated JSON).\n */\nexport interface EnumAccessor {\n readonly values: readonly JsonValue[];\n readonly names: readonly string[];\n readonly members: Readonly<Record<string, JsonValue>>;\n has(v: JsonValue): boolean;\n hasName(name: string): boolean;\n nameOf(v: JsonValue): string | undefined;\n ordinalOf(v: JsonValue): number;\n}\n\nexport function createEnumAccessor(contractEnum: ContractEnum): EnumAccessor {\n const values = Object.freeze(contractEnum.members.map((m) => m.value));\n const names = Object.freeze(contractEnum.members.map((m) => m.name));\n const members: Readonly<Record<string, JsonValue>> = Object.freeze(\n Object.fromEntries(contractEnum.members.map((m) => [m.name, m.value])),\n );\n\n const valueSet = new Set(values);\n const nameSet = Object.freeze(new Set(names));\n const valueToName = new Map(contractEnum.members.map((m) => [m.value, m.name]));\n const valueToOrdinal = new Map(values.map((v, i) => [v, i]));\n\n return {\n values,\n names,\n members,\n has: (v: JsonValue) => valueSet.has(v),\n hasName: (name: string) => nameSet.has(name),\n nameOf: (v: JsonValue) => valueToName.get(v),\n ordinalOf: (v: JsonValue) => valueToOrdinal.get(v) ?? -1,\n };\n}\n\n/**\n * Build the enum-accessor map for a single namespace, keyed by enum name.\n * Each namespace facet exposes only its own enums — the IR keys enums under\n * `domain.namespaces[ns].enum`, so the same name in two namespaces resolves\n * independently rather than colliding in one flat map.\n */\nexport function buildEnumsMapForNamespace(\n domain: {\n readonly namespaces: Readonly<\n Record<string, { readonly enum?: Readonly<Record<string, ContractEnum>> }>\n >;\n },\n namespaceId: string,\n): Record<string, EnumAccessor> {\n const result: Record<string, EnumAccessor> = {};\n const namespace = domain.namespaces[namespaceId];\n if (namespace?.enum) {\n for (const [name, contractEnum] of Object.entries(namespace.enum)) {\n result[name] = createEnumAccessor(contractEnum);\n }\n }\n return result;\n}\n\n/**\n * Build the enum-accessor map for every namespace of a domain, keyed by\n * namespace id then enum name. This is the lane-agnostic enum surface the\n * `db.enums` facade member exposes: enums are contract metadata, the same\n * whether reached through the sql lane or the orm lane, so the facade builds\n * this once and projects it per target.\n */\nexport function buildNamespacedEnums(domain: {\n readonly namespaces: Readonly<\n Record<string, { readonly enum?: Readonly<Record<string, ContractEnum>> }>\n >;\n}): Record<string, Record<string, EnumAccessor>> {\n const result: Record<string, Record<string, EnumAccessor>> = {};\n for (const namespaceId of Object.keys(domain.namespaces)) {\n result[namespaceId] = buildEnumsMapForNamespace(domain, namespaceId);\n }\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Type-level projection of the namespaced enum surface.\n//\n// These types derive the literal-preserving accessor shape from the contract,\n// hung off the `db.enums` facade map (`db.enums.<ns>.<Name>`). They are the\n// same accessors the runtime builds above, but typed from the two emission\n// paths:\n// - Emitted contracts carry the literal enum entries under\n// `domain.namespaces[ns].enum`; each maps to a `ContractEnumAccessor`.\n// - The no-emit (built) contract carries them flat on `enumAccessors`\n// (already accessor-shaped, literal-preserving), since its built domain\n// type does not narrow `namespaces[ns].enum`. All authored enums land in\n// the single built namespace, so exposing the flat map per namespace is\n// correct there.\n// Only `SqlContractResult` carries `enumAccessors`; emitted contracts never\n// do, so the two carriers never overlap.\n// ---------------------------------------------------------------------------\n\ntype Present<T> = Exclude<T, undefined>;\n\n// A domain enum entry as carried in `domain.namespaces[ns].enum[name]`: an\n// ordered member tuple. The no-emit (built) path preserves the literal member\n// values so the derived accessor keeps its literal `values`/`names`/`members`.\ntype EnumMemberEntry = { readonly name: string; readonly value: JsonValue };\ntype EnumEntry = { readonly members: readonly EnumMemberEntry[] };\n\ntype EnumEntryValues<Entry extends EnumEntry> = {\n readonly [I in keyof Entry['members']]: Entry['members'][I] extends EnumMemberEntry\n ? Entry['members'][I]['value']\n : never;\n};\n\ntype EnumEntryNames<Entry extends EnumEntry> = {\n readonly [I in keyof Entry['members']]: Entry['members'][I] extends EnumMemberEntry\n ? Entry['members'][I]['name']\n : never;\n};\n\ntype EnumEntryMembers<Entry extends EnumEntry> = {\n readonly [M in Entry['members'][number] as M['name']]: M['value'];\n};\n\n// The runtime accessor shape for one enum, with literal `values`/`names`/\n// `members` derived from the entry's member tuple. Mirrors `EnumAccessor`'s\n// runtime surface and the authoring `EnumTypeHandle` accessor.\nexport type ContractEnumAccessor<Entry extends EnumEntry> = {\n readonly values: EnumEntryValues<Entry>;\n readonly names: EnumEntryNames<Entry>;\n readonly members: EnumEntryMembers<Entry>;\n /** Returns true and narrows `v` to the enum's value union when `v` is a declared member value. */\n has(v: JsonValue): v is EnumEntryValues<Entry>[number];\n /** Returns true and narrows `name` to the enum's member-name union when `name` is a declared member name. */\n hasName(name: string): name is Extract<EnumEntryNames<Entry>[number], string>;\n nameOf(v: EnumEntryValues<Entry>[number]): string | undefined;\n ordinalOf(v: EnumEntryValues<Entry>[number]): number;\n};\n\n/**\n * The value union for a `ContractEnumAccessor`.\n * Use in function signatures to accept any declared enum value without re-exporting\n * the member type alias from the accessor's generic entry.\n */\nexport type EnumValues<A> = A extends { readonly values: ReadonlyArray<infer V> } ? V : never;\n\n/**\n * The member-name union for a `ContractEnumAccessor`.\n */\nexport type EnumMemberNames<A> = A extends { readonly names: ReadonlyArray<infer N> } ? N : never;\n\ntype EnumEntriesToAccessors<Enums> = {\n readonly [K in keyof Enums]: Enums[K] extends EnumEntry ? ContractEnumAccessor<Enums[K]> : never;\n};\n\ntype BuiltEnumAccessorsOf<TContract> = TContract extends {\n readonly enumAccessors: infer A;\n}\n ? A\n : Record<never, never>;\n\ntype NamespaceEnumEntries<TNamespace> = TNamespace extends {\n readonly enum?: infer E;\n}\n ? unknown extends E\n ? Record<never, never>\n : Present<E>\n : Record<never, never>;\n\n// The per-namespace enum accessors. Each namespace exposes only its own enums\n// (the IR's `domain.namespaces[ns].enum`), so the same enum name in two\n// namespaces resolves to each namespace's own accessor.\nexport type NamespaceEnumAccessors<\n TContract extends Contract,\n NsId extends keyof TContract['domain']['namespaces'],\n> = EnumEntriesToAccessors<NamespaceEnumEntries<TContract['domain']['namespaces'][NsId]>> &\n BuiltEnumAccessorsOf<TContract>;\n\n// The lane-agnostic enum surface exposed on the `db.enums` facade member: a\n// namespace-keyed map projected per target exactly like `db.sql` / `db.orm`.\nexport type NamespacedEnums<TContract extends Contract> = {\n readonly [Ns in keyof TContract['domain']['namespaces']]: NamespaceEnumAccessors<TContract, Ns>;\n};\n"],"mappings":";AAwBA,SAAgB,mBAAmB,cAA0C;CAC3E,MAAM,SAAS,OAAO,OAAO,aAAa,QAAQ,KAAK,MAAM,EAAE,KAAK,CAAC;CACrE,MAAM,QAAQ,OAAO,OAAO,aAAa,QAAQ,KAAK,MAAM,EAAE,IAAI,CAAC;CACnE,MAAM,UAA+C,OAAO,OAC1D,OAAO,YAAY,aAAa,QAAQ,KAAK,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CACvE;CAEA,MAAM,WAAW,IAAI,IAAI,MAAM;CAC/B,MAAM,UAAU,OAAO,OAAO,IAAI,IAAI,KAAK,CAAC;CAC5C,MAAM,cAAc,IAAI,IAAI,aAAa,QAAQ,KAAK,MAAM,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;CAC9E,MAAM,iBAAiB,IAAI,IAAI,OAAO,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;CAE3D,OAAO;EACL;EACA;EACA;EACA,MAAM,MAAiB,SAAS,IAAI,CAAC;EACrC,UAAU,SAAiB,QAAQ,IAAI,IAAI;EAC3C,SAAS,MAAiB,YAAY,IAAI,CAAC;EAC3C,YAAY,MAAiB,eAAe,IAAI,CAAC,KAAK;CACxD;AACF;;;;;;;AAQA,SAAgB,0BACd,QAKA,aAC8B;CAC9B,MAAM,SAAuC,CAAC;CAC9C,MAAM,YAAY,OAAO,WAAW;CACpC,IAAI,WAAW,MACb,KAAK,MAAM,CAAC,MAAM,iBAAiB,OAAO,QAAQ,UAAU,IAAI,GAC9D,OAAO,QAAQ,mBAAmB,YAAY;CAGlD,OAAO;AACT;;;;;;;;AASA,SAAgB,qBAAqB,QAIY;CAC/C,MAAM,SAAuD,CAAC;CAC9D,KAAK,MAAM,eAAe,OAAO,KAAK,OAAO,UAAU,GACrD,OAAO,eAAe,0BAA0B,QAAQ,WAAW;CAErE,OAAO;AACT"}
1
+ {"version":3,"file":"enum-accessor.mjs","names":[],"sources":["../src/enum-accessor.ts"],"sourcesContent":["import { blindCast } from '@prisma-next/utils/casts';\nimport type { Contract } from './contract-types';\nimport type { ContractEnum } from './domain-types';\nimport type { JsonValue } from './types';\n\n/**\n * Runtime view of a domain enum, built at the client from the emitted\n * `ContractEnum` JSON (codec-encoded `JsonValue` members, literal types erased).\n *\n * This deliberately mirrors the accessor shape of the authoring-time\n * `EnumTypeHandle` (in `contract-ts`) rather than reusing it: that handle carries\n * the literal value generics and lives in the authoring layer, which the\n * foundation layer cannot depend on. The two are the same surface seen from the\n * two planes — authoring (typed) and runtime (validated JSON).\n */\nexport interface EnumAccessor {\n readonly values: readonly JsonValue[];\n readonly names: readonly string[];\n readonly members: Readonly<Record<string, JsonValue>>;\n has(v: JsonValue): boolean;\n hasName(name: string): boolean;\n nameOf(v: JsonValue): string | undefined;\n ordinalOf(v: JsonValue): number;\n}\n\nexport function createEnumAccessor(contractEnum: ContractEnum): EnumAccessor {\n const values = Object.freeze(contractEnum.members.map((m) => m.value));\n const names = Object.freeze(contractEnum.members.map((m) => m.name));\n const members: Readonly<Record<string, JsonValue>> = Object.freeze(\n Object.fromEntries(contractEnum.members.map((m) => [m.name, m.value])),\n );\n\n const valueSet = new Set(values);\n const nameSet = Object.freeze(new Set(names));\n const valueToName = new Map(contractEnum.members.map((m) => [m.value, m.name]));\n const valueToOrdinal = new Map(values.map((v, i) => [v, i]));\n\n return {\n values,\n names,\n members,\n has: (v: JsonValue) => valueSet.has(v),\n hasName: (name: string) => nameSet.has(name),\n nameOf: (v: JsonValue) => valueToName.get(v),\n ordinalOf: (v: JsonValue) => valueToOrdinal.get(v) ?? -1,\n };\n}\n\nexport function buildEnumsMapForNamespace(\n domain: {\n readonly namespaces: Readonly<\n Record<string, { readonly enum?: Readonly<Record<string, ContractEnum>> }>\n >;\n },\n namespaceId: string,\n): Record<string, EnumAccessor> {\n const result: Record<string, EnumAccessor> = {};\n const namespace = domain.namespaces[namespaceId];\n if (namespace?.enum) {\n for (const [name, contractEnum] of Object.entries(namespace.enum)) {\n result[name] = createEnumAccessor(contractEnum);\n }\n }\n return result;\n}\n\nexport function buildNamespacedEnums<TContract extends Contract>(\n domain: TContract['domain'],\n): NamespacedEnums<TContract> {\n const result: Record<string, Record<string, EnumAccessor>> = {};\n for (const namespaceId of Object.keys(domain.namespaces)) {\n result[namespaceId] = buildEnumsMapForNamespace(domain, namespaceId);\n }\n return blindCast<\n NamespacedEnums<TContract>,\n 'built dynamically from domain.namespaces; the mapped-type shape cannot be proven statically'\n >(result);\n}\n\ntype Present<T> = Exclude<T, undefined>;\n\ntype EnumMemberEntry = { readonly name: string; readonly value: JsonValue };\ntype EnumEntry = { readonly members: readonly EnumMemberEntry[] };\n\n// Mapped over a bare type parameter so the mapped type is homomorphic a\n// readonly tuple of members yields a readonly tuple of values/names (array\n// methods preserved), not a plain `{ 0: …; 1: }` mapped object.\ntype MemberValues<Members> = {\n readonly [I in keyof Members]: Members[I] extends EnumMemberEntry ? Members[I]['value'] : never;\n};\n\ntype MemberNames<Members> = {\n readonly [I in keyof Members]: Members[I] extends EnumMemberEntry ? Members[I]['name'] : never;\n};\n\ntype EnumEntryValues<Entry extends EnumEntry> = MemberValues<Entry['members']>;\n\ntype EnumEntryNames<Entry extends EnumEntry> = MemberNames<Entry['members']>;\n\ntype EnumEntryMembers<Entry extends EnumEntry> = {\n readonly [M in Entry['members'][number] as M['name']]: M['value'];\n};\n\nexport type ContractEnumAccessor<Entry extends EnumEntry> = {\n readonly values: EnumEntryValues<Entry>;\n readonly names: EnumEntryNames<Entry>;\n readonly members: EnumEntryMembers<Entry>;\n /** Returns true and narrows `v` to the enum's value union when `v` is a declared member value. */\n has(v: JsonValue): v is EnumEntryValues<Entry>[number];\n /** Returns true and narrows `name` to the enum's member-name union when `name` is a declared member name. */\n hasName(name: string): name is Extract<EnumEntryNames<Entry>[number], string>;\n nameOf(v: EnumEntryValues<Entry>[number]): string | undefined;\n ordinalOf(v: EnumEntryValues<Entry>[number]): number;\n /**\n * Type-only: the enum's value union. Absent at runtime — use `typeof X.Value`\n * to derive the type; never read `X.Value` as a value.\n */\n readonly Value: EnumEntryValues<Entry>[number];\n};\n\n/**\n * The value union for a `ContractEnumAccessor`.\n * Use in function signatures to accept any declared enum value without re-exporting\n * the member type alias from the accessor's generic entry.\n */\nexport type EnumValues<A> = A extends { readonly values: ReadonlyArray<infer V> } ? V : never;\n\n/**\n * The member-name union for a `ContractEnumAccessor`.\n */\nexport type EnumMemberNames<A> = A extends { readonly names: ReadonlyArray<infer N> } ? N : never;\n\nexport type EnumEntriesToAccessors<Enums> = {\n readonly [K in keyof Enums]: Enums[K] extends EnumEntry ? ContractEnumAccessor<Enums[K]> : never;\n};\n\ntype BuiltEnumAccessorsOf<TContract> = TContract extends {\n readonly enumAccessors?: infer A;\n}\n ? Exclude<A, undefined>\n : Record<never, never>;\n\ntype NamespaceEnumEntries<TNamespace> = TNamespace extends {\n readonly enum?: infer E;\n}\n ? unknown extends E\n ? Record<never, never>\n : Present<E>\n : Record<never, never>;\n\n// When `enumAccessors` is present (TS-DSL contract), it is the sole source because merging\n// both carriers would create conflicting `values` types for the same enum key.\nexport type NamespaceEnumAccessors<\n TContract extends Contract,\n NsId extends keyof TContract['domain']['namespaces'],\n> = keyof BuiltEnumAccessorsOf<TContract> extends never\n ? EnumEntriesToAccessors<NamespaceEnumEntries<TContract['domain']['namespaces'][NsId]>>\n : BuiltEnumAccessorsOf<TContract>;\n\nexport type NamespacedEnums<TContract extends Contract> = {\n readonly [Ns in keyof TContract['domain']['namespaces']]: NamespaceEnumAccessors<TContract, Ns>;\n};\n"],"mappings":";;AAyBA,SAAgB,mBAAmB,cAA0C;CAC3E,MAAM,SAAS,OAAO,OAAO,aAAa,QAAQ,KAAK,MAAM,EAAE,KAAK,CAAC;CACrE,MAAM,QAAQ,OAAO,OAAO,aAAa,QAAQ,KAAK,MAAM,EAAE,IAAI,CAAC;CACnE,MAAM,UAA+C,OAAO,OAC1D,OAAO,YAAY,aAAa,QAAQ,KAAK,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CACvE;CAEA,MAAM,WAAW,IAAI,IAAI,MAAM;CAC/B,MAAM,UAAU,OAAO,OAAO,IAAI,IAAI,KAAK,CAAC;CAC5C,MAAM,cAAc,IAAI,IAAI,aAAa,QAAQ,KAAK,MAAM,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;CAC9E,MAAM,iBAAiB,IAAI,IAAI,OAAO,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;CAE3D,OAAO;EACL;EACA;EACA;EACA,MAAM,MAAiB,SAAS,IAAI,CAAC;EACrC,UAAU,SAAiB,QAAQ,IAAI,IAAI;EAC3C,SAAS,MAAiB,YAAY,IAAI,CAAC;EAC3C,YAAY,MAAiB,eAAe,IAAI,CAAC,KAAK;CACxD;AACF;AAEA,SAAgB,0BACd,QAKA,aAC8B;CAC9B,MAAM,SAAuC,CAAC;CAC9C,MAAM,YAAY,OAAO,WAAW;CACpC,IAAI,WAAW,MACb,KAAK,MAAM,CAAC,MAAM,iBAAiB,OAAO,QAAQ,UAAU,IAAI,GAC9D,OAAO,QAAQ,mBAAmB,YAAY;CAGlD,OAAO;AACT;AAEA,SAAgB,qBACd,QAC4B;CAC5B,MAAM,SAAuD,CAAC;CAC9D,KAAK,MAAM,eAAe,OAAO,KAAK,OAAO,UAAU,GACrD,OAAO,eAAe,0BAA0B,QAAQ,WAAW;CAErE,OAAO,UAGL,MAAM;AACV"}
package/package.json CHANGED
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "@prisma-next/contract",
3
- "version": "0.14.0-dev.5",
3
+ "version": "0.14.0-dev.51",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "description": "Data contract type definitions and JSON schema for Prisma Next",
8
8
  "dependencies": {
9
- "@prisma-next/utils": "0.14.0-dev.5",
9
+ "@prisma-next/utils": "0.14.0-dev.51",
10
10
  "@standard-schema/spec": "^1.1.0",
11
11
  "arktype": "^2.2.0"
12
12
  },
13
13
  "devDependencies": {
14
- "@prisma-next/tsconfig": "0.14.0-dev.5",
15
- "@prisma-next/tsdown": "0.14.0-dev.5",
14
+ "@prisma-next/tsconfig": "0.14.0-dev.51",
15
+ "@prisma-next/tsdown": "0.14.0-dev.51",
16
16
  "tsdown": "0.22.1",
17
17
  "typescript": "5.9.3",
18
18
  "vitest": "4.1.8"
@@ -1,3 +1,4 @@
1
+ import { blindCast } from '@prisma-next/utils/casts';
1
2
  import type { Contract } from './contract-types';
2
3
  import type { ContractEnum } from './domain-types';
3
4
  import type { JsonValue } from './types';
@@ -45,12 +46,6 @@ export function createEnumAccessor(contractEnum: ContractEnum): EnumAccessor {
45
46
  };
46
47
  }
47
48
 
48
- /**
49
- * Build the enum-accessor map for a single namespace, keyed by enum name.
50
- * Each namespace facet exposes only its own enums — the IR keys enums under
51
- * `domain.namespaces[ns].enum`, so the same name in two namespaces resolves
52
- * independently rather than colliding in one flat map.
53
- */
54
49
  export function buildEnumsMapForNamespace(
55
50
  domain: {
56
51
  readonly namespaces: Readonly<
@@ -69,70 +64,43 @@ export function buildEnumsMapForNamespace(
69
64
  return result;
70
65
  }
71
66
 
72
- /**
73
- * Build the enum-accessor map for every namespace of a domain, keyed by
74
- * namespace id then enum name. This is the lane-agnostic enum surface the
75
- * `db.enums` facade member exposes: enums are contract metadata, the same
76
- * whether reached through the sql lane or the orm lane, so the facade builds
77
- * this once and projects it per target.
78
- */
79
- export function buildNamespacedEnums(domain: {
80
- readonly namespaces: Readonly<
81
- Record<string, { readonly enum?: Readonly<Record<string, ContractEnum>> }>
82
- >;
83
- }): Record<string, Record<string, EnumAccessor>> {
67
+ export function buildNamespacedEnums<TContract extends Contract>(
68
+ domain: TContract['domain'],
69
+ ): NamespacedEnums<TContract> {
84
70
  const result: Record<string, Record<string, EnumAccessor>> = {};
85
71
  for (const namespaceId of Object.keys(domain.namespaces)) {
86
72
  result[namespaceId] = buildEnumsMapForNamespace(domain, namespaceId);
87
73
  }
88
- return result;
74
+ return blindCast<
75
+ NamespacedEnums<TContract>,
76
+ 'built dynamically from domain.namespaces; the mapped-type shape cannot be proven statically'
77
+ >(result);
89
78
  }
90
79
 
91
- // ---------------------------------------------------------------------------
92
- // Type-level projection of the namespaced enum surface.
93
- //
94
- // These types derive the literal-preserving accessor shape from the contract,
95
- // hung off the `db.enums` facade map (`db.enums.<ns>.<Name>`). They are the
96
- // same accessors the runtime builds above, but typed from the two emission
97
- // paths:
98
- // - Emitted contracts carry the literal enum entries under
99
- // `domain.namespaces[ns].enum`; each maps to a `ContractEnumAccessor`.
100
- // - The no-emit (built) contract carries them flat on `enumAccessors`
101
- // (already accessor-shaped, literal-preserving), since its built domain
102
- // type does not narrow `namespaces[ns].enum`. All authored enums land in
103
- // the single built namespace, so exposing the flat map per namespace is
104
- // correct there.
105
- // Only `SqlContractResult` carries `enumAccessors`; emitted contracts never
106
- // do, so the two carriers never overlap.
107
- // ---------------------------------------------------------------------------
108
-
109
80
  type Present<T> = Exclude<T, undefined>;
110
81
 
111
- // A domain enum entry as carried in `domain.namespaces[ns].enum[name]`: an
112
- // ordered member tuple. The no-emit (built) path preserves the literal member
113
- // values so the derived accessor keeps its literal `values`/`names`/`members`.
114
82
  type EnumMemberEntry = { readonly name: string; readonly value: JsonValue };
115
83
  type EnumEntry = { readonly members: readonly EnumMemberEntry[] };
116
84
 
117
- type EnumEntryValues<Entry extends EnumEntry> = {
118
- readonly [I in keyof Entry['members']]: Entry['members'][I] extends EnumMemberEntry
119
- ? Entry['members'][I]['value']
120
- : never;
85
+ // Mapped over a bare type parameter so the mapped type is homomorphic — a
86
+ // readonly tuple of members yields a readonly tuple of values/names (array
87
+ // methods preserved), not a plain `{ 0: …; 1: … }` mapped object.
88
+ type MemberValues<Members> = {
89
+ readonly [I in keyof Members]: Members[I] extends EnumMemberEntry ? Members[I]['value'] : never;
121
90
  };
122
91
 
123
- type EnumEntryNames<Entry extends EnumEntry> = {
124
- readonly [I in keyof Entry['members']]: Entry['members'][I] extends EnumMemberEntry
125
- ? Entry['members'][I]['name']
126
- : never;
92
+ type MemberNames<Members> = {
93
+ readonly [I in keyof Members]: Members[I] extends EnumMemberEntry ? Members[I]['name'] : never;
127
94
  };
128
95
 
96
+ type EnumEntryValues<Entry extends EnumEntry> = MemberValues<Entry['members']>;
97
+
98
+ type EnumEntryNames<Entry extends EnumEntry> = MemberNames<Entry['members']>;
99
+
129
100
  type EnumEntryMembers<Entry extends EnumEntry> = {
130
101
  readonly [M in Entry['members'][number] as M['name']]: M['value'];
131
102
  };
132
103
 
133
- // The runtime accessor shape for one enum, with literal `values`/`names`/
134
- // `members` derived from the entry's member tuple. Mirrors `EnumAccessor`'s
135
- // runtime surface and the authoring `EnumTypeHandle` accessor.
136
104
  export type ContractEnumAccessor<Entry extends EnumEntry> = {
137
105
  readonly values: EnumEntryValues<Entry>;
138
106
  readonly names: EnumEntryNames<Entry>;
@@ -143,6 +111,11 @@ export type ContractEnumAccessor<Entry extends EnumEntry> = {
143
111
  hasName(name: string): name is Extract<EnumEntryNames<Entry>[number], string>;
144
112
  nameOf(v: EnumEntryValues<Entry>[number]): string | undefined;
145
113
  ordinalOf(v: EnumEntryValues<Entry>[number]): number;
114
+ /**
115
+ * Type-only: the enum's value union. Absent at runtime — use `typeof X.Value`
116
+ * to derive the type; never read `X.Value` as a value.
117
+ */
118
+ readonly Value: EnumEntryValues<Entry>[number];
146
119
  };
147
120
 
148
121
  /**
@@ -157,14 +130,14 @@ export type EnumValues<A> = A extends { readonly values: ReadonlyArray<infer V>
157
130
  */
158
131
  export type EnumMemberNames<A> = A extends { readonly names: ReadonlyArray<infer N> } ? N : never;
159
132
 
160
- type EnumEntriesToAccessors<Enums> = {
133
+ export type EnumEntriesToAccessors<Enums> = {
161
134
  readonly [K in keyof Enums]: Enums[K] extends EnumEntry ? ContractEnumAccessor<Enums[K]> : never;
162
135
  };
163
136
 
164
137
  type BuiltEnumAccessorsOf<TContract> = TContract extends {
165
- readonly enumAccessors: infer A;
138
+ readonly enumAccessors?: infer A;
166
139
  }
167
- ? A
140
+ ? Exclude<A, undefined>
168
141
  : Record<never, never>;
169
142
 
170
143
  type NamespaceEnumEntries<TNamespace> = TNamespace extends {
@@ -175,17 +148,15 @@ type NamespaceEnumEntries<TNamespace> = TNamespace extends {
175
148
  : Present<E>
176
149
  : Record<never, never>;
177
150
 
178
- // The per-namespace enum accessors. Each namespace exposes only its own enums
179
- // (the IR's `domain.namespaces[ns].enum`), so the same enum name in two
180
- // namespaces resolves to each namespace's own accessor.
151
+ // When `enumAccessors` is present (TS-DSL contract), it is the sole source because merging
152
+ // both carriers would create conflicting `values` types for the same enum key.
181
153
  export type NamespaceEnumAccessors<
182
154
  TContract extends Contract,
183
155
  NsId extends keyof TContract['domain']['namespaces'],
184
- > = EnumEntriesToAccessors<NamespaceEnumEntries<TContract['domain']['namespaces'][NsId]>> &
185
- BuiltEnumAccessorsOf<TContract>;
156
+ > = keyof BuiltEnumAccessorsOf<TContract> extends never
157
+ ? EnumEntriesToAccessors<NamespaceEnumEntries<TContract['domain']['namespaces'][NsId]>>
158
+ : BuiltEnumAccessorsOf<TContract>;
186
159
 
187
- // The lane-agnostic enum surface exposed on the `db.enums` facade member: a
188
- // namespace-keyed map projected per target exactly like `db.sql` / `db.orm`.
189
160
  export type NamespacedEnums<TContract extends Contract> = {
190
161
  readonly [Ns in keyof TContract['domain']['namespaces']]: NamespaceEnumAccessors<TContract, Ns>;
191
162
  };
@@ -1,6 +1,7 @@
1
1
  export type {
2
2
  ContractEnumAccessor,
3
3
  EnumAccessor,
4
+ EnumEntriesToAccessors,
4
5
  EnumMemberNames,
5
6
  EnumValues,
6
7
  NamespacedEnums,