@prisma-next/mongo-emitter 0.14.0-dev.3 → 0.14.0-dev.30

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.
@@ -1,4 +1,4 @@
1
- import { Contract, ContractModelBase } from "@prisma-next/contract/types";
1
+ import { Contract, ContractModelBase, JsonValue } from "@prisma-next/contract/types";
2
2
  import { ValidationContext } from "@prisma-next/framework-components/emission";
3
3
 
4
4
  //#region src/index.d.ts
@@ -8,6 +8,10 @@ declare const mongoEmission: {
8
8
  validateStructure(contract: Contract): void;
9
9
  generateStorageType(contract: Contract, storageHashTypeName: string): string;
10
10
  generateModelStorageType(_modelName: string, model: ContractModelBase): string;
11
+ resolveFieldValueSet(_modelName: string, fieldName: string, model: ContractModelBase, contract: Contract): {
12
+ readonly encodedValues: readonly JsonValue[];
13
+ readonly codecId: string;
14
+ } | undefined;
11
15
  getFamilyImports(): string[];
12
16
  getFamilyTypeAliases(): string;
13
17
  getTypeMapsExpression(): string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/index.ts"],"mappings":";;;;cAuEa,aAAA;;0BAGa,QAAA,EAAQ,IAAA,EAAQ,iBAAA;8BA6CZ,QAAA;gCAyGE,QAAA,EAAQ,mBAAA;+CAMK,KAAA,EAAS,iBAAA;;;;+CAuCT,YAAA;AAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/index.ts"],"mappings":";;;;cA4Ea,aAAA;;0BAGa,QAAA,EAAQ,IAAA,EAAQ,iBAAA;8BA6CZ,QAAA;gCAyGE,QAAA,EAAQ,mBAAA;+CAMK,KAAA,EAAS,iBAAA;2CA2BhC,SAAA,UACD,KAAA,EACV,iBAAA,EAAiB,QAAA,EACd,QAAA;IAAA,SACE,aAAA,WAAwB,SAAA;IAAA,SAAsB,OAAA;EAAA;;;;+CA+BjB,YAAA;AAAA"}
@@ -114,6 +114,16 @@ const mongoEmission = {
114
114
  }
115
115
  return parts.length > 0 ? `{ ${parts.join("; ")} }` : "Record<string, never>";
116
116
  },
117
+ resolveFieldValueSet(_modelName, fieldName, model, contract) {
118
+ const ref = model.fields[fieldName]?.valueSet;
119
+ if (ref?.entityKind !== "enum") return void 0;
120
+ const domainEnum = contract.domain.namespaces[ref.namespaceId]?.enum?.[ref.entityName];
121
+ if (!domainEnum) return void 0;
122
+ return {
123
+ encodedValues: domainEnum.members.map((m) => m.value),
124
+ codecId: domainEnum.codecId
125
+ };
126
+ },
117
127
  getFamilyImports() {
118
128
  return [
119
129
  "import type {",
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["relObj"],"sources":["../../src/index.ts"],"sourcesContent":["import type { Contract, ContractModel, ContractModelBase } from '@prisma-next/contract/types';\nimport { serializeObjectKey, serializeValue } from '@prisma-next/emitter/domain-type-generation';\nimport type { ValidationContext } from '@prisma-next/framework-components/emission';\nimport type { Namespace } from '@prisma-next/framework-components/ir';\nimport type { MongoCollection, MongoStorage } from '@prisma-next/mongo-contract';\n\nconst MONGO_NAMESPACE_KIND_FALLBACK = 'mongo-namespace' as const;\n\nfunction mongoNamespaceSerializedKind(ns: Namespace): string {\n const kind = (ns as { kind?: unknown }).kind;\n if (typeof kind === 'string') {\n return `readonly kind: ${serializeValue(kind)}`;\n }\n return `readonly kind: '${MONGO_NAMESPACE_KIND_FALLBACK}'`;\n}\n\nfunction assertUniqueMongoCollectionNames(storage: MongoStorage): void {\n const seen = new Map<string, string>();\n for (const [namespaceId, ns] of Object.entries(storage.namespaces)) {\n for (const coll of Object.keys(ns.entries.collection ?? {})) {\n const existing = seen.get(coll);\n if (existing !== undefined && existing !== namespaceId) {\n throw new Error(\n `Duplicate collection name \"${coll}\" in namespaces \"${existing}\" and \"${namespaceId}\"`,\n );\n }\n seen.set(coll, namespaceId);\n }\n }\n}\n\nfunction generateMongoCollectionEntryType(coll: MongoCollection): string {\n const entries = Object.entries(coll).filter(([key, v]) => v !== undefined && key !== 'kind');\n if (entries.length === 0) {\n return 'MongoCollection';\n }\n return serializeValue(coll);\n}\n\nfunction generateMongoNamespaceCollectionsType(\n collections: Readonly<Record<string, MongoCollection>>,\n): string {\n const entries: string[] = [];\n for (const [collName, coll] of Object.entries(collections).sort(([a], [b]) =>\n a.localeCompare(b),\n )) {\n entries.push(\n `readonly ${serializeObjectKey(collName)}: ${generateMongoCollectionEntryType(coll)}`,\n );\n }\n if (entries.length === 0) {\n return 'Record<string, never>';\n }\n return `{ ${entries.join('; ')} }`;\n}\n\nfunction generateMongoNamespacesType(namespaces: MongoStorage['namespaces']): string {\n const sorted = Object.entries(namespaces ?? {}).sort(([a], [b]) => a.localeCompare(b));\n if (sorted.length === 0) {\n return 'Record<string, never>';\n }\n const parts: string[] = [];\n for (const [name, ns] of sorted) {\n const collectionsType = generateMongoNamespaceCollectionsType(ns.entries.collection ?? {});\n parts.push(\n `readonly ${serializeObjectKey(name)}: { readonly id: ${serializeValue(ns.id)}; ${mongoNamespaceSerializedKind(ns)}; readonly entries: { readonly collection: ${collectionsType} } }`,\n );\n }\n return `{ ${parts.join('; ')} }`;\n}\n\nexport const mongoEmission = {\n id: 'mongo',\n\n validateTypes(contract: Contract, _ctx: ValidationContext): void {\n const typeIdRegex = /^([^/]+)\\/([^@]+)@(\\d+)$/;\n\n for (const [namespaceId, domainNs] of Object.entries(contract.domain.namespaces)) {\n const models = domainNs.models as Record<string, ContractModel>;\n for (const [modelName, model] of Object.entries(models)) {\n const qualifiedName = `${namespaceId}:${modelName}`;\n for (const [fieldName, field] of Object.entries(model.fields)) {\n const fieldType = (\n field as {\n type?: {\n kind: string;\n codecId?: string;\n members?: ReadonlyArray<{ kind: string; codecId?: string }>;\n };\n }\n ).type;\n if (!fieldType) continue;\n\n const scalarTypes: Array<{ codecId?: string }> =\n fieldType.kind === 'scalar'\n ? [fieldType]\n : fieldType.kind === 'union' && fieldType.members\n ? fieldType.members.filter((m) => m.kind === 'scalar')\n : [];\n\n for (const scalarType of scalarTypes) {\n const { codecId } = scalarType;\n if (!codecId) {\n throw new Error(\n `Field \"${fieldName}\" on model \"${qualifiedName}\" is missing codecId`,\n );\n }\n const match = codecId.match(typeIdRegex);\n if (!match?.[1]) {\n throw new Error(\n `Field \"${fieldName}\" on model \"${qualifiedName}\" has invalid codec ID format \"${codecId}\". Expected format: ns/name@version`,\n );\n }\n }\n }\n }\n }\n },\n\n validateStructure(contract: Contract): void {\n if (contract.targetFamily !== 'mongo') {\n throw new Error(`Expected targetFamily \"mongo\", got \"${contract.targetFamily}\"`);\n }\n\n const storage = contract.storage as MongoStorage | undefined;\n if (!storage?.namespaces || typeof storage.namespaces !== 'object') {\n throw new Error('Mongo contract must have storage.namespaces');\n }\n\n assertUniqueMongoCollectionNames(storage);\n\n const collectionNames = new Set<string>();\n for (const ns of Object.values(storage.namespaces)) {\n for (const c of Object.keys(ns.entries.collection ?? {})) {\n collectionNames.add(c);\n }\n }\n\n for (const [namespaceId, domainNs] of Object.entries(contract.domain.namespaces)) {\n const models = domainNs.models as Record<string, ContractModel>;\n if (Object.keys(models).length === 0) continue;\n\n for (const [modelName, model] of Object.entries(models)) {\n const qualifiedName = `${namespaceId}:${modelName}`;\n if (!model.fields || typeof model.fields !== 'object') {\n throw new Error(`Model \"${qualifiedName}\" is missing required field \"fields\"`);\n }\n if (!model.relations || typeof model.relations !== 'object') {\n throw new Error(\n `Model \"${qualifiedName}\" is missing required field \"relations\" (must be an object)`,\n );\n }\n if (!model.storage || typeof model.storage !== 'object') {\n throw new Error(\n `Model \"${qualifiedName}\" is missing required field \"storage\" (must be an object)`,\n );\n }\n\n const collectionValue = model.storage['collection'];\n const collection = typeof collectionValue === 'string' ? collectionValue : undefined;\n\n if (model.owner) {\n if (collection) {\n throw new Error(\n `Owned model \"${qualifiedName}\" must not have storage.collection (embedded models are stored within their owner)`,\n );\n }\n if (!models[model.owner]) {\n throw new Error(\n `Model \"${qualifiedName}\" declares owner \"${model.owner}\" which does not exist in models`,\n );\n }\n } else if (collection) {\n if (!collectionNames.has(collection)) {\n throw new Error(\n `Model \"${qualifiedName}\" references collection \"${collection}\" which is not in storage.namespaces[..].entries.collection`,\n );\n }\n }\n\n if (model.base) {\n const baseModel = models[model.base.model];\n if (!baseModel) {\n throw new Error(\n `Model \"${qualifiedName}\" declares base \"${model.base.namespace}:${model.base.model}\" which does not exist in models`,\n );\n }\n const variantCollection = collection;\n const baseCollection = baseModel.storage['collection'] as string | undefined;\n if (variantCollection !== baseCollection) {\n throw new Error(\n `Variant \"${qualifiedName}\" must share its base's collection (\"${baseCollection ?? '(none)'}\"), but has \"${variantCollection ?? '(none)'}\"`,\n );\n }\n }\n\n const storageRelations = model.storage['relations'] as Record<string, unknown> | undefined;\n if (storageRelations) {\n for (const relName of Object.keys(storageRelations)) {\n if (!model.relations[relName]) {\n throw new Error(\n `Model \"${qualifiedName}\" has storage.relations.${relName} but no matching domain-level relation`,\n );\n }\n }\n }\n\n for (const [relName, rel] of Object.entries(model.relations)) {\n const relObj = rel as Record<string, unknown>;\n const targetRef = relObj['to'] as { readonly model?: string } | undefined;\n const targetModelName = targetRef?.model;\n if (targetModelName) {\n const targetModel = models[targetModelName];\n if (targetModel?.owner === modelName && !storageRelations?.[relName]) {\n throw new Error(\n `Model \"${qualifiedName}\" has embed relation \"${relName}\" to owned model \"${targetModelName}\" but no matching storage.relations entry`,\n );\n }\n }\n }\n }\n }\n },\n\n generateStorageType(contract: Contract, storageHashTypeName: string): string {\n const storage = contract.storage as MongoStorage;\n const namespacesType = generateMongoNamespacesType(storage.namespaces);\n return `{ readonly namespaces: ${namespacesType}; readonly storageHash: ${storageHashTypeName} }`;\n },\n\n generateModelStorageType(_modelName: string, model: ContractModelBase): string {\n const parts: string[] = [];\n const collection = model.storage['collection'] as string | undefined;\n if (collection) {\n parts.push(`readonly collection: ${serializeValue(collection)}`);\n }\n\n const storageRelations = model.storage['relations'] as\n | Record<string, Record<string, unknown>>\n | undefined;\n if (storageRelations && Object.keys(storageRelations).length > 0) {\n const relEntries: string[] = [];\n for (const [relName, relVal] of Object.entries(storageRelations)) {\n relEntries.push(`readonly ${serializeObjectKey(relName)}: ${serializeValue(relVal)}`);\n }\n parts.push(`readonly relations: { ${relEntries.join('; ')} }`);\n }\n\n return parts.length > 0 ? `{ ${parts.join('; ')} }` : 'Record<string, never>';\n },\n\n getFamilyImports(): string[] {\n return [\n 'import type {',\n ' MongoCollection,',\n ' MongoContractWithTypeMaps,',\n ' MongoTypeMaps,',\n \"} from '@prisma-next/mongo-contract';\",\n ];\n },\n\n getFamilyTypeAliases(): string {\n return '';\n },\n\n getTypeMapsExpression(): string {\n return 'MongoTypeMaps<CodecTypes, FieldOutputTypes, FieldInputTypes>';\n },\n\n getContractWrapper(contractBaseName: string, typeMapsName: string): string {\n return `export type Contract = MongoContractWithTypeMaps<${contractBaseName}, ${typeMapsName}>;`;\n },\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["relObj"],"sources":["../../src/index.ts"],"sourcesContent":["import type {\n Contract,\n ContractModel,\n ContractModelBase,\n JsonValue,\n} from '@prisma-next/contract/types';\nimport { serializeObjectKey, serializeValue } from '@prisma-next/emitter/domain-type-generation';\nimport type { ValidationContext } from '@prisma-next/framework-components/emission';\nimport type { Namespace } from '@prisma-next/framework-components/ir';\nimport type { MongoCollection, MongoStorage } from '@prisma-next/mongo-contract';\n\nconst MONGO_NAMESPACE_KIND_FALLBACK = 'mongo-namespace' as const;\n\nfunction mongoNamespaceSerializedKind(ns: Namespace): string {\n const kind = (ns as { kind?: unknown }).kind;\n if (typeof kind === 'string') {\n return `readonly kind: ${serializeValue(kind)}`;\n }\n return `readonly kind: '${MONGO_NAMESPACE_KIND_FALLBACK}'`;\n}\n\nfunction assertUniqueMongoCollectionNames(storage: MongoStorage): void {\n const seen = new Map<string, string>();\n for (const [namespaceId, ns] of Object.entries(storage.namespaces)) {\n for (const coll of Object.keys(ns.entries.collection ?? {})) {\n const existing = seen.get(coll);\n if (existing !== undefined && existing !== namespaceId) {\n throw new Error(\n `Duplicate collection name \"${coll}\" in namespaces \"${existing}\" and \"${namespaceId}\"`,\n );\n }\n seen.set(coll, namespaceId);\n }\n }\n}\n\nfunction generateMongoCollectionEntryType(coll: MongoCollection): string {\n const entries = Object.entries(coll).filter(([key, v]) => v !== undefined && key !== 'kind');\n if (entries.length === 0) {\n return 'MongoCollection';\n }\n return serializeValue(coll);\n}\n\nfunction generateMongoNamespaceCollectionsType(\n collections: Readonly<Record<string, MongoCollection>>,\n): string {\n const entries: string[] = [];\n for (const [collName, coll] of Object.entries(collections).sort(([a], [b]) =>\n a.localeCompare(b),\n )) {\n entries.push(\n `readonly ${serializeObjectKey(collName)}: ${generateMongoCollectionEntryType(coll)}`,\n );\n }\n if (entries.length === 0) {\n return 'Record<string, never>';\n }\n return `{ ${entries.join('; ')} }`;\n}\n\nfunction generateMongoNamespacesType(namespaces: MongoStorage['namespaces']): string {\n const sorted = Object.entries(namespaces ?? {}).sort(([a], [b]) => a.localeCompare(b));\n if (sorted.length === 0) {\n return 'Record<string, never>';\n }\n const parts: string[] = [];\n for (const [name, ns] of sorted) {\n const collectionsType = generateMongoNamespaceCollectionsType(ns.entries.collection ?? {});\n parts.push(\n `readonly ${serializeObjectKey(name)}: { readonly id: ${serializeValue(ns.id)}; ${mongoNamespaceSerializedKind(ns)}; readonly entries: { readonly collection: ${collectionsType} } }`,\n );\n }\n return `{ ${parts.join('; ')} }`;\n}\n\nexport const mongoEmission = {\n id: 'mongo',\n\n validateTypes(contract: Contract, _ctx: ValidationContext): void {\n const typeIdRegex = /^([^/]+)\\/([^@]+)@(\\d+)$/;\n\n for (const [namespaceId, domainNs] of Object.entries(contract.domain.namespaces)) {\n const models = domainNs.models as Record<string, ContractModel>;\n for (const [modelName, model] of Object.entries(models)) {\n const qualifiedName = `${namespaceId}:${modelName}`;\n for (const [fieldName, field] of Object.entries(model.fields)) {\n const fieldType = (\n field as {\n type?: {\n kind: string;\n codecId?: string;\n members?: ReadonlyArray<{ kind: string; codecId?: string }>;\n };\n }\n ).type;\n if (!fieldType) continue;\n\n const scalarTypes: Array<{ codecId?: string }> =\n fieldType.kind === 'scalar'\n ? [fieldType]\n : fieldType.kind === 'union' && fieldType.members\n ? fieldType.members.filter((m) => m.kind === 'scalar')\n : [];\n\n for (const scalarType of scalarTypes) {\n const { codecId } = scalarType;\n if (!codecId) {\n throw new Error(\n `Field \"${fieldName}\" on model \"${qualifiedName}\" is missing codecId`,\n );\n }\n const match = codecId.match(typeIdRegex);\n if (!match?.[1]) {\n throw new Error(\n `Field \"${fieldName}\" on model \"${qualifiedName}\" has invalid codec ID format \"${codecId}\". Expected format: ns/name@version`,\n );\n }\n }\n }\n }\n }\n },\n\n validateStructure(contract: Contract): void {\n if (contract.targetFamily !== 'mongo') {\n throw new Error(`Expected targetFamily \"mongo\", got \"${contract.targetFamily}\"`);\n }\n\n const storage = contract.storage as MongoStorage | undefined;\n if (!storage?.namespaces || typeof storage.namespaces !== 'object') {\n throw new Error('Mongo contract must have storage.namespaces');\n }\n\n assertUniqueMongoCollectionNames(storage);\n\n const collectionNames = new Set<string>();\n for (const ns of Object.values(storage.namespaces)) {\n for (const c of Object.keys(ns.entries.collection ?? {})) {\n collectionNames.add(c);\n }\n }\n\n for (const [namespaceId, domainNs] of Object.entries(contract.domain.namespaces)) {\n const models = domainNs.models as Record<string, ContractModel>;\n if (Object.keys(models).length === 0) continue;\n\n for (const [modelName, model] of Object.entries(models)) {\n const qualifiedName = `${namespaceId}:${modelName}`;\n if (!model.fields || typeof model.fields !== 'object') {\n throw new Error(`Model \"${qualifiedName}\" is missing required field \"fields\"`);\n }\n if (!model.relations || typeof model.relations !== 'object') {\n throw new Error(\n `Model \"${qualifiedName}\" is missing required field \"relations\" (must be an object)`,\n );\n }\n if (!model.storage || typeof model.storage !== 'object') {\n throw new Error(\n `Model \"${qualifiedName}\" is missing required field \"storage\" (must be an object)`,\n );\n }\n\n const collectionValue = model.storage['collection'];\n const collection = typeof collectionValue === 'string' ? collectionValue : undefined;\n\n if (model.owner) {\n if (collection) {\n throw new Error(\n `Owned model \"${qualifiedName}\" must not have storage.collection (embedded models are stored within their owner)`,\n );\n }\n if (!models[model.owner]) {\n throw new Error(\n `Model \"${qualifiedName}\" declares owner \"${model.owner}\" which does not exist in models`,\n );\n }\n } else if (collection) {\n if (!collectionNames.has(collection)) {\n throw new Error(\n `Model \"${qualifiedName}\" references collection \"${collection}\" which is not in storage.namespaces[..].entries.collection`,\n );\n }\n }\n\n if (model.base) {\n const baseModel = models[model.base.model];\n if (!baseModel) {\n throw new Error(\n `Model \"${qualifiedName}\" declares base \"${model.base.namespace}:${model.base.model}\" which does not exist in models`,\n );\n }\n const variantCollection = collection;\n const baseCollection = baseModel.storage['collection'] as string | undefined;\n if (variantCollection !== baseCollection) {\n throw new Error(\n `Variant \"${qualifiedName}\" must share its base's collection (\"${baseCollection ?? '(none)'}\"), but has \"${variantCollection ?? '(none)'}\"`,\n );\n }\n }\n\n const storageRelations = model.storage['relations'] as Record<string, unknown> | undefined;\n if (storageRelations) {\n for (const relName of Object.keys(storageRelations)) {\n if (!model.relations[relName]) {\n throw new Error(\n `Model \"${qualifiedName}\" has storage.relations.${relName} but no matching domain-level relation`,\n );\n }\n }\n }\n\n for (const [relName, rel] of Object.entries(model.relations)) {\n const relObj = rel as Record<string, unknown>;\n const targetRef = relObj['to'] as { readonly model?: string } | undefined;\n const targetModelName = targetRef?.model;\n if (targetModelName) {\n const targetModel = models[targetModelName];\n if (targetModel?.owner === modelName && !storageRelations?.[relName]) {\n throw new Error(\n `Model \"${qualifiedName}\" has embed relation \"${relName}\" to owned model \"${targetModelName}\" but no matching storage.relations entry`,\n );\n }\n }\n }\n }\n }\n },\n\n generateStorageType(contract: Contract, storageHashTypeName: string): string {\n const storage = contract.storage as MongoStorage;\n const namespacesType = generateMongoNamespacesType(storage.namespaces);\n return `{ readonly namespaces: ${namespacesType}; readonly storageHash: ${storageHashTypeName} }`;\n },\n\n generateModelStorageType(_modelName: string, model: ContractModelBase): string {\n const parts: string[] = [];\n const collection = model.storage['collection'] as string | undefined;\n if (collection) {\n parts.push(`readonly collection: ${serializeValue(collection)}`);\n }\n\n const storageRelations = model.storage['relations'] as\n | Record<string, Record<string, unknown>>\n | undefined;\n if (storageRelations && Object.keys(storageRelations).length > 0) {\n const relEntries: string[] = [];\n for (const [relName, relVal] of Object.entries(storageRelations)) {\n relEntries.push(`readonly ${serializeObjectKey(relName)}: ${serializeValue(relVal)}`);\n }\n parts.push(`readonly relations: { ${relEntries.join('; ')} }`);\n }\n\n return parts.length > 0 ? `{ ${parts.join('; ')} }` : 'Record<string, never>';\n },\n\n // INTERIM (TML-2953): Mongo has no storage value-set entity yet, so a value-set field's permitted\n // values are sourced from `domain.enum`. This is the only remaining domain-enum typing reader in\n // the repo; TML-2953 adds Mongo's value set and deletes this, routing the field type through\n // storage. The values still flow through the codec seam (`renderValueLiteral`), so this keeps\n // Mongo's emitted field types byte-identical without re-introducing a direct render.\n resolveFieldValueSet(\n _modelName: string,\n fieldName: string,\n model: ContractModelBase,\n contract: Contract,\n ): { readonly encodedValues: readonly JsonValue[]; readonly codecId: string } | undefined {\n const field = model.fields[fieldName];\n const ref = field?.valueSet;\n if (ref?.entityKind !== 'enum') return undefined;\n const domainNs = contract.domain.namespaces[ref.namespaceId];\n const domainEnum = domainNs?.enum?.[ref.entityName];\n if (!domainEnum) return undefined;\n return {\n encodedValues: domainEnum.members.map((m) => m.value),\n codecId: domainEnum.codecId,\n };\n },\n\n getFamilyImports(): string[] {\n return [\n 'import type {',\n ' MongoCollection,',\n ' MongoContractWithTypeMaps,',\n ' MongoTypeMaps,',\n \"} from '@prisma-next/mongo-contract';\",\n ];\n },\n\n getFamilyTypeAliases(): string {\n return '';\n },\n\n getTypeMapsExpression(): string {\n return 'MongoTypeMaps<CodecTypes, FieldOutputTypes, FieldInputTypes>';\n },\n\n getContractWrapper(contractBaseName: string, typeMapsName: string): string {\n return `export type Contract = MongoContractWithTypeMaps<${contractBaseName}, ${typeMapsName}>;`;\n },\n};\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "name": "@prisma-next/mongo-emitter",
3
- "version": "0.14.0-dev.3",
3
+ "version": "0.14.0-dev.30",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "description": "Mongo emitter hook for Prisma Next",
8
8
  "dependencies": {
9
- "@prisma-next/contract": "0.14.0-dev.3",
10
- "@prisma-next/emitter": "0.14.0-dev.3",
11
- "@prisma-next/framework-components": "0.14.0-dev.3",
12
- "@prisma-next/mongo-contract": "0.14.0-dev.3"
9
+ "@prisma-next/contract": "0.14.0-dev.30",
10
+ "@prisma-next/emitter": "0.14.0-dev.30",
11
+ "@prisma-next/framework-components": "0.14.0-dev.30",
12
+ "@prisma-next/mongo-contract": "0.14.0-dev.30"
13
13
  },
14
14
  "devDependencies": {
15
- "@prisma-next/test-utils": "0.14.0-dev.3",
16
- "@prisma-next/tsconfig": "0.14.0-dev.3",
17
- "@prisma-next/tsdown": "0.14.0-dev.3",
15
+ "@prisma-next/test-utils": "0.14.0-dev.30",
16
+ "@prisma-next/tsconfig": "0.14.0-dev.30",
17
+ "@prisma-next/tsdown": "0.14.0-dev.30",
18
18
  "tsdown": "0.22.1",
19
19
  "typescript": "5.9.3",
20
20
  "vitest": "4.1.8"
package/src/index.ts CHANGED
@@ -1,4 +1,9 @@
1
- import type { Contract, ContractModel, ContractModelBase } from '@prisma-next/contract/types';
1
+ import type {
2
+ Contract,
3
+ ContractModel,
4
+ ContractModelBase,
5
+ JsonValue,
6
+ } from '@prisma-next/contract/types';
2
7
  import { serializeObjectKey, serializeValue } from '@prisma-next/emitter/domain-type-generation';
3
8
  import type { ValidationContext } from '@prisma-next/framework-components/emission';
4
9
  import type { Namespace } from '@prisma-next/framework-components/ir';
@@ -249,6 +254,29 @@ export const mongoEmission = {
249
254
  return parts.length > 0 ? `{ ${parts.join('; ')} }` : 'Record<string, never>';
250
255
  },
251
256
 
257
+ // INTERIM (TML-2953): Mongo has no storage value-set entity yet, so a value-set field's permitted
258
+ // values are sourced from `domain.enum`. This is the only remaining domain-enum typing reader in
259
+ // the repo; TML-2953 adds Mongo's value set and deletes this, routing the field type through
260
+ // storage. The values still flow through the codec seam (`renderValueLiteral`), so this keeps
261
+ // Mongo's emitted field types byte-identical without re-introducing a direct render.
262
+ resolveFieldValueSet(
263
+ _modelName: string,
264
+ fieldName: string,
265
+ model: ContractModelBase,
266
+ contract: Contract,
267
+ ): { readonly encodedValues: readonly JsonValue[]; readonly codecId: string } | undefined {
268
+ const field = model.fields[fieldName];
269
+ const ref = field?.valueSet;
270
+ if (ref?.entityKind !== 'enum') return undefined;
271
+ const domainNs = contract.domain.namespaces[ref.namespaceId];
272
+ const domainEnum = domainNs?.enum?.[ref.entityName];
273
+ if (!domainEnum) return undefined;
274
+ return {
275
+ encodedValues: domainEnum.members.map((m) => m.value),
276
+ codecId: domainEnum.codecId,
277
+ };
278
+ },
279
+
252
280
  getFamilyImports(): string[] {
253
281
  return [
254
282
  'import type {',