@prisma-next/contract-authoring
Advanced tools
+277
| import type { ColumnTypeDescriptor } from '@prisma-next/framework-components/codec'; | ||
| import { blindCast } from '@prisma-next/utils/casts'; | ||
| /** | ||
| * A single enum member produced by `member()`. The `Name` and `Value` generics | ||
| * are preserved as literal types so `enumType()` can carry the ordered value | ||
| * tuple in its return type. `Value` is whatever the codec dictates — its type | ||
| * is constrained at `enumType` against the codec's input type, not here. | ||
| */ | ||
| export interface EnumMember<Name extends string, Value> { | ||
| readonly name: Name; | ||
| readonly value: Value; | ||
| } | ||
| /** | ||
| * Declare an enum member. The `value` defaults to `name` when omitted. The | ||
| * value is an unconstrained literal here; `enumType` constrains it against the | ||
| * codec's input type. Both generics are preserved as literals so downstream | ||
| * `enumType` carries the value union in its type; the value is serialized to its | ||
| * codec string form only at lowering. | ||
| */ | ||
| export function member<const Name extends string>(name: Name): EnumMember<Name, Name>; | ||
| export function member<const Name extends string, const Value>( | ||
| name: Name, | ||
| value: Value, | ||
| ): EnumMember<Name, Value>; | ||
| export function member<const Name extends string, const Value = Name>( | ||
| name: Name, | ||
| value?: Value, | ||
| ): EnumMember<Name, Value> { | ||
| return { | ||
| name, | ||
| value: blindCast< | ||
| Value, | ||
| 'overload signatures enforce Value=Name when value is omitted; default generic Value=Name makes this safe' | ||
| >(value === undefined ? name : value), | ||
| }; | ||
| } | ||
| type MembersToValues<Members extends readonly EnumMember<string, unknown>[]> = { | ||
| readonly [K in keyof Members]: Members[K] extends EnumMember<string, infer V> ? V : never; | ||
| }; | ||
| type MembersToNames<Members extends readonly EnumMember<string, unknown>[]> = { | ||
| readonly [K in keyof Members]: Members[K] extends EnumMember<infer N, unknown> ? N : never; | ||
| }; | ||
| type MembersAccessorMap<Members extends readonly EnumMember<string, unknown>[]> = { | ||
| readonly [M in Members[number] as M['name']]: M['value']; | ||
| }; | ||
| // String-key brand rather than unique symbol: tsdown inlines a fresh unique | ||
| // symbol into each extension's .d.mts when the source package is transitive, | ||
| // breaking nominal type identity across chunk boundaries. A string constant | ||
| // survives .d.mts de-duplication because string equality is preserved by value. | ||
| // Mirrors the FILTER_EXPR_BRAND pattern in mongo-query-ast/filter-expressions.ts. | ||
| export const ENUM_TYPE_HANDLE_BRAND = '__prismaNextEnumTypeHandle__'; | ||
| /** Authoring handle returned by `enumType()`. Generic over the ordered value tuple so `const Role = enumType(...)` retains literal types. */ | ||
| export interface EnumTypeHandle< | ||
| Name extends string = string, | ||
| Values extends readonly unknown[] = readonly unknown[], | ||
| Names extends readonly string[] = readonly string[], | ||
| MembersMap extends Record<string, unknown> = Record<string, unknown>, | ||
| > { | ||
| /** Internal brand for lowering-pipeline detection. */ | ||
| readonly [ENUM_TYPE_HANDLE_BRAND]: true; | ||
| /** The enum's declared name (used as the key in domain `enum` / storage `valueSet`). */ | ||
| readonly enumName: Name; | ||
| /** codecId from the codec passed to `enumType`. */ | ||
| readonly codecId: string; | ||
| /** nativeType from the codec passed to `enumType`. */ | ||
| readonly nativeType: string; | ||
| /** Ordered member list for lowering (name + value pairs). */ | ||
| readonly enumMembers: readonly { readonly name: string; readonly value: Values[number] }[]; | ||
| /** Ordered literal value tuple. Declaration order is preserved. */ | ||
| readonly values: Values; | ||
| /** Ordered literal name tuple. Declaration order is preserved. */ | ||
| readonly names: Names; | ||
| /** | ||
| * Namespaced accessor map: `Role.members.User === 'user'`. | ||
| * Namespaced under `.members` to avoid collisions with `.values` / `.has`. | ||
| */ | ||
| readonly members: MembersMap; | ||
| /** Returns `true` if `v` is a declared member value. */ | ||
| has(v: Values[number]): boolean; | ||
| /** Returns the member name for a value, or `undefined` if not found. */ | ||
| nameOf(v: Values[number]): string | undefined; | ||
| /** Returns the zero-based declaration index of a value, or `-1` if not found. */ | ||
| ordinalOf(v: Values[number]): number; | ||
| } | ||
| /** | ||
| * A codec typemap: codecId → `{ input, output }`, the same shape the query | ||
| * lanes consume. The bound `enumType` wrappers supply the target pack's | ||
| * typemap; the core defaults to an empty map (no codec is known), so member | ||
| * values stay unconstrained. | ||
| */ | ||
| export type CodecTypeMap = Record<string, { readonly input?: unknown }>; | ||
| /** | ||
| * The application input type the codec dictates for an enum's member values: | ||
| * looks `Codec['codecId']` up in the supplied codec typemap. When the codecId | ||
| * isn't in the map the input is unconstrained, so any member-value literal is | ||
| * accepted and inferred verbatim. | ||
| */ | ||
| export type CodecInput< | ||
| CodecTypes extends CodecTypeMap, | ||
| Codec extends { readonly codecId: string }, | ||
| > = Codec['codecId'] extends keyof CodecTypes | ||
| ? CodecTypes[Codec['codecId']] extends { readonly input: infer In } | ||
| ? In | ||
| : unknown | ||
| : unknown; | ||
| /** | ||
| * Declare a domain enum for use in TS-authoring contracts. | ||
| * | ||
| * - The codec is an explicit required argument — the `codecId` and | ||
| * `nativeType` are taken from the passed `ColumnTypeDescriptor` (e.g. | ||
| * `{ codecId: 'pg/text@1', nativeType: 'text' }` from a field preset | ||
| * output or a direct inline object). | ||
| * - `const` generics on the members spread preserve the ordered literal | ||
| * value tuple so `Role.values` is `readonly ['user','admin']`, not | ||
| * `string[]`. | ||
| * - Well-formedness assertions at construction: non-empty member list; | ||
| * unique names; unique values. | ||
| * | ||
| * The returned handle wires into `field.namedType(handle)` to set | ||
| * `valueSet` refs on both the domain field and the storage column. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * const Role = enumType('Role', { codecId: 'pg/text@1', nativeType: 'text' }, | ||
| * member('User', 'user'), | ||
| * member('Admin', 'admin'), | ||
| * ); | ||
| * // Role.values → readonly ['user', 'admin'] | ||
| * // Role.members.User → 'user' | ||
| * ``` | ||
| */ | ||
| export function enumType< | ||
| CodecTypes extends CodecTypeMap = Record<string, never>, | ||
| const Name extends string = string, | ||
| const Codec extends Pick<ColumnTypeDescriptor, 'codecId' | 'nativeType'> = Pick< | ||
| ColumnTypeDescriptor, | ||
| 'codecId' | 'nativeType' | ||
| >, | ||
| const Members extends readonly [ | ||
| EnumMember<string, CodecInput<CodecTypes, Codec>>, | ||
| ...EnumMember<string, CodecInput<CodecTypes, Codec>>[], | ||
| ] = readonly [EnumMember<string, CodecInput<CodecTypes, Codec>>], | ||
| >( | ||
| name: Name, | ||
| codec: Codec, | ||
| ...members: Members | ||
| ): EnumTypeHandle< | ||
| Name, | ||
| MembersToValues<[...Members]>, | ||
| MembersToNames<[...Members]>, | ||
| MembersAccessorMap<[...Members]> | ||
| >; | ||
| export function enumType( | ||
| name: string, | ||
| codec: Pick<ColumnTypeDescriptor, 'codecId' | 'nativeType'>, | ||
| ...members: EnumMember<string, unknown>[] | ||
| ): EnumTypeHandle; | ||
| export function enumType( | ||
| name: string, | ||
| codec: Pick<ColumnTypeDescriptor, 'codecId' | 'nativeType'>, | ||
| ...members: EnumMember<string, unknown>[] | ||
| ): EnumTypeHandle { | ||
| if (members.length === 0) { | ||
| throw new Error(`enumType("${name}"): must have at least one member.`); | ||
| } | ||
| const seenNames = new Set<string>(); | ||
| const seenValues = new Set<string>(); | ||
| for (const m of members) { | ||
| if (seenNames.has(m.name)) { | ||
| throw new Error( | ||
| `enumType("${name}"): duplicate member name "${m.name}". Member names must be unique.`, | ||
| ); | ||
| } | ||
| seenNames.add(m.name); | ||
| const loweredValue = String(m.value); | ||
| if (seenValues.has(loweredValue)) { | ||
| throw new Error( | ||
| `enumType("${name}"): duplicate member value "${loweredValue}". Member values must be unique.`, | ||
| ); | ||
| } | ||
| seenValues.add(loweredValue); | ||
| } | ||
| const values = Object.freeze(members.map((m) => m.value)); | ||
| const names = Object.freeze(members.map((m) => m.name)); | ||
| const enumMembers = Object.freeze(members.map((m) => ({ name: m.name, value: m.value }))); | ||
| const membersAccessor = Object.freeze(Object.fromEntries(members.map((m) => [m.name, m.value]))); | ||
| const valueSet = new Set(values); | ||
| const valueToName = new Map(members.map((m) => [m.value, m.name])); | ||
| const valueToOrdinal = new Map(values.map((v, i) => [v, i])); | ||
| return { | ||
| [ENUM_TYPE_HANDLE_BRAND]: true, | ||
| enumName: name, | ||
| codecId: codec.codecId, | ||
| nativeType: codec.nativeType, | ||
| enumMembers, | ||
| values, | ||
| names, | ||
| members: membersAccessor, | ||
| has: (v: unknown) => valueSet.has(v), | ||
| nameOf: (v: unknown) => valueToName.get(v), | ||
| ordinalOf: (v: unknown) => valueToOrdinal.get(v) ?? -1, | ||
| }; | ||
| } | ||
| /** | ||
| * The signature of an `enumType` whose codec typemap is already bound — the | ||
| * shape a target-bound wrapper (e.g. `@prisma-next/postgres/contract-builder`) | ||
| * exposes. The member values are constrained to the codec's input type drawn | ||
| * from `CodecTypes`, while `Name`, `Codec`, and the member tuple still infer | ||
| * from the call. | ||
| */ | ||
| export type BoundEnumType<CodecTypes extends CodecTypeMap> = < | ||
| const Name extends string, | ||
| const Codec extends Pick<ColumnTypeDescriptor, 'codecId' | 'nativeType'>, | ||
| const Members extends readonly [ | ||
| EnumMember<string, CodecInput<CodecTypes, Codec>>, | ||
| ...EnumMember<string, CodecInput<CodecTypes, Codec>>[], | ||
| ], | ||
| >( | ||
| name: Name, | ||
| codec: Codec, | ||
| ...members: Members | ||
| ) => EnumTypeHandle< | ||
| Name, | ||
| MembersToValues<[...Members]>, | ||
| MembersToNames<[...Members]>, | ||
| MembersAccessorMap<[...Members]> | ||
| >; | ||
| /** | ||
| * Bind `enumType` to a target's codec typemap. The returned function is the | ||
| * same runtime `enumType`, retyped so member values are constrained to the | ||
| * codec's input type. Target packages call this with their pack's | ||
| * `ExtractCodecTypesFromPack<Pack>` to expose a codec-aware `enumType`. | ||
| */ | ||
| export function bindEnumType<CodecTypes extends CodecTypeMap>(): BoundEnumType<CodecTypes> { | ||
| return enumType; | ||
| } | ||
| /** | ||
| * Returns true when the value is an `EnumTypeHandle` produced by | ||
| * `enumType()`. Used in the lowering pipeline to detect enum handles | ||
| * in field state without importing the brand key at every call site. | ||
| */ | ||
| export function isEnumTypeHandle(value: unknown): value is EnumTypeHandle { | ||
| return ( | ||
| typeof value === 'object' && | ||
| value !== null && | ||
| Reflect.get(value, ENUM_TYPE_HANDLE_BRAND) === true | ||
| ); | ||
| } |
+127
-1
| import { AuthoringEntityContext, AuthoringEntityTypeDescriptor, AuthoringEntityTypeNamespace } from "@prisma-next/framework-components/authoring"; | ||
| import { ColumnTypeDescriptor } from "@prisma-next/framework-components/codec"; | ||
@@ -85,3 +86,128 @@ //#region src/capability-registry.d.ts | ||
| //#endregion | ||
| export { type AuthoringNamespaceKey, type CapabilityMatrix, type EntityHelperFactoryOptions, type EntityHelperFunction, type EntityHelpersFromNamespace, type ExtractAuthoringNamespaceFromPack, type ForeignKeyDefaultsState, type IndexDef, type MergeExtensionAuthoringNamespaces, type UnionToIntersection, createEntityHelpersFromNamespace, mergeCapabilityMatrices }; | ||
| //#region src/enum-type.d.ts | ||
| /** | ||
| * A single enum member produced by `member()`. The `Name` and `Value` generics | ||
| * are preserved as literal types so `enumType()` can carry the ordered value | ||
| * tuple in its return type. `Value` is whatever the codec dictates — its type | ||
| * is constrained at `enumType` against the codec's input type, not here. | ||
| */ | ||
| interface EnumMember<Name extends string, Value> { | ||
| readonly name: Name; | ||
| readonly value: Value; | ||
| } | ||
| /** | ||
| * Declare an enum member. The `value` defaults to `name` when omitted. The | ||
| * value is an unconstrained literal here; `enumType` constrains it against the | ||
| * codec's input type. Both generics are preserved as literals so downstream | ||
| * `enumType` carries the value union in its type; the value is serialized to its | ||
| * codec string form only at lowering. | ||
| */ | ||
| declare function member<const Name extends string>(name: Name): EnumMember<Name, Name>; | ||
| declare function member<const Name extends string, const Value>(name: Name, value: Value): EnumMember<Name, Value>; | ||
| type MembersToValues<Members extends readonly EnumMember<string, unknown>[]> = { readonly [K in keyof Members]: Members[K] extends EnumMember<string, infer V> ? V : never }; | ||
| type MembersToNames<Members extends readonly EnumMember<string, unknown>[]> = { readonly [K in keyof Members]: Members[K] extends EnumMember<infer N, unknown> ? N : never }; | ||
| type MembersAccessorMap<Members extends readonly EnumMember<string, unknown>[]> = { readonly [M in Members[number] as M['name']]: M['value'] }; | ||
| declare const ENUM_TYPE_HANDLE_BRAND = "__prismaNextEnumTypeHandle__"; | ||
| /** Authoring handle returned by `enumType()`. Generic over the ordered value tuple so `const Role = enumType(...)` retains literal types. */ | ||
| interface EnumTypeHandle<Name extends string = string, Values extends readonly unknown[] = readonly unknown[], Names extends readonly string[] = readonly string[], MembersMap extends Record<string, unknown> = Record<string, unknown>> { | ||
| /** Internal brand for lowering-pipeline detection. */ | ||
| readonly [ENUM_TYPE_HANDLE_BRAND]: true; | ||
| /** The enum's declared name (used as the key in domain `enum` / storage `valueSet`). */ | ||
| readonly enumName: Name; | ||
| /** codecId from the codec passed to `enumType`. */ | ||
| readonly codecId: string; | ||
| /** nativeType from the codec passed to `enumType`. */ | ||
| readonly nativeType: string; | ||
| /** Ordered member list for lowering (name + value pairs). */ | ||
| readonly enumMembers: readonly { | ||
| readonly name: string; | ||
| readonly value: Values[number]; | ||
| }[]; | ||
| /** Ordered literal value tuple. Declaration order is preserved. */ | ||
| readonly values: Values; | ||
| /** Ordered literal name tuple. Declaration order is preserved. */ | ||
| readonly names: Names; | ||
| /** | ||
| * Namespaced accessor map: `Role.members.User === 'user'`. | ||
| * Namespaced under `.members` to avoid collisions with `.values` / `.has`. | ||
| */ | ||
| readonly members: MembersMap; | ||
| /** Returns `true` if `v` is a declared member value. */ | ||
| has(v: Values[number]): boolean; | ||
| /** Returns the member name for a value, or `undefined` if not found. */ | ||
| nameOf(v: Values[number]): string | undefined; | ||
| /** Returns the zero-based declaration index of a value, or `-1` if not found. */ | ||
| ordinalOf(v: Values[number]): number; | ||
| } | ||
| /** | ||
| * A codec typemap: codecId → `{ input, output }`, the same shape the query | ||
| * lanes consume. The bound `enumType` wrappers supply the target pack's | ||
| * typemap; the core defaults to an empty map (no codec is known), so member | ||
| * values stay unconstrained. | ||
| */ | ||
| type CodecTypeMap = Record<string, { | ||
| readonly input?: unknown; | ||
| }>; | ||
| /** | ||
| * The application input type the codec dictates for an enum's member values: | ||
| * looks `Codec['codecId']` up in the supplied codec typemap. When the codecId | ||
| * isn't in the map the input is unconstrained, so any member-value literal is | ||
| * accepted and inferred verbatim. | ||
| */ | ||
| type CodecInput<CodecTypes extends CodecTypeMap, Codec extends { | ||
| readonly codecId: string; | ||
| }> = Codec['codecId'] extends keyof CodecTypes ? CodecTypes[Codec['codecId']] extends { | ||
| readonly input: infer In; | ||
| } ? In : unknown : unknown; | ||
| /** | ||
| * Declare a domain enum for use in TS-authoring contracts. | ||
| * | ||
| * - The codec is an explicit required argument — the `codecId` and | ||
| * `nativeType` are taken from the passed `ColumnTypeDescriptor` (e.g. | ||
| * `{ codecId: 'pg/text@1', nativeType: 'text' }` from a field preset | ||
| * output or a direct inline object). | ||
| * - `const` generics on the members spread preserve the ordered literal | ||
| * value tuple so `Role.values` is `readonly ['user','admin']`, not | ||
| * `string[]`. | ||
| * - Well-formedness assertions at construction: non-empty member list; | ||
| * unique names; unique values. | ||
| * | ||
| * The returned handle wires into `field.namedType(handle)` to set | ||
| * `valueSet` refs on both the domain field and the storage column. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * const Role = enumType('Role', { codecId: 'pg/text@1', nativeType: 'text' }, | ||
| * member('User', 'user'), | ||
| * member('Admin', 'admin'), | ||
| * ); | ||
| * // Role.values → readonly ['user', 'admin'] | ||
| * // Role.members.User → 'user' | ||
| * ``` | ||
| */ | ||
| declare function enumType<CodecTypes extends CodecTypeMap = Record<string, never>, const Name extends string = string, const Codec extends Pick<ColumnTypeDescriptor, 'codecId' | 'nativeType'> = Pick<ColumnTypeDescriptor, 'codecId' | 'nativeType'>, const Members extends readonly [EnumMember<string, CodecInput<CodecTypes, Codec>>, ...EnumMember<string, CodecInput<CodecTypes, Codec>>[]] = readonly [EnumMember<string, CodecInput<CodecTypes, Codec>>]>(name: Name, codec: Codec, ...members: Members): EnumTypeHandle<Name, MembersToValues<[...Members]>, MembersToNames<[...Members]>, MembersAccessorMap<[...Members]>>; | ||
| declare function enumType(name: string, codec: Pick<ColumnTypeDescriptor, 'codecId' | 'nativeType'>, ...members: EnumMember<string, unknown>[]): EnumTypeHandle; | ||
| /** | ||
| * The signature of an `enumType` whose codec typemap is already bound — the | ||
| * shape a target-bound wrapper (e.g. `@prisma-next/postgres/contract-builder`) | ||
| * exposes. The member values are constrained to the codec's input type drawn | ||
| * from `CodecTypes`, while `Name`, `Codec`, and the member tuple still infer | ||
| * from the call. | ||
| */ | ||
| type BoundEnumType<CodecTypes extends CodecTypeMap> = <const Name extends string, const Codec extends Pick<ColumnTypeDescriptor, 'codecId' | 'nativeType'>, const Members extends readonly [EnumMember<string, CodecInput<CodecTypes, Codec>>, ...EnumMember<string, CodecInput<CodecTypes, Codec>>[]]>(name: Name, codec: Codec, ...members: Members) => EnumTypeHandle<Name, MembersToValues<[...Members]>, MembersToNames<[...Members]>, MembersAccessorMap<[...Members]>>; | ||
| /** | ||
| * Bind `enumType` to a target's codec typemap. The returned function is the | ||
| * same runtime `enumType`, retyped so member values are constrained to the | ||
| * codec's input type. Target packages call this with their pack's | ||
| * `ExtractCodecTypesFromPack<Pack>` to expose a codec-aware `enumType`. | ||
| */ | ||
| declare function bindEnumType<CodecTypes extends CodecTypeMap>(): BoundEnumType<CodecTypes>; | ||
| /** | ||
| * Returns true when the value is an `EnumTypeHandle` produced by | ||
| * `enumType()`. Used in the lowering pipeline to detect enum handles | ||
| * in field state without importing the brand key at every call site. | ||
| */ | ||
| declare function isEnumTypeHandle(value: unknown): value is EnumTypeHandle; | ||
| //#endregion | ||
| export { type AuthoringNamespaceKey, type BoundEnumType, type CapabilityMatrix, type CodecInput, type CodecTypeMap, ENUM_TYPE_HANDLE_BRAND, type EntityHelperFactoryOptions, type EntityHelperFunction, type EntityHelpersFromNamespace, type EnumMember, type EnumTypeHandle, type ExtractAuthoringNamespaceFromPack, type ForeignKeyDefaultsState, type IndexDef, type MergeExtensionAuthoringNamespaces, type UnionToIntersection, bindEnumType, createEntityHelpersFromNamespace, enumType, isEnumTypeHandle, member, mergeCapabilityMatrices }; | ||
| //# sourceMappingURL=index.d.mts.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.d.mts","names":[],"sources":["../src/capability-registry.ts","../src/composed-helpers-scaffolding.ts","../src/descriptors.ts"],"mappings":";;;KAAY,gBAAA,GAAmB,QAAA,CAAS,MAAA,SAAe,QAAA,CAAS,MAAA;;;AAAhE;;;;;;;;;;iBAcgB,uBAAA,IACX,OAAA,EAAS,aAAA,CAAc,gBAAA,gBACzB,MAAA,SAAe,MAAA;;;;;AAhBlB;;;;;;;;;;;;;;AAAsE;KCyB1D,mBAAA,OAA0B,CAAA,oBAAqB,KAAA,EAAO,CAAC,6BACjE,KAAA,sBAEE,CAAA;AAAA,KAGQ,qBAAA;AAAA,KAEA,iCAAA,mBAEE,qBAAA,oBAEV,IAAA;EAAA,SACO,SAAA,oBAA6B,GAAA;AAAA,IAEpC,SAAA,SAAkB,MAAA,oBAChB,SAAA,GACA,cAAA,GACF,cAAA;AAAA,KAEQ,iCAAA,6BAEE,qBAAA,mBACK,MAAA,kBAEjB,cAAA,SAAuB,MAAA,0BACb,cAAA,iBACJ,cAAA,GACA,mBAAA,eAEgB,cAAA,GAAiB,iCAAA,CAC3B,cAAA,CAAe,CAAA,GACf,GAAA,EACA,cAAA,UAEI,cAAA,KAEZ,cAAA;;;;;;KAOD,4BAAA,oBAAgD,6BAAA,IACnD,UAAA;EAAA,SACW,OAAA,GAAU,KAAA,eAAoB,GAAA,EAAK,sBAAA;AAAA;EAExC,KAAA,EAAO,KAAA;EAAO,MAAA,EAAQ,MAAA;AAAA;EACtB,KAAA;EAAgB,MAAA;AAAA;AAAA,KAEZ,oBAAA,oBAAwC,6BAAA,IAClD,4BAAA,CAA6B,UAAA;EAAsB,KAAA;EAAoB,MAAA;AAAA,KAClE,KAAA,EAAO,KAAA,KAAU,MAAA;AAAA,KAGZ,0BAAA,qCACW,SAAA,GAAY,SAAA,CAAU,CAAA,UAAW,6BAAA,GAClD,oBAAA,CAAqB,SAAA,CAAU,CAAA,KAC/B,SAAA,CAAU,CAAA,UAAW,MAAA,oBACnB,0BAAA,CAA2B,SAAA,CAAU,CAAA;AAAA,UAI5B,0BAAA;EAAA,SACN,GAAA,EAAK,sBAAsB;AAAA;;;AA3DL;AAEjC;;;iBAkEgB,gCAAA,CACd,SAAA,EAAW,4BAAA,EACX,OAAA,EAAS,0BAAA,EACT,IAAA,uBACC,MAAA;;;UCvGc,QAAA;EAAA,SACN,OAAA;EAAA,SACA,IAAA;EAAA,SACA,IAAA;EAAA,SACA,OAAA,GAAU,MAAM;AAAA;AAAA,UAGV,uBAAA;EAAA,SACN,UAAA;EAAA,SACA,KAAK;AAAA"} | ||
| {"version":3,"file":"index.d.mts","names":[],"sources":["../src/capability-registry.ts","../src/composed-helpers-scaffolding.ts","../src/descriptors.ts","../src/enum-type.ts"],"mappings":"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"} |
+63
-1
| import { instantiateAuthoringEntityType } from "@prisma-next/framework-components/authoring"; | ||
| import { blindCast } from "@prisma-next/utils/casts"; | ||
| //#region src/capability-registry.ts | ||
@@ -58,4 +59,65 @@ /** | ||
| //#endregion | ||
| export { createEntityHelpersFromNamespace, mergeCapabilityMatrices }; | ||
| //#region src/enum-type.ts | ||
| function member(name, value) { | ||
| return { | ||
| name, | ||
| value: blindCast(value === void 0 ? name : value) | ||
| }; | ||
| } | ||
| const ENUM_TYPE_HANDLE_BRAND = "__prismaNextEnumTypeHandle__"; | ||
| function enumType(name, codec, ...members) { | ||
| if (members.length === 0) throw new Error(`enumType("${name}"): must have at least one member.`); | ||
| const seenNames = /* @__PURE__ */ new Set(); | ||
| const seenValues = /* @__PURE__ */ new Set(); | ||
| for (const m of members) { | ||
| if (seenNames.has(m.name)) throw new Error(`enumType("${name}"): duplicate member name "${m.name}". Member names must be unique.`); | ||
| seenNames.add(m.name); | ||
| const loweredValue = String(m.value); | ||
| if (seenValues.has(loweredValue)) throw new Error(`enumType("${name}"): duplicate member value "${loweredValue}". Member values must be unique.`); | ||
| seenValues.add(loweredValue); | ||
| } | ||
| const values = Object.freeze(members.map((m) => m.value)); | ||
| const names = Object.freeze(members.map((m) => m.name)); | ||
| const enumMembers = Object.freeze(members.map((m) => ({ | ||
| name: m.name, | ||
| value: m.value | ||
| }))); | ||
| const membersAccessor = Object.freeze(Object.fromEntries(members.map((m) => [m.name, m.value]))); | ||
| const valueSet = new Set(values); | ||
| const valueToName = new Map(members.map((m) => [m.value, m.name])); | ||
| const valueToOrdinal = new Map(values.map((v, i) => [v, i])); | ||
| return { | ||
| [ENUM_TYPE_HANDLE_BRAND]: true, | ||
| enumName: name, | ||
| codecId: codec.codecId, | ||
| nativeType: codec.nativeType, | ||
| enumMembers, | ||
| values, | ||
| names, | ||
| members: membersAccessor, | ||
| has: (v) => valueSet.has(v), | ||
| nameOf: (v) => valueToName.get(v), | ||
| ordinalOf: (v) => valueToOrdinal.get(v) ?? -1 | ||
| }; | ||
| } | ||
| /** | ||
| * Bind `enumType` to a target's codec typemap. The returned function is the | ||
| * same runtime `enumType`, retyped so member values are constrained to the | ||
| * codec's input type. Target packages call this with their pack's | ||
| * `ExtractCodecTypesFromPack<Pack>` to expose a codec-aware `enumType`. | ||
| */ | ||
| function bindEnumType() { | ||
| return enumType; | ||
| } | ||
| /** | ||
| * Returns true when the value is an `EnumTypeHandle` produced by | ||
| * `enumType()`. Used in the lowering pipeline to detect enum handles | ||
| * in field state without importing the brand key at every call site. | ||
| */ | ||
| function isEnumTypeHandle(value) { | ||
| return typeof value === "object" && value !== null && Reflect.get(value, "__prismaNextEnumTypeHandle__") === true; | ||
| } | ||
| //#endregion | ||
| export { ENUM_TYPE_HANDLE_BRAND, bindEnumType, createEntityHelpersFromNamespace, enumType, isEnumTypeHandle, member, mergeCapabilityMatrices }; | ||
| //# sourceMappingURL=index.mjs.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.mjs","names":[],"sources":["../src/capability-registry.ts","../src/composed-helpers-scaffolding.ts"],"sourcesContent":["export type CapabilityMatrix = Readonly<Record<string, Readonly<Record<string, boolean>>>>;\n\n/**\n * Deep-merges any number of capability matrices into a single matrix.\n *\n * Merge is purely structural — namespaces and capability flags from later\n * sources overlay earlier ones. Undefined entries are skipped so callers\n * can pass optional sources without pre-filtering.\n *\n * The helper is target-agnostic: it contains no SQL or family-specific\n * knowledge. Higher layers contribute their own capability matrices (for\n * example, a target pack, an extension pack, or the contract author's\n * own `capabilities` block) and call this helper to fold them together.\n */\nexport function mergeCapabilityMatrices(\n ...sources: ReadonlyArray<CapabilityMatrix | undefined>\n): Record<string, Record<string, boolean>> {\n const result: Record<string, Record<string, boolean>> = {};\n for (const source of sources) {\n if (source === undefined) continue;\n for (const [namespace, capabilities] of Object.entries(source)) {\n const existing = result[namespace];\n result[namespace] = existing ? { ...existing, ...capabilities } : { ...capabilities };\n }\n }\n return result;\n}\n","import type {\n AuthoringEntityContext,\n AuthoringEntityTypeDescriptor,\n AuthoringEntityTypeNamespace,\n} from '@prisma-next/framework-components/authoring';\nimport { instantiateAuthoringEntityType } from '@prisma-next/framework-components/authoring';\n\n/**\n * Family-agnostic merge / instantiation scaffolding for pack-bag-driven\n * authoring contributions. The per-namespace shape (`field`, `type`,\n * `entityTypes`) is parameterized by the discriminator key so each\n * namespace can reuse the same extractor + cross-pack merger without\n * re-deriving the template per family. Call sites flatten merged\n * `entityTypes` onto the user-facing top-level helpers surface\n * alongside the built-in `model` / `rel` (e.g. `helpers.enum(...)`).\n * The contribution data structure stays as\n * `authoring.entityTypes.<name>` — pack authors keep contributing\n * through the namespace; the composed-helpers template performs the\n * rename in the type system.\n *\n * SQL-specific composition (the `field` / `model` / `rel` / `type` core\n * helpers, the SQL index-types merge) lives in the SQL contract-ts\n * package and imports from here.\n */\n\nexport type UnionToIntersection<U> = (U extends unknown ? (value: U) => void : never) extends (\n value: infer I,\n) => void\n ? I\n : never;\n\nexport type AuthoringNamespaceKey = 'field' | 'type' | 'entityTypes';\n\nexport type ExtractAuthoringNamespaceFromPack<\n Pack,\n Key extends AuthoringNamespaceKey,\n EmptyNamespace,\n> = Pack extends {\n readonly authoring?: { readonly [P in Key]?: infer Namespace };\n}\n ? Namespace extends Record<string, unknown>\n ? Namespace\n : EmptyNamespace\n : EmptyNamespace;\n\nexport type MergeExtensionAuthoringNamespaces<\n ExtensionPacks,\n Key extends AuthoringNamespaceKey,\n EmptyNamespace = Record<never, never>,\n> =\n ExtensionPacks extends Record<string, unknown>\n ? keyof ExtensionPacks extends never\n ? EmptyNamespace\n : UnionToIntersection<\n {\n [K in keyof ExtensionPacks]: ExtractAuthoringNamespaceFromPack<\n ExtensionPacks[K],\n Key,\n EmptyNamespace\n >;\n }[keyof ExtensionPacks]\n >\n : EmptyNamespace;\n\n/**\n * Entity-helper shape derivation. Mirrors `FieldHelpersFromNamespace` /\n * `TypeHelpersFromNamespace` in the SQL package: leaf descriptors become\n * callable helpers; nested namespaces recurse.\n */\ntype ExtractFactoryInputAndOutput<Descriptor extends AuthoringEntityTypeDescriptor> =\n Descriptor['output'] extends {\n readonly factory: (input: infer Input, ctx: AuthoringEntityContext) => infer Output;\n }\n ? { input: Input; output: Output }\n : { input: unknown; output: unknown };\n\nexport type EntityHelperFunction<Descriptor extends AuthoringEntityTypeDescriptor> =\n ExtractFactoryInputAndOutput<Descriptor> extends { input: infer Input; output: infer Output }\n ? (input: Input) => Output\n : never;\n\nexport type EntityHelpersFromNamespace<Namespace> = {\n readonly [K in keyof Namespace]: Namespace[K] extends AuthoringEntityTypeDescriptor\n ? EntityHelperFunction<Namespace[K]>\n : Namespace[K] extends Record<string, unknown>\n ? EntityHelpersFromNamespace<Namespace[K]>\n : never;\n};\n\nexport interface EntityHelperFactoryOptions {\n readonly ctx: AuthoringEntityContext;\n}\n\n/**\n * Walks an entity-type namespace (after cross-pack merge) and produces the\n * runtime callable surface mirroring its tree shape. Each leaf\n * descriptor becomes a function `(input) => factory(input, ctx)`;\n * nested namespace objects recurse.\n */\nexport function createEntityHelpersFromNamespace(\n namespace: AuthoringEntityTypeNamespace,\n options: EntityHelperFactoryOptions,\n path: readonly string[] = [],\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(namespace)) {\n const currentPath = [...path, key];\n if (isLeafEntityDescriptor(value)) {\n result[key] = createEntityHelper(currentPath.join('.'), value, options);\n continue;\n }\n if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n result[key] = createEntityHelpersFromNamespace(\n value as AuthoringEntityTypeNamespace,\n options,\n currentPath,\n );\n }\n }\n return result;\n}\n\nfunction isLeafEntityDescriptor(value: unknown): value is AuthoringEntityTypeDescriptor {\n if (\n typeof value !== 'object' ||\n value === null ||\n (value as { kind?: unknown }).kind !== 'entity'\n ) {\n return false;\n }\n const discriminator = (value as { discriminator?: unknown }).discriminator;\n return typeof discriminator === 'string' && discriminator.length > 0;\n}\n\nfunction createEntityHelper(\n helperPath: string,\n descriptor: AuthoringEntityTypeDescriptor,\n options: EntityHelperFactoryOptions,\n): (...args: readonly unknown[]) => unknown {\n return (...args: readonly unknown[]) =>\n instantiateAuthoringEntityType(helperPath, descriptor, args, options.ctx);\n}\n"],"mappings":";;;;;;;;;;;;;;AAcA,SAAgB,wBACd,GAAG,SACsC;CACzC,MAAM,SAAkD,CAAC;CACzD,KAAK,MAAM,UAAU,SAAS;EAC5B,IAAI,WAAW,KAAA,GAAW;EAC1B,KAAK,MAAM,CAAC,WAAW,iBAAiB,OAAO,QAAQ,MAAM,GAAG;GAC9D,MAAM,WAAW,OAAO;GACxB,OAAO,aAAa,WAAW;IAAE,GAAG;IAAU,GAAG;GAAa,IAAI,EAAE,GAAG,aAAa;EACtF;CACF;CACA,OAAO;AACT;;;;;;;;;ACyEA,SAAgB,iCACd,WACA,SACA,OAA0B,CAAC,GACF;CACzB,MAAM,SAAkC,CAAC;CACzC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,SAAS,GAAG;EACpD,MAAM,cAAc,CAAC,GAAG,MAAM,GAAG;EACjC,IAAI,uBAAuB,KAAK,GAAG;GACjC,OAAO,OAAO,mBAAmB,YAAY,KAAK,GAAG,GAAG,OAAO,OAAO;GACtE;EACF;EACA,IAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK,GACrE,OAAO,OAAO,iCACZ,OACA,SACA,WACF;CAEJ;CACA,OAAO;AACT;AAEA,SAAS,uBAAuB,OAAwD;CACtF,IACE,OAAO,UAAU,YACjB,UAAU,QACT,MAA6B,SAAS,UAEvC,OAAO;CAET,MAAM,gBAAiB,MAAsC;CAC7D,OAAO,OAAO,kBAAkB,YAAY,cAAc,SAAS;AACrE;AAEA,SAAS,mBACP,YACA,YACA,SAC0C;CAC1C,QAAQ,GAAG,SACT,+BAA+B,YAAY,YAAY,MAAM,QAAQ,GAAG;AAC5E"} | ||
| {"version":3,"file":"index.mjs","names":[],"sources":["../src/capability-registry.ts","../src/composed-helpers-scaffolding.ts","../src/enum-type.ts"],"sourcesContent":["export type CapabilityMatrix = Readonly<Record<string, Readonly<Record<string, boolean>>>>;\n\n/**\n * Deep-merges any number of capability matrices into a single matrix.\n *\n * Merge is purely structural — namespaces and capability flags from later\n * sources overlay earlier ones. Undefined entries are skipped so callers\n * can pass optional sources without pre-filtering.\n *\n * The helper is target-agnostic: it contains no SQL or family-specific\n * knowledge. Higher layers contribute their own capability matrices (for\n * example, a target pack, an extension pack, or the contract author's\n * own `capabilities` block) and call this helper to fold them together.\n */\nexport function mergeCapabilityMatrices(\n ...sources: ReadonlyArray<CapabilityMatrix | undefined>\n): Record<string, Record<string, boolean>> {\n const result: Record<string, Record<string, boolean>> = {};\n for (const source of sources) {\n if (source === undefined) continue;\n for (const [namespace, capabilities] of Object.entries(source)) {\n const existing = result[namespace];\n result[namespace] = existing ? { ...existing, ...capabilities } : { ...capabilities };\n }\n }\n return result;\n}\n","import type {\n AuthoringEntityContext,\n AuthoringEntityTypeDescriptor,\n AuthoringEntityTypeNamespace,\n} from '@prisma-next/framework-components/authoring';\nimport { instantiateAuthoringEntityType } from '@prisma-next/framework-components/authoring';\n\n/**\n * Family-agnostic merge / instantiation scaffolding for pack-bag-driven\n * authoring contributions. The per-namespace shape (`field`, `type`,\n * `entityTypes`) is parameterized by the discriminator key so each\n * namespace can reuse the same extractor + cross-pack merger without\n * re-deriving the template per family. Call sites flatten merged\n * `entityTypes` onto the user-facing top-level helpers surface\n * alongside the built-in `model` / `rel` (e.g. `helpers.enum(...)`).\n * The contribution data structure stays as\n * `authoring.entityTypes.<name>` — pack authors keep contributing\n * through the namespace; the composed-helpers template performs the\n * rename in the type system.\n *\n * SQL-specific composition (the `field` / `model` / `rel` / `type` core\n * helpers, the SQL index-types merge) lives in the SQL contract-ts\n * package and imports from here.\n */\n\nexport type UnionToIntersection<U> = (U extends unknown ? (value: U) => void : never) extends (\n value: infer I,\n) => void\n ? I\n : never;\n\nexport type AuthoringNamespaceKey = 'field' | 'type' | 'entityTypes';\n\nexport type ExtractAuthoringNamespaceFromPack<\n Pack,\n Key extends AuthoringNamespaceKey,\n EmptyNamespace,\n> = Pack extends {\n readonly authoring?: { readonly [P in Key]?: infer Namespace };\n}\n ? Namespace extends Record<string, unknown>\n ? Namespace\n : EmptyNamespace\n : EmptyNamespace;\n\nexport type MergeExtensionAuthoringNamespaces<\n ExtensionPacks,\n Key extends AuthoringNamespaceKey,\n EmptyNamespace = Record<never, never>,\n> =\n ExtensionPacks extends Record<string, unknown>\n ? keyof ExtensionPacks extends never\n ? EmptyNamespace\n : UnionToIntersection<\n {\n [K in keyof ExtensionPacks]: ExtractAuthoringNamespaceFromPack<\n ExtensionPacks[K],\n Key,\n EmptyNamespace\n >;\n }[keyof ExtensionPacks]\n >\n : EmptyNamespace;\n\n/**\n * Entity-helper shape derivation. Mirrors `FieldHelpersFromNamespace` /\n * `TypeHelpersFromNamespace` in the SQL package: leaf descriptors become\n * callable helpers; nested namespaces recurse.\n */\ntype ExtractFactoryInputAndOutput<Descriptor extends AuthoringEntityTypeDescriptor> =\n Descriptor['output'] extends {\n readonly factory: (input: infer Input, ctx: AuthoringEntityContext) => infer Output;\n }\n ? { input: Input; output: Output }\n : { input: unknown; output: unknown };\n\nexport type EntityHelperFunction<Descriptor extends AuthoringEntityTypeDescriptor> =\n ExtractFactoryInputAndOutput<Descriptor> extends { input: infer Input; output: infer Output }\n ? (input: Input) => Output\n : never;\n\nexport type EntityHelpersFromNamespace<Namespace> = {\n readonly [K in keyof Namespace]: Namespace[K] extends AuthoringEntityTypeDescriptor\n ? EntityHelperFunction<Namespace[K]>\n : Namespace[K] extends Record<string, unknown>\n ? EntityHelpersFromNamespace<Namespace[K]>\n : never;\n};\n\nexport interface EntityHelperFactoryOptions {\n readonly ctx: AuthoringEntityContext;\n}\n\n/**\n * Walks an entity-type namespace (after cross-pack merge) and produces the\n * runtime callable surface mirroring its tree shape. Each leaf\n * descriptor becomes a function `(input) => factory(input, ctx)`;\n * nested namespace objects recurse.\n */\nexport function createEntityHelpersFromNamespace(\n namespace: AuthoringEntityTypeNamespace,\n options: EntityHelperFactoryOptions,\n path: readonly string[] = [],\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(namespace)) {\n const currentPath = [...path, key];\n if (isLeafEntityDescriptor(value)) {\n result[key] = createEntityHelper(currentPath.join('.'), value, options);\n continue;\n }\n if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n result[key] = createEntityHelpersFromNamespace(\n value as AuthoringEntityTypeNamespace,\n options,\n currentPath,\n );\n }\n }\n return result;\n}\n\nfunction isLeafEntityDescriptor(value: unknown): value is AuthoringEntityTypeDescriptor {\n if (\n typeof value !== 'object' ||\n value === null ||\n (value as { kind?: unknown }).kind !== 'entity'\n ) {\n return false;\n }\n const discriminator = (value as { discriminator?: unknown }).discriminator;\n return typeof discriminator === 'string' && discriminator.length > 0;\n}\n\nfunction createEntityHelper(\n helperPath: string,\n descriptor: AuthoringEntityTypeDescriptor,\n options: EntityHelperFactoryOptions,\n): (...args: readonly unknown[]) => unknown {\n return (...args: readonly unknown[]) =>\n instantiateAuthoringEntityType(helperPath, descriptor, args, options.ctx);\n}\n","import type { ColumnTypeDescriptor } from '@prisma-next/framework-components/codec';\nimport { blindCast } from '@prisma-next/utils/casts';\n\n/**\n * A single enum member produced by `member()`. The `Name` and `Value` generics\n * are preserved as literal types so `enumType()` can carry the ordered value\n * tuple in its return type. `Value` is whatever the codec dictates — its type\n * is constrained at `enumType` against the codec's input type, not here.\n */\nexport interface EnumMember<Name extends string, Value> {\n readonly name: Name;\n readonly value: Value;\n}\n\n/**\n * Declare an enum member. The `value` defaults to `name` when omitted. The\n * value is an unconstrained literal here; `enumType` constrains it against the\n * codec's input type. Both generics are preserved as literals so downstream\n * `enumType` carries the value union in its type; the value is serialized to its\n * codec string form only at lowering.\n */\nexport function member<const Name extends string>(name: Name): EnumMember<Name, Name>;\nexport function member<const Name extends string, const Value>(\n name: Name,\n value: Value,\n): EnumMember<Name, Value>;\nexport function member<const Name extends string, const Value = Name>(\n name: Name,\n value?: Value,\n): EnumMember<Name, Value> {\n return {\n name,\n value: blindCast<\n Value,\n 'overload signatures enforce Value=Name when value is omitted; default generic Value=Name makes this safe'\n >(value === undefined ? name : value),\n };\n}\n\ntype MembersToValues<Members extends readonly EnumMember<string, unknown>[]> = {\n readonly [K in keyof Members]: Members[K] extends EnumMember<string, infer V> ? V : never;\n};\n\ntype MembersToNames<Members extends readonly EnumMember<string, unknown>[]> = {\n readonly [K in keyof Members]: Members[K] extends EnumMember<infer N, unknown> ? N : never;\n};\n\ntype MembersAccessorMap<Members extends readonly EnumMember<string, unknown>[]> = {\n readonly [M in Members[number] as M['name']]: M['value'];\n};\n\n// String-key brand rather than unique symbol: tsdown inlines a fresh unique\n// symbol into each extension's .d.mts when the source package is transitive,\n// breaking nominal type identity across chunk boundaries. A string constant\n// survives .d.mts de-duplication because string equality is preserved by value.\n// Mirrors the FILTER_EXPR_BRAND pattern in mongo-query-ast/filter-expressions.ts.\nexport const ENUM_TYPE_HANDLE_BRAND = '__prismaNextEnumTypeHandle__';\n\n/** Authoring handle returned by `enumType()`. Generic over the ordered value tuple so `const Role = enumType(...)` retains literal types. */\nexport interface EnumTypeHandle<\n Name extends string = string,\n Values extends readonly unknown[] = readonly unknown[],\n Names extends readonly string[] = readonly string[],\n MembersMap extends Record<string, unknown> = Record<string, unknown>,\n> {\n /** Internal brand for lowering-pipeline detection. */\n readonly [ENUM_TYPE_HANDLE_BRAND]: true;\n\n /** The enum's declared name (used as the key in domain `enum` / storage `valueSet`). */\n readonly enumName: Name;\n\n /** codecId from the codec passed to `enumType`. */\n readonly codecId: string;\n\n /** nativeType from the codec passed to `enumType`. */\n readonly nativeType: string;\n\n /** Ordered member list for lowering (name + value pairs). */\n readonly enumMembers: readonly { readonly name: string; readonly value: Values[number] }[];\n\n /** Ordered literal value tuple. Declaration order is preserved. */\n readonly values: Values;\n\n /** Ordered literal name tuple. Declaration order is preserved. */\n readonly names: Names;\n\n /**\n * Namespaced accessor map: `Role.members.User === 'user'`.\n * Namespaced under `.members` to avoid collisions with `.values` / `.has`.\n */\n readonly members: MembersMap;\n\n /** Returns `true` if `v` is a declared member value. */\n has(v: Values[number]): boolean;\n\n /** Returns the member name for a value, or `undefined` if not found. */\n nameOf(v: Values[number]): string | undefined;\n\n /** Returns the zero-based declaration index of a value, or `-1` if not found. */\n ordinalOf(v: Values[number]): number;\n}\n\n/**\n * A codec typemap: codecId → `{ input, output }`, the same shape the query\n * lanes consume. The bound `enumType` wrappers supply the target pack's\n * typemap; the core defaults to an empty map (no codec is known), so member\n * values stay unconstrained.\n */\nexport type CodecTypeMap = Record<string, { readonly input?: unknown }>;\n\n/**\n * The application input type the codec dictates for an enum's member values:\n * looks `Codec['codecId']` up in the supplied codec typemap. When the codecId\n * isn't in the map the input is unconstrained, so any member-value literal is\n * accepted and inferred verbatim.\n */\nexport type CodecInput<\n CodecTypes extends CodecTypeMap,\n Codec extends { readonly codecId: string },\n> = Codec['codecId'] extends keyof CodecTypes\n ? CodecTypes[Codec['codecId']] extends { readonly input: infer In }\n ? In\n : unknown\n : unknown;\n\n/**\n * Declare a domain enum for use in TS-authoring contracts.\n *\n * - The codec is an explicit required argument — the `codecId` and\n * `nativeType` are taken from the passed `ColumnTypeDescriptor` (e.g.\n * `{ codecId: 'pg/text@1', nativeType: 'text' }` from a field preset\n * output or a direct inline object).\n * - `const` generics on the members spread preserve the ordered literal\n * value tuple so `Role.values` is `readonly ['user','admin']`, not\n * `string[]`.\n * - Well-formedness assertions at construction: non-empty member list;\n * unique names; unique values.\n *\n * The returned handle wires into `field.namedType(handle)` to set\n * `valueSet` refs on both the domain field and the storage column.\n *\n * @example\n * ```ts\n * const Role = enumType('Role', { codecId: 'pg/text@1', nativeType: 'text' },\n * member('User', 'user'),\n * member('Admin', 'admin'),\n * );\n * // Role.values → readonly ['user', 'admin']\n * // Role.members.User → 'user'\n * ```\n */\nexport function enumType<\n CodecTypes extends CodecTypeMap = Record<string, never>,\n const Name extends string = string,\n const Codec extends Pick<ColumnTypeDescriptor, 'codecId' | 'nativeType'> = Pick<\n ColumnTypeDescriptor,\n 'codecId' | 'nativeType'\n >,\n const Members extends readonly [\n EnumMember<string, CodecInput<CodecTypes, Codec>>,\n ...EnumMember<string, CodecInput<CodecTypes, Codec>>[],\n ] = readonly [EnumMember<string, CodecInput<CodecTypes, Codec>>],\n>(\n name: Name,\n codec: Codec,\n ...members: Members\n): EnumTypeHandle<\n Name,\n MembersToValues<[...Members]>,\n MembersToNames<[...Members]>,\n MembersAccessorMap<[...Members]>\n>;\nexport function enumType(\n name: string,\n codec: Pick<ColumnTypeDescriptor, 'codecId' | 'nativeType'>,\n ...members: EnumMember<string, unknown>[]\n): EnumTypeHandle;\nexport function enumType(\n name: string,\n codec: Pick<ColumnTypeDescriptor, 'codecId' | 'nativeType'>,\n ...members: EnumMember<string, unknown>[]\n): EnumTypeHandle {\n if (members.length === 0) {\n throw new Error(`enumType(\"${name}\"): must have at least one member.`);\n }\n\n const seenNames = new Set<string>();\n const seenValues = new Set<string>();\n for (const m of members) {\n if (seenNames.has(m.name)) {\n throw new Error(\n `enumType(\"${name}\"): duplicate member name \"${m.name}\". Member names must be unique.`,\n );\n }\n seenNames.add(m.name);\n\n const loweredValue = String(m.value);\n if (seenValues.has(loweredValue)) {\n throw new Error(\n `enumType(\"${name}\"): duplicate member value \"${loweredValue}\". Member values must be unique.`,\n );\n }\n seenValues.add(loweredValue);\n }\n\n const values = Object.freeze(members.map((m) => m.value));\n const names = Object.freeze(members.map((m) => m.name));\n const enumMembers = Object.freeze(members.map((m) => ({ name: m.name, value: m.value })));\n\n const membersAccessor = Object.freeze(Object.fromEntries(members.map((m) => [m.name, m.value])));\n\n const valueSet = new Set(values);\n const valueToName = new Map(members.map((m) => [m.value, m.name]));\n const valueToOrdinal = new Map(values.map((v, i) => [v, i]));\n\n return {\n [ENUM_TYPE_HANDLE_BRAND]: true,\n enumName: name,\n codecId: codec.codecId,\n nativeType: codec.nativeType,\n enumMembers,\n values,\n names,\n members: membersAccessor,\n has: (v: unknown) => valueSet.has(v),\n nameOf: (v: unknown) => valueToName.get(v),\n ordinalOf: (v: unknown) => valueToOrdinal.get(v) ?? -1,\n };\n}\n\n/**\n * The signature of an `enumType` whose codec typemap is already bound — the\n * shape a target-bound wrapper (e.g. `@prisma-next/postgres/contract-builder`)\n * exposes. The member values are constrained to the codec's input type drawn\n * from `CodecTypes`, while `Name`, `Codec`, and the member tuple still infer\n * from the call.\n */\nexport type BoundEnumType<CodecTypes extends CodecTypeMap> = <\n const Name extends string,\n const Codec extends Pick<ColumnTypeDescriptor, 'codecId' | 'nativeType'>,\n const Members extends readonly [\n EnumMember<string, CodecInput<CodecTypes, Codec>>,\n ...EnumMember<string, CodecInput<CodecTypes, Codec>>[],\n ],\n>(\n name: Name,\n codec: Codec,\n ...members: Members\n) => EnumTypeHandle<\n Name,\n MembersToValues<[...Members]>,\n MembersToNames<[...Members]>,\n MembersAccessorMap<[...Members]>\n>;\n\n/**\n * Bind `enumType` to a target's codec typemap. The returned function is the\n * same runtime `enumType`, retyped so member values are constrained to the\n * codec's input type. Target packages call this with their pack's\n * `ExtractCodecTypesFromPack<Pack>` to expose a codec-aware `enumType`.\n */\nexport function bindEnumType<CodecTypes extends CodecTypeMap>(): BoundEnumType<CodecTypes> {\n return enumType;\n}\n\n/**\n * Returns true when the value is an `EnumTypeHandle` produced by\n * `enumType()`. Used in the lowering pipeline to detect enum handles\n * in field state without importing the brand key at every call site.\n */\nexport function isEnumTypeHandle(value: unknown): value is EnumTypeHandle {\n return (\n typeof value === 'object' &&\n value !== null &&\n Reflect.get(value, ENUM_TYPE_HANDLE_BRAND) === true\n );\n}\n"],"mappings":"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"} |
+7
-6
| { | ||
| "name": "@prisma-next/contract-authoring", | ||
| "version": "0.14.0", | ||
| "version": "0.15.0-dev.1", | ||
| "license": "Apache-2.0", | ||
@@ -9,10 +9,11 @@ "type": "module", | ||
| "dependencies": { | ||
| "@prisma-next/framework-components": "0.14.0" | ||
| "@prisma-next/framework-components": "0.15.0-dev.1", | ||
| "@prisma-next/utils": "0.15.0-dev.1" | ||
| }, | ||
| "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" | ||
| }, | ||
@@ -19,0 +20,0 @@ "peerDependencies": { |
+14
-0
@@ -14,1 +14,15 @@ export type { CapabilityMatrix } from './capability-registry'; | ||
| export type { ForeignKeyDefaultsState, IndexDef } from './descriptors'; | ||
| export type { | ||
| BoundEnumType, | ||
| CodecInput, | ||
| CodecTypeMap, | ||
| EnumMember, | ||
| EnumTypeHandle, | ||
| } from './enum-type'; | ||
| export { | ||
| bindEnumType, | ||
| ENUM_TYPE_HANDLE_BRAND, | ||
| enumType, | ||
| isEnumTypeHandle, | ||
| member, | ||
| } from './enum-type'; |
72337
90.82%12
9.09%561
136.71%3
50%+ Added
+ Added
+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
- Removed