@prisma-next/mongo-emitter 0.11.0 → 0.12.0

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 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/index.ts"],"mappings":";;;;cA8Da,aAAA;;0BAGa,QAAA,EAAQ,IAAA,EAAQ,iBAAA;8BAuCZ,QAAA;gCAqGE,QAAA,EAAQ,mBAAA;+CAMK,KAAA,EAAS,aAAA;;;;+CAsCT,YAAA;AAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/index.ts"],"mappings":";;;;cAyEa,aAAA;;0BAGa,QAAA,EAAQ,IAAA,EAAQ,iBAAA;8BA6CZ,QAAA;gCAyGE,QAAA,EAAQ,mBAAA;+CAMK,KAAA,EAAS,aAAA;;;;+CAuCT,YAAA;AAAA"}
@@ -1,5 +1,11 @@
1
1
  import { serializeObjectKey, serializeValue } from "@prisma-next/emitter/domain-type-generation";
2
2
  //#region src/index.ts
3
+ const MONGO_NAMESPACE_KIND_FALLBACK = "mongo-namespace";
4
+ function mongoNamespaceSerializedKind(ns) {
5
+ const kind = ns.kind;
6
+ if (typeof kind === "string") return `readonly kind: ${serializeValue(kind)}`;
7
+ return `readonly kind: '${MONGO_NAMESPACE_KIND_FALLBACK}'`;
8
+ }
3
9
  function assertUniqueMongoCollectionNames(storage) {
4
10
  const seen = /* @__PURE__ */ new Map();
5
11
  for (const [namespaceId, ns] of Object.entries(storage.namespaces)) for (const coll of Object.keys(ns.collections)) {
@@ -9,7 +15,7 @@ function assertUniqueMongoCollectionNames(storage) {
9
15
  }
10
16
  }
11
17
  function generateMongoCollectionEntryType(coll) {
12
- if (Object.entries(coll).filter(([, v]) => v !== void 0).length === 0) return "Record<string, never>";
18
+ if (Object.entries(coll).filter(([key, v]) => v !== void 0 && key !== "kind").length === 0) return "MongoCollection";
13
19
  return serializeValue(coll);
14
20
  }
15
21
  function generateMongoNamespaceCollectionsType(collections) {
@@ -24,7 +30,7 @@ function generateMongoNamespacesType(namespaces) {
24
30
  const parts = [];
25
31
  for (const [name, ns] of sorted) {
26
32
  const collectionsType = generateMongoNamespaceCollectionsType(ns.collections);
27
- parts.push(`readonly ${serializeObjectKey(name)}: { readonly id: ${serializeValue(ns.id)}; readonly kind: ${serializeValue(ns.kind)}; readonly collections: ${collectionsType} }`);
33
+ parts.push(`readonly ${serializeObjectKey(name)}: { readonly id: ${serializeValue(ns.id)}; ${mongoNamespaceSerializedKind(ns)}; readonly collections: ${collectionsType} }`);
28
34
  }
29
35
  return `{ ${parts.join("; ")} }`;
30
36
  }
@@ -32,14 +38,20 @@ const mongoEmission = {
32
38
  id: "mongo",
33
39
  validateTypes(contract, _ctx) {
34
40
  const typeIdRegex = /^([^/]+)\/([^@]+)@(\d+)$/;
35
- for (const [modelName, model] of Object.entries(contract.models)) for (const [fieldName, field] of Object.entries(model.fields)) {
36
- const fieldType = field.type;
37
- if (!fieldType) continue;
38
- const scalarTypes = fieldType.kind === "scalar" ? [fieldType] : fieldType.kind === "union" && fieldType.members ? fieldType.members.filter((m) => m.kind === "scalar") : [];
39
- for (const scalarType of scalarTypes) {
40
- const { codecId } = scalarType;
41
- if (!codecId) throw new Error(`Field "${fieldName}" on model "${modelName}" is missing codecId`);
42
- if (!codecId.match(typeIdRegex)?.[1]) throw new Error(`Field "${fieldName}" on model "${modelName}" has invalid codec ID format "${codecId}". Expected format: ns/name@version`);
41
+ for (const [namespaceId, domainNs] of Object.entries(contract.domain.namespaces)) {
42
+ const models = domainNs.models;
43
+ for (const [modelName, model] of Object.entries(models)) {
44
+ const qualifiedName = `${namespaceId}:${modelName}`;
45
+ for (const [fieldName, field] of Object.entries(model.fields)) {
46
+ const fieldType = field.type;
47
+ if (!fieldType) continue;
48
+ const scalarTypes = fieldType.kind === "scalar" ? [fieldType] : fieldType.kind === "union" && fieldType.members ? fieldType.members.filter((m) => m.kind === "scalar") : [];
49
+ for (const scalarType of scalarTypes) {
50
+ const { codecId } = scalarType;
51
+ if (!codecId) throw new Error(`Field "${fieldName}" on model "${qualifiedName}" is missing codecId`);
52
+ if (!codecId.match(typeIdRegex)?.[1]) throw new Error(`Field "${fieldName}" on model "${qualifiedName}" has invalid codec ID format "${codecId}". Expected format: ns/name@version`);
53
+ }
54
+ }
43
55
  }
44
56
  }
45
57
  },
@@ -50,35 +62,38 @@ const mongoEmission = {
50
62
  assertUniqueMongoCollectionNames(storage);
51
63
  const collectionNames = /* @__PURE__ */ new Set();
52
64
  for (const ns of Object.values(storage.namespaces)) for (const c of Object.keys(ns.collections)) collectionNames.add(c);
53
- const models = contract.models;
54
- if (!models || Object.keys(models).length === 0) return;
55
- for (const [modelName, model] of Object.entries(models)) {
56
- if (!model.fields || typeof model.fields !== "object") throw new Error(`Model "${modelName}" is missing required field "fields"`);
57
- if (!model.relations || typeof model.relations !== "object") throw new Error(`Model "${modelName}" is missing required field "relations" (must be an object)`);
58
- if (!model.storage || typeof model.storage !== "object") throw new Error(`Model "${modelName}" is missing required field "storage" (must be an object)`);
59
- const collectionValue = model.storage["collection"];
60
- const collection = typeof collectionValue === "string" ? collectionValue : void 0;
61
- if (model.owner) {
62
- if (collection) throw new Error(`Owned model "${modelName}" must not have storage.collection (embedded models are stored within their owner)`);
63
- if (!models[model.owner]) throw new Error(`Model "${modelName}" declares owner "${model.owner}" which does not exist in models`);
64
- } else if (collection) {
65
- if (!collectionNames.has(collection)) throw new Error(`Model "${modelName}" references collection "${collection}" which is not in storage.namespaces[..].collections`);
66
- }
67
- if (model.base) {
68
- const baseModel = models[model.base];
69
- if (!baseModel) throw new Error(`Model "${modelName}" declares base "${model.base}" which does not exist in models`);
70
- const variantCollection = collection;
71
- const baseCollection = baseModel.storage["collection"];
72
- if (variantCollection !== baseCollection) throw new Error(`Variant "${modelName}" must share its base's collection ("${baseCollection ?? "(none)"}"), but has "${variantCollection ?? "(none)"}"`);
73
- }
74
- const storageRelations = model.storage["relations"];
75
- if (storageRelations) {
76
- for (const relName of Object.keys(storageRelations)) if (!model.relations[relName]) throw new Error(`Model "${modelName}" has storage.relations.${relName} but no matching domain-level relation`);
77
- }
78
- for (const [relName, rel] of Object.entries(model.relations)) {
79
- const targetModelName = rel["to"];
80
- if (targetModelName) {
81
- if (models[targetModelName]?.owner === modelName && !storageRelations?.[relName]) throw new Error(`Model "${modelName}" has embed relation "${relName}" to owned model "${targetModelName}" but no matching storage.relations entry`);
65
+ for (const [namespaceId, domainNs] of Object.entries(contract.domain.namespaces)) {
66
+ const models = domainNs.models;
67
+ if (Object.keys(models).length === 0) continue;
68
+ for (const [modelName, model] of Object.entries(models)) {
69
+ const qualifiedName = `${namespaceId}:${modelName}`;
70
+ if (!model.fields || typeof model.fields !== "object") throw new Error(`Model "${qualifiedName}" is missing required field "fields"`);
71
+ if (!model.relations || typeof model.relations !== "object") throw new Error(`Model "${qualifiedName}" is missing required field "relations" (must be an object)`);
72
+ if (!model.storage || typeof model.storage !== "object") throw new Error(`Model "${qualifiedName}" is missing required field "storage" (must be an object)`);
73
+ const collectionValue = model.storage["collection"];
74
+ const collection = typeof collectionValue === "string" ? collectionValue : void 0;
75
+ if (model.owner) {
76
+ if (collection) throw new Error(`Owned model "${qualifiedName}" must not have storage.collection (embedded models are stored within their owner)`);
77
+ if (!models[model.owner]) throw new Error(`Model "${qualifiedName}" declares owner "${model.owner}" which does not exist in models`);
78
+ } else if (collection) {
79
+ if (!collectionNames.has(collection)) throw new Error(`Model "${qualifiedName}" references collection "${collection}" which is not in storage.namespaces[..].collections`);
80
+ }
81
+ if (model.base) {
82
+ const baseModel = models[model.base.model];
83
+ if (!baseModel) throw new Error(`Model "${qualifiedName}" declares base "${model.base.namespace}:${model.base.model}" which does not exist in models`);
84
+ const variantCollection = collection;
85
+ const baseCollection = baseModel.storage["collection"];
86
+ if (variantCollection !== baseCollection) throw new Error(`Variant "${qualifiedName}" must share its base's collection ("${baseCollection ?? "(none)"}"), but has "${variantCollection ?? "(none)"}"`);
87
+ }
88
+ const storageRelations = model.storage["relations"];
89
+ if (storageRelations) {
90
+ for (const relName of Object.keys(storageRelations)) if (!model.relations[relName]) throw new Error(`Model "${qualifiedName}" has storage.relations.${relName} but no matching domain-level relation`);
91
+ }
92
+ for (const [relName, rel] of Object.entries(model.relations)) {
93
+ const targetModelName = rel["to"]?.model;
94
+ if (targetModelName) {
95
+ if (models[targetModelName]?.owner === modelName && !storageRelations?.[relName]) throw new Error(`Model "${qualifiedName}" has embed relation "${relName}" to owned model "${targetModelName}" but no matching storage.relations entry`);
96
+ }
82
97
  }
83
98
  }
84
99
  }
@@ -102,6 +117,7 @@ const mongoEmission = {
102
117
  getFamilyImports() {
103
118
  return [
104
119
  "import type {",
120
+ " MongoCollection,",
105
121
  " MongoContractWithTypeMaps,",
106
122
  " MongoTypeMaps,",
107
123
  "} from '@prisma-next/mongo-contract';"
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["relObj"],"sources":["../../src/index.ts"],"sourcesContent":["import type { Contract, ContractModel } 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 { MongoCollection, MongoStorage } from '@prisma-next/mongo-contract';\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.collections)) {\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(([, v]) => v !== undefined);\n if (entries.length === 0) {\n return 'Record<string, never>';\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(\n ns.collections as Readonly<Record<string, MongoCollection>>,\n );\n parts.push(\n `readonly ${serializeObjectKey(name)}: { readonly id: ${serializeValue(ns.id)}; readonly kind: ${serializeValue(ns.kind)}; readonly collections: ${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 [modelName, model] of Object.entries(contract.models)) {\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(`Field \"${fieldName}\" on model \"${modelName}\" is missing codecId`);\n }\n const match = codecId.match(typeIdRegex);\n if (!match?.[1]) {\n throw new Error(\n `Field \"${fieldName}\" on model \"${modelName}\" has invalid codec ID format \"${codecId}\". Expected format: ns/name@version`,\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.collections)) {\n collectionNames.add(c);\n }\n }\n\n const models = contract.models;\n if (!models || Object.keys(models).length === 0) return;\n\n for (const [modelName, model] of Object.entries(models)) {\n if (!model.fields || typeof model.fields !== 'object') {\n throw new Error(`Model \"${modelName}\" is missing required field \"fields\"`);\n }\n if (!model.relations || typeof model.relations !== 'object') {\n throw new Error(\n `Model \"${modelName}\" 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 \"${modelName}\" 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 \"${modelName}\" 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 \"${modelName}\" 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 \"${modelName}\" references collection \"${collection}\" which is not in storage.namespaces[..].collections`,\n );\n }\n }\n\n if (model.base) {\n const baseModel = models[model.base];\n if (!baseModel) {\n throw new Error(\n `Model \"${modelName}\" declares base \"${model.base}\" 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 \"${modelName}\" 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 \"${modelName}\" 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 targetModelName = relObj['to'] as string | undefined;\n if (targetModelName) {\n const targetModel = models[targetModelName];\n if (targetModel?.owner === modelName && !storageRelations?.[relName]) {\n throw new Error(\n `Model \"${modelName}\" has embed relation \"${relName}\" to owned model \"${targetModelName}\" but no matching storage.relations entry`,\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: ContractModel): 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 ' 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 { Contract, ContractModel } 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.collections)) {\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(\n ns.collections as Readonly<Record<string, MongoCollection>>,\n );\n parts.push(\n `readonly ${serializeObjectKey(name)}: { readonly id: ${serializeValue(ns.id)}; ${mongoNamespaceSerializedKind(ns)}; readonly collections: ${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.collections)) {\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[..].collections`,\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: ContractModel): 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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"}
package/package.json CHANGED
@@ -1,24 +1,32 @@
1
1
  {
2
2
  "name": "@prisma-next/mongo-emitter",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
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.11.0",
10
- "@prisma-next/emitter": "0.11.0",
11
- "@prisma-next/framework-components": "0.11.0",
12
- "@prisma-next/mongo-contract": "0.11.0"
9
+ "@prisma-next/contract": "0.12.0",
10
+ "@prisma-next/emitter": "0.12.0",
11
+ "@prisma-next/framework-components": "0.12.0",
12
+ "@prisma-next/mongo-contract": "0.12.0"
13
13
  },
14
14
  "devDependencies": {
15
- "@prisma-next/test-utils": "0.11.0",
16
- "@prisma-next/tsconfig": "0.11.0",
17
- "@prisma-next/tsdown": "0.11.0",
15
+ "@prisma-next/test-utils": "0.12.0",
16
+ "@prisma-next/tsconfig": "0.12.0",
17
+ "@prisma-next/tsdown": "0.12.0",
18
18
  "tsdown": "0.22.0",
19
19
  "typescript": "5.9.3",
20
20
  "vitest": "4.1.6"
21
21
  },
22
+ "peerDependencies": {
23
+ "typescript": ">=5.9"
24
+ },
25
+ "peerDependenciesMeta": {
26
+ "typescript": {
27
+ "optional": true
28
+ }
29
+ },
22
30
  "files": [
23
31
  "dist",
24
32
  "src"
@@ -30,6 +38,9 @@
30
38
  },
31
39
  "./package.json": "./package.json"
32
40
  },
41
+ "engines": {
42
+ "node": ">=24"
43
+ },
33
44
  "repository": {
34
45
  "type": "git",
35
46
  "url": "https://github.com/prisma/prisma-next.git",
package/src/index.ts CHANGED
@@ -1,8 +1,19 @@
1
1
  import type { Contract, ContractModel } from '@prisma-next/contract/types';
2
2
  import { serializeObjectKey, serializeValue } from '@prisma-next/emitter/domain-type-generation';
3
3
  import type { ValidationContext } from '@prisma-next/framework-components/emission';
4
+ import type { Namespace } from '@prisma-next/framework-components/ir';
4
5
  import type { MongoCollection, MongoStorage } from '@prisma-next/mongo-contract';
5
6
 
7
+ const MONGO_NAMESPACE_KIND_FALLBACK = 'mongo-namespace' as const;
8
+
9
+ function mongoNamespaceSerializedKind(ns: Namespace): string {
10
+ const kind = (ns as { kind?: unknown }).kind;
11
+ if (typeof kind === 'string') {
12
+ return `readonly kind: ${serializeValue(kind)}`;
13
+ }
14
+ return `readonly kind: '${MONGO_NAMESPACE_KIND_FALLBACK}'`;
15
+ }
16
+
6
17
  function assertUniqueMongoCollectionNames(storage: MongoStorage): void {
7
18
  const seen = new Map<string, string>();
8
19
  for (const [namespaceId, ns] of Object.entries(storage.namespaces)) {
@@ -19,9 +30,9 @@ function assertUniqueMongoCollectionNames(storage: MongoStorage): void {
19
30
  }
20
31
 
21
32
  function generateMongoCollectionEntryType(coll: MongoCollection): string {
22
- const entries = Object.entries(coll).filter(([, v]) => v !== undefined);
33
+ const entries = Object.entries(coll).filter(([key, v]) => v !== undefined && key !== 'kind');
23
34
  if (entries.length === 0) {
24
- return 'Record<string, never>';
35
+ return 'MongoCollection';
25
36
  }
26
37
  return serializeValue(coll);
27
38
  }
@@ -54,7 +65,7 @@ function generateMongoNamespacesType(namespaces: MongoStorage['namespaces']): st
54
65
  ns.collections as Readonly<Record<string, MongoCollection>>,
55
66
  );
56
67
  parts.push(
57
- `readonly ${serializeObjectKey(name)}: { readonly id: ${serializeValue(ns.id)}; readonly kind: ${serializeValue(ns.kind)}; readonly collections: ${collectionsType} }`,
68
+ `readonly ${serializeObjectKey(name)}: { readonly id: ${serializeValue(ns.id)}; ${mongoNamespaceSerializedKind(ns)}; readonly collections: ${collectionsType} }`,
58
69
  );
59
70
  }
60
71
  return `{ ${parts.join('; ')} }`;
@@ -66,36 +77,42 @@ export const mongoEmission = {
66
77
  validateTypes(contract: Contract, _ctx: ValidationContext): void {
67
78
  const typeIdRegex = /^([^/]+)\/([^@]+)@(\d+)$/;
68
79
 
69
- for (const [modelName, model] of Object.entries(contract.models)) {
70
- for (const [fieldName, field] of Object.entries(model.fields)) {
71
- const fieldType = (
72
- field as {
73
- type?: {
74
- kind: string;
75
- codecId?: string;
76
- members?: ReadonlyArray<{ kind: string; codecId?: string }>;
77
- };
78
- }
79
- ).type;
80
- if (!fieldType) continue;
81
-
82
- const scalarTypes: Array<{ codecId?: string }> =
83
- fieldType.kind === 'scalar'
84
- ? [fieldType]
85
- : fieldType.kind === 'union' && fieldType.members
86
- ? fieldType.members.filter((m) => m.kind === 'scalar')
87
- : [];
88
-
89
- for (const scalarType of scalarTypes) {
90
- const { codecId } = scalarType;
91
- if (!codecId) {
92
- throw new Error(`Field "${fieldName}" on model "${modelName}" is missing codecId`);
93
- }
94
- const match = codecId.match(typeIdRegex);
95
- if (!match?.[1]) {
96
- throw new Error(
97
- `Field "${fieldName}" on model "${modelName}" has invalid codec ID format "${codecId}". Expected format: ns/name@version`,
98
- );
80
+ for (const [namespaceId, domainNs] of Object.entries(contract.domain.namespaces)) {
81
+ const models = domainNs.models as Record<string, ContractModel>;
82
+ for (const [modelName, model] of Object.entries(models)) {
83
+ const qualifiedName = `${namespaceId}:${modelName}`;
84
+ for (const [fieldName, field] of Object.entries(model.fields)) {
85
+ const fieldType = (
86
+ field as {
87
+ type?: {
88
+ kind: string;
89
+ codecId?: string;
90
+ members?: ReadonlyArray<{ kind: string; codecId?: string }>;
91
+ };
92
+ }
93
+ ).type;
94
+ if (!fieldType) continue;
95
+
96
+ const scalarTypes: Array<{ codecId?: string }> =
97
+ fieldType.kind === 'scalar'
98
+ ? [fieldType]
99
+ : fieldType.kind === 'union' && fieldType.members
100
+ ? fieldType.members.filter((m) => m.kind === 'scalar')
101
+ : [];
102
+
103
+ for (const scalarType of scalarTypes) {
104
+ const { codecId } = scalarType;
105
+ if (!codecId) {
106
+ throw new Error(
107
+ `Field "${fieldName}" on model "${qualifiedName}" is missing codecId`,
108
+ );
109
+ }
110
+ const match = codecId.match(typeIdRegex);
111
+ if (!match?.[1]) {
112
+ throw new Error(
113
+ `Field "${fieldName}" on model "${qualifiedName}" has invalid codec ID format "${codecId}". Expected format: ns/name@version`,
114
+ );
115
+ }
99
116
  }
100
117
  }
101
118
  }
@@ -121,84 +138,88 @@ export const mongoEmission = {
121
138
  }
122
139
  }
123
140
 
124
- const models = contract.models;
125
- if (!models || Object.keys(models).length === 0) return;
141
+ for (const [namespaceId, domainNs] of Object.entries(contract.domain.namespaces)) {
142
+ const models = domainNs.models as Record<string, ContractModel>;
143
+ if (Object.keys(models).length === 0) continue;
126
144
 
127
- for (const [modelName, model] of Object.entries(models)) {
128
- if (!model.fields || typeof model.fields !== 'object') {
129
- throw new Error(`Model "${modelName}" is missing required field "fields"`);
130
- }
131
- if (!model.relations || typeof model.relations !== 'object') {
132
- throw new Error(
133
- `Model "${modelName}" is missing required field "relations" (must be an object)`,
134
- );
135
- }
136
- if (!model.storage || typeof model.storage !== 'object') {
137
- throw new Error(
138
- `Model "${modelName}" is missing required field "storage" (must be an object)`,
139
- );
140
- }
141
-
142
- const collectionValue = model.storage['collection'];
143
- const collection = typeof collectionValue === 'string' ? collectionValue : undefined;
144
-
145
- if (model.owner) {
146
- if (collection) {
147
- throw new Error(
148
- `Owned model "${modelName}" must not have storage.collection (embedded models are stored within their owner)`,
149
- );
145
+ for (const [modelName, model] of Object.entries(models)) {
146
+ const qualifiedName = `${namespaceId}:${modelName}`;
147
+ if (!model.fields || typeof model.fields !== 'object') {
148
+ throw new Error(`Model "${qualifiedName}" is missing required field "fields"`);
150
149
  }
151
- if (!models[model.owner]) {
150
+ if (!model.relations || typeof model.relations !== 'object') {
152
151
  throw new Error(
153
- `Model "${modelName}" declares owner "${model.owner}" which does not exist in models`,
152
+ `Model "${qualifiedName}" is missing required field "relations" (must be an object)`,
154
153
  );
155
154
  }
156
- } else if (collection) {
157
- if (!collectionNames.has(collection)) {
155
+ if (!model.storage || typeof model.storage !== 'object') {
158
156
  throw new Error(
159
- `Model "${modelName}" references collection "${collection}" which is not in storage.namespaces[..].collections`,
157
+ `Model "${qualifiedName}" is missing required field "storage" (must be an object)`,
160
158
  );
161
159
  }
162
- }
163
160
 
164
- if (model.base) {
165
- const baseModel = models[model.base];
166
- if (!baseModel) {
167
- throw new Error(
168
- `Model "${modelName}" declares base "${model.base}" which does not exist in models`,
169
- );
170
- }
171
- const variantCollection = collection;
172
- const baseCollection = baseModel.storage['collection'] as string | undefined;
173
- if (variantCollection !== baseCollection) {
174
- throw new Error(
175
- `Variant "${modelName}" must share its base's collection ("${baseCollection ?? '(none)'}"), but has "${variantCollection ?? '(none)'}"`,
176
- );
177
- }
178
- }
161
+ const collectionValue = model.storage['collection'];
162
+ const collection = typeof collectionValue === 'string' ? collectionValue : undefined;
179
163
 
180
- const storageRelations = model.storage['relations'] as Record<string, unknown> | undefined;
181
- if (storageRelations) {
182
- for (const relName of Object.keys(storageRelations)) {
183
- if (!model.relations[relName]) {
164
+ if (model.owner) {
165
+ if (collection) {
166
+ throw new Error(
167
+ `Owned model "${qualifiedName}" must not have storage.collection (embedded models are stored within their owner)`,
168
+ );
169
+ }
170
+ if (!models[model.owner]) {
184
171
  throw new Error(
185
- `Model "${modelName}" has storage.relations.${relName} but no matching domain-level relation`,
172
+ `Model "${qualifiedName}" declares owner "${model.owner}" which does not exist in models`,
173
+ );
174
+ }
175
+ } else if (collection) {
176
+ if (!collectionNames.has(collection)) {
177
+ throw new Error(
178
+ `Model "${qualifiedName}" references collection "${collection}" which is not in storage.namespaces[..].collections`,
186
179
  );
187
180
  }
188
181
  }
189
- }
190
182
 
191
- for (const [relName, rel] of Object.entries(model.relations)) {
192
- const relObj = rel as Record<string, unknown>;
193
- const targetModelName = relObj['to'] as string | undefined;
194
- if (targetModelName) {
195
- const targetModel = models[targetModelName];
196
- if (targetModel?.owner === modelName && !storageRelations?.[relName]) {
183
+ if (model.base) {
184
+ const baseModel = models[model.base.model];
185
+ if (!baseModel) {
186
+ throw new Error(
187
+ `Model "${qualifiedName}" declares base "${model.base.namespace}:${model.base.model}" which does not exist in models`,
188
+ );
189
+ }
190
+ const variantCollection = collection;
191
+ const baseCollection = baseModel.storage['collection'] as string | undefined;
192
+ if (variantCollection !== baseCollection) {
197
193
  throw new Error(
198
- `Model "${modelName}" has embed relation "${relName}" to owned model "${targetModelName}" but no matching storage.relations entry`,
194
+ `Variant "${qualifiedName}" must share its base's collection ("${baseCollection ?? '(none)'}"), but has "${variantCollection ?? '(none)'}"`,
199
195
  );
200
196
  }
201
197
  }
198
+
199
+ const storageRelations = model.storage['relations'] as Record<string, unknown> | undefined;
200
+ if (storageRelations) {
201
+ for (const relName of Object.keys(storageRelations)) {
202
+ if (!model.relations[relName]) {
203
+ throw new Error(
204
+ `Model "${qualifiedName}" has storage.relations.${relName} but no matching domain-level relation`,
205
+ );
206
+ }
207
+ }
208
+ }
209
+
210
+ for (const [relName, rel] of Object.entries(model.relations)) {
211
+ const relObj = rel as Record<string, unknown>;
212
+ const targetRef = relObj['to'] as { readonly model?: string } | undefined;
213
+ const targetModelName = targetRef?.model;
214
+ if (targetModelName) {
215
+ const targetModel = models[targetModelName];
216
+ if (targetModel?.owner === modelName && !storageRelations?.[relName]) {
217
+ throw new Error(
218
+ `Model "${qualifiedName}" has embed relation "${relName}" to owned model "${targetModelName}" but no matching storage.relations entry`,
219
+ );
220
+ }
221
+ }
222
+ }
202
223
  }
203
224
  }
204
225
  },
@@ -233,6 +254,7 @@ export const mongoEmission = {
233
254
  getFamilyImports(): string[] {
234
255
  return [
235
256
  'import type {',
257
+ ' MongoCollection,',
236
258
  ' MongoContractWithTypeMaps,',
237
259
  ' MongoTypeMaps,',
238
260
  "} from '@prisma-next/mongo-contract';",