@prisma-next/mongo-contract 0.13.0-dev.4 → 0.13.0-dev.40
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.
- package/dist/entity-kinds-C9KNr9IE.mjs +572 -0
- package/dist/entity-kinds-C9KNr9IE.mjs.map +1 -0
- package/dist/entity-kinds.d.mts +16 -0
- package/dist/entity-kinds.d.mts.map +1 -0
- package/dist/entity-kinds.mjs +2 -0
- package/dist/index.d.mts +9 -526
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +18 -563
- package/dist/index.mjs.map +1 -1
- package/dist/mongo-collection-BCi-Bq_a.d.mts +530 -0
- package/dist/mongo-collection-BCi-Bq_a.d.mts.map +1 -0
- package/package.json +11 -8
- package/src/contract-schema.ts +1 -1
- package/src/contract-types.ts +41 -18
- package/src/entity-kinds.ts +34 -0
- package/src/exports/entity-kinds.ts +1 -0
- package/src/exports/index.ts +4 -2
- package/src/ir/build-mongo-namespace.ts +31 -55
- package/src/ir/mongo-index.ts +3 -2
- package/src/ir/mongo-storage.ts +6 -11
- package/src/ir/mongo-unbound-namespace.ts +4 -4
- package/src/validate-storage.ts +1 -1
package/src/contract-types.ts
CHANGED
|
@@ -2,13 +2,11 @@ import type {
|
|
|
2
2
|
Contract,
|
|
3
3
|
ContractField,
|
|
4
4
|
ContractModel,
|
|
5
|
-
ContractModelDefinitions,
|
|
6
5
|
ContractValueObject,
|
|
7
6
|
ContractValueObjectDefinitions,
|
|
8
7
|
StorageBase,
|
|
9
8
|
} from '@prisma-next/contract/types';
|
|
10
|
-
import type { Namespace } from '@prisma-next/framework-components/ir';
|
|
11
|
-
import type { MongoCollection } from './ir/mongo-collection';
|
|
9
|
+
import type { Namespace, UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
|
|
12
10
|
import type { MongoIndexOptionsInput } from './ir/mongo-index-options';
|
|
13
11
|
|
|
14
12
|
export type MongoIndexFieldValue = 1 | -1 | 'text' | '2dsphere' | '2d' | 'hashed';
|
|
@@ -59,30 +57,38 @@ export type MongoModelDefinition = ContractModel<MongoModelStorage>;
|
|
|
59
57
|
* `namespaces` field) or a fully-constructed class instance (with
|
|
60
58
|
* `namespaces`). The class structurally satisfies this shape.
|
|
61
59
|
*/
|
|
60
|
+
import type { MongoCollection } from './ir/mongo-collection';
|
|
61
|
+
|
|
62
|
+
type MongoNamespaceEntries = Readonly<Record<string, Readonly<Record<string, unknown>>>> & {
|
|
63
|
+
readonly collection?: Readonly<Record<string, MongoCollection>>;
|
|
64
|
+
};
|
|
65
|
+
|
|
62
66
|
export type MongoStorageShape<THash extends string = string> = StorageBase<THash> & {
|
|
63
67
|
readonly namespaces: Record<
|
|
64
68
|
string,
|
|
65
69
|
Namespace & {
|
|
66
|
-
readonly entries:
|
|
67
|
-
readonly collection: Readonly<Record<string, MongoCollection>>;
|
|
68
|
-
}>;
|
|
70
|
+
readonly entries: MongoNamespaceEntries;
|
|
69
71
|
}
|
|
70
72
|
>;
|
|
71
73
|
};
|
|
72
74
|
|
|
73
|
-
export type MongoContract<
|
|
74
|
-
S extends MongoStorageShape = MongoStorageShape,
|
|
75
|
-
M extends Record<string, MongoModelDefinition> = Record<string, MongoModelDefinition>,
|
|
76
|
-
> = Contract<S, M>;
|
|
75
|
+
export type MongoContract<S extends MongoStorageShape = MongoStorageShape> = Contract<S>;
|
|
77
76
|
|
|
78
|
-
/**
|
|
79
|
-
|
|
77
|
+
/**
|
|
78
|
+
* Model map for the contract's sole (unbound) domain namespace. Mongo is
|
|
79
|
+
* structurally single-namespace, so its models live under
|
|
80
|
+
* {@link UNBOUND_NAMESPACE_ID} rather than in a flat cross-namespace union.
|
|
81
|
+
* Every Mongo type that needs the model map reads it through here, so none
|
|
82
|
+
* indexes the contract's namespaces directly.
|
|
83
|
+
*/
|
|
84
|
+
export type MongoModelsMap<TContract extends MongoContract> =
|
|
85
|
+
TContract['domain']['namespaces'][typeof UNBOUND_NAMESPACE_ID]['models'];
|
|
80
86
|
|
|
81
87
|
export type RootModelName<
|
|
82
88
|
TContract extends MongoContract,
|
|
83
89
|
RootName extends keyof TContract['roots'] & string,
|
|
84
90
|
> = TContract['roots'][RootName] extends { readonly model: infer M extends string }
|
|
85
|
-
? M & keyof
|
|
91
|
+
? M & keyof MongoModelsMap<TContract>
|
|
86
92
|
: never;
|
|
87
93
|
|
|
88
94
|
export type MongoTypeMaps<
|
|
@@ -132,6 +138,26 @@ export type ExtractMongoFieldInputTypes<T> =
|
|
|
132
138
|
: Record<string, never>
|
|
133
139
|
: Record<string, never>;
|
|
134
140
|
|
|
141
|
+
/**
|
|
142
|
+
* The per-model field-output map at the contract's unbound namespace. The
|
|
143
|
+
* framework emitter nests `FieldOutputTypes` by namespace id
|
|
144
|
+
* (`{ [ns]: { [model]: { [field]: <refined> } } }`); Mongo is structurally
|
|
145
|
+
* single-namespace, so its refined rows resolve the per-model map under
|
|
146
|
+
* {@link UNBOUND_NAMESPACE_ID}. A map without that coordinate (e.g. a contract
|
|
147
|
+
* carrying no type maps) resolves to `never`, which the row resolvers read as
|
|
148
|
+
* "no refined map" and fall back to codec-based inference.
|
|
149
|
+
*/
|
|
150
|
+
export type MongoUnboundFieldOutputTypes<T> =
|
|
151
|
+
ExtractMongoFieldOutputTypes<T> extends Record<typeof UNBOUND_NAMESPACE_ID, infer Inner>
|
|
152
|
+
? Inner
|
|
153
|
+
: never;
|
|
154
|
+
|
|
155
|
+
/** Input-side counterpart of {@link MongoUnboundFieldOutputTypes}. */
|
|
156
|
+
export type MongoUnboundFieldInputTypes<T> =
|
|
157
|
+
ExtractMongoFieldInputTypes<T> extends Record<typeof UNBOUND_NAMESPACE_ID, infer Inner>
|
|
158
|
+
? Inner
|
|
159
|
+
: never;
|
|
160
|
+
|
|
135
161
|
type ExtractValueObjects<TContract extends Contract> = ContractValueObjectDefinitions<TContract>;
|
|
136
162
|
|
|
137
163
|
type NormalizeContractFields<TFields> = {
|
|
@@ -184,11 +210,8 @@ type InferFieldType<
|
|
|
184
210
|
|
|
185
211
|
export type InferModelRow<
|
|
186
212
|
TContract extends MongoContractWithTypeMaps<MongoContract, MongoTypeMaps>,
|
|
187
|
-
ModelName extends string & keyof
|
|
188
|
-
TFields extends Record<
|
|
189
|
-
string,
|
|
190
|
-
ContractField
|
|
191
|
-
> = ContractModelDefinitions<TContract>[ModelName]['fields'],
|
|
213
|
+
ModelName extends string & keyof MongoModelsMap<TContract>,
|
|
214
|
+
TFields extends Record<string, ContractField> = MongoModelsMap<TContract>[ModelName]['fields'],
|
|
192
215
|
TCodecTypes extends Record<string, { output: unknown }> = ExtractMongoCodecTypes<TContract>,
|
|
193
216
|
TValueObjects extends Record<string, ContractValueObject> = ExtractValueObjects<TContract>,
|
|
194
217
|
> = {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AnyEntityKindDescriptor,
|
|
3
|
+
EntityKindDescriptor,
|
|
4
|
+
} from '@prisma-next/framework-components/ir';
|
|
5
|
+
import { StorageCollectionSchema } from './contract-schema';
|
|
6
|
+
import { MongoCollection, type MongoCollectionInput } from './ir/mongo-collection';
|
|
7
|
+
|
|
8
|
+
export const collectionEntityKind: EntityKindDescriptor<MongoCollectionInput, MongoCollection> = {
|
|
9
|
+
kind: 'collection',
|
|
10
|
+
schema: StorageCollectionSchema,
|
|
11
|
+
construct: (input) => new MongoCollection(input),
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Assembles the `kind → descriptor` registry for Mongo namespaces: the built-in
|
|
16
|
+
* `collection` kind plus any target `packKinds`. This builds the lookup table —
|
|
17
|
+
* it does not touch contract data. `hydrateNamespaceEntities` later consumes
|
|
18
|
+
* this registry to turn a namespace's raw entries into IR instances. Throws on
|
|
19
|
+
* a duplicate kind.
|
|
20
|
+
*/
|
|
21
|
+
export function composeMongoEntityKinds(
|
|
22
|
+
packKinds: readonly AnyEntityKindDescriptor[] = [],
|
|
23
|
+
): ReadonlyMap<string, AnyEntityKindDescriptor> {
|
|
24
|
+
const kinds = new Map<string, AnyEntityKindDescriptor>([['collection', collectionEntityKind]]);
|
|
25
|
+
for (const descriptor of packKinds) {
|
|
26
|
+
if (kinds.has(descriptor.kind)) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
`composeMongoEntityKinds: duplicate entity kind "${descriptor.kind}" — each kind may be registered only once`,
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
kinds.set(descriptor.kind, descriptor);
|
|
32
|
+
}
|
|
33
|
+
return kinds;
|
|
34
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { collectionEntityKind, composeMongoEntityKinds } from '../entity-kinds';
|
package/src/exports/index.ts
CHANGED
|
@@ -25,6 +25,8 @@ export type {
|
|
|
25
25
|
MongoStorageShape,
|
|
26
26
|
MongoTypeMaps,
|
|
27
27
|
MongoTypeMapsPhantomKey,
|
|
28
|
+
MongoUnboundFieldInputTypes,
|
|
29
|
+
MongoUnboundFieldOutputTypes,
|
|
28
30
|
MongoWildcardProjection,
|
|
29
31
|
RootModelName,
|
|
30
32
|
} from '../contract-types';
|
|
@@ -32,7 +34,7 @@ export {
|
|
|
32
34
|
defaultMongoDomainNamespaceId,
|
|
33
35
|
defaultMongoStorageNamespaceId,
|
|
34
36
|
} from '../default-namespace';
|
|
35
|
-
export { buildMongoNamespace
|
|
37
|
+
export { buildMongoNamespace } from '../ir/build-mongo-namespace';
|
|
36
38
|
export type { MongoChangeStreamPreAndPostImagesOptionsInput } from '../ir/mongo-change-stream-pre-and-post-images-options';
|
|
37
39
|
export { MongoChangeStreamPreAndPostImagesOptions } from '../ir/mongo-change-stream-pre-and-post-images-options';
|
|
38
40
|
export type {
|
|
@@ -63,7 +65,7 @@ export type { MongoIndexOptionDefaultsInput } from '../ir/mongo-index-option-def
|
|
|
63
65
|
export { MongoIndexOptionDefaults } from '../ir/mongo-index-option-defaults';
|
|
64
66
|
export type { MongoIndexOptionsInput } from '../ir/mongo-index-options';
|
|
65
67
|
export { MongoIndexOptions } from '../ir/mongo-index-options';
|
|
66
|
-
export type { MongoStorageInput } from '../ir/mongo-storage';
|
|
68
|
+
export type { MongoNamespace, MongoNamespaceEntries, MongoStorageInput } from '../ir/mongo-storage';
|
|
67
69
|
export { MongoStorage } from '../ir/mongo-storage';
|
|
68
70
|
export type {
|
|
69
71
|
MongoTimeSeriesCollectionOptionsInput,
|
|
@@ -1,58 +1,51 @@
|
|
|
1
1
|
import {
|
|
2
2
|
freezeNode,
|
|
3
|
-
|
|
3
|
+
hydrateNamespaceEntities,
|
|
4
4
|
NamespaceBase,
|
|
5
5
|
UNBOUND_NAMESPACE_ID,
|
|
6
6
|
} from '@prisma-next/framework-components/ir';
|
|
7
|
-
import { blindCast
|
|
8
|
-
import {
|
|
9
|
-
import type {
|
|
7
|
+
import { blindCast } from '@prisma-next/utils/casts';
|
|
8
|
+
import { composeMongoEntityKinds } from '../entity-kinds';
|
|
9
|
+
import type { MongoCollection } from './mongo-collection';
|
|
10
|
+
import type {
|
|
11
|
+
MongoNamespace,
|
|
12
|
+
MongoNamespaceCollectionsInput,
|
|
13
|
+
MongoNamespaceEntries,
|
|
14
|
+
} from './mongo-storage';
|
|
10
15
|
import { MongoUnboundNamespace } from './mongo-unbound-namespace';
|
|
11
16
|
|
|
12
17
|
const MONGO_NAMESPACE_KIND = 'mongo-namespace' as const;
|
|
13
18
|
|
|
14
|
-
function isMaterializedMongoNamespace(
|
|
15
|
-
ns: Namespace | MongoNamespaceCollectionsInput,
|
|
16
|
-
): ns is MongoNamespace {
|
|
17
|
-
if (typeof ns !== 'object' || ns === null) {
|
|
18
|
-
return false;
|
|
19
|
-
}
|
|
20
|
-
const proto = Object.getPrototypeOf(ns);
|
|
21
|
-
if (proto === Object.prototype || proto === null) {
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
24
|
-
return (ns as Namespace).kind === MONGO_NAMESPACE_KIND;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
19
|
class MongoBoundNamespace extends NamespaceBase {
|
|
28
20
|
declare readonly kind: string;
|
|
29
21
|
|
|
30
22
|
readonly id: string;
|
|
31
|
-
readonly entries:
|
|
32
|
-
readonly collection: Readonly<Record<string, MongoCollection>>;
|
|
33
|
-
}>;
|
|
23
|
+
readonly entries: MongoNamespaceEntries;
|
|
34
24
|
|
|
35
25
|
static fromCollectionsInput(input: MongoNamespaceCollectionsInput): MongoNamespace {
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
26
|
+
const collectionMap = input.entries['collection'];
|
|
27
|
+
const collectionCount = collectionMap !== undefined ? Object.keys(collectionMap).length : 0;
|
|
28
|
+
const hasUnknownKinds = Object.keys(input.entries).some((kind) => kind !== 'collection');
|
|
29
|
+
if (input.id === UNBOUND_NAMESPACE_ID && collectionCount === 0 && !hasUnknownKinds) {
|
|
30
|
+
return MongoUnboundNamespace.instance;
|
|
39
31
|
}
|
|
40
|
-
return
|
|
32
|
+
return new MongoBoundNamespace(input);
|
|
41
33
|
}
|
|
42
34
|
|
|
43
35
|
private constructor(input: MongoNamespaceCollectionsInput) {
|
|
44
36
|
super();
|
|
45
37
|
this.id = input.id;
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
38
|
+
|
|
39
|
+
const rawEntries: Record<string, Readonly<Record<string, unknown>>> = {
|
|
40
|
+
collection: {},
|
|
41
|
+
...input.entries,
|
|
42
|
+
};
|
|
43
|
+
this.entries = Object.freeze(
|
|
44
|
+
blindCast<
|
|
45
|
+
MongoNamespaceEntries,
|
|
46
|
+
'composeMongoEntityKinds() supplies the collection→MongoCollection descriptor, so this open-dict result holds the typed collection member MongoNamespaceEntries declares; the descriptor Map erases that per-kind Node type from the return.'
|
|
47
|
+
>(hydrateNamespaceEntities(rawEntries, composeMongoEntityKinds(), 'carry')),
|
|
48
|
+
);
|
|
56
49
|
Object.defineProperty(this, 'kind', {
|
|
57
50
|
value: MONGO_NAMESPACE_KIND,
|
|
58
51
|
writable: false,
|
|
@@ -61,29 +54,12 @@ class MongoBoundNamespace extends NamespaceBase {
|
|
|
61
54
|
});
|
|
62
55
|
freezeNode(this);
|
|
63
56
|
}
|
|
57
|
+
|
|
58
|
+
get collection(): Readonly<Record<string, MongoCollection>> {
|
|
59
|
+
return this.entries.collection ?? Object.freeze({});
|
|
60
|
+
}
|
|
64
61
|
}
|
|
65
62
|
|
|
66
63
|
export function buildMongoNamespace(input: MongoNamespaceCollectionsInput): MongoNamespace {
|
|
67
64
|
return MongoBoundNamespace.fromCollectionsInput(input);
|
|
68
65
|
}
|
|
69
|
-
|
|
70
|
-
export function buildMongoNamespaceMap(
|
|
71
|
-
namespaces: Readonly<Record<string, Namespace | MongoNamespaceCollectionsInput>>,
|
|
72
|
-
): Readonly<Record<string, MongoNamespace>> {
|
|
73
|
-
return Object.fromEntries(
|
|
74
|
-
Object.entries(namespaces).map(([nsKey, ns]) => [
|
|
75
|
-
nsKey,
|
|
76
|
-
isMaterializedMongoNamespace(ns)
|
|
77
|
-
? blindCast<
|
|
78
|
-
MongoNamespace,
|
|
79
|
-
'a materialised Mongo-family namespace entry in a namespace map is a MongoNamespace'
|
|
80
|
-
>(ns)
|
|
81
|
-
: MongoBoundNamespace.fromCollectionsInput(
|
|
82
|
-
blindCast<
|
|
83
|
-
MongoNamespaceCollectionsInput,
|
|
84
|
-
'non-materialized Mongo namespace map entry is a MongoNamespaceCollectionsInput'
|
|
85
|
-
>(ns),
|
|
86
|
-
),
|
|
87
|
-
]),
|
|
88
|
-
);
|
|
89
|
-
}
|
package/src/ir/mongo-index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { freezeNode, IRNodeBase } from '@prisma-next/framework-components/ir';
|
|
2
|
+
import type { CollationOptions } from '@prisma-next/mongo-value/mongodb-types';
|
|
2
3
|
import type { MongoIndexKey } from '../contract-types';
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -14,7 +15,7 @@ export interface MongoIndexInput {
|
|
|
14
15
|
readonly expireAfterSeconds?: number;
|
|
15
16
|
readonly partialFilterExpression?: Record<string, unknown>;
|
|
16
17
|
readonly wildcardProjection?: Record<string, 0 | 1>;
|
|
17
|
-
readonly collation?:
|
|
18
|
+
readonly collation?: CollationOptions;
|
|
18
19
|
readonly weights?: Record<string, number>;
|
|
19
20
|
readonly default_language?: string;
|
|
20
21
|
readonly language_override?: string;
|
|
@@ -41,7 +42,7 @@ export class MongoIndex extends IRNodeBase {
|
|
|
41
42
|
declare readonly expireAfterSeconds?: number;
|
|
42
43
|
declare readonly partialFilterExpression?: Record<string, unknown>;
|
|
43
44
|
declare readonly wildcardProjection?: Record<string, 0 | 1>;
|
|
44
|
-
declare readonly collation?:
|
|
45
|
+
declare readonly collation?: CollationOptions;
|
|
45
46
|
declare readonly weights?: Record<string, number>;
|
|
46
47
|
declare readonly default_language?: string;
|
|
47
48
|
declare readonly language_override?: string;
|
package/src/ir/mongo-storage.ts
CHANGED
|
@@ -7,22 +7,17 @@ import {
|
|
|
7
7
|
} from '@prisma-next/framework-components/ir';
|
|
8
8
|
import type { MongoCollection, MongoCollectionInput } from './mongo-collection';
|
|
9
9
|
|
|
10
|
+
export type MongoNamespaceEntries = Readonly<Record<string, Readonly<Record<string, unknown>>>> & {
|
|
11
|
+
readonly collection?: Readonly<Record<string, MongoCollection>>;
|
|
12
|
+
};
|
|
13
|
+
|
|
10
14
|
export interface MongoNamespaceCollectionsInput {
|
|
11
15
|
readonly id: string;
|
|
12
|
-
readonly entries:
|
|
13
|
-
readonly collection: Record<string, MongoCollection | MongoCollectionInput>;
|
|
14
|
-
};
|
|
16
|
+
readonly entries: Readonly<Record<string, Readonly<Record<string, MongoCollectionInput>>>>;
|
|
15
17
|
}
|
|
16
18
|
|
|
17
|
-
// Mongo concretions store `MongoCollection` instances under
|
|
18
|
-
// `entries.collection` (Mongo idiom — distinct from the SQL family's
|
|
19
|
-
// `entries.table`). Narrowing the namespace map here lets target/family-
|
|
20
|
-
// level consumers iterate collection slots and recover the concrete type
|
|
21
|
-
// without the framework's wider `Namespace` tripping them up.
|
|
22
19
|
export type MongoNamespace = Namespace & {
|
|
23
|
-
readonly entries:
|
|
24
|
-
readonly collection: Readonly<Record<string, MongoCollection>>;
|
|
25
|
-
}>;
|
|
20
|
+
readonly entries: MongoNamespaceEntries;
|
|
26
21
|
};
|
|
27
22
|
|
|
28
23
|
export interface MongoStorageInput<THash extends string = string> {
|
|
@@ -3,15 +3,15 @@ import {
|
|
|
3
3
|
NamespaceBase,
|
|
4
4
|
UNBOUND_NAMESPACE_ID,
|
|
5
5
|
} from '@prisma-next/framework-components/ir';
|
|
6
|
-
import type {
|
|
6
|
+
import type { MongoNamespaceEntries } from './mongo-storage';
|
|
7
7
|
|
|
8
8
|
export class MongoUnboundNamespace extends NamespaceBase {
|
|
9
9
|
static readonly instance: MongoUnboundNamespace = new MongoUnboundNamespace();
|
|
10
10
|
|
|
11
11
|
readonly id = UNBOUND_NAMESPACE_ID;
|
|
12
|
-
readonly entries:
|
|
13
|
-
|
|
14
|
-
}
|
|
12
|
+
readonly entries: MongoNamespaceEntries = Object.freeze({
|
|
13
|
+
collection: Object.freeze({}),
|
|
14
|
+
});
|
|
15
15
|
declare readonly kind: string;
|
|
16
16
|
|
|
17
17
|
private constructor() {
|
package/src/validate-storage.ts
CHANGED
|
@@ -9,7 +9,7 @@ function storageDeclaresCollection(
|
|
|
9
9
|
collectionName: string,
|
|
10
10
|
): boolean {
|
|
11
11
|
for (const ns of Object.values(storage.namespaces)) {
|
|
12
|
-
if (Object.hasOwn(ns.entries
|
|
12
|
+
if (Object.hasOwn(ns.entries['collection'] ?? {}, collectionName)) {
|
|
13
13
|
return true;
|
|
14
14
|
}
|
|
15
15
|
}
|