@prisma-next/contract
Advanced tools
+14
-24
@@ -25,8 +25,2 @@ import { t as Contract } from "./contract-types-DrjCtVOs.mjs"; | ||
| declare function createEnumAccessor(contractEnum: ContractEnum): EnumAccessor; | ||
| /** | ||
| * Build the enum-accessor map for a single namespace, keyed by enum name. | ||
| * Each namespace facet exposes only its own enums — the IR keys enums under | ||
| * `domain.namespaces[ns].enum`, so the same name in two namespaces resolves | ||
| * independently rather than colliding in one flat map. | ||
| */ | ||
| declare function buildEnumsMapForNamespace(domain: { | ||
@@ -37,14 +31,3 @@ readonly namespaces: Readonly<Record<string, { | ||
| }, namespaceId: string): Record<string, EnumAccessor>; | ||
| /** | ||
| * Build the enum-accessor map for every namespace of a domain, keyed by | ||
| * namespace id then enum name. This is the lane-agnostic enum surface the | ||
| * `db.enums` facade member exposes: enums are contract metadata, the same | ||
| * whether reached through the sql lane or the orm lane, so the facade builds | ||
| * this once and projects it per target. | ||
| */ | ||
| declare function buildNamespacedEnums(domain: { | ||
| readonly namespaces: Readonly<Record<string, { | ||
| readonly enum?: Readonly<Record<string, ContractEnum>>; | ||
| }>>; | ||
| }): Record<string, Record<string, EnumAccessor>>; | ||
| declare function buildNamespacedEnums<TContract extends Contract>(domain: TContract['domain']): NamespacedEnums<TContract>; | ||
| type Present<T> = Exclude<T, undefined>; | ||
@@ -58,4 +41,6 @@ type EnumMemberEntry = { | ||
| }; | ||
| type EnumEntryValues<Entry extends EnumEntry> = { readonly [I in keyof Entry['members']]: Entry['members'][I] extends EnumMemberEntry ? Entry['members'][I]['value'] : never }; | ||
| type EnumEntryNames<Entry extends EnumEntry> = { readonly [I in keyof Entry['members']]: Entry['members'][I] extends EnumMemberEntry ? Entry['members'][I]['name'] : never }; | ||
| type MemberValues<Members> = { readonly [I in keyof Members]: Members[I] extends EnumMemberEntry ? Members[I]['value'] : never }; | ||
| type MemberNames<Members> = { readonly [I in keyof Members]: Members[I] extends EnumMemberEntry ? Members[I]['name'] : never }; | ||
| type EnumEntryValues<Entry extends EnumEntry> = MemberValues<Entry['members']>; | ||
| type EnumEntryNames<Entry extends EnumEntry> = MemberNames<Entry['members']>; | ||
| type EnumEntryMembers<Entry extends EnumEntry> = { readonly [M in Entry['members'][number] as M['name']]: M['value'] }; | ||
@@ -70,2 +55,7 @@ type ContractEnumAccessor<Entry extends EnumEntry> = { | ||
| ordinalOf(v: EnumEntryValues<Entry>[number]): number; | ||
| /** | ||
| * Type-only: the enum's value union. Absent at runtime — use `typeof X.Value` | ||
| * to derive the type; never read `X.Value` as a value. | ||
| */ | ||
| readonly Value: EnumEntryValues<Entry>[number]; | ||
| }; | ||
@@ -88,11 +78,11 @@ /** | ||
| type BuiltEnumAccessorsOf<TContract> = TContract extends { | ||
| readonly enumAccessors: infer A; | ||
| } ? A : Record<never, never>; | ||
| readonly enumAccessors?: infer A; | ||
| } ? Exclude<A, undefined> : Record<never, never>; | ||
| type NamespaceEnumEntries<TNamespace> = TNamespace extends { | ||
| readonly enum?: infer E; | ||
| } ? unknown extends E ? Record<never, never> : Present<E> : Record<never, never>; | ||
| type NamespaceEnumAccessors<TContract extends Contract, NsId extends keyof TContract['domain']['namespaces']> = EnumEntriesToAccessors<NamespaceEnumEntries<TContract['domain']['namespaces'][NsId]>> & BuiltEnumAccessorsOf<TContract>; | ||
| type NamespaceEnumAccessors<TContract extends Contract, NsId extends keyof TContract['domain']['namespaces']> = keyof BuiltEnumAccessorsOf<TContract> extends never ? EnumEntriesToAccessors<NamespaceEnumEntries<TContract['domain']['namespaces'][NsId]>> : BuiltEnumAccessorsOf<TContract>; | ||
| type NamespacedEnums<TContract extends Contract> = { readonly [Ns in keyof TContract['domain']['namespaces']]: NamespaceEnumAccessors<TContract, Ns> }; | ||
| //#endregion | ||
| export { type ContractEnumAccessor, type EnumAccessor, type EnumMemberNames, type EnumValues, type NamespaceEnumAccessors, type NamespacedEnums, buildEnumsMapForNamespace, buildNamespacedEnums, createEnumAccessor }; | ||
| export { type ContractEnumAccessor, type EnumAccessor, type EnumEntriesToAccessors, type EnumMemberNames, type EnumValues, type NamespaceEnumAccessors, type NamespacedEnums, buildEnumsMapForNamespace, buildNamespacedEnums, createEnumAccessor }; | ||
| //# sourceMappingURL=enum-accessor.d.mts.map |
@@ -1,1 +0,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"} | ||
| {"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"} |
@@ -0,1 +1,2 @@ | ||
| import { blindCast } from "@prisma-next/utils/casts"; | ||
| //#region src/enum-accessor.ts | ||
@@ -20,8 +21,2 @@ function createEnumAccessor(contractEnum) { | ||
| } | ||
| /** | ||
| * Build the enum-accessor map for a single namespace, keyed by enum name. | ||
| * Each namespace facet exposes only its own enums — the IR keys enums under | ||
| * `domain.namespaces[ns].enum`, so the same name in two namespaces resolves | ||
| * independently rather than colliding in one flat map. | ||
| */ | ||
| function buildEnumsMapForNamespace(domain, namespaceId) { | ||
@@ -33,13 +28,6 @@ const result = {}; | ||
| } | ||
| /** | ||
| * Build the enum-accessor map for every namespace of a domain, keyed by | ||
| * namespace id then enum name. This is the lane-agnostic enum surface the | ||
| * `db.enums` facade member exposes: enums are contract metadata, the same | ||
| * whether reached through the sql lane or the orm lane, so the facade builds | ||
| * this once and projects it per target. | ||
| */ | ||
| function buildNamespacedEnums(domain) { | ||
| const result = {}; | ||
| for (const namespaceId of Object.keys(domain.namespaces)) result[namespaceId] = buildEnumsMapForNamespace(domain, namespaceId); | ||
| return result; | ||
| return blindCast(result); | ||
| } | ||
@@ -46,0 +34,0 @@ //#endregion |
@@ -1,1 +0,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"} | ||
| {"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"} |
+7
-7
| { | ||
| "name": "@prisma-next/contract", | ||
| "version": "0.14.0", | ||
| "version": "0.15.0-dev.1", | ||
| "license": "Apache-2.0", | ||
@@ -9,12 +9,12 @@ "type": "module", | ||
| "dependencies": { | ||
| "@prisma-next/utils": "0.14.0", | ||
| "@prisma-next/utils": "0.15.0-dev.1", | ||
| "@standard-schema/spec": "^1.1.0", | ||
| "arktype": "^2.2.0" | ||
| "arktype": "^2.2.2" | ||
| }, | ||
| "devDependencies": { | ||
| "@prisma-next/tsconfig": "0.14.0", | ||
| "@prisma-next/tsdown": "0.14.0", | ||
| "tsdown": "0.22.1", | ||
| "@prisma-next/tsconfig": "0.15.0-dev.1", | ||
| "@prisma-next/tsdown": "0.15.0-dev.1", | ||
| "tsdown": "0.22.3", | ||
| "typescript": "5.9.3", | ||
| "vitest": "4.1.8" | ||
| "vitest": "4.1.10" | ||
| }, | ||
@@ -21,0 +21,0 @@ "peerDependencies": { |
+32
-61
@@ -0,1 +1,2 @@ | ||
| import { blindCast } from '@prisma-next/utils/casts'; | ||
| import type { Contract } from './contract-types'; | ||
@@ -48,8 +49,2 @@ import type { ContractEnum } from './domain-types'; | ||
| /** | ||
| * Build the enum-accessor map for a single namespace, keyed by enum name. | ||
| * Each namespace facet exposes only its own enums — the IR keys enums under | ||
| * `domain.namespaces[ns].enum`, so the same name in two namespaces resolves | ||
| * independently rather than colliding in one flat map. | ||
| */ | ||
| export function buildEnumsMapForNamespace( | ||
@@ -73,14 +68,5 @@ domain: { | ||
| /** | ||
| * Build the enum-accessor map for every namespace of a domain, keyed by | ||
| * namespace id then enum name. This is the lane-agnostic enum surface the | ||
| * `db.enums` facade member exposes: enums are contract metadata, the same | ||
| * whether reached through the sql lane or the orm lane, so the facade builds | ||
| * this once and projects it per target. | ||
| */ | ||
| export function buildNamespacedEnums(domain: { | ||
| readonly namespaces: Readonly< | ||
| Record<string, { readonly enum?: Readonly<Record<string, ContractEnum>> }> | ||
| >; | ||
| }): Record<string, Record<string, EnumAccessor>> { | ||
| export function buildNamespacedEnums<TContract extends Contract>( | ||
| domain: TContract['domain'], | ||
| ): NamespacedEnums<TContract> { | ||
| const result: Record<string, Record<string, EnumAccessor>> = {}; | ||
@@ -90,43 +76,28 @@ for (const namespaceId of Object.keys(domain.namespaces)) { | ||
| } | ||
| return result; | ||
| return blindCast< | ||
| NamespacedEnums<TContract>, | ||
| 'built dynamically from domain.namespaces; the mapped-type shape cannot be proven statically' | ||
| >(result); | ||
| } | ||
| // --------------------------------------------------------------------------- | ||
| // Type-level projection of the namespaced enum surface. | ||
| // | ||
| // These types derive the literal-preserving accessor shape from the contract, | ||
| // hung off the `db.enums` facade map (`db.enums.<ns>.<Name>`). They are the | ||
| // same accessors the runtime builds above, but typed from the two emission | ||
| // paths: | ||
| // - Emitted contracts carry the literal enum entries under | ||
| // `domain.namespaces[ns].enum`; each maps to a `ContractEnumAccessor`. | ||
| // - The no-emit (built) contract carries them flat on `enumAccessors` | ||
| // (already accessor-shaped, literal-preserving), since its built domain | ||
| // type does not narrow `namespaces[ns].enum`. All authored enums land in | ||
| // the single built namespace, so exposing the flat map per namespace is | ||
| // correct there. | ||
| // Only `SqlContractResult` carries `enumAccessors`; emitted contracts never | ||
| // do, so the two carriers never overlap. | ||
| // --------------------------------------------------------------------------- | ||
| type Present<T> = Exclude<T, undefined>; | ||
| // A domain enum entry as carried in `domain.namespaces[ns].enum[name]`: an | ||
| // ordered member tuple. The no-emit (built) path preserves the literal member | ||
| // values so the derived accessor keeps its literal `values`/`names`/`members`. | ||
| type EnumMemberEntry = { readonly name: string; readonly value: JsonValue }; | ||
| type EnumEntry = { readonly members: readonly EnumMemberEntry[] }; | ||
| type EnumEntryValues<Entry extends EnumEntry> = { | ||
| readonly [I in keyof Entry['members']]: Entry['members'][I] extends EnumMemberEntry | ||
| ? Entry['members'][I]['value'] | ||
| : never; | ||
| // Mapped over a bare type parameter so the mapped type is homomorphic — a | ||
| // readonly tuple of members yields a readonly tuple of values/names (array | ||
| // methods preserved), not a plain `{ 0: …; 1: … }` mapped object. | ||
| type MemberValues<Members> = { | ||
| readonly [I in keyof Members]: Members[I] extends EnumMemberEntry ? Members[I]['value'] : never; | ||
| }; | ||
| type EnumEntryNames<Entry extends EnumEntry> = { | ||
| readonly [I in keyof Entry['members']]: Entry['members'][I] extends EnumMemberEntry | ||
| ? Entry['members'][I]['name'] | ||
| : never; | ||
| type MemberNames<Members> = { | ||
| readonly [I in keyof Members]: Members[I] extends EnumMemberEntry ? Members[I]['name'] : never; | ||
| }; | ||
| type EnumEntryValues<Entry extends EnumEntry> = MemberValues<Entry['members']>; | ||
| type EnumEntryNames<Entry extends EnumEntry> = MemberNames<Entry['members']>; | ||
| type EnumEntryMembers<Entry extends EnumEntry> = { | ||
@@ -136,5 +107,2 @@ readonly [M in Entry['members'][number] as M['name']]: M['value']; | ||
| // The runtime accessor shape for one enum, with literal `values`/`names`/ | ||
| // `members` derived from the entry's member tuple. Mirrors `EnumAccessor`'s | ||
| // runtime surface and the authoring `EnumTypeHandle` accessor. | ||
| export type ContractEnumAccessor<Entry extends EnumEntry> = { | ||
@@ -150,2 +118,7 @@ readonly values: EnumEntryValues<Entry>; | ||
| ordinalOf(v: EnumEntryValues<Entry>[number]): number; | ||
| /** | ||
| * Type-only: the enum's value union. Absent at runtime — use `typeof X.Value` | ||
| * to derive the type; never read `X.Value` as a value. | ||
| */ | ||
| readonly Value: EnumEntryValues<Entry>[number]; | ||
| }; | ||
@@ -165,3 +138,3 @@ | ||
| type EnumEntriesToAccessors<Enums> = { | ||
| export type EnumEntriesToAccessors<Enums> = { | ||
| readonly [K in keyof Enums]: Enums[K] extends EnumEntry ? ContractEnumAccessor<Enums[K]> : never; | ||
@@ -171,5 +144,5 @@ }; | ||
| type BuiltEnumAccessorsOf<TContract> = TContract extends { | ||
| readonly enumAccessors: infer A; | ||
| readonly enumAccessors?: infer A; | ||
| } | ||
| ? A | ||
| ? Exclude<A, undefined> | ||
| : Record<never, never>; | ||
@@ -185,15 +158,13 @@ | ||
| // The per-namespace enum accessors. Each namespace exposes only its own enums | ||
| // (the IR's `domain.namespaces[ns].enum`), so the same enum name in two | ||
| // namespaces resolves to each namespace's own accessor. | ||
| // When `enumAccessors` is present (TS-DSL contract), it is the sole source because merging | ||
| // both carriers would create conflicting `values` types for the same enum key. | ||
| export type NamespaceEnumAccessors< | ||
| TContract extends Contract, | ||
| NsId extends keyof TContract['domain']['namespaces'], | ||
| > = EnumEntriesToAccessors<NamespaceEnumEntries<TContract['domain']['namespaces'][NsId]>> & | ||
| BuiltEnumAccessorsOf<TContract>; | ||
| > = keyof BuiltEnumAccessorsOf<TContract> extends never | ||
| ? EnumEntriesToAccessors<NamespaceEnumEntries<TContract['domain']['namespaces'][NsId]>> | ||
| : BuiltEnumAccessorsOf<TContract>; | ||
| // The lane-agnostic enum surface exposed on the `db.enums` facade member: a | ||
| // namespace-keyed map projected per target exactly like `db.sql` / `db.orm`. | ||
| export type NamespacedEnums<TContract extends Contract> = { | ||
| readonly [Ns in keyof TContract['domain']['namespaces']]: NamespaceEnumAccessors<TContract, Ns>; | ||
| }; |
| export type { | ||
| ContractEnumAccessor, | ||
| EnumAccessor, | ||
| EnumEntriesToAccessors, | ||
| EnumMemberNames, | ||
@@ -5,0 +6,0 @@ EnumValues, |
237050
-1.73%2579
-1.56%+ Added
- Removed
Updated