@prisma-next/mongo-contract 0.8.0 → 0.9.0-dev.1

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.
@@ -0,0 +1,32 @@
1
+ import { freezeNode, IRNodeBase } from '@prisma-next/framework-components/ir';
2
+
3
+ export type MongoClusteredCollectionKey = Readonly<Record<string, 1>>;
4
+
5
+ export interface MongoClusteredCollectionOptionsInput {
6
+ readonly name?: string;
7
+ readonly key: MongoClusteredCollectionKey;
8
+ readonly unique: boolean;
9
+ }
10
+
11
+ /**
12
+ * Clustered-collection options (the `clusteredIndex` collection-creation
13
+ * field). Lifted from a `type =` data shape to an AST class extending
14
+ * `IRNodeBase` per FR18.
15
+ *
16
+ * MongoDB requires `key` and `unique` for any clustered collection; the
17
+ * constructor enforces presence by type signature.
18
+ */
19
+ export class MongoClusteredCollectionOptions extends IRNodeBase {
20
+ readonly kind = 'mongo-clustered-collection-options' as const;
21
+ declare readonly name?: string;
22
+ readonly key: MongoClusteredCollectionKey;
23
+ readonly unique: boolean;
24
+
25
+ constructor(options: MongoClusteredCollectionOptionsInput) {
26
+ super();
27
+ if (options.name !== undefined) this.name = options.name;
28
+ this.key = options.key;
29
+ this.unique = options.unique;
30
+ freezeNode(this);
31
+ }
32
+ }
@@ -0,0 +1,66 @@
1
+ import { freezeNode, IRNodeBase } from '@prisma-next/framework-components/ir';
2
+
3
+ export type MongoCollationCaseFirst = 'off' | 'upper' | 'lower';
4
+ export type MongoCollationStrength = 1 | 2 | 3 | 4 | 5;
5
+ export type MongoCollationAlternate = 'non-ignorable' | 'shifted';
6
+ export type MongoCollationMaxVariable = 'punct' | 'space';
7
+
8
+ /**
9
+ * Authoring / hydration input shape for {@link MongoCollationOptions}. Carries
10
+ * the canonical data without the IR-class `kind` discriminator; the class
11
+ * fabricates `kind` so the authoring DSL and the SPI hydration walker can
12
+ * pass plain data literals through the constructor without forcing every
13
+ * call site to spell out `kind: 'mongo-collation-options'`.
14
+ */
15
+ export interface MongoCollationOptionsInput {
16
+ readonly locale: string;
17
+ readonly caseLevel?: boolean;
18
+ readonly caseFirst?: MongoCollationCaseFirst;
19
+ readonly strength?: MongoCollationStrength;
20
+ readonly numericOrdering?: boolean;
21
+ readonly alternate?: MongoCollationAlternate;
22
+ readonly maxVariable?: MongoCollationMaxVariable;
23
+ readonly backwards?: boolean;
24
+ readonly normalization?: boolean;
25
+ }
26
+
27
+ /**
28
+ * Mongo Contract IR leaf for collection / index collation options.
29
+ *
30
+ * Lifted from a `type =` data shape to an AST class extending
31
+ * `IRNodeBase` per FR18 ("Mongo's Contract IR is fully unified under
32
+ * the AST-class pattern, layered family / target"). Single concrete class
33
+ * (no target subclass): collation options carry no target-specific
34
+ * variation at this layer — both Atlas and self-hosted Mongo consume the
35
+ * same option vocabulary.
36
+ *
37
+ * Undefined optional fields are not assigned, so `JSON.stringify` omits
38
+ * them from the canonical JSON output (matches the pre-lift data shape's
39
+ * round-trip behaviour, modulo the new `kind` discriminator).
40
+ */
41
+ export class MongoCollationOptions extends IRNodeBase {
42
+ readonly kind = 'mongo-collation-options' as const;
43
+ readonly locale: string;
44
+ declare readonly caseLevel?: boolean;
45
+ declare readonly caseFirst?: MongoCollationCaseFirst;
46
+ declare readonly strength?: MongoCollationStrength;
47
+ declare readonly numericOrdering?: boolean;
48
+ declare readonly alternate?: MongoCollationAlternate;
49
+ declare readonly maxVariable?: MongoCollationMaxVariable;
50
+ declare readonly backwards?: boolean;
51
+ declare readonly normalization?: boolean;
52
+
53
+ constructor(options: MongoCollationOptionsInput) {
54
+ super();
55
+ this.locale = options.locale;
56
+ if (options.caseLevel !== undefined) this.caseLevel = options.caseLevel;
57
+ if (options.caseFirst !== undefined) this.caseFirst = options.caseFirst;
58
+ if (options.strength !== undefined) this.strength = options.strength;
59
+ if (options.numericOrdering !== undefined) this.numericOrdering = options.numericOrdering;
60
+ if (options.alternate !== undefined) this.alternate = options.alternate;
61
+ if (options.maxVariable !== undefined) this.maxVariable = options.maxVariable;
62
+ if (options.backwards !== undefined) this.backwards = options.backwards;
63
+ if (options.normalization !== undefined) this.normalization = options.normalization;
64
+ freezeNode(this);
65
+ }
66
+ }
@@ -0,0 +1,159 @@
1
+ import { freezeNode, IRNodeBase } from '@prisma-next/framework-components/ir';
2
+ import type { MongoJsonObject } from '../contract-types';
3
+ import {
4
+ MongoChangeStreamPreAndPostImagesOptions,
5
+ type MongoChangeStreamPreAndPostImagesOptionsInput,
6
+ } from './mongo-change-stream-pre-and-post-images-options';
7
+ import type {
8
+ MongoClusteredCollectionOptions,
9
+ MongoClusteredCollectionOptionsInput,
10
+ } from './mongo-clustered-collection-options';
11
+ import { MongoCollationOptions, type MongoCollationOptionsInput } from './mongo-collation-options';
12
+ import {
13
+ MongoIndexOptionDefaults,
14
+ type MongoIndexOptionDefaultsInput,
15
+ } from './mongo-index-option-defaults';
16
+ import {
17
+ MongoTimeSeriesCollectionOptions,
18
+ type MongoTimeSeriesCollectionOptionsInput,
19
+ } from './mongo-time-series-collection-options';
20
+
21
+ /**
22
+ * Storage-shape sub-shape: only `name` is persisted on the storage
23
+ * `clusteredIndex` field. The richer authoring vocabulary
24
+ * (`MongoClusteredCollectionOptions.key`, `…unique`) is intentionally
25
+ * not round-tripped through the on-disk JSON envelope — those fields
26
+ * are application-side configuration that informs collection creation
27
+ * but does not survive into the persisted collection options.
28
+ */
29
+ export interface MongoStorageClusteredIndexShape {
30
+ readonly name?: string;
31
+ }
32
+
33
+ /**
34
+ * Storage-shape sub-shape: `capped` collections persist `size` (required)
35
+ * and optionally `max` document count. The authoring DSL surface uses a
36
+ * flat `capped: boolean` + separate `size` / `max` fields; builders
37
+ * translate that authoring vocabulary into this nested storage form
38
+ * before constructing {@link MongoCollectionOptions}.
39
+ */
40
+ export interface MongoStorageCappedShape {
41
+ readonly size: number;
42
+ readonly max?: number;
43
+ }
44
+
45
+ /**
46
+ * Hydration / construction input shape for {@link MongoCollectionOptions}.
47
+ * Mirrors the on-disk storage JSON envelope exactly (nested `capped`,
48
+ * `clusteredIndex`, …) so the family-base serializer's hydration walker
49
+ * can hand an arktype-validated object literal straight to `new`.
50
+ * Nested IR-class fields may be supplied as either plain data literals
51
+ * (typical for JSON-derived input) or already-constructed class
52
+ * instances (typical when re-wrapping during a partial walk).
53
+ */
54
+ export interface MongoCollectionOptionsInput {
55
+ readonly capped?: MongoStorageCappedShape;
56
+ readonly storageEngine?: MongoJsonObject;
57
+ readonly indexOptionDefaults?: MongoIndexOptionDefaults | MongoIndexOptionDefaultsInput;
58
+ readonly collation?: MongoCollationOptions | MongoCollationOptionsInput;
59
+ readonly timeseries?: MongoTimeSeriesCollectionOptions | MongoTimeSeriesCollectionOptionsInput;
60
+ readonly clusteredIndex?: MongoStorageClusteredIndexShape;
61
+ readonly expireAfterSeconds?: number;
62
+ readonly changeStreamPreAndPostImages?:
63
+ | MongoChangeStreamPreAndPostImagesOptions
64
+ | MongoChangeStreamPreAndPostImagesOptionsInput;
65
+ }
66
+
67
+ /**
68
+ * Authoring-side flat vocabulary accepted by the contract-ts builder
69
+ * DSL (e.g. `capped: boolean` + separate `size` / `max` scalars). The
70
+ * builder translates this surface into a {@link MongoCollectionOptionsInput}
71
+ * before constructing {@link MongoCollectionOptions}. Kept as a
72
+ * standalone type so authoring DSL ergonomics do not leak into the
73
+ * storage IR construction contract.
74
+ */
75
+ export interface MongoCollectionOptionsAuthoringInput {
76
+ readonly capped?: boolean;
77
+ readonly size?: number;
78
+ readonly max?: number;
79
+ readonly storageEngine?: MongoJsonObject;
80
+ readonly indexOptionDefaults?: MongoIndexOptionDefaults | MongoIndexOptionDefaultsInput;
81
+ readonly collation?: MongoCollationOptions | MongoCollationOptionsInput;
82
+ readonly timeseries?: MongoTimeSeriesCollectionOptions | MongoTimeSeriesCollectionOptionsInput;
83
+ readonly clusteredIndex?: MongoClusteredCollectionOptions | MongoClusteredCollectionOptionsInput;
84
+ readonly expireAfterSeconds?: number;
85
+ readonly changeStreamPreAndPostImages?:
86
+ | MongoChangeStreamPreAndPostImagesOptions
87
+ | MongoChangeStreamPreAndPostImagesOptionsInput;
88
+ }
89
+
90
+ /**
91
+ * Mongo Contract IR node for collection-level creation options (the
92
+ * second argument to `db.createCollection(name, options)`). Lifted from
93
+ * the pre-M2R2 `MongoStorageCollectionOptions` storage interface to a
94
+ * class extending `IRNodeBase` per FR18.
95
+ *
96
+ * Single concrete family-layer class (no target subclass). The
97
+ * constructor accepts the storage JSON envelope shape ({@link
98
+ * MongoCollectionOptionsInput}) so the family-base hydration walker
99
+ * can pass arktype-validated objects directly to `new`. Authoring
100
+ * vocabulary is translated to this shape upstream in the contract-ts
101
+ * builder.
102
+ *
103
+ * Nested IR sub-shapes (collation, timeseries, …) are normalised to
104
+ * their respective IR class instances inside the constructor so
105
+ * downstream walks see a uniform AST regardless of whether the input
106
+ * was a JSON literal or an already-constructed class.
107
+ */
108
+ export class MongoCollectionOptions extends IRNodeBase {
109
+ readonly kind = 'mongo-collection-options' as const;
110
+ declare readonly capped?: MongoStorageCappedShape;
111
+ declare readonly storageEngine?: MongoJsonObject;
112
+ declare readonly indexOptionDefaults?: MongoIndexOptionDefaults;
113
+ declare readonly collation?: MongoCollationOptions;
114
+ declare readonly timeseries?: MongoTimeSeriesCollectionOptions;
115
+ declare readonly clusteredIndex?: MongoStorageClusteredIndexShape;
116
+ declare readonly expireAfterSeconds?: number;
117
+ declare readonly changeStreamPreAndPostImages?: MongoChangeStreamPreAndPostImagesOptions;
118
+
119
+ constructor(input: MongoCollectionOptionsInput = {}) {
120
+ super();
121
+ if (input.capped !== undefined) {
122
+ this.capped = {
123
+ size: input.capped.size,
124
+ ...(input.capped.max != null && { max: input.capped.max }),
125
+ };
126
+ }
127
+ if (input.storageEngine !== undefined) this.storageEngine = input.storageEngine;
128
+ if (input.indexOptionDefaults !== undefined) {
129
+ this.indexOptionDefaults =
130
+ input.indexOptionDefaults instanceof MongoIndexOptionDefaults
131
+ ? input.indexOptionDefaults
132
+ : new MongoIndexOptionDefaults(input.indexOptionDefaults);
133
+ }
134
+ if (input.collation !== undefined) {
135
+ this.collation =
136
+ input.collation instanceof MongoCollationOptions
137
+ ? input.collation
138
+ : new MongoCollationOptions(input.collation);
139
+ }
140
+ if (input.timeseries !== undefined) {
141
+ this.timeseries =
142
+ input.timeseries instanceof MongoTimeSeriesCollectionOptions
143
+ ? input.timeseries
144
+ : new MongoTimeSeriesCollectionOptions(input.timeseries);
145
+ }
146
+ if (input.clusteredIndex !== undefined) {
147
+ this.clusteredIndex =
148
+ input.clusteredIndex.name !== undefined ? { name: input.clusteredIndex.name } : {};
149
+ }
150
+ if (input.expireAfterSeconds !== undefined) this.expireAfterSeconds = input.expireAfterSeconds;
151
+ if (input.changeStreamPreAndPostImages !== undefined) {
152
+ this.changeStreamPreAndPostImages =
153
+ input.changeStreamPreAndPostImages instanceof MongoChangeStreamPreAndPostImagesOptions
154
+ ? input.changeStreamPreAndPostImages
155
+ : new MongoChangeStreamPreAndPostImagesOptions(input.changeStreamPreAndPostImages);
156
+ }
157
+ freezeNode(this);
158
+ }
159
+ }
@@ -0,0 +1,71 @@
1
+ import { freezeNode, IRNodeBase } from '@prisma-next/framework-components/ir';
2
+ import {
3
+ MongoCollectionOptions,
4
+ type MongoCollectionOptionsInput,
5
+ } from './mongo-collection-options';
6
+ import { MongoIndex, type MongoIndexInput } from './mongo-index';
7
+ import { MongoValidator, type MongoValidatorInput } from './mongo-validator';
8
+
9
+ /**
10
+ * Hydration / construction input shape for {@link MongoCollection}.
11
+ * Mirrors the on-disk storage JSON envelope exactly (the value held at
12
+ * `contract.storage.collections[<name>]`) so the family-base
13
+ * serializer's hydration walker can hand an arktype-validated literal
14
+ * straight to `new`. Nested IR-class fields may be supplied as either
15
+ * plain data literals (typical for JSON-derived input) or
16
+ * already-constructed class instances.
17
+ */
18
+ export interface MongoCollectionInput {
19
+ readonly indexes?: ReadonlyArray<MongoIndex | MongoIndexInput>;
20
+ readonly validator?: MongoValidator | MongoValidatorInput;
21
+ readonly options?: MongoCollectionOptions | MongoCollectionOptionsInput;
22
+ }
23
+
24
+ /**
25
+ * Mongo Contract IR node for a single collection entry in
26
+ * `contract.storage.collections`. Lifted from the pre-M2R2
27
+ * `MongoStorageCollection` storage interface to a class extending
28
+ * `IRNodeBase` per FR18.
29
+ *
30
+ * Concrete at the family layer (no target subclass). The spec's
31
+ * `MongoTargetCollection extends MongoCollection` pattern remains
32
+ * additive: a future Mongo target with target-specific extensions is
33
+ * free to subclass without breaking the family-layer construction
34
+ * sites.
35
+ *
36
+ * The unprefixed name `MongoCollection` is now the contract IR class.
37
+ * Note that `@prisma-next/mongo-orm` also exports a (different)
38
+ * `MongoCollection<TContract, ModelName>` generic for the user-facing
39
+ * ORM query builder; the two live in separate packages and are
40
+ * resolved by import path. A source file that needs both should alias
41
+ * one (e.g. `import { MongoCollection as MongoContractCollection }
42
+ * from '@prisma-next/mongo-contract'`).
43
+ */
44
+ export class MongoCollection extends IRNodeBase {
45
+ readonly kind = 'mongo-collection' as const;
46
+ declare readonly indexes?: ReadonlyArray<MongoIndex>;
47
+ declare readonly validator?: MongoValidator;
48
+ declare readonly options?: MongoCollectionOptions;
49
+
50
+ constructor(input: MongoCollectionInput = {}) {
51
+ super();
52
+ if (input.indexes !== undefined) {
53
+ this.indexes = input.indexes.map((idx) =>
54
+ idx instanceof MongoIndex ? idx : new MongoIndex(idx),
55
+ );
56
+ }
57
+ if (input.validator !== undefined) {
58
+ this.validator =
59
+ input.validator instanceof MongoValidator
60
+ ? input.validator
61
+ : new MongoValidator(input.validator);
62
+ }
63
+ if (input.options !== undefined) {
64
+ this.options =
65
+ input.options instanceof MongoCollectionOptions
66
+ ? input.options
67
+ : new MongoCollectionOptions(input.options);
68
+ }
69
+ freezeNode(this);
70
+ }
71
+ }
@@ -0,0 +1,27 @@
1
+ import { freezeNode, IRNodeBase } from '@prisma-next/framework-components/ir';
2
+ import type { MongoJsonObject } from '../contract-types';
3
+
4
+ export interface MongoIndexOptionDefaultsInput {
5
+ readonly storageEngine?: MongoJsonObject;
6
+ }
7
+
8
+ /**
9
+ * Collection-level default index options (the `indexOptionDefaults`
10
+ * collection-creation field on Mongo's `createCollection`). Lifted from
11
+ * a `type =` data shape to an AST class extending `IRNodeBase` per
12
+ * FR18 (Mongo Contract IR fully unified under the AST-class pattern).
13
+ *
14
+ * Carries `storageEngine` only — the underlying MongoDB option set is
15
+ * intentionally narrow at this layer; per-engine richer option vocabularies
16
+ * are out of scope for this project.
17
+ */
18
+ export class MongoIndexOptionDefaults extends IRNodeBase {
19
+ readonly kind = 'mongo-index-option-defaults' as const;
20
+ declare readonly storageEngine?: MongoJsonObject;
21
+
22
+ constructor(options: MongoIndexOptionDefaultsInput = {}) {
23
+ super();
24
+ if (options.storageEngine !== undefined) this.storageEngine = options.storageEngine;
25
+ freezeNode(this);
26
+ }
27
+ }
@@ -0,0 +1,91 @@
1
+ import { freezeNode, IRNodeBase } from '@prisma-next/framework-components/ir';
2
+ import type { MongoJsonObject, MongoWildcardProjection } from '../contract-types';
3
+ import { MongoCollationOptions, type MongoCollationOptionsInput } from './mongo-collation-options';
4
+
5
+ /**
6
+ * Authoring / hydration input shape for {@link MongoIndexOptions}. Carries
7
+ * the index option vocabulary as plain data without the IR-class `kind`
8
+ * discriminator. `collation` accepts either a class instance or its own
9
+ * input shape — the constructor normalises to the class form internally.
10
+ */
11
+ export interface MongoIndexOptionsInput {
12
+ readonly unique?: boolean;
13
+ readonly name?: string;
14
+ readonly partialFilterExpression?: MongoJsonObject;
15
+ readonly sparse?: boolean;
16
+ readonly expireAfterSeconds?: number;
17
+ readonly weights?: Readonly<Record<string, number>>;
18
+ readonly default_language?: string;
19
+ readonly language_override?: string;
20
+ readonly textIndexVersion?: number;
21
+ readonly '2dsphereIndexVersion'?: number;
22
+ readonly bits?: number;
23
+ readonly min?: number;
24
+ readonly max?: number;
25
+ readonly bucketSize?: number;
26
+ readonly hidden?: boolean;
27
+ readonly collation?: MongoCollationOptions | MongoCollationOptionsInput;
28
+ readonly wildcardProjection?: MongoWildcardProjection;
29
+ }
30
+
31
+ /**
32
+ * Mongo Contract IR node for the per-index option vocabulary (the second
33
+ * argument to `db.collection.createIndex(keys, options)` minus the keys
34
+ * themselves). Lifted from a `type =` data shape to an AST class
35
+ * extending `IRNodeBase` per FR18.
36
+ *
37
+ * Nested `collation` is itself an IR class (`MongoCollationOptions`); the
38
+ * constructor accepts either a class instance or a data literal and
39
+ * normalises to the class form so downstream walks see a uniform IR tree.
40
+ */
41
+ export class MongoIndexOptions extends IRNodeBase {
42
+ readonly kind = 'mongo-index-options' as const;
43
+ declare readonly unique?: boolean;
44
+ declare readonly name?: string;
45
+ declare readonly partialFilterExpression?: MongoJsonObject;
46
+ declare readonly sparse?: boolean;
47
+ declare readonly expireAfterSeconds?: number;
48
+ declare readonly weights?: Readonly<Record<string, number>>;
49
+ declare readonly default_language?: string;
50
+ declare readonly language_override?: string;
51
+ declare readonly textIndexVersion?: number;
52
+ declare readonly '2dsphereIndexVersion'?: number;
53
+ declare readonly bits?: number;
54
+ declare readonly min?: number;
55
+ declare readonly max?: number;
56
+ declare readonly bucketSize?: number;
57
+ declare readonly hidden?: boolean;
58
+ declare readonly collation?: MongoCollationOptions;
59
+ declare readonly wildcardProjection?: MongoWildcardProjection;
60
+
61
+ constructor(options: MongoIndexOptionsInput = {}) {
62
+ super();
63
+ if (options.unique !== undefined) this.unique = options.unique;
64
+ if (options.name !== undefined) this.name = options.name;
65
+ if (options.partialFilterExpression !== undefined)
66
+ this.partialFilterExpression = options.partialFilterExpression;
67
+ if (options.sparse !== undefined) this.sparse = options.sparse;
68
+ if (options.expireAfterSeconds !== undefined)
69
+ this.expireAfterSeconds = options.expireAfterSeconds;
70
+ if (options.weights !== undefined) this.weights = options.weights;
71
+ if (options.default_language !== undefined) this.default_language = options.default_language;
72
+ if (options.language_override !== undefined) this.language_override = options.language_override;
73
+ if (options.textIndexVersion !== undefined) this.textIndexVersion = options.textIndexVersion;
74
+ if (options['2dsphereIndexVersion'] !== undefined)
75
+ this['2dsphereIndexVersion'] = options['2dsphereIndexVersion'];
76
+ if (options.bits !== undefined) this.bits = options.bits;
77
+ if (options.min !== undefined) this.min = options.min;
78
+ if (options.max !== undefined) this.max = options.max;
79
+ if (options.bucketSize !== undefined) this.bucketSize = options.bucketSize;
80
+ if (options.hidden !== undefined) this.hidden = options.hidden;
81
+ if (options.collation !== undefined) {
82
+ this.collation =
83
+ options.collation instanceof MongoCollationOptions
84
+ ? options.collation
85
+ : new MongoCollationOptions(options.collation);
86
+ }
87
+ if (options.wildcardProjection !== undefined)
88
+ this.wildcardProjection = options.wildcardProjection;
89
+ freezeNode(this);
90
+ }
91
+ }
@@ -0,0 +1,64 @@
1
+ import { freezeNode, IRNodeBase } from '@prisma-next/framework-components/ir';
2
+ import type { MongoIndexKey } from '../contract-types';
3
+
4
+ /**
5
+ * Hydration / construction input shape for {@link MongoIndex}. Mirrors
6
+ * the on-disk storage JSON envelope exactly so the family-base
7
+ * serializer's hydration walker can hand an arktype-validated literal
8
+ * straight to `new`.
9
+ */
10
+ export interface MongoIndexInput {
11
+ readonly keys: ReadonlyArray<MongoIndexKey>;
12
+ readonly unique?: boolean;
13
+ readonly sparse?: boolean;
14
+ readonly expireAfterSeconds?: number;
15
+ readonly partialFilterExpression?: Record<string, unknown>;
16
+ readonly wildcardProjection?: Record<string, 0 | 1>;
17
+ readonly collation?: Record<string, unknown>;
18
+ readonly weights?: Record<string, number>;
19
+ readonly default_language?: string;
20
+ readonly language_override?: string;
21
+ }
22
+
23
+ /**
24
+ * Mongo Contract IR node for a single collection index entry (one
25
+ * element of `MongoCollection.indexes`). Lifted from the
26
+ * pre-M2R2 `MongoStorageIndex` storage interface to a class extending
27
+ * `IRNodeBase` per FR18.
28
+ *
29
+ * Single concrete family-layer class (no target subclass). The spec's
30
+ * `MongoTargetIndex extends MongoIndex` pattern remains additive — a
31
+ * future Mongo target with target-specific index extensions is free to
32
+ * subclass; for the single Mongo target shipped today a concrete
33
+ * family-layer class is enough and avoids a target-import layering
34
+ * violation from the contract-ts builder.
35
+ */
36
+ export class MongoIndex extends IRNodeBase {
37
+ readonly kind = 'mongo-index' as const;
38
+ readonly keys: ReadonlyArray<MongoIndexKey>;
39
+ declare readonly unique?: boolean;
40
+ declare readonly sparse?: boolean;
41
+ declare readonly expireAfterSeconds?: number;
42
+ declare readonly partialFilterExpression?: Record<string, unknown>;
43
+ declare readonly wildcardProjection?: Record<string, 0 | 1>;
44
+ declare readonly collation?: Record<string, unknown>;
45
+ declare readonly weights?: Record<string, number>;
46
+ declare readonly default_language?: string;
47
+ declare readonly language_override?: string;
48
+
49
+ constructor(input: MongoIndexInput) {
50
+ super();
51
+ this.keys = input.keys;
52
+ if (input.unique !== undefined) this.unique = input.unique;
53
+ if (input.sparse !== undefined) this.sparse = input.sparse;
54
+ if (input.expireAfterSeconds !== undefined) this.expireAfterSeconds = input.expireAfterSeconds;
55
+ if (input.partialFilterExpression !== undefined)
56
+ this.partialFilterExpression = input.partialFilterExpression;
57
+ if (input.wildcardProjection !== undefined) this.wildcardProjection = input.wildcardProjection;
58
+ if (input.collation !== undefined) this.collation = input.collation;
59
+ if (input.weights !== undefined) this.weights = input.weights;
60
+ if (input.default_language !== undefined) this.default_language = input.default_language;
61
+ if (input.language_override !== undefined) this.language_override = input.language_override;
62
+ freezeNode(this);
63
+ }
64
+ }
@@ -0,0 +1,55 @@
1
+ import type { StorageHashBase } from '@prisma-next/contract/types';
2
+ import {
3
+ freezeNode,
4
+ IRNodeBase,
5
+ type Namespace,
6
+ type Storage,
7
+ } from '@prisma-next/framework-components/ir';
8
+ import type { MongoCollection } from './mongo-collection';
9
+
10
+ /**
11
+ * Construction input shape for {@link MongoStorage}. Mirrors the
12
+ * required runtime fields explicitly so the family-base serializer's
13
+ * hydration walker can hand the class a typed literal.
14
+ */
15
+ export interface MongoStorageInput<THash extends string = string> {
16
+ readonly storageHash: StorageHashBase<THash>;
17
+ readonly collections: Readonly<Record<string, MongoCollection>>;
18
+ readonly namespaces: Readonly<Record<string, Namespace>>;
19
+ }
20
+
21
+ /**
22
+ * Mongo family storage IR class. Carries the family-shared
23
+ * `storage.collections` map alongside the framework-promised
24
+ * `namespaces` map — single concrete class at the family layer (the
25
+ * Mongo family has one target today, and the data shape is uniform
26
+ * across the family; no abstract base earns its existence yet).
27
+ *
28
+ * `namespaces` is supplied by the caller. The Mongo target wraps a
29
+ * deserialized `MongoContract` envelope at
30
+ * `MongoTargetContractSerializer.constructTargetContract`, providing
31
+ * the default `{ [UNSPECIFIED_NAMESPACE_ID]:
32
+ * MongoTargetUnspecifiedDatabase.instance }` map at that target-layer
33
+ * site. The foundation-layer class stays target-agnostic.
34
+ *
35
+ * Constructed instances are frozen via `freezeNode(this)`; instance
36
+ * fields are JSON-clean by construction (`storageHash`, `collections`,
37
+ * `namespaces` all enumerable own properties). The persisted on-disk
38
+ * envelope shape is target-owned: `MongoTargetContractSerializer.serializeContract`
39
+ * decides whether `namespaces` round-trips through JSON or is stripped
40
+ * for the JSON envelope.
41
+ */
42
+ export class MongoStorage<THash extends string = string> extends IRNodeBase implements Storage {
43
+ readonly kind = 'mongo-storage' as const;
44
+ readonly storageHash: StorageHashBase<THash>;
45
+ readonly collections: Readonly<Record<string, MongoCollection>>;
46
+ readonly namespaces: Readonly<Record<string, Namespace>>;
47
+
48
+ constructor(input: MongoStorageInput<THash>) {
49
+ super();
50
+ this.storageHash = input.storageHash;
51
+ this.collections = input.collections;
52
+ this.namespaces = input.namespaces;
53
+ freezeNode(this);
54
+ }
55
+ }
@@ -0,0 +1,40 @@
1
+ import { freezeNode, IRNodeBase } from '@prisma-next/framework-components/ir';
2
+
3
+ export type MongoTimeSeriesGranularity = 'seconds' | 'minutes' | 'hours';
4
+
5
+ export interface MongoTimeSeriesCollectionOptionsInput {
6
+ readonly timeField: string;
7
+ readonly metaField?: string;
8
+ readonly granularity?: MongoTimeSeriesGranularity;
9
+ readonly bucketMaxSpanSeconds?: number;
10
+ readonly bucketRoundingSeconds?: number;
11
+ }
12
+
13
+ /**
14
+ * Time-series collection options. Lifted from a `type =` data shape to
15
+ * an AST class extending `IRNodeBase` per FR18.
16
+ *
17
+ * MongoDB requires `timeField` for any time-series collection; the
18
+ * constructor enforces presence by type signature (`timeField: string`
19
+ * is required on the input).
20
+ */
21
+ export class MongoTimeSeriesCollectionOptions extends IRNodeBase {
22
+ readonly kind = 'mongo-time-series-collection-options' as const;
23
+ readonly timeField: string;
24
+ declare readonly metaField?: string;
25
+ declare readonly granularity?: MongoTimeSeriesGranularity;
26
+ declare readonly bucketMaxSpanSeconds?: number;
27
+ declare readonly bucketRoundingSeconds?: number;
28
+
29
+ constructor(options: MongoTimeSeriesCollectionOptionsInput) {
30
+ super();
31
+ this.timeField = options.timeField;
32
+ if (options.metaField !== undefined) this.metaField = options.metaField;
33
+ if (options.granularity !== undefined) this.granularity = options.granularity;
34
+ if (options.bucketMaxSpanSeconds !== undefined)
35
+ this.bucketMaxSpanSeconds = options.bucketMaxSpanSeconds;
36
+ if (options.bucketRoundingSeconds !== undefined)
37
+ this.bucketRoundingSeconds = options.bucketRoundingSeconds;
38
+ freezeNode(this);
39
+ }
40
+ }
@@ -0,0 +1,42 @@
1
+ import { freezeNode, IRNodeBase } from '@prisma-next/framework-components/ir';
2
+
3
+ export type MongoValidatorValidationLevel = 'strict' | 'moderate';
4
+ export type MongoValidatorValidationAction = 'error' | 'warn';
5
+
6
+ export interface MongoValidatorInput {
7
+ readonly jsonSchema: Record<string, unknown>;
8
+ readonly validationLevel: MongoValidatorValidationLevel;
9
+ readonly validationAction: MongoValidatorValidationAction;
10
+ }
11
+
12
+ /**
13
+ * Mongo Contract IR node for collection-level document validators (the
14
+ * `validator` field on Mongo's `createCollection`). Lifted from the
15
+ * pre-M2R2 `MongoStorageValidator` storage interface to a class extending
16
+ * `IRNodeBase` per FR18.
17
+ *
18
+ * Concrete at the family layer (no target subclass). The spec's
19
+ * abstract-family + target-concrete pattern (`MongoTargetValidator
20
+ * extends MongoValidator`) becomes meaningful when a second Mongo target
21
+ * introduces target-specific validator extensions (Atlas search rules,
22
+ * DocumentDB-specific levels, …); for the single Mongo target shipped
23
+ * today, a concrete family-layer class lets the PSL JSON-Schema deriver
24
+ * and the contract-ts builder construct instances directly without a
25
+ * target-import layering violation. Target subclassing remains additive
26
+ * — a future `MongoTargetValidator extends MongoValidator` is an
27
+ * additive change, not a breaking one.
28
+ */
29
+ export class MongoValidator extends IRNodeBase {
30
+ readonly kind = 'mongo-validator' as const;
31
+ readonly jsonSchema: Record<string, unknown>;
32
+ readonly validationLevel: MongoValidatorValidationLevel;
33
+ readonly validationAction: MongoValidatorValidationAction;
34
+
35
+ constructor(input: MongoValidatorInput) {
36
+ super();
37
+ this.jsonSchema = input.jsonSchema;
38
+ this.validationLevel = input.validationLevel;
39
+ this.validationAction = input.validationAction;
40
+ freezeNode(this);
41
+ }
42
+ }