@prisma-next/family-mongo 0.9.0 → 0.10.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.
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +2 -2
- package/dist/ir.d.mts +11 -11
- package/dist/ir.d.mts.map +1 -1
- package/dist/ir.mjs +1 -1
- package/dist/{mongo-contract-serializer-Co3EaTVj.mjs → mongo-contract-serializer-D9zGIqE9.mjs} +20 -13
- package/dist/mongo-contract-serializer-D9zGIqE9.mjs.map +1 -0
- package/dist/schema-verify.mjs +1 -1
- package/dist/{verify-mongo-schema-BL7t9YTB.mjs → verify-mongo-schema-Bhxvdah3.mjs} +18 -15
- package/dist/verify-mongo-schema-Bhxvdah3.mjs.map +1 -0
- package/package.json +15 -15
- package/src/core/contract-to-schema.ts +6 -3
- package/src/core/ir/mongo-contract-serializer-base.ts +36 -22
- package/src/core/schema-diff.ts +14 -13
- package/dist/mongo-contract-serializer-Co3EaTVj.mjs.map +0 -1
- package/dist/verify-mongo-schema-BL7t9YTB.mjs.map +0 -1
package/dist/control.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control.d.mts","names":[],"sources":["../src/core/contract-to-schema.ts","../src/core/control-instance.ts","../src/core/control-descriptor.ts","../src/core/control-target-descriptor.ts","../src/core/control-types.ts","../src/core/operation-preview.ts","../src/core/schema-diff.ts","../src/core/schema-verify/canonicalize-introspection.ts"],"mappings":";;;;;;iBAyEgB,uBAAA,CAAwB,QAAA,EAAU,aAAA,UAAuB,aAAA;;;UCzCxD,0BAAA,SACP,qBAAA,UAA+B,aAAA,GACrC,iBAAA,CAAkB,aAAA,GAClB,uBAAA;;;ADsCJ;;;;;;;;EC3BE,mBAAA,CAAoB,YAAA,YAAwB,QAAA;AAAA;AAAA,iBAmF9B,yBAAA,CAA0B,YAAA,EAAc,YAAA,GAAe,0BAAA;;;cC1G1D,qBAAA,EAAuB,uBAAA,UAAiC,0BAAA;;;;;AFkDrE;;;;;;;;;;UGpDiB,4BAAA,mBAA+C,aAAA,GAAgB,aAAA,UACtE,0BAAA,mBAA6C,0BAAA;EAAA,SAC5C,kBAAA,EAAoB,kBAAA,CAAmB,SAAA;EAAA,SACvC,cAAA,EAAgB,cAAA,CAAe,SAAA,EAAW,aAAA;AAAA;;;;;;;AHiDrD;;;;;;;;;;;UIpDiB,+BAAA,SACP,0BAAA;EAAA,SACC,aAAA,GAAgB,aAAA,CAAc,aAAA,CAAc,iBAAA;AAAA;;;iBC6EvC,qBAAA,CAAsB,UAAA,WAAqB,sBAAA;;;;;AL3B3D;iBKgDgB,wBAAA,CACd,UAAA,WAAqB,sBAAA,KACpB,gBAAA;;;
|
|
1
|
+
{"version":3,"file":"control.d.mts","names":[],"sources":["../src/core/contract-to-schema.ts","../src/core/control-instance.ts","../src/core/control-descriptor.ts","../src/core/control-target-descriptor.ts","../src/core/control-types.ts","../src/core/operation-preview.ts","../src/core/schema-diff.ts","../src/core/schema-verify/canonicalize-introspection.ts"],"mappings":";;;;;;iBAyEgB,uBAAA,CAAwB,QAAA,EAAU,aAAA,UAAuB,aAAA;;;UCzCxD,0BAAA,SACP,qBAAA,UAA+B,aAAA,GACrC,iBAAA,CAAkB,aAAA,GAClB,uBAAA;;;ADsCJ;;;;;;;;EC3BE,mBAAA,CAAoB,YAAA,YAAwB,QAAA;AAAA;AAAA,iBAmF9B,yBAAA,CAA0B,YAAA,EAAc,YAAA,GAAe,0BAAA;;;cC1G1D,qBAAA,EAAuB,uBAAA,UAAiC,0BAAA;;;;;AFkDrE;;;;;;;;;;UGpDiB,4BAAA,mBAA+C,aAAA,GAAgB,aAAA,UACtE,0BAAA,mBAA6C,0BAAA;EAAA,SAC5C,kBAAA,EAAoB,kBAAA,CAAmB,SAAA;EAAA,SACvC,cAAA,EAAgB,cAAA,CAAe,SAAA,EAAW,aAAA;AAAA;;;;;;;AHiDrD;;;;;;;;;;;UIpDiB,+BAAA,SACP,0BAAA;EAAA,SACC,aAAA,GAAgB,aAAA,CAAc,aAAA,CAAc,iBAAA;AAAA;;;iBC6EvC,qBAAA,CAAsB,UAAA,WAAqB,sBAAA;;;;;AL3B3D;iBKgDgB,wBAAA,CACd,UAAA,WAAqB,sBAAA,KACpB,gBAAA;;;iBC/Ga,gBAAA,CACd,IAAA,EAAM,aAAA,EACN,QAAA,EAAU,aAAA,EACV,MAAA;EAEA,IAAA,EAAM,sBAAA;EACN,MAAA,EAAQ,WAAA;EACR,MAAA;IAAU,IAAA;IAAc,IAAA;IAAc,IAAA;IAAc,UAAA;EAAA;AAAA;;;UCWrC,oBAAA;EAAA,SACN,IAAA,EAAM,aAAA;EAAA,SACN,QAAA,EAAU,aAAA;AAAA;AAAA,iBAGL,kCAAA,CACd,IAAA,EAAM,aAAA,EACN,QAAA,EAAU,aAAA,GACT,oBAAA"}
|
package/dist/control.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { i as contractToMongoSchemaIR, n as canonicalizeSchemasForVerification, r as diffMongoSchemas, t as verifyMongoSchema } from "./verify-mongo-schema-
|
|
2
|
-
import { t as MongoContractSerializer } from "./mongo-contract-serializer-
|
|
1
|
+
import { i as contractToMongoSchemaIR, n as canonicalizeSchemasForVerification, r as diffMongoSchemas, t as verifyMongoSchema } from "./verify-mongo-schema-Bhxvdah3.mjs";
|
|
2
|
+
import { t as MongoContractSerializer } from "./mongo-contract-serializer-D9zGIqE9.mjs";
|
|
3
3
|
import { mongoEmission } from "@prisma-next/mongo-emitter";
|
|
4
4
|
import { APP_SPACE_ID, SchemaTreeNode, VERIFY_CODE_HASH_MISMATCH, VERIFY_CODE_MARKER_MISSING, VERIFY_CODE_TARGET_MISMATCH } from "@prisma-next/framework-components/control";
|
|
5
5
|
import { assertDescriptorSelfConsistency } from "@prisma-next/migration-tools/spaces";
|
package/dist/ir.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ContractSerializer, SchemaIssue, SchemaVerifier, SchemaVerifyOptions, SchemaVerifyResult } from "@prisma-next/framework-components/control";
|
|
2
2
|
import { MongoContract, MongoStorage } from "@prisma-next/mongo-contract";
|
|
3
|
-
import { JsonObject } from "@prisma-next/utils/json";
|
|
4
3
|
import { Namespace } from "@prisma-next/framework-components/ir";
|
|
4
|
+
import { JsonObject } from "@prisma-next/utils/json";
|
|
5
5
|
|
|
6
6
|
//#region src/core/ir/mongo-contract-serializer-base.d.ts
|
|
7
7
|
/**
|
|
@@ -26,7 +26,7 @@ import { Namespace } from "@prisma-next/framework-components/ir";
|
|
|
26
26
|
* canonicalization override `serializeContract`.
|
|
27
27
|
*/
|
|
28
28
|
declare abstract class MongoContractSerializerBase<TContract> implements ContractSerializer<TContract> {
|
|
29
|
-
deserializeContract(json: unknown):
|
|
29
|
+
deserializeContract<T extends TContract = TContract>(json: unknown): T;
|
|
30
30
|
serializeContract(contract: TContract): JsonObject;
|
|
31
31
|
/**
|
|
32
32
|
* Family-shared structural validation: parse against the Mongo
|
|
@@ -37,18 +37,18 @@ declare abstract class MongoContractSerializerBase<TContract> implements Contrac
|
|
|
37
37
|
* default.
|
|
38
38
|
*
|
|
39
39
|
* The returned `MongoContract` carries class instances under
|
|
40
|
-
* `storage.collections` (each value is a
|
|
41
|
-
* nested `MongoIndex` / `MongoValidator` /
|
|
42
|
-
* constructed by the `MongoCollection` constructor).
|
|
43
|
-
* contract envelope (models, valueObjects, capabilities, …)
|
|
44
|
-
* in plain-JSON form; those IR layers are handled by sibling
|
|
40
|
+
* `storage.namespaces[namespaceId].collections[collectionName]` (each value is a
|
|
41
|
+
* `MongoCollection`, with nested `MongoIndex` / `MongoValidator` /
|
|
42
|
+
* `MongoCollectionOptions` constructed by the `MongoCollection` constructor).
|
|
43
|
+
* The rest of the contract envelope (models, valueObjects, capabilities, …)
|
|
44
|
+
* remains in plain-JSON form; those IR layers are handled by sibling
|
|
45
45
|
* subsystems and don't sit behind this SPI.
|
|
46
46
|
*/
|
|
47
47
|
protected parseMongoContractStructure(json: unknown): MongoContract;
|
|
48
48
|
/**
|
|
49
|
-
* Walk a structurally-validated Mongo contract and convert
|
|
50
|
-
* `storage.collections`
|
|
51
|
-
* `MongoCollection` IR-class instances. Idempotent: already-class
|
|
49
|
+
* Walk a structurally-validated Mongo contract and convert each
|
|
50
|
+
* `storage.namespaces[nsId].collections[collectionName]` entry from plain
|
|
51
|
+
* data into `MongoCollection` IR-class instances. Idempotent: already-class
|
|
52
52
|
* instances pass through unchanged.
|
|
53
53
|
*/
|
|
54
54
|
protected hydrateMongoContract(contract: MongoContract): MongoContract;
|
|
@@ -56,7 +56,7 @@ declare abstract class MongoContractSerializerBase<TContract> implements Contrac
|
|
|
56
56
|
* Target-specific class construction from the validated structural
|
|
57
57
|
* data. The target wraps the contract envelope in the family-layer
|
|
58
58
|
* `MongoStorage` class instance, supplying the `namespaces` map
|
|
59
|
-
* (target concretions like `
|
|
59
|
+
* (target concretions like `MongoTargetUnboundDatabase`). The
|
|
60
60
|
* leaf collection / index shapes are already family-layer IR-class
|
|
61
61
|
* instances after the hydration walk above.
|
|
62
62
|
*/
|
package/dist/ir.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ir.d.mts","names":[],"sources":["../src/core/ir/mongo-contract-serializer-base.ts","../src/core/ir/mongo-contract-serializer.ts","../src/core/ir/mongo-schema-verifier-base.ts"],"mappings":";;;;;;;;;AAiCA;;;;;;;;;;;;;;;;;;uBAAsB,2BAAA,uBACT,kBAAA,CAAmB,SAAA;EAE9B,mBAAA,
|
|
1
|
+
{"version":3,"file":"ir.d.mts","names":[],"sources":["../src/core/ir/mongo-contract-serializer-base.ts","../src/core/ir/mongo-contract-serializer.ts","../src/core/ir/mongo-schema-verifier-base.ts"],"mappings":";;;;;;;;;AAiCA;;;;;;;;;;;;;;;;;;uBAAsB,2BAAA,uBACT,kBAAA,CAAmB,SAAA;EAE9B,mBAAA,WAA8B,SAAA,GAAY,SAAA,CAAA,CAAW,IAAA,YAAgB,CAAA;EAKrE,iBAAA,CAAkB,QAAA,EAAU,SAAA,GAAY,UAAA;EALpB;;;;;;;;;;;;;;;;EAAA,UA4BV,2BAAA,CAA4B,IAAA,YAAgB,aAAA;EAwEA;;;;;;EAAA,UAtC5C,oBAAA,CAAqB,QAAA,EAAU,aAAA,GAAgB,aAAA;;ACrF3D;;;;;;;qBD2HqB,uBAAA,CAAwB,SAAA,EAAW,aAAA,GAAgB,SAAA;AAAA;;;;;;;AAvGxE;;;;;;cCpBa,uBAAA,SAAgC,2BAAA,CAA4B,aAAA;EAAA,UAC7D,uBAAA,CAAwB,SAAA,EAAW,aAAA,GAAgB,aAAA;AAAA;;;;;;ADmB/D;;;;;;;;;;;;;;;;;;uBEHsB,uBAAA;EAAA,SACS,OAAA,EAAS,YAAA;AAAA,uBAE3B,cAAA,CAAe,SAAA,EAAW,OAAA;EAErC,YAAA,CAAa,OAAA,EAAS,mBAAA,CAAoB,SAAA,EAAW,OAAA,IAAW,kBAAA;EAAA,UAOtD,uBAAA,CACR,OAAA,EAAS,mBAAA,CAAoB,SAAA,EAAW,OAAA,aAC9B,WAAA;EFR8B;;;;;;;;EAAA,mBEmCvB,eAAA,CAAgB,OAAA;IAAA,SACxB,QAAA,EAAU,SAAA;IAAA,SACV,MAAA,EAAQ,OAAA;IAAA,SACR,WAAA;IAAA,SACA,SAAA,EAAW,SAAA;EAAA,aACT,WAAA;EF4DM;;;;;EAAA,mBErDA,sBAAA,CACjB,OAAA,EAAS,mBAAA,CAAoB,SAAA,EAAW,OAAA,aAC9B,WAAA;AAAA"}
|
package/dist/ir.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as MongoContractSerializerBase, t as MongoContractSerializer } from "./mongo-contract-serializer-
|
|
1
|
+
import { n as MongoContractSerializerBase, t as MongoContractSerializer } from "./mongo-contract-serializer-D9zGIqE9.mjs";
|
|
2
2
|
//#region src/core/ir/mongo-schema-verifier-base.ts
|
|
3
3
|
/**
|
|
4
4
|
* Mongo family `SchemaVerifier` abstract base. Commits the Mongo family
|
package/dist/{mongo-contract-serializer-Co3EaTVj.mjs → mongo-contract-serializer-D9zGIqE9.mjs}
RENAMED
|
@@ -40,11 +40,11 @@ var MongoContractSerializerBase = class {
|
|
|
40
40
|
* default.
|
|
41
41
|
*
|
|
42
42
|
* The returned `MongoContract` carries class instances under
|
|
43
|
-
* `storage.collections` (each value is a
|
|
44
|
-
* nested `MongoIndex` / `MongoValidator` /
|
|
45
|
-
* constructed by the `MongoCollection` constructor).
|
|
46
|
-
* contract envelope (models, valueObjects, capabilities, …)
|
|
47
|
-
* in plain-JSON form; those IR layers are handled by sibling
|
|
43
|
+
* `storage.namespaces[namespaceId].collections[collectionName]` (each value is a
|
|
44
|
+
* `MongoCollection`, with nested `MongoIndex` / `MongoValidator` /
|
|
45
|
+
* `MongoCollectionOptions` constructed by the `MongoCollection` constructor).
|
|
46
|
+
* The rest of the contract envelope (models, valueObjects, capabilities, …)
|
|
47
|
+
* remains in plain-JSON form; those IR layers are handled by sibling
|
|
48
48
|
* subsystems and don't sit behind this SPI.
|
|
49
49
|
*/
|
|
50
50
|
parseMongoContractStructure(json) {
|
|
@@ -57,20 +57,27 @@ var MongoContractSerializerBase = class {
|
|
|
57
57
|
return hydratedContract;
|
|
58
58
|
}
|
|
59
59
|
/**
|
|
60
|
-
* Walk a structurally-validated Mongo contract and convert
|
|
61
|
-
* `storage.collections`
|
|
62
|
-
* `MongoCollection` IR-class instances. Idempotent: already-class
|
|
60
|
+
* Walk a structurally-validated Mongo contract and convert each
|
|
61
|
+
* `storage.namespaces[nsId].collections[collectionName]` entry from plain
|
|
62
|
+
* data into `MongoCollection` IR-class instances. Idempotent: already-class
|
|
63
63
|
* instances pass through unchanged.
|
|
64
64
|
*/
|
|
65
65
|
hydrateMongoContract(contract) {
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
|
|
66
|
+
const rawNamespaces = contract.storage.namespaces;
|
|
67
|
+
const hydratedNamespaces = Object.fromEntries(Object.entries(rawNamespaces).map(([nsId, nsEnvelope]) => {
|
|
68
|
+
const rawCollections = nsEnvelope.collections ?? {};
|
|
69
|
+
const hydratedCollections = Object.fromEntries(Object.entries(rawCollections).map(([name, raw]) => [name, raw instanceof MongoCollection ? raw : new MongoCollection(raw)]));
|
|
70
|
+
return [nsId, {
|
|
71
|
+
...nsEnvelope,
|
|
72
|
+
id: nsEnvelope.id,
|
|
73
|
+
collections: hydratedCollections
|
|
74
|
+
}];
|
|
75
|
+
}));
|
|
69
76
|
return {
|
|
70
77
|
...contract,
|
|
71
78
|
storage: {
|
|
72
79
|
...contract.storage,
|
|
73
|
-
|
|
80
|
+
namespaces: hydratedNamespaces
|
|
74
81
|
}
|
|
75
82
|
};
|
|
76
83
|
}
|
|
@@ -95,4 +102,4 @@ var MongoContractSerializer = class extends MongoContractSerializerBase {
|
|
|
95
102
|
//#endregion
|
|
96
103
|
export { MongoContractSerializerBase as n, MongoContractSerializer as t };
|
|
97
104
|
|
|
98
|
-
//# sourceMappingURL=mongo-contract-serializer-
|
|
105
|
+
//# sourceMappingURL=mongo-contract-serializer-D9zGIqE9.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mongo-contract-serializer-D9zGIqE9.mjs","names":["arktypeType"],"sources":["../src/core/ir/mongo-contract-serializer-base.ts","../src/core/ir/mongo-contract-serializer.ts"],"sourcesContent":["import { validateContractDomain } from '@prisma-next/contract/validate-domain';\nimport type { ContractSerializer } from '@prisma-next/framework-components/control';\nimport {\n MongoCollection,\n type MongoCollectionInput,\n type MongoContract,\n MongoContractSchema,\n validateMongoStorage,\n} from '@prisma-next/mongo-contract';\nimport type { JsonObject } from '@prisma-next/utils/json';\nimport { type as arktypeType } from 'arktype';\n\n/**\n * Mongo family `ContractSerializer` abstract base. Owns the family-shared\n * deserialization pipeline:\n *\n * 1. Structural validation against the Mongo contract arktype schema\n * (`MongoContractSchema`).\n * 2. Framework-shared domain validation (`validateContractDomain`).\n * 3. Family-shared storage validation (`validateMongoStorage`).\n *\n * The validated value is handed to the target via the\n * `constructTargetContract` hook, which wraps the plain-JSON shape in\n * the family-layer `MongoStorage` class instance (carrying the\n * target-supplied `namespaces` map). Targets that need to add\n * structural checks beyond the family default can override\n * `parseMongoContractStructure`.\n *\n * Default `serializeContract` is identity over the contract — Mongo\n * target classes carry JSON-clean fields by construction, so the value\n * can be `JSON.stringify`'d directly. Targets that need on-the-way-out\n * canonicalization override `serializeContract`.\n */\nexport abstract class MongoContractSerializerBase<TContract>\n implements ContractSerializer<TContract>\n{\n deserializeContract<T extends TContract = TContract>(json: unknown): T {\n const validated = this.parseMongoContractStructure(json);\n return this.constructTargetContract(validated) as T;\n }\n\n serializeContract(contract: TContract): JsonObject {\n // Mongo contract class fields are JSON-clean by construction; the\n // cast asserts that. Targets that need to canonicalize on the way\n // out override this method.\n return contract as unknown as JsonObject;\n }\n\n /**\n * Family-shared structural validation: parse against the Mongo\n * contract arktype schema, then run framework-shared domain + Mongo\n * family storage checks, then hydrate the validated tree into Mongo\n * Contract IR class instances. Targets can override to add\n * target-specific structural checks; most targets accept the family\n * default.\n *\n * The returned `MongoContract` carries class instances under\n * `storage.namespaces[namespaceId].collections[collectionName]` (each value is a\n * `MongoCollection`, with nested `MongoIndex` / `MongoValidator` /\n * `MongoCollectionOptions` constructed by the `MongoCollection` constructor).\n * The rest of the contract envelope (models, valueObjects, capabilities, …)\n * remains in plain-JSON form; those IR layers are handled by sibling\n * subsystems and don't sit behind this SPI.\n */\n protected parseMongoContractStructure(json: unknown): MongoContract {\n const parsed = MongoContractSchema(json);\n if (parsed instanceof arktypeType.errors) {\n throw new Error(`Contract structural validation failed: ${parsed.summary}`);\n }\n\n // arktype's `infer`d type for `MongoContractSchema` is structurally\n // equivalent to `MongoContract` (both describe the same on-disk JSON\n // envelope) but not nominally so: the arktype DSL produces a type whose\n // optional/readonly profile, narrowed string-literal positions, and\n // utility-type wrappings (`Type.infer`, `Out`, …) differ from the\n // hand-authored `MongoContract<S, M>` generic surface. The schema and\n // the type are kept in lockstep by the round-trip fixtures under\n // `test/validate.test.ts`. The hydration walk below additionally\n // re-shapes `storage.namespaces.*.collections` from plain data into IR-class\n // instances, so the `MongoContract` returned here carries class identity\n // under those collections maps (and transitively under `indexes` / `validator`\n // / `options`).\n const validatedShape = parsed as unknown as MongoContract;\n\n const hydratedContract = this.hydrateMongoContract(validatedShape);\n\n validateContractDomain(hydratedContract);\n validateMongoStorage(hydratedContract);\n\n return hydratedContract;\n }\n\n /**\n * Walk a structurally-validated Mongo contract and convert each\n * `storage.namespaces[nsId].collections[collectionName]` entry from plain\n * data into `MongoCollection` IR-class instances. Idempotent: already-class\n * instances pass through unchanged.\n */\n protected hydrateMongoContract(contract: MongoContract): MongoContract {\n const rawNamespaces = contract.storage.namespaces;\n const hydratedNamespaces = Object.fromEntries(\n Object.entries(rawNamespaces).map(([nsId, nsEnvelope]) => {\n const rawCollections = nsEnvelope.collections ?? {};\n const hydratedCollections = Object.fromEntries(\n Object.entries(rawCollections).map(([name, raw]) => [\n name,\n raw instanceof MongoCollection ? raw : new MongoCollection(raw as MongoCollectionInput),\n ]),\n );\n return [\n nsId,\n {\n ...nsEnvelope,\n id: nsEnvelope.id,\n collections: hydratedCollections,\n },\n ];\n }),\n );\n return {\n ...contract,\n storage: {\n ...contract.storage,\n namespaces: hydratedNamespaces,\n },\n };\n }\n\n /**\n * Target-specific class construction from the validated structural\n * data. The target wraps the contract envelope in the family-layer\n * `MongoStorage` class instance, supplying the `namespaces` map\n * (target concretions like `MongoTargetUnboundDatabase`). The\n * leaf collection / index shapes are already family-layer IR-class\n * instances after the hydration walk above.\n */\n protected abstract constructTargetContract(validated: MongoContract): TContract;\n}\n","import type { MongoContract } from '@prisma-next/mongo-contract';\nimport { MongoContractSerializerBase } from './mongo-contract-serializer-base';\n\n/**\n * Default Mongo family `ContractSerializer` concretion. Inherits the\n * Mongo-shared deserialization pipeline (structural validation +\n * collection-level hydration) and falls through `constructTargetContract`\n * with the validated `MongoContract` shape. Family-level call sites\n * (family-instance methods, family-layer tests that don't reach into\n * a target descriptor) instantiate this directly; targets with their\n * own storage concretion (`target-mongo`'s `MongoTargetContractSerializer`)\n * override `constructTargetContract` to wrap the storage shape.\n */\nexport class MongoContractSerializer extends MongoContractSerializerBase<MongoContract> {\n protected constructTargetContract(validated: MongoContract): MongoContract {\n return validated;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,IAAsB,8BAAtB,MAEA;CACE,oBAAqD,MAAkB;EACrE,MAAM,YAAY,KAAK,4BAA4B,KAAK;EACxD,OAAO,KAAK,wBAAwB,UAAU;;CAGhD,kBAAkB,UAAiC;EAIjD,OAAO;;;;;;;;;;;;;;;;;;CAmBT,4BAAsC,MAA8B;EAClE,MAAM,SAAS,oBAAoB,KAAK;EACxC,IAAI,kBAAkBA,KAAY,QAChC,MAAM,IAAI,MAAM,0CAA0C,OAAO,UAAU;EAe7E,MAAM,iBAAiB;EAEvB,MAAM,mBAAmB,KAAK,qBAAqB,eAAe;EAElE,uBAAuB,iBAAiB;EACxC,qBAAqB,iBAAiB;EAEtC,OAAO;;;;;;;;CAST,qBAA+B,UAAwC;EACrE,MAAM,gBAAgB,SAAS,QAAQ;EACvC,MAAM,qBAAqB,OAAO,YAChC,OAAO,QAAQ,cAAc,CAAC,KAAK,CAAC,MAAM,gBAAgB;GACxD,MAAM,iBAAiB,WAAW,eAAe,EAAE;GACnD,MAAM,sBAAsB,OAAO,YACjC,OAAO,QAAQ,eAAe,CAAC,KAAK,CAAC,MAAM,SAAS,CAClD,MACA,eAAe,kBAAkB,MAAM,IAAI,gBAAgB,IAA4B,CACxF,CAAC,CACH;GACD,OAAO,CACL,MACA;IACE,GAAG;IACH,IAAI,WAAW;IACf,aAAa;IACd,CACF;IACD,CACH;EACD,OAAO;GACL,GAAG;GACH,SAAS;IACP,GAAG,SAAS;IACZ,YAAY;IACb;GACF;;;;;;;;;;;;;;;AChHL,IAAa,0BAAb,cAA6C,4BAA2C;CACtF,wBAAkC,WAAyC;EACzE,OAAO"}
|
package/dist/schema-verify.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as verifyMongoSchema } from "./verify-mongo-schema-
|
|
1
|
+
import { t as verifyMongoSchema } from "./verify-mongo-schema-Bhxvdah3.mjs";
|
|
2
2
|
export { verifyMongoSchema };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { MongoSchemaCollection, MongoSchemaCollectionOptions, MongoSchemaIR, MongoSchemaIndex, MongoSchemaValidator, canonicalize, deepEqual } from "@prisma-next/mongo-schema-ir";
|
|
2
2
|
import { VERIFY_CODE_SCHEMA_FAILURE } from "@prisma-next/framework-components/control";
|
|
3
3
|
import { ifDefined } from "@prisma-next/utils/defined";
|
|
4
|
+
import { UNBOUND_NAMESPACE_ID } from "@prisma-next/framework-components/ir";
|
|
4
5
|
//#region src/core/contract-to-schema.ts
|
|
5
6
|
function stripIrKind(node) {
|
|
6
7
|
const { kind: _kind, ...rest } = node;
|
|
@@ -50,7 +51,9 @@ function convertCollection(name, def) {
|
|
|
50
51
|
}
|
|
51
52
|
function contractToMongoSchemaIR(contract) {
|
|
52
53
|
if (!contract) return new MongoSchemaIR([]);
|
|
53
|
-
|
|
54
|
+
const collections = [];
|
|
55
|
+
for (const ns of Object.values(contract.storage.namespaces)) for (const [name, def] of Object.entries(ns.collections)) collections.push(convertCollection(name, def));
|
|
56
|
+
return new MongoSchemaIR(collections);
|
|
54
57
|
}
|
|
55
58
|
//#endregion
|
|
56
59
|
//#region src/core/schema-diff.ts
|
|
@@ -74,7 +77,7 @@ function diffMongoSchemas(live, expected, strict) {
|
|
|
74
77
|
status: "fail",
|
|
75
78
|
kind: "collection",
|
|
76
79
|
name,
|
|
77
|
-
contractPath: `storage.collections.${name}`,
|
|
80
|
+
contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${name}`,
|
|
78
81
|
code: "MISSING_COLLECTION",
|
|
79
82
|
message: `Collection "${name}" is missing`,
|
|
80
83
|
expected: name,
|
|
@@ -95,7 +98,7 @@ function diffMongoSchemas(live, expected, strict) {
|
|
|
95
98
|
status,
|
|
96
99
|
kind: "collection",
|
|
97
100
|
name,
|
|
98
|
-
contractPath: `storage.collections.${name}`,
|
|
101
|
+
contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${name}`,
|
|
99
102
|
code: "EXTRA_COLLECTION",
|
|
100
103
|
message: `Extra collection "${name}" found`,
|
|
101
104
|
expected: null,
|
|
@@ -125,7 +128,7 @@ function diffMongoSchemas(live, expected, strict) {
|
|
|
125
128
|
status: worstStatus,
|
|
126
129
|
kind: "collection",
|
|
127
130
|
name,
|
|
128
|
-
contractPath: `storage.collections.${name}`,
|
|
131
|
+
contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${name}`,
|
|
129
132
|
code: worstStatus === "pass" ? "MATCH" : "DRIFT",
|
|
130
133
|
message: worstStatus === "pass" ? `Collection "${name}" matches` : `Collection "${name}" has drift`,
|
|
131
134
|
expected: name,
|
|
@@ -184,7 +187,7 @@ function diffIndexes(collName, live, expected, strict, issues) {
|
|
|
184
187
|
status: "pass",
|
|
185
188
|
kind: "index",
|
|
186
189
|
name: formatIndexName(idx),
|
|
187
|
-
contractPath: `storage.collections.${collName}.indexes`,
|
|
190
|
+
contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${collName}.indexes`,
|
|
188
191
|
code: "MATCH",
|
|
189
192
|
message: `Index ${formatIndexName(idx)} matches`,
|
|
190
193
|
expected: key,
|
|
@@ -202,7 +205,7 @@ function diffIndexes(collName, live, expected, strict, issues) {
|
|
|
202
205
|
status: "fail",
|
|
203
206
|
kind: "index",
|
|
204
207
|
name: formatIndexName(idx),
|
|
205
|
-
contractPath: `storage.collections.${collName}.indexes`,
|
|
208
|
+
contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${collName}.indexes`,
|
|
206
209
|
code: "MISSING_INDEX",
|
|
207
210
|
message: `Index ${formatIndexName(idx)} missing`,
|
|
208
211
|
expected: key,
|
|
@@ -222,7 +225,7 @@ function diffIndexes(collName, live, expected, strict, issues) {
|
|
|
222
225
|
status,
|
|
223
226
|
kind: "index",
|
|
224
227
|
name: formatIndexName(idx),
|
|
225
|
-
contractPath: `storage.collections.${collName}.indexes`,
|
|
228
|
+
contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${collName}.indexes`,
|
|
226
229
|
code: "EXTRA_INDEX",
|
|
227
230
|
message: `Extra index ${formatIndexName(idx)}`,
|
|
228
231
|
expected: null,
|
|
@@ -244,7 +247,7 @@ function diffValidator(collName, live, expected, strict, issues) {
|
|
|
244
247
|
status: "fail",
|
|
245
248
|
kind: "validator",
|
|
246
249
|
name: "validator",
|
|
247
|
-
contractPath: `storage.collections.${collName}.validator`,
|
|
250
|
+
contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${collName}.validator`,
|
|
248
251
|
code: "MISSING_VALIDATOR",
|
|
249
252
|
message: "Validator missing",
|
|
250
253
|
expected: canonicalize(expected.validator.jsonSchema),
|
|
@@ -263,7 +266,7 @@ function diffValidator(collName, live, expected, strict, issues) {
|
|
|
263
266
|
status,
|
|
264
267
|
kind: "validator",
|
|
265
268
|
name: "validator",
|
|
266
|
-
contractPath: `storage.collections.${collName}.validator`,
|
|
269
|
+
contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${collName}.validator`,
|
|
267
270
|
code: "EXTRA_VALIDATOR",
|
|
268
271
|
message: "Extra validator found",
|
|
269
272
|
expected: null,
|
|
@@ -287,7 +290,7 @@ function diffValidator(collName, live, expected, strict, issues) {
|
|
|
287
290
|
status: "fail",
|
|
288
291
|
kind: "validator",
|
|
289
292
|
name: "validator",
|
|
290
|
-
contractPath: `storage.collections.${collName}.validator`,
|
|
293
|
+
contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${collName}.validator`,
|
|
291
294
|
code: "VALIDATOR_MISMATCH",
|
|
292
295
|
message: "Validator mismatch",
|
|
293
296
|
expected: {
|
|
@@ -307,7 +310,7 @@ function diffValidator(collName, live, expected, strict, issues) {
|
|
|
307
310
|
status: "pass",
|
|
308
311
|
kind: "validator",
|
|
309
312
|
name: "validator",
|
|
310
|
-
contractPath: `storage.collections.${collName}.validator`,
|
|
313
|
+
contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${collName}.validator`,
|
|
311
314
|
code: "MATCH",
|
|
312
315
|
message: "Validator matches",
|
|
313
316
|
expected: expectedSchema,
|
|
@@ -329,7 +332,7 @@ function diffOptions(collName, live, expected, strict, issues) {
|
|
|
329
332
|
status,
|
|
330
333
|
kind: "options",
|
|
331
334
|
name: "options",
|
|
332
|
-
contractPath: `storage.collections.${collName}.options`,
|
|
335
|
+
contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${collName}.options`,
|
|
333
336
|
code: "EXTRA_OPTIONS",
|
|
334
337
|
message: "Extra collection options found",
|
|
335
338
|
expected: null,
|
|
@@ -341,7 +344,7 @@ function diffOptions(collName, live, expected, strict, issues) {
|
|
|
341
344
|
status: "pass",
|
|
342
345
|
kind: "options",
|
|
343
346
|
name: "options",
|
|
344
|
-
contractPath: `storage.collections.${collName}.options`,
|
|
347
|
+
contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${collName}.options`,
|
|
345
348
|
code: "MATCH",
|
|
346
349
|
message: "Collection options match",
|
|
347
350
|
expected: canonicalize(expected.options),
|
|
@@ -359,7 +362,7 @@ function diffOptions(collName, live, expected, strict, issues) {
|
|
|
359
362
|
status: "fail",
|
|
360
363
|
kind: "options",
|
|
361
364
|
name: "options",
|
|
362
|
-
contractPath: `storage.collections.${collName}.options`,
|
|
365
|
+
contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${collName}.options`,
|
|
363
366
|
code: "OPTIONS_MISMATCH",
|
|
364
367
|
message: "Collection options mismatch",
|
|
365
368
|
expected: expected.options,
|
|
@@ -589,4 +592,4 @@ function verifyMongoSchema(options) {
|
|
|
589
592
|
//#endregion
|
|
590
593
|
export { contractToMongoSchemaIR as i, canonicalizeSchemasForVerification as n, diffMongoSchemas as r, verifyMongoSchema as t };
|
|
591
594
|
|
|
592
|
-
//# sourceMappingURL=verify-mongo-schema-
|
|
595
|
+
//# sourceMappingURL=verify-mongo-schema-Bhxvdah3.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify-mongo-schema-Bhxvdah3.mjs","names":["MongoSchemaIRCtor","MongoSchemaCollectionCtor","MongoSchemaIndexCtor","MongoSchemaCollectionOptionsCtor"],"sources":["../src/core/contract-to-schema.ts","../src/core/schema-diff.ts","../src/core/schema-verify/canonicalize-introspection.ts","../src/core/schema-verify/verify-mongo-schema.ts"],"sourcesContent":["import type {\n MongoCollection,\n MongoCollectionOptions,\n MongoContract,\n MongoIndex,\n MongoValidator,\n} from '@prisma-next/mongo-contract';\nimport {\n MongoSchemaCollection,\n MongoSchemaCollectionOptions,\n MongoSchemaIndex,\n MongoSchemaIR,\n MongoSchemaValidator,\n} from '@prisma-next/mongo-schema-ir';\n\nfunction stripIrKind(node: object): Record<string, unknown> {\n const { kind: _kind, ...rest } = node as Record<string, unknown>;\n return rest;\n}\n\nfunction convertIndex(index: MongoIndex): MongoSchemaIndex {\n return new MongoSchemaIndex({\n keys: index.keys,\n unique: index.unique,\n sparse: index.sparse,\n expireAfterSeconds: index.expireAfterSeconds,\n partialFilterExpression: index.partialFilterExpression,\n wildcardProjection: index.wildcardProjection,\n collation: index.collation,\n weights: index.weights,\n default_language: index.default_language,\n language_override: index.language_override,\n });\n}\n\nfunction convertValidator(v: MongoValidator): MongoSchemaValidator {\n return new MongoSchemaValidator({\n jsonSchema: v.jsonSchema,\n validationLevel: v.validationLevel,\n validationAction: v.validationAction,\n });\n}\n\nfunction convertOptions(o: MongoCollectionOptions): MongoSchemaCollectionOptions {\n return new MongoSchemaCollectionOptions({\n ...(o.capped !== undefined && { capped: o.capped }),\n ...(o.timeseries !== undefined && {\n timeseries: {\n timeField: o.timeseries.timeField,\n ...(o.timeseries.metaField !== undefined && { metaField: o.timeseries.metaField }),\n ...(o.timeseries.granularity !== undefined && { granularity: o.timeseries.granularity }),\n },\n }),\n ...(o.collation !== undefined && {\n collation: stripIrKind(o.collation),\n }),\n ...(o.changeStreamPreAndPostImages !== undefined && {\n changeStreamPreAndPostImages: { enabled: o.changeStreamPreAndPostImages.enabled },\n }),\n ...(o.clusteredIndex !== undefined && { clusteredIndex: o.clusteredIndex }),\n });\n}\n\nfunction convertCollection(name: string, def: MongoCollection): MongoSchemaCollection {\n const indexes = (def.indexes ?? []).map(convertIndex);\n return new MongoSchemaCollection({\n name,\n indexes,\n ...(def.validator != null && { validator: convertValidator(def.validator) }),\n ...(def.options != null && { options: convertOptions(def.options) }),\n });\n}\n\nexport function contractToMongoSchemaIR(contract: MongoContract | null): MongoSchemaIR {\n if (!contract) {\n return new MongoSchemaIR([]);\n }\n\n const collections: MongoSchemaCollection[] = [];\n for (const ns of Object.values(contract.storage.namespaces)) {\n for (const [name, def] of Object.entries(ns.collections)) {\n collections.push(convertCollection(name, def));\n }\n }\n\n return new MongoSchemaIR(collections);\n}\n","import type {\n SchemaIssue,\n SchemaVerificationNode,\n} from '@prisma-next/framework-components/control';\nimport { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';\nimport type {\n MongoSchemaCollection,\n MongoSchemaIndex,\n MongoSchemaIR,\n} from '@prisma-next/mongo-schema-ir';\nimport { canonicalize, deepEqual } from '@prisma-next/mongo-schema-ir';\n\nexport function diffMongoSchemas(\n live: MongoSchemaIR,\n expected: MongoSchemaIR,\n strict: boolean,\n): {\n root: SchemaVerificationNode;\n issues: SchemaIssue[];\n counts: { pass: number; warn: number; fail: number; totalNodes: number };\n} {\n const issues: SchemaIssue[] = [];\n const collectionChildren: SchemaVerificationNode[] = [];\n let pass = 0;\n let warn = 0;\n let fail = 0;\n\n const allNames = new Set([...live.collectionNames, ...expected.collectionNames]);\n\n for (const name of [...allNames].sort()) {\n const liveColl = live.collection(name);\n const expectedColl = expected.collection(name);\n\n if (!liveColl && expectedColl) {\n issues.push({\n kind: 'missing_table',\n table: name,\n message: `Collection \"${name}\" is missing from the database`,\n });\n collectionChildren.push({\n status: 'fail',\n kind: 'collection',\n name,\n contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${name}`,\n code: 'MISSING_COLLECTION',\n message: `Collection \"${name}\" is missing`,\n expected: name,\n actual: null,\n children: [],\n });\n fail++;\n continue;\n }\n\n if (liveColl && !expectedColl) {\n const status = strict ? 'fail' : 'warn';\n issues.push({\n kind: 'extra_table',\n table: name,\n message: `Extra collection \"${name}\" exists in the database but not in the contract`,\n });\n collectionChildren.push({\n status,\n kind: 'collection',\n name,\n contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${name}`,\n code: 'EXTRA_COLLECTION',\n message: `Extra collection \"${name}\" found`,\n expected: null,\n actual: name,\n children: [],\n });\n if (status === 'fail') fail++;\n else warn++;\n continue;\n }\n\n const lc = liveColl as MongoSchemaCollection;\n const ec = expectedColl as MongoSchemaCollection;\n const indexChildren = diffIndexes(name, lc, ec, strict, issues);\n const validatorChildren = diffValidator(name, lc, ec, strict, issues);\n const optionsChildren = diffOptions(name, lc, ec, strict, issues);\n const children = [...indexChildren, ...validatorChildren, ...optionsChildren];\n\n const worstStatus = children.reduce<'pass' | 'warn' | 'fail'>(\n (s, c) => (c.status === 'fail' ? 'fail' : c.status === 'warn' && s !== 'fail' ? 'warn' : s),\n 'pass',\n );\n\n for (const c of children) {\n if (c.status === 'pass') pass++;\n else if (c.status === 'warn') warn++;\n else fail++;\n }\n\n if (children.length === 0) {\n pass++;\n }\n\n collectionChildren.push({\n status: worstStatus,\n kind: 'collection',\n name,\n contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${name}`,\n code: worstStatus === 'pass' ? 'MATCH' : 'DRIFT',\n message:\n worstStatus === 'pass' ? `Collection \"${name}\" matches` : `Collection \"${name}\" has drift`,\n expected: name,\n actual: name,\n children,\n });\n }\n\n const rootStatus = fail > 0 ? 'fail' : warn > 0 ? 'warn' : 'pass';\n const totalNodes = pass + warn + fail + collectionChildren.length;\n\n const root: SchemaVerificationNode = {\n status: rootStatus,\n kind: 'root',\n name: 'mongo-schema',\n contractPath: 'storage',\n code: rootStatus === 'pass' ? 'MATCH' : 'DRIFT',\n message: rootStatus === 'pass' ? 'Schema matches' : 'Schema has drift',\n expected: null,\n actual: null,\n children: collectionChildren,\n };\n\n return { root, issues, counts: { pass, warn, fail, totalNodes } };\n}\n\nfunction buildIndexLookupKey(index: MongoSchemaIndex): string {\n const keys = index.keys.map((k) => `${k.field}:${k.direction}`).join(',');\n const opts = [\n index.unique ? 'unique' : '',\n index.sparse ? 'sparse' : '',\n index.expireAfterSeconds != null ? `ttl:${index.expireAfterSeconds}` : '',\n index.partialFilterExpression ? `pfe:${canonicalize(index.partialFilterExpression)}` : '',\n index.wildcardProjection ? `wp:${canonicalize(index.wildcardProjection)}` : '',\n index.collation ? `col:${canonicalize(index.collation)}` : '',\n index.weights ? `wt:${canonicalize(index.weights)}` : '',\n index.default_language ? `dl:${index.default_language}` : '',\n index.language_override ? `lo:${index.language_override}` : '',\n ]\n .filter(Boolean)\n .join(';');\n return opts ? `${keys}|${opts}` : keys;\n}\n\nfunction formatIndexName(index: MongoSchemaIndex): string {\n return index.keys.map((k) => `${k.field}:${k.direction}`).join(', ');\n}\n\nfunction diffIndexes(\n collName: string,\n live: MongoSchemaCollection,\n expected: MongoSchemaCollection,\n strict: boolean,\n issues: SchemaIssue[],\n): SchemaVerificationNode[] {\n const nodes: SchemaVerificationNode[] = [];\n const liveLookup = new Map<string, MongoSchemaIndex>();\n for (const idx of live.indexes) liveLookup.set(buildIndexLookupKey(idx), idx);\n\n const expectedLookup = new Map<string, MongoSchemaIndex>();\n for (const idx of expected.indexes) expectedLookup.set(buildIndexLookupKey(idx), idx);\n\n for (const [key, idx] of expectedLookup) {\n if (liveLookup.has(key)) {\n nodes.push({\n status: 'pass',\n kind: 'index',\n name: formatIndexName(idx),\n contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${collName}.indexes`,\n code: 'MATCH',\n message: `Index ${formatIndexName(idx)} matches`,\n expected: key,\n actual: key,\n children: [],\n });\n } else {\n issues.push({\n kind: 'index_mismatch',\n table: collName,\n indexOrConstraint: formatIndexName(idx),\n message: `Index ${formatIndexName(idx)} missing on collection \"${collName}\"`,\n });\n nodes.push({\n status: 'fail',\n kind: 'index',\n name: formatIndexName(idx),\n contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${collName}.indexes`,\n code: 'MISSING_INDEX',\n message: `Index ${formatIndexName(idx)} missing`,\n expected: key,\n actual: null,\n children: [],\n });\n }\n }\n\n for (const [key, idx] of liveLookup) {\n if (!expectedLookup.has(key)) {\n const status = strict ? 'fail' : 'warn';\n issues.push({\n kind: 'extra_index',\n table: collName,\n indexOrConstraint: formatIndexName(idx),\n message: `Extra index ${formatIndexName(idx)} on collection \"${collName}\"`,\n });\n nodes.push({\n status,\n kind: 'index',\n name: formatIndexName(idx),\n contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${collName}.indexes`,\n code: 'EXTRA_INDEX',\n message: `Extra index ${formatIndexName(idx)}`,\n expected: null,\n actual: key,\n children: [],\n });\n }\n }\n\n return nodes;\n}\n\nfunction diffValidator(\n collName: string,\n live: MongoSchemaCollection,\n expected: MongoSchemaCollection,\n strict: boolean,\n issues: SchemaIssue[],\n): SchemaVerificationNode[] {\n if (!live.validator && !expected.validator) return [];\n\n if (expected.validator && !live.validator) {\n issues.push({\n kind: 'type_missing',\n table: collName,\n message: `Validator missing on collection \"${collName}\"`,\n });\n return [\n {\n status: 'fail',\n kind: 'validator',\n name: 'validator',\n contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${collName}.validator`,\n code: 'MISSING_VALIDATOR',\n message: 'Validator missing',\n expected: canonicalize(expected.validator.jsonSchema),\n actual: null,\n children: [],\n },\n ];\n }\n\n if (!expected.validator && live.validator) {\n const status = strict ? 'fail' : 'warn';\n issues.push({\n kind: 'extra_validator',\n table: collName,\n message: `Extra validator on collection \"${collName}\"`,\n });\n return [\n {\n status,\n kind: 'validator',\n name: 'validator',\n contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${collName}.validator`,\n code: 'EXTRA_VALIDATOR',\n message: 'Extra validator found',\n expected: null,\n actual: canonicalize(live.validator.jsonSchema),\n children: [],\n },\n ];\n }\n\n const liveVal = live.validator as NonNullable<typeof live.validator>;\n const expectedVal = expected.validator as NonNullable<typeof expected.validator>;\n const liveSchema = canonicalize(liveVal.jsonSchema);\n const expectedSchema = canonicalize(expectedVal.jsonSchema);\n\n if (\n liveSchema !== expectedSchema ||\n liveVal.validationLevel !== expectedVal.validationLevel ||\n liveVal.validationAction !== expectedVal.validationAction\n ) {\n issues.push({\n kind: 'type_mismatch',\n table: collName,\n expected: expectedSchema,\n actual: liveSchema,\n message: `Validator mismatch on collection \"${collName}\"`,\n });\n return [\n {\n status: 'fail',\n kind: 'validator',\n name: 'validator',\n contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${collName}.validator`,\n code: 'VALIDATOR_MISMATCH',\n message: 'Validator mismatch',\n expected: {\n jsonSchema: expectedVal.jsonSchema,\n validationLevel: expectedVal.validationLevel,\n validationAction: expectedVal.validationAction,\n },\n actual: {\n jsonSchema: liveVal.jsonSchema,\n validationLevel: liveVal.validationLevel,\n validationAction: liveVal.validationAction,\n },\n children: [],\n },\n ];\n }\n\n return [\n {\n status: 'pass',\n kind: 'validator',\n name: 'validator',\n contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${collName}.validator`,\n code: 'MATCH',\n message: 'Validator matches',\n expected: expectedSchema,\n actual: liveSchema,\n children: [],\n },\n ];\n}\n\nfunction diffOptions(\n collName: string,\n live: MongoSchemaCollection,\n expected: MongoSchemaCollection,\n strict: boolean,\n issues: SchemaIssue[],\n): SchemaVerificationNode[] {\n if (!live.options && !expected.options) return [];\n\n if (!expected.options && live.options) {\n const status = strict ? 'fail' : 'warn';\n issues.push({\n kind: 'type_mismatch',\n table: collName,\n actual: canonicalize(live.options),\n message: `Extra collection options on \"${collName}\"`,\n });\n return [\n {\n status,\n kind: 'options',\n name: 'options',\n contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${collName}.options`,\n code: 'EXTRA_OPTIONS',\n message: 'Extra collection options found',\n expected: null,\n actual: live.options,\n children: [],\n },\n ];\n }\n\n if (deepEqual(live.options, expected.options)) {\n return [\n {\n status: 'pass',\n kind: 'options',\n name: 'options',\n contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${collName}.options`,\n code: 'MATCH',\n message: 'Collection options match',\n expected: canonicalize(expected.options),\n actual: canonicalize(live.options),\n children: [],\n },\n ];\n }\n\n issues.push({\n kind: 'type_mismatch',\n table: collName,\n expected: canonicalize(expected.options),\n actual: canonicalize(live.options),\n message: `Collection options mismatch on \"${collName}\"`,\n });\n return [\n {\n status: 'fail',\n kind: 'options',\n name: 'options',\n contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${collName}.options`,\n code: 'OPTIONS_MISMATCH',\n message: 'Collection options mismatch',\n expected: expected.options,\n actual: live.options,\n children: [],\n },\n ];\n}\n","/**\n * Canonicalizes a live (introspected) `MongoSchemaIR` against the expected\n * (contract-built) IR before diffing. MongoDB applies server-side defaults\n * to several option/index families that are absent from authored contracts,\n * which would otherwise cause `verifyMongoSchema` to report false-positive\n * drift on a fresh `migrate` run.\n *\n * The normalization is contract-aware where it has to be: server defaults\n * are stripped from the live IR for fields the contract did not specify, so\n * a contract that *does* specify a value still gets compared faithfully.\n *\n * Symmetric defaults — like `changeStreamPreAndPostImages: { enabled: false }`,\n * which is equivalent to \"absent\" on both sides — are stripped from both IRs\n * so either authoring style verifies.\n */\n\nimport type {\n MongoSchemaCollection,\n MongoSchemaCollectionOptions,\n MongoSchemaIndex,\n MongoSchemaIR,\n} from '@prisma-next/mongo-schema-ir';\nimport {\n MongoSchemaCollection as MongoSchemaCollectionCtor,\n MongoSchemaCollectionOptions as MongoSchemaCollectionOptionsCtor,\n MongoSchemaIndex as MongoSchemaIndexCtor,\n MongoSchemaIR as MongoSchemaIRCtor,\n} from '@prisma-next/mongo-schema-ir';\nimport { ifDefined } from '@prisma-next/utils/defined';\n\nexport interface CanonicalizedSchemas {\n readonly live: MongoSchemaIR;\n readonly expected: MongoSchemaIR;\n}\n\nexport function canonicalizeSchemasForVerification(\n live: MongoSchemaIR,\n expected: MongoSchemaIR,\n): CanonicalizedSchemas {\n const expectedByName = new Map<string, MongoSchemaCollection>();\n for (const c of expected.collections) expectedByName.set(c.name, c);\n\n const liveByName = new Map<string, MongoSchemaCollection>();\n for (const c of live.collections) liveByName.set(c.name, c);\n\n const canonicalLive = live.collections.map((c) =>\n canonicalizeLiveCollection(c, expectedByName.get(c.name)),\n );\n const canonicalExpected = expected.collections.map((c) =>\n canonicalizeExpectedCollection(c, liveByName.get(c.name)),\n );\n\n return {\n live: new MongoSchemaIRCtor(canonicalLive),\n expected: new MongoSchemaIRCtor(canonicalExpected),\n };\n}\n\nfunction canonicalizeLiveCollection(\n liveColl: MongoSchemaCollection,\n expectedColl: MongoSchemaCollection | undefined,\n): MongoSchemaCollection {\n const expectedIndexes = expectedColl?.indexes ?? [];\n const indexes = liveColl.indexes.map((idx) =>\n canonicalizeLiveIndex(idx, findExpectedIndexCounterpart(idx, expectedIndexes)),\n );\n\n const options = liveColl.options\n ? canonicalizeLiveOptions(liveColl.options, expectedColl?.options)\n : undefined;\n\n return new MongoSchemaCollectionCtor({\n name: liveColl.name,\n indexes,\n ...ifDefined('validator', liveColl.validator),\n ...ifDefined('options', options),\n });\n}\n\nfunction canonicalizeExpectedCollection(\n expectedColl: MongoSchemaCollection,\n liveColl: MongoSchemaCollection | undefined,\n): MongoSchemaCollection {\n // Symmetric text-index key ordering: a contract-shaped text index preserves\n // the user-authored field order, but the introspected counterpart comes\n // back from MongoDB with `weights` keys in alphabetical order, so we\n // canonicalize both sides to alphabetical text-key order. The order of\n // text fields within the text block is semantically irrelevant — relevance\n // is governed by `weights`, not key order.\n const indexes = expectedColl.indexes.map(canonicalizeTextIndexKeyOrder);\n\n const options = expectedColl.options\n ? canonicalizeExpectedOptions(expectedColl.options, liveColl?.options)\n : undefined;\n\n return new MongoSchemaCollectionCtor({\n name: expectedColl.name,\n indexes,\n ...ifDefined('validator', expectedColl.validator),\n ...ifDefined('options', options),\n });\n}\n\nfunction canonicalizeTextIndexKeyOrder(index: MongoSchemaIndex): MongoSchemaIndex {\n const hasTextKey = index.keys.some((k) => k.direction === 'text');\n if (!hasTextKey) return index;\n return new MongoSchemaIndexCtor({\n keys: sortTextKeys(index.keys),\n unique: index.unique,\n ...ifDefined('sparse', index.sparse),\n ...ifDefined('expireAfterSeconds', index.expireAfterSeconds),\n ...ifDefined('partialFilterExpression', index.partialFilterExpression),\n ...ifDefined('wildcardProjection', index.wildcardProjection),\n ...ifDefined('collation', index.collation),\n ...ifDefined('weights', index.weights),\n ...ifDefined('default_language', index.default_language),\n ...ifDefined('language_override', index.language_override),\n });\n}\n\n/**\n * Returns a copy of `keys` with text-direction entries sorted alphabetically\n * while preserving the relative position of non-text entries. Compound text\n * indexes (`{a: 1, _fts: 'text', _ftsx: 1, b: 1}`) keep their scalar\n * prefix/suffix layout; only the contiguous text block is reordered.\n */\nfunction sortTextKeys(\n keys: ReadonlyArray<{\n readonly field: string;\n readonly direction: 'text' | 1 | -1 | '2dsphere' | '2d' | 'hashed';\n }>,\n): ReadonlyArray<{\n readonly field: string;\n readonly direction: 'text' | 1 | -1 | '2dsphere' | '2d' | 'hashed';\n}> {\n const textEntries = keys.filter((k) => k.direction === 'text');\n if (textEntries.length <= 1) return keys;\n const sortedText = [...textEntries].sort((a, b) => a.field.localeCompare(b.field));\n let textIdx = 0;\n return keys.map((k) => {\n if (k.direction !== 'text') return k;\n const next = sortedText[textIdx++];\n /* v8 ignore next 3 -- @preserve invariant guard: textIdx is always < sortedText.length here because we only consume sortedText for text-direction entries and sortedText is built from the same filter. */\n if (next === undefined) {\n throw new Error('sortTextKeys: text-key counts mismatched');\n }\n return next;\n });\n}\n\nfunction canonicalizeLiveIndex(\n liveIndex: MongoSchemaIndex,\n expectedIndex: MongoSchemaIndex | undefined,\n): MongoSchemaIndex {\n const projectedKeys = sortTextKeys(projectTextIndexKeys(liveIndex));\n const collation = liveIndex.collation\n ? stripUnspecifiedFields(liveIndex.collation, expectedIndex?.collation)\n : liveIndex.collation;\n\n // Text-index server defaults: when the contract did not set\n // `weights`/`default_language`/`language_override`, MongoDB applies\n // `weights = {<field>: 1, ...}` (uniform), `'english'`, and `'language'`\n // respectively. Strip them from live *only* when the live value matches\n // those defaults — preserving non-default live values lets the verifier\n // surface drift when the live index is tampered (e.g. weights tuned\n // out-of-band, custom `default_language`/`language_override`) even though\n // the contract authored neither.\n const weights =\n expectedIndex?.weights === undefined && hasDefaultTextWeights(projectedKeys, liveIndex.weights)\n ? undefined\n : liveIndex.weights;\n const default_language =\n expectedIndex?.default_language === undefined && liveIndex.default_language === 'english'\n ? undefined\n : liveIndex.default_language;\n const language_override =\n expectedIndex?.language_override === undefined && liveIndex.language_override === 'language'\n ? undefined\n : liveIndex.language_override;\n\n return new MongoSchemaIndexCtor({\n keys: projectedKeys,\n unique: liveIndex.unique,\n ...ifDefined('sparse', liveIndex.sparse),\n ...ifDefined('expireAfterSeconds', liveIndex.expireAfterSeconds),\n ...ifDefined('partialFilterExpression', liveIndex.partialFilterExpression),\n ...ifDefined('wildcardProjection', liveIndex.wildcardProjection),\n ...ifDefined('collation', collation),\n ...ifDefined('weights', weights),\n ...ifDefined('default_language', default_language),\n ...ifDefined('language_override', language_override),\n });\n}\n\n/**\n * Locate the contract-side index that corresponds to a live index for the\n * purpose of contract-aware normalization. We deliberately match by the\n * *projected* (contract-shaped) key list — so a live `_fts/_ftsx` index\n * resolves to a contract `{title: 'text', body: 'text'}` index — and pick\n * the first match. Contracts very rarely contain duplicate-key indexes; if\n * we have no counterpart we fall back to no normalization for that index.\n */\nfunction findExpectedIndexCounterpart(\n liveIndex: MongoSchemaIndex,\n expectedIndexes: ReadonlyArray<MongoSchemaIndex>,\n): MongoSchemaIndex | undefined {\n const projectedLiveKeys = sortTextKeys(projectTextIndexKeys(liveIndex));\n const liveKeySig = projectedLiveKeys.map((k) => `${k.field}:${k.direction}`).join(',');\n for (const expected of expectedIndexes) {\n const expectedKeySig = sortTextKeys(expected.keys)\n .map((k) => `${k.field}:${k.direction}`)\n .join(',');\n if (expectedKeySig === liveKeySig) return expected;\n }\n return undefined;\n}\n\n/**\n * MongoDB expands a contract-shaped text index like\n * `[{title: 'text'}, {body: 'text'}]` into its internal weighted vector\n * representation `[{_fts: 'text'}, {_ftsx: 1}]`. We project back to\n * contract-shaped keys via `weights`, iterating in whatever order MongoDB\n * returns them (alphabetical, in practice). `sortTextKeys` is applied\n * downstream to canonicalize the order on both sides, so this projection\n * does not depend on a specific iteration order.\n */\nfunction projectTextIndexKeys(liveIndex: MongoSchemaIndex): ReadonlyArray<{\n readonly field: string;\n readonly direction: 'text' | 1 | -1 | '2dsphere' | '2d' | 'hashed';\n}> {\n const isTextIndex =\n liveIndex.keys.length >= 1 &&\n liveIndex.keys.some((k) => k.field === '_fts' && k.direction === 'text');\n\n if (!isTextIndex || !liveIndex.weights) return liveIndex.keys;\n\n const textKeys = Object.keys(liveIndex.weights).map((field) => ({\n field,\n direction: 'text' as const,\n }));\n\n // Splice the projected text fields into the original `_fts/_ftsx` slot so\n // compound text indexes that mix scalar prefixes *and* suffixes — e.g.\n // `[prefix, _fts, _ftsx, suffix]` — keep their original layout. Flattening\n // scalars first would yield `[prefix, suffix, ...text]`, which `sortTextKeys`\n // (downstream) cannot recover because it only reorders text-direction\n // entries within their existing positions. MongoDB always emits exactly one\n // `_fts`/`_ftsx` pair per index, so we don't need to guard against\n // duplicates.\n type IndexKey = (typeof liveIndex.keys)[number];\n const projectedKeys: IndexKey[] = [];\n for (const key of liveIndex.keys) {\n if (key.field === '_ftsx') continue;\n if (key.field === '_fts') {\n projectedKeys.push(...textKeys);\n continue;\n }\n projectedKeys.push(key);\n }\n return projectedKeys;\n}\n\n/**\n * MongoDB's server-default `weights` for an authored-without-weights text\n * index assigns `1` to every text-direction field. Returns `true` only when\n * `liveWeights` is exactly that uniform shape (every projected text-direction\n * key weighted at `1`) so the canonicalizer leaves non-default weights —\n * including out-of-band relevance tweaks — visible to the verifier.\n *\n * `projectTextIndexKeys` derives text-direction keys from the live weights\n * map, so the count is guaranteed to match; we only have to check the value\n * shape.\n */\nfunction hasDefaultTextWeights(\n projectedKeys: ReadonlyArray<{\n readonly field: string;\n readonly direction: 'text' | 1 | -1 | '2dsphere' | '2d' | 'hashed';\n }>,\n liveWeights: MongoSchemaIndex['weights'],\n): boolean {\n if (liveWeights === undefined) return false;\n const textFields = projectedKeys.filter((k) => k.direction === 'text').map((k) => k.field);\n return textFields.every((field) => liveWeights[field] === 1);\n}\n\nfunction canonicalizeLiveOptions(\n liveOptions: MongoSchemaCollectionOptions,\n expectedOptions: MongoSchemaCollectionOptions | undefined,\n): MongoSchemaCollectionOptions | undefined {\n const collation = liveOptions.collation\n ? stripUnspecifiedFields(liveOptions.collation, expectedOptions?.collation)\n : undefined;\n\n // Timeseries: drop `bucketMaxSpanSeconds` (and any other server-applied\n // extras) when the contract did not specify them.\n const timeseries = liveOptions.timeseries\n ? (stripUnspecifiedFields(\n liveOptions.timeseries as Record<string, unknown>,\n expectedOptions?.timeseries as Record<string, unknown> | undefined,\n ) as MongoSchemaCollectionOptions['timeseries'])\n : undefined;\n\n // ClusteredIndex: drop `key`, `unique`, `v` and any other server-applied\n // extras when the contract did not specify them.\n const clusteredIndex = liveOptions.clusteredIndex\n ? (stripUnspecifiedFields(\n liveOptions.clusteredIndex as Record<string, unknown>,\n expectedOptions?.clusteredIndex as Record<string, unknown> | undefined,\n ) as MongoSchemaCollectionOptions['clusteredIndex'])\n : undefined;\n\n // changeStreamPreAndPostImages: `{enabled: false}` is equivalent to\n // \"absent\". Strip it from live so it round-trips with a contract that\n // omits the field, and is symmetric with the expected-side stripping.\n const changeStreamPreAndPostImages = isDisabledChangeStream(\n liveOptions.changeStreamPreAndPostImages,\n )\n ? undefined\n : liveOptions.changeStreamPreAndPostImages;\n\n const hasMeaningful =\n liveOptions.capped || timeseries || collation || changeStreamPreAndPostImages || clusteredIndex;\n if (!hasMeaningful) return undefined;\n\n return new MongoSchemaCollectionOptionsCtor({\n ...ifDefined('capped', liveOptions.capped),\n ...ifDefined('timeseries', timeseries),\n ...ifDefined('collation', collation),\n ...ifDefined('changeStreamPreAndPostImages', changeStreamPreAndPostImages),\n ...ifDefined('clusteredIndex', clusteredIndex),\n });\n}\n\nfunction canonicalizeExpectedOptions(\n expectedOptions: MongoSchemaCollectionOptions,\n _liveOptions: MongoSchemaCollectionOptions | undefined,\n): MongoSchemaCollectionOptions | undefined {\n // Symmetric: a contract `{enabled: false}` is equivalent to absent.\n const changeStreamPreAndPostImages = isDisabledChangeStream(\n expectedOptions.changeStreamPreAndPostImages,\n )\n ? undefined\n : expectedOptions.changeStreamPreAndPostImages;\n\n const hasMeaningful =\n expectedOptions.capped ||\n expectedOptions.timeseries ||\n expectedOptions.collation ||\n changeStreamPreAndPostImages ||\n expectedOptions.clusteredIndex;\n if (!hasMeaningful) return undefined;\n\n return new MongoSchemaCollectionOptionsCtor({\n ...ifDefined('capped', expectedOptions.capped),\n ...ifDefined('timeseries', expectedOptions.timeseries),\n ...ifDefined('collation', expectedOptions.collation),\n ...ifDefined('changeStreamPreAndPostImages', changeStreamPreAndPostImages),\n ...ifDefined('clusteredIndex', expectedOptions.clusteredIndex),\n });\n}\n\nfunction isDisabledChangeStream(value: { enabled: boolean } | undefined): boolean {\n return value !== undefined && value.enabled === false;\n}\n\n/**\n * Returns a copy of `live` containing only the keys that `expected` defines.\n * Used for option families whose individual sub-fields are server-extended\n * with platform defaults (collation, timeseries, clusteredIndex), so the\n * verifier should compare only what the contract actually authored.\n *\n * When `expected` is `undefined` — i.e. the contract authored nothing for\n * this whole option family but the live IR has it — we return `live`\n * unchanged so the verifier still sees the entire live block and can\n * surface it as drift. (Returning `undefined` here would silently strip a\n * server-attached collation/timeseries/clusteredIndex that the contract\n * never asked for, hiding real drift.)\n */\nfunction stripUnspecifiedFields(\n live: Record<string, unknown>,\n expected: Record<string, unknown> | undefined,\n): Record<string, unknown> {\n if (expected === undefined) return live;\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(expected)) {\n if (Object.hasOwn(live, key)) out[key] = live[key];\n }\n return out;\n}\n","import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';\nimport type {\n OperationContext,\n VerifyDatabaseSchemaResult,\n} from '@prisma-next/framework-components/control';\nimport { VERIFY_CODE_SCHEMA_FAILURE } from '@prisma-next/framework-components/control';\nimport type { MongoContract } from '@prisma-next/mongo-contract';\nimport type { MongoSchemaIR } from '@prisma-next/mongo-schema-ir';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport { contractToMongoSchemaIR } from '../contract-to-schema';\nimport { diffMongoSchemas } from '../schema-diff';\nimport { canonicalizeSchemasForVerification } from './canonicalize-introspection';\n\nexport interface VerifyMongoSchemaOptions {\n readonly contract: MongoContract;\n readonly schema: MongoSchemaIR;\n readonly strict: boolean;\n readonly context?: OperationContext;\n /**\n * Active framework components participating in this composition. Mongo\n * verification does not currently consult them, but the parameter exists\n * for parity with `verifySqlSchema` so callers can pass the same envelope.\n */\n readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'mongo', string>>;\n}\n\nexport function verifyMongoSchema(options: VerifyMongoSchemaOptions): VerifyDatabaseSchemaResult {\n const { contract, schema, strict, context } = options;\n const startTime = Date.now();\n\n const expectedIR = contractToMongoSchemaIR(contract);\n // Strip server-applied defaults (and authored equivalents) before diffing so\n // the verifier compares like-with-like — see `canonicalize-introspection.ts`.\n const { live: canonicalLive, expected: canonicalExpected } = canonicalizeSchemasForVerification(\n schema,\n expectedIR,\n );\n const { root, issues, counts } = diffMongoSchemas(canonicalLive, canonicalExpected, strict);\n\n const ok = counts.fail === 0;\n const profileHash = typeof contract.profileHash === 'string' ? contract.profileHash : '';\n\n return {\n ok,\n ...ifDefined('code', ok ? undefined : VERIFY_CODE_SCHEMA_FAILURE),\n summary: ok ? 'Schema matches contract' : `Schema verification found ${counts.fail} issue(s)`,\n contract: {\n storageHash: contract.storage.storageHash,\n ...(profileHash ? { profileHash } : {}),\n },\n target: { expected: contract.target },\n schema: { issues, root, counts },\n meta: {\n strict,\n ...ifDefined('contractPath', context?.contractPath),\n ...ifDefined('configPath', context?.configPath),\n },\n timings: { total: Date.now() - startTime },\n };\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma-next/family-mongo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"description": "Mongo family descriptor for Prisma Next",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@prisma-next/contract": "0.
|
|
10
|
-
"@prisma-next/emitter": "0.
|
|
11
|
-
"@prisma-next/errors": "0.
|
|
12
|
-
"@prisma-next/framework-components": "0.
|
|
13
|
-
"@prisma-next/migration-tools": "0.
|
|
14
|
-
"@prisma-next/mongo-contract": "0.
|
|
15
|
-
"@prisma-next/mongo-emitter": "0.
|
|
16
|
-
"@prisma-next/mongo-query-ast": "0.
|
|
17
|
-
"@prisma-next/mongo-schema-ir": "0.
|
|
18
|
-
"@prisma-next/utils": "0.
|
|
9
|
+
"@prisma-next/contract": "0.10.0",
|
|
10
|
+
"@prisma-next/emitter": "0.10.0",
|
|
11
|
+
"@prisma-next/errors": "0.10.0",
|
|
12
|
+
"@prisma-next/framework-components": "0.10.0",
|
|
13
|
+
"@prisma-next/migration-tools": "0.10.0",
|
|
14
|
+
"@prisma-next/mongo-contract": "0.10.0",
|
|
15
|
+
"@prisma-next/mongo-emitter": "0.10.0",
|
|
16
|
+
"@prisma-next/mongo-query-ast": "0.10.0",
|
|
17
|
+
"@prisma-next/mongo-schema-ir": "0.10.0",
|
|
18
|
+
"@prisma-next/utils": "0.10.0",
|
|
19
19
|
"arktype": "^2.2.0",
|
|
20
20
|
"mongodb": "^6.16.0",
|
|
21
21
|
"pathe": "^2.0.3"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@prisma-next/mongo-contract-ts": "0.
|
|
25
|
-
"@prisma-next/test-utils": "0.
|
|
26
|
-
"@prisma-next/tsconfig": "0.
|
|
27
|
-
"@prisma-next/tsdown": "0.
|
|
24
|
+
"@prisma-next/mongo-contract-ts": "0.10.0",
|
|
25
|
+
"@prisma-next/test-utils": "0.10.0",
|
|
26
|
+
"@prisma-next/tsconfig": "0.10.0",
|
|
27
|
+
"@prisma-next/tsdown": "0.10.0",
|
|
28
28
|
"tsdown": "0.22.0",
|
|
29
29
|
"typescript": "5.9.3",
|
|
30
30
|
"vitest": "4.1.6"
|
|
@@ -76,9 +76,12 @@ export function contractToMongoSchemaIR(contract: MongoContract | null): MongoSc
|
|
|
76
76
|
return new MongoSchemaIR([]);
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
const collections =
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
const collections: MongoSchemaCollection[] = [];
|
|
80
|
+
for (const ns of Object.values(contract.storage.namespaces)) {
|
|
81
|
+
for (const [name, def] of Object.entries(ns.collections)) {
|
|
82
|
+
collections.push(convertCollection(name, def));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
82
85
|
|
|
83
86
|
return new MongoSchemaIR(collections);
|
|
84
87
|
}
|
|
@@ -34,9 +34,9 @@ import { type as arktypeType } from 'arktype';
|
|
|
34
34
|
export abstract class MongoContractSerializerBase<TContract>
|
|
35
35
|
implements ContractSerializer<TContract>
|
|
36
36
|
{
|
|
37
|
-
deserializeContract(json: unknown):
|
|
37
|
+
deserializeContract<T extends TContract = TContract>(json: unknown): T {
|
|
38
38
|
const validated = this.parseMongoContractStructure(json);
|
|
39
|
-
return this.constructTargetContract(validated);
|
|
39
|
+
return this.constructTargetContract(validated) as T;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
serializeContract(contract: TContract): JsonObject {
|
|
@@ -55,11 +55,11 @@ export abstract class MongoContractSerializerBase<TContract>
|
|
|
55
55
|
* default.
|
|
56
56
|
*
|
|
57
57
|
* The returned `MongoContract` carries class instances under
|
|
58
|
-
* `storage.collections` (each value is a
|
|
59
|
-
* nested `MongoIndex` / `MongoValidator` /
|
|
60
|
-
* constructed by the `MongoCollection` constructor).
|
|
61
|
-
* contract envelope (models, valueObjects, capabilities, …)
|
|
62
|
-
* in plain-JSON form; those IR layers are handled by sibling
|
|
58
|
+
* `storage.namespaces[namespaceId].collections[collectionName]` (each value is a
|
|
59
|
+
* `MongoCollection`, with nested `MongoIndex` / `MongoValidator` /
|
|
60
|
+
* `MongoCollectionOptions` constructed by the `MongoCollection` constructor).
|
|
61
|
+
* The rest of the contract envelope (models, valueObjects, capabilities, …)
|
|
62
|
+
* remains in plain-JSON form; those IR layers are handled by sibling
|
|
63
63
|
* subsystems and don't sit behind this SPI.
|
|
64
64
|
*/
|
|
65
65
|
protected parseMongoContractStructure(json: unknown): MongoContract {
|
|
@@ -76,10 +76,10 @@ export abstract class MongoContractSerializerBase<TContract>
|
|
|
76
76
|
// hand-authored `MongoContract<S, M>` generic surface. The schema and
|
|
77
77
|
// the type are kept in lockstep by the round-trip fixtures under
|
|
78
78
|
// `test/validate.test.ts`. The hydration walk below additionally
|
|
79
|
-
// re-shapes `storage.collections` from plain data into IR-class
|
|
80
|
-
// instances, so the `MongoContract` returned here carries class
|
|
81
|
-
//
|
|
82
|
-
//
|
|
79
|
+
// re-shapes `storage.namespaces.*.collections` from plain data into IR-class
|
|
80
|
+
// instances, so the `MongoContract` returned here carries class identity
|
|
81
|
+
// under those collections maps (and transitively under `indexes` / `validator`
|
|
82
|
+
// / `options`).
|
|
83
83
|
const validatedShape = parsed as unknown as MongoContract;
|
|
84
84
|
|
|
85
85
|
const hydratedContract = this.hydrateMongoContract(validatedShape);
|
|
@@ -91,23 +91,37 @@ export abstract class MongoContractSerializerBase<TContract>
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
/**
|
|
94
|
-
* Walk a structurally-validated Mongo contract and convert
|
|
95
|
-
* `storage.collections`
|
|
96
|
-
* `MongoCollection` IR-class instances. Idempotent: already-class
|
|
94
|
+
* Walk a structurally-validated Mongo contract and convert each
|
|
95
|
+
* `storage.namespaces[nsId].collections[collectionName]` entry from plain
|
|
96
|
+
* data into `MongoCollection` IR-class instances. Idempotent: already-class
|
|
97
97
|
* instances pass through unchanged.
|
|
98
98
|
*/
|
|
99
99
|
protected hydrateMongoContract(contract: MongoContract): MongoContract {
|
|
100
|
-
const
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
100
|
+
const rawNamespaces = contract.storage.namespaces;
|
|
101
|
+
const hydratedNamespaces = Object.fromEntries(
|
|
102
|
+
Object.entries(rawNamespaces).map(([nsId, nsEnvelope]) => {
|
|
103
|
+
const rawCollections = nsEnvelope.collections ?? {};
|
|
104
|
+
const hydratedCollections = Object.fromEntries(
|
|
105
|
+
Object.entries(rawCollections).map(([name, raw]) => [
|
|
106
|
+
name,
|
|
107
|
+
raw instanceof MongoCollection ? raw : new MongoCollection(raw as MongoCollectionInput),
|
|
108
|
+
]),
|
|
109
|
+
);
|
|
110
|
+
return [
|
|
111
|
+
nsId,
|
|
112
|
+
{
|
|
113
|
+
...nsEnvelope,
|
|
114
|
+
id: nsEnvelope.id,
|
|
115
|
+
collections: hydratedCollections,
|
|
116
|
+
},
|
|
117
|
+
];
|
|
118
|
+
}),
|
|
119
|
+
);
|
|
106
120
|
return {
|
|
107
121
|
...contract,
|
|
108
122
|
storage: {
|
|
109
123
|
...contract.storage,
|
|
110
|
-
|
|
124
|
+
namespaces: hydratedNamespaces,
|
|
111
125
|
},
|
|
112
126
|
};
|
|
113
127
|
}
|
|
@@ -116,7 +130,7 @@ export abstract class MongoContractSerializerBase<TContract>
|
|
|
116
130
|
* Target-specific class construction from the validated structural
|
|
117
131
|
* data. The target wraps the contract envelope in the family-layer
|
|
118
132
|
* `MongoStorage` class instance, supplying the `namespaces` map
|
|
119
|
-
* (target concretions like `
|
|
133
|
+
* (target concretions like `MongoTargetUnboundDatabase`). The
|
|
120
134
|
* leaf collection / index shapes are already family-layer IR-class
|
|
121
135
|
* instances after the hydration walk above.
|
|
122
136
|
*/
|
package/src/core/schema-diff.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type {
|
|
|
2
2
|
SchemaIssue,
|
|
3
3
|
SchemaVerificationNode,
|
|
4
4
|
} from '@prisma-next/framework-components/control';
|
|
5
|
+
import { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
|
|
5
6
|
import type {
|
|
6
7
|
MongoSchemaCollection,
|
|
7
8
|
MongoSchemaIndex,
|
|
@@ -40,7 +41,7 @@ export function diffMongoSchemas(
|
|
|
40
41
|
status: 'fail',
|
|
41
42
|
kind: 'collection',
|
|
42
43
|
name,
|
|
43
|
-
contractPath: `storage.collections.${name}`,
|
|
44
|
+
contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${name}`,
|
|
44
45
|
code: 'MISSING_COLLECTION',
|
|
45
46
|
message: `Collection "${name}" is missing`,
|
|
46
47
|
expected: name,
|
|
@@ -62,7 +63,7 @@ export function diffMongoSchemas(
|
|
|
62
63
|
status,
|
|
63
64
|
kind: 'collection',
|
|
64
65
|
name,
|
|
65
|
-
contractPath: `storage.collections.${name}`,
|
|
66
|
+
contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${name}`,
|
|
66
67
|
code: 'EXTRA_COLLECTION',
|
|
67
68
|
message: `Extra collection "${name}" found`,
|
|
68
69
|
expected: null,
|
|
@@ -100,7 +101,7 @@ export function diffMongoSchemas(
|
|
|
100
101
|
status: worstStatus,
|
|
101
102
|
kind: 'collection',
|
|
102
103
|
name,
|
|
103
|
-
contractPath: `storage.collections.${name}`,
|
|
104
|
+
contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${name}`,
|
|
104
105
|
code: worstStatus === 'pass' ? 'MATCH' : 'DRIFT',
|
|
105
106
|
message:
|
|
106
107
|
worstStatus === 'pass' ? `Collection "${name}" matches` : `Collection "${name}" has drift`,
|
|
@@ -170,7 +171,7 @@ function diffIndexes(
|
|
|
170
171
|
status: 'pass',
|
|
171
172
|
kind: 'index',
|
|
172
173
|
name: formatIndexName(idx),
|
|
173
|
-
contractPath: `storage.collections.${collName}.indexes`,
|
|
174
|
+
contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${collName}.indexes`,
|
|
174
175
|
code: 'MATCH',
|
|
175
176
|
message: `Index ${formatIndexName(idx)} matches`,
|
|
176
177
|
expected: key,
|
|
@@ -188,7 +189,7 @@ function diffIndexes(
|
|
|
188
189
|
status: 'fail',
|
|
189
190
|
kind: 'index',
|
|
190
191
|
name: formatIndexName(idx),
|
|
191
|
-
contractPath: `storage.collections.${collName}.indexes`,
|
|
192
|
+
contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${collName}.indexes`,
|
|
192
193
|
code: 'MISSING_INDEX',
|
|
193
194
|
message: `Index ${formatIndexName(idx)} missing`,
|
|
194
195
|
expected: key,
|
|
@@ -211,7 +212,7 @@ function diffIndexes(
|
|
|
211
212
|
status,
|
|
212
213
|
kind: 'index',
|
|
213
214
|
name: formatIndexName(idx),
|
|
214
|
-
contractPath: `storage.collections.${collName}.indexes`,
|
|
215
|
+
contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${collName}.indexes`,
|
|
215
216
|
code: 'EXTRA_INDEX',
|
|
216
217
|
message: `Extra index ${formatIndexName(idx)}`,
|
|
217
218
|
expected: null,
|
|
@@ -244,7 +245,7 @@ function diffValidator(
|
|
|
244
245
|
status: 'fail',
|
|
245
246
|
kind: 'validator',
|
|
246
247
|
name: 'validator',
|
|
247
|
-
contractPath: `storage.collections.${collName}.validator`,
|
|
248
|
+
contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${collName}.validator`,
|
|
248
249
|
code: 'MISSING_VALIDATOR',
|
|
249
250
|
message: 'Validator missing',
|
|
250
251
|
expected: canonicalize(expected.validator.jsonSchema),
|
|
@@ -266,7 +267,7 @@ function diffValidator(
|
|
|
266
267
|
status,
|
|
267
268
|
kind: 'validator',
|
|
268
269
|
name: 'validator',
|
|
269
|
-
contractPath: `storage.collections.${collName}.validator`,
|
|
270
|
+
contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${collName}.validator`,
|
|
270
271
|
code: 'EXTRA_VALIDATOR',
|
|
271
272
|
message: 'Extra validator found',
|
|
272
273
|
expected: null,
|
|
@@ -298,7 +299,7 @@ function diffValidator(
|
|
|
298
299
|
status: 'fail',
|
|
299
300
|
kind: 'validator',
|
|
300
301
|
name: 'validator',
|
|
301
|
-
contractPath: `storage.collections.${collName}.validator`,
|
|
302
|
+
contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${collName}.validator`,
|
|
302
303
|
code: 'VALIDATOR_MISMATCH',
|
|
303
304
|
message: 'Validator mismatch',
|
|
304
305
|
expected: {
|
|
@@ -321,7 +322,7 @@ function diffValidator(
|
|
|
321
322
|
status: 'pass',
|
|
322
323
|
kind: 'validator',
|
|
323
324
|
name: 'validator',
|
|
324
|
-
contractPath: `storage.collections.${collName}.validator`,
|
|
325
|
+
contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${collName}.validator`,
|
|
325
326
|
code: 'MATCH',
|
|
326
327
|
message: 'Validator matches',
|
|
327
328
|
expected: expectedSchema,
|
|
@@ -353,7 +354,7 @@ function diffOptions(
|
|
|
353
354
|
status,
|
|
354
355
|
kind: 'options',
|
|
355
356
|
name: 'options',
|
|
356
|
-
contractPath: `storage.collections.${collName}.options`,
|
|
357
|
+
contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${collName}.options`,
|
|
357
358
|
code: 'EXTRA_OPTIONS',
|
|
358
359
|
message: 'Extra collection options found',
|
|
359
360
|
expected: null,
|
|
@@ -369,7 +370,7 @@ function diffOptions(
|
|
|
369
370
|
status: 'pass',
|
|
370
371
|
kind: 'options',
|
|
371
372
|
name: 'options',
|
|
372
|
-
contractPath: `storage.collections.${collName}.options`,
|
|
373
|
+
contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${collName}.options`,
|
|
373
374
|
code: 'MATCH',
|
|
374
375
|
message: 'Collection options match',
|
|
375
376
|
expected: canonicalize(expected.options),
|
|
@@ -391,7 +392,7 @@ function diffOptions(
|
|
|
391
392
|
status: 'fail',
|
|
392
393
|
kind: 'options',
|
|
393
394
|
name: 'options',
|
|
394
|
-
contractPath: `storage.collections.${collName}.options`,
|
|
395
|
+
contractPath: `storage.namespaces.${UNBOUND_NAMESPACE_ID}.collections.${collName}.options`,
|
|
395
396
|
code: 'OPTIONS_MISMATCH',
|
|
396
397
|
message: 'Collection options mismatch',
|
|
397
398
|
expected: expected.options,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mongo-contract-serializer-Co3EaTVj.mjs","names":["arktypeType"],"sources":["../src/core/ir/mongo-contract-serializer-base.ts","../src/core/ir/mongo-contract-serializer.ts"],"sourcesContent":["import { validateContractDomain } from '@prisma-next/contract/validate-domain';\nimport type { ContractSerializer } from '@prisma-next/framework-components/control';\nimport {\n MongoCollection,\n type MongoCollectionInput,\n type MongoContract,\n MongoContractSchema,\n validateMongoStorage,\n} from '@prisma-next/mongo-contract';\nimport type { JsonObject } from '@prisma-next/utils/json';\nimport { type as arktypeType } from 'arktype';\n\n/**\n * Mongo family `ContractSerializer` abstract base. Owns the family-shared\n * deserialization pipeline:\n *\n * 1. Structural validation against the Mongo contract arktype schema\n * (`MongoContractSchema`).\n * 2. Framework-shared domain validation (`validateContractDomain`).\n * 3. Family-shared storage validation (`validateMongoStorage`).\n *\n * The validated value is handed to the target via the\n * `constructTargetContract` hook, which wraps the plain-JSON shape in\n * the family-layer `MongoStorage` class instance (carrying the\n * target-supplied `namespaces` map). Targets that need to add\n * structural checks beyond the family default can override\n * `parseMongoContractStructure`.\n *\n * Default `serializeContract` is identity over the contract — Mongo\n * target classes carry JSON-clean fields by construction, so the value\n * can be `JSON.stringify`'d directly. Targets that need on-the-way-out\n * canonicalization override `serializeContract`.\n */\nexport abstract class MongoContractSerializerBase<TContract>\n implements ContractSerializer<TContract>\n{\n deserializeContract(json: unknown): TContract {\n const validated = this.parseMongoContractStructure(json);\n return this.constructTargetContract(validated);\n }\n\n serializeContract(contract: TContract): JsonObject {\n // Mongo contract class fields are JSON-clean by construction; the\n // cast asserts that. Targets that need to canonicalize on the way\n // out override this method.\n return contract as unknown as JsonObject;\n }\n\n /**\n * Family-shared structural validation: parse against the Mongo\n * contract arktype schema, then run framework-shared domain + Mongo\n * family storage checks, then hydrate the validated tree into Mongo\n * Contract IR class instances. Targets can override to add\n * target-specific structural checks; most targets accept the family\n * default.\n *\n * The returned `MongoContract` carries class instances under\n * `storage.collections` (each value is a `MongoCollection`, with\n * nested `MongoIndex` / `MongoValidator` / `MongoCollectionOptions`\n * constructed by the `MongoCollection` constructor). The rest of the\n * contract envelope (models, valueObjects, capabilities, …) remains\n * in plain-JSON form; those IR layers are handled by sibling\n * subsystems and don't sit behind this SPI.\n */\n protected parseMongoContractStructure(json: unknown): MongoContract {\n const parsed = MongoContractSchema(json);\n if (parsed instanceof arktypeType.errors) {\n throw new Error(`Contract structural validation failed: ${parsed.summary}`);\n }\n\n // arktype's `infer`d type for `MongoContractSchema` is structurally\n // equivalent to `MongoContract` (both describe the same on-disk JSON\n // envelope) but not nominally so: the arktype DSL produces a type whose\n // optional/readonly profile, narrowed string-literal positions, and\n // utility-type wrappings (`Type.infer`, `Out`, …) differ from the\n // hand-authored `MongoContract<S, M>` generic surface. The schema and\n // the type are kept in lockstep by the round-trip fixtures under\n // `test/validate.test.ts`. The hydration walk below additionally\n // re-shapes `storage.collections` from plain data into IR-class\n // instances, so the `MongoContract` returned here carries class\n // identity under `storage.collections.*` (and transitively under\n // `indexes` / `validator` / `options`).\n const validatedShape = parsed as unknown as MongoContract;\n\n const hydratedContract = this.hydrateMongoContract(validatedShape);\n\n validateContractDomain(hydratedContract);\n validateMongoStorage(hydratedContract);\n\n return hydratedContract;\n }\n\n /**\n * Walk a structurally-validated Mongo contract and convert\n * `storage.collections` entries from plain data into\n * `MongoCollection` IR-class instances. Idempotent: already-class\n * instances pass through unchanged.\n */\n protected hydrateMongoContract(contract: MongoContract): MongoContract {\n const rawCollections = contract.storage.collections;\n const hydrated: Record<string, MongoCollection> = {};\n for (const [name, raw] of Object.entries(rawCollections)) {\n hydrated[name] =\n raw instanceof MongoCollection ? raw : new MongoCollection(raw as MongoCollectionInput);\n }\n return {\n ...contract,\n storage: {\n ...contract.storage,\n collections: hydrated,\n },\n };\n }\n\n /**\n * Target-specific class construction from the validated structural\n * data. The target wraps the contract envelope in the family-layer\n * `MongoStorage` class instance, supplying the `namespaces` map\n * (target concretions like `MongoTargetUnspecifiedDatabase`). The\n * leaf collection / index shapes are already family-layer IR-class\n * instances after the hydration walk above.\n */\n protected abstract constructTargetContract(validated: MongoContract): TContract;\n}\n","import type { MongoContract } from '@prisma-next/mongo-contract';\nimport { MongoContractSerializerBase } from './mongo-contract-serializer-base';\n\n/**\n * Default Mongo family `ContractSerializer` concretion. Inherits the\n * Mongo-shared deserialization pipeline (structural validation +\n * collection-level hydration) and falls through `constructTargetContract`\n * with the validated `MongoContract` shape. Family-level call sites\n * (family-instance methods, family-layer tests that don't reach into\n * a target descriptor) instantiate this directly; targets with their\n * own storage concretion (`target-mongo`'s `MongoTargetContractSerializer`)\n * override `constructTargetContract` to wrap the storage shape.\n */\nexport class MongoContractSerializer extends MongoContractSerializerBase<MongoContract> {\n protected constructTargetContract(validated: MongoContract): MongoContract {\n return validated;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,IAAsB,8BAAtB,MAEA;CACE,oBAAoB,MAA0B;EAC5C,MAAM,YAAY,KAAK,4BAA4B,KAAK;EACxD,OAAO,KAAK,wBAAwB,UAAU;;CAGhD,kBAAkB,UAAiC;EAIjD,OAAO;;;;;;;;;;;;;;;;;;CAmBT,4BAAsC,MAA8B;EAClE,MAAM,SAAS,oBAAoB,KAAK;EACxC,IAAI,kBAAkBA,KAAY,QAChC,MAAM,IAAI,MAAM,0CAA0C,OAAO,UAAU;EAe7E,MAAM,iBAAiB;EAEvB,MAAM,mBAAmB,KAAK,qBAAqB,eAAe;EAElE,uBAAuB,iBAAiB;EACxC,qBAAqB,iBAAiB;EAEtC,OAAO;;;;;;;;CAST,qBAA+B,UAAwC;EACrE,MAAM,iBAAiB,SAAS,QAAQ;EACxC,MAAM,WAA4C,EAAE;EACpD,KAAK,MAAM,CAAC,MAAM,QAAQ,OAAO,QAAQ,eAAe,EACtD,SAAS,QACP,eAAe,kBAAkB,MAAM,IAAI,gBAAgB,IAA4B;EAE3F,OAAO;GACL,GAAG;GACH,SAAS;IACP,GAAG,SAAS;IACZ,aAAa;IACd;GACF;;;;;;;;;;;;;;;AClGL,IAAa,0BAAb,cAA6C,4BAA2C;CACtF,wBAAkC,WAAyC;EACzE,OAAO"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"verify-mongo-schema-BL7t9YTB.mjs","names":["MongoSchemaIRCtor","MongoSchemaCollectionCtor","MongoSchemaIndexCtor","MongoSchemaCollectionOptionsCtor"],"sources":["../src/core/contract-to-schema.ts","../src/core/schema-diff.ts","../src/core/schema-verify/canonicalize-introspection.ts","../src/core/schema-verify/verify-mongo-schema.ts"],"sourcesContent":["import type {\n MongoCollection,\n MongoCollectionOptions,\n MongoContract,\n MongoIndex,\n MongoValidator,\n} from '@prisma-next/mongo-contract';\nimport {\n MongoSchemaCollection,\n MongoSchemaCollectionOptions,\n MongoSchemaIndex,\n MongoSchemaIR,\n MongoSchemaValidator,\n} from '@prisma-next/mongo-schema-ir';\n\nfunction stripIrKind(node: object): Record<string, unknown> {\n const { kind: _kind, ...rest } = node as Record<string, unknown>;\n return rest;\n}\n\nfunction convertIndex(index: MongoIndex): MongoSchemaIndex {\n return new MongoSchemaIndex({\n keys: index.keys,\n unique: index.unique,\n sparse: index.sparse,\n expireAfterSeconds: index.expireAfterSeconds,\n partialFilterExpression: index.partialFilterExpression,\n wildcardProjection: index.wildcardProjection,\n collation: index.collation,\n weights: index.weights,\n default_language: index.default_language,\n language_override: index.language_override,\n });\n}\n\nfunction convertValidator(v: MongoValidator): MongoSchemaValidator {\n return new MongoSchemaValidator({\n jsonSchema: v.jsonSchema,\n validationLevel: v.validationLevel,\n validationAction: v.validationAction,\n });\n}\n\nfunction convertOptions(o: MongoCollectionOptions): MongoSchemaCollectionOptions {\n return new MongoSchemaCollectionOptions({\n ...(o.capped !== undefined && { capped: o.capped }),\n ...(o.timeseries !== undefined && {\n timeseries: {\n timeField: o.timeseries.timeField,\n ...(o.timeseries.metaField !== undefined && { metaField: o.timeseries.metaField }),\n ...(o.timeseries.granularity !== undefined && { granularity: o.timeseries.granularity }),\n },\n }),\n ...(o.collation !== undefined && {\n collation: stripIrKind(o.collation),\n }),\n ...(o.changeStreamPreAndPostImages !== undefined && {\n changeStreamPreAndPostImages: { enabled: o.changeStreamPreAndPostImages.enabled },\n }),\n ...(o.clusteredIndex !== undefined && { clusteredIndex: o.clusteredIndex }),\n });\n}\n\nfunction convertCollection(name: string, def: MongoCollection): MongoSchemaCollection {\n const indexes = (def.indexes ?? []).map(convertIndex);\n return new MongoSchemaCollection({\n name,\n indexes,\n ...(def.validator != null && { validator: convertValidator(def.validator) }),\n ...(def.options != null && { options: convertOptions(def.options) }),\n });\n}\n\nexport function contractToMongoSchemaIR(contract: MongoContract | null): MongoSchemaIR {\n if (!contract) {\n return new MongoSchemaIR([]);\n }\n\n const collections = Object.entries(contract.storage.collections).map(([name, def]) =>\n convertCollection(name, def),\n );\n\n return new MongoSchemaIR(collections);\n}\n","import type {\n SchemaIssue,\n SchemaVerificationNode,\n} from '@prisma-next/framework-components/control';\nimport type {\n MongoSchemaCollection,\n MongoSchemaIndex,\n MongoSchemaIR,\n} from '@prisma-next/mongo-schema-ir';\nimport { canonicalize, deepEqual } from '@prisma-next/mongo-schema-ir';\n\nexport function diffMongoSchemas(\n live: MongoSchemaIR,\n expected: MongoSchemaIR,\n strict: boolean,\n): {\n root: SchemaVerificationNode;\n issues: SchemaIssue[];\n counts: { pass: number; warn: number; fail: number; totalNodes: number };\n} {\n const issues: SchemaIssue[] = [];\n const collectionChildren: SchemaVerificationNode[] = [];\n let pass = 0;\n let warn = 0;\n let fail = 0;\n\n const allNames = new Set([...live.collectionNames, ...expected.collectionNames]);\n\n for (const name of [...allNames].sort()) {\n const liveColl = live.collection(name);\n const expectedColl = expected.collection(name);\n\n if (!liveColl && expectedColl) {\n issues.push({\n kind: 'missing_table',\n table: name,\n message: `Collection \"${name}\" is missing from the database`,\n });\n collectionChildren.push({\n status: 'fail',\n kind: 'collection',\n name,\n contractPath: `storage.collections.${name}`,\n code: 'MISSING_COLLECTION',\n message: `Collection \"${name}\" is missing`,\n expected: name,\n actual: null,\n children: [],\n });\n fail++;\n continue;\n }\n\n if (liveColl && !expectedColl) {\n const status = strict ? 'fail' : 'warn';\n issues.push({\n kind: 'extra_table',\n table: name,\n message: `Extra collection \"${name}\" exists in the database but not in the contract`,\n });\n collectionChildren.push({\n status,\n kind: 'collection',\n name,\n contractPath: `storage.collections.${name}`,\n code: 'EXTRA_COLLECTION',\n message: `Extra collection \"${name}\" found`,\n expected: null,\n actual: name,\n children: [],\n });\n if (status === 'fail') fail++;\n else warn++;\n continue;\n }\n\n const lc = liveColl as MongoSchemaCollection;\n const ec = expectedColl as MongoSchemaCollection;\n const indexChildren = diffIndexes(name, lc, ec, strict, issues);\n const validatorChildren = diffValidator(name, lc, ec, strict, issues);\n const optionsChildren = diffOptions(name, lc, ec, strict, issues);\n const children = [...indexChildren, ...validatorChildren, ...optionsChildren];\n\n const worstStatus = children.reduce<'pass' | 'warn' | 'fail'>(\n (s, c) => (c.status === 'fail' ? 'fail' : c.status === 'warn' && s !== 'fail' ? 'warn' : s),\n 'pass',\n );\n\n for (const c of children) {\n if (c.status === 'pass') pass++;\n else if (c.status === 'warn') warn++;\n else fail++;\n }\n\n if (children.length === 0) {\n pass++;\n }\n\n collectionChildren.push({\n status: worstStatus,\n kind: 'collection',\n name,\n contractPath: `storage.collections.${name}`,\n code: worstStatus === 'pass' ? 'MATCH' : 'DRIFT',\n message:\n worstStatus === 'pass' ? `Collection \"${name}\" matches` : `Collection \"${name}\" has drift`,\n expected: name,\n actual: name,\n children,\n });\n }\n\n const rootStatus = fail > 0 ? 'fail' : warn > 0 ? 'warn' : 'pass';\n const totalNodes = pass + warn + fail + collectionChildren.length;\n\n const root: SchemaVerificationNode = {\n status: rootStatus,\n kind: 'root',\n name: 'mongo-schema',\n contractPath: 'storage',\n code: rootStatus === 'pass' ? 'MATCH' : 'DRIFT',\n message: rootStatus === 'pass' ? 'Schema matches' : 'Schema has drift',\n expected: null,\n actual: null,\n children: collectionChildren,\n };\n\n return { root, issues, counts: { pass, warn, fail, totalNodes } };\n}\n\nfunction buildIndexLookupKey(index: MongoSchemaIndex): string {\n const keys = index.keys.map((k) => `${k.field}:${k.direction}`).join(',');\n const opts = [\n index.unique ? 'unique' : '',\n index.sparse ? 'sparse' : '',\n index.expireAfterSeconds != null ? `ttl:${index.expireAfterSeconds}` : '',\n index.partialFilterExpression ? `pfe:${canonicalize(index.partialFilterExpression)}` : '',\n index.wildcardProjection ? `wp:${canonicalize(index.wildcardProjection)}` : '',\n index.collation ? `col:${canonicalize(index.collation)}` : '',\n index.weights ? `wt:${canonicalize(index.weights)}` : '',\n index.default_language ? `dl:${index.default_language}` : '',\n index.language_override ? `lo:${index.language_override}` : '',\n ]\n .filter(Boolean)\n .join(';');\n return opts ? `${keys}|${opts}` : keys;\n}\n\nfunction formatIndexName(index: MongoSchemaIndex): string {\n return index.keys.map((k) => `${k.field}:${k.direction}`).join(', ');\n}\n\nfunction diffIndexes(\n collName: string,\n live: MongoSchemaCollection,\n expected: MongoSchemaCollection,\n strict: boolean,\n issues: SchemaIssue[],\n): SchemaVerificationNode[] {\n const nodes: SchemaVerificationNode[] = [];\n const liveLookup = new Map<string, MongoSchemaIndex>();\n for (const idx of live.indexes) liveLookup.set(buildIndexLookupKey(idx), idx);\n\n const expectedLookup = new Map<string, MongoSchemaIndex>();\n for (const idx of expected.indexes) expectedLookup.set(buildIndexLookupKey(idx), idx);\n\n for (const [key, idx] of expectedLookup) {\n if (liveLookup.has(key)) {\n nodes.push({\n status: 'pass',\n kind: 'index',\n name: formatIndexName(idx),\n contractPath: `storage.collections.${collName}.indexes`,\n code: 'MATCH',\n message: `Index ${formatIndexName(idx)} matches`,\n expected: key,\n actual: key,\n children: [],\n });\n } else {\n issues.push({\n kind: 'index_mismatch',\n table: collName,\n indexOrConstraint: formatIndexName(idx),\n message: `Index ${formatIndexName(idx)} missing on collection \"${collName}\"`,\n });\n nodes.push({\n status: 'fail',\n kind: 'index',\n name: formatIndexName(idx),\n contractPath: `storage.collections.${collName}.indexes`,\n code: 'MISSING_INDEX',\n message: `Index ${formatIndexName(idx)} missing`,\n expected: key,\n actual: null,\n children: [],\n });\n }\n }\n\n for (const [key, idx] of liveLookup) {\n if (!expectedLookup.has(key)) {\n const status = strict ? 'fail' : 'warn';\n issues.push({\n kind: 'extra_index',\n table: collName,\n indexOrConstraint: formatIndexName(idx),\n message: `Extra index ${formatIndexName(idx)} on collection \"${collName}\"`,\n });\n nodes.push({\n status,\n kind: 'index',\n name: formatIndexName(idx),\n contractPath: `storage.collections.${collName}.indexes`,\n code: 'EXTRA_INDEX',\n message: `Extra index ${formatIndexName(idx)}`,\n expected: null,\n actual: key,\n children: [],\n });\n }\n }\n\n return nodes;\n}\n\nfunction diffValidator(\n collName: string,\n live: MongoSchemaCollection,\n expected: MongoSchemaCollection,\n strict: boolean,\n issues: SchemaIssue[],\n): SchemaVerificationNode[] {\n if (!live.validator && !expected.validator) return [];\n\n if (expected.validator && !live.validator) {\n issues.push({\n kind: 'type_missing',\n table: collName,\n message: `Validator missing on collection \"${collName}\"`,\n });\n return [\n {\n status: 'fail',\n kind: 'validator',\n name: 'validator',\n contractPath: `storage.collections.${collName}.validator`,\n code: 'MISSING_VALIDATOR',\n message: 'Validator missing',\n expected: canonicalize(expected.validator.jsonSchema),\n actual: null,\n children: [],\n },\n ];\n }\n\n if (!expected.validator && live.validator) {\n const status = strict ? 'fail' : 'warn';\n issues.push({\n kind: 'extra_validator',\n table: collName,\n message: `Extra validator on collection \"${collName}\"`,\n });\n return [\n {\n status,\n kind: 'validator',\n name: 'validator',\n contractPath: `storage.collections.${collName}.validator`,\n code: 'EXTRA_VALIDATOR',\n message: 'Extra validator found',\n expected: null,\n actual: canonicalize(live.validator.jsonSchema),\n children: [],\n },\n ];\n }\n\n const liveVal = live.validator as NonNullable<typeof live.validator>;\n const expectedVal = expected.validator as NonNullable<typeof expected.validator>;\n const liveSchema = canonicalize(liveVal.jsonSchema);\n const expectedSchema = canonicalize(expectedVal.jsonSchema);\n\n if (\n liveSchema !== expectedSchema ||\n liveVal.validationLevel !== expectedVal.validationLevel ||\n liveVal.validationAction !== expectedVal.validationAction\n ) {\n issues.push({\n kind: 'type_mismatch',\n table: collName,\n expected: expectedSchema,\n actual: liveSchema,\n message: `Validator mismatch on collection \"${collName}\"`,\n });\n return [\n {\n status: 'fail',\n kind: 'validator',\n name: 'validator',\n contractPath: `storage.collections.${collName}.validator`,\n code: 'VALIDATOR_MISMATCH',\n message: 'Validator mismatch',\n expected: {\n jsonSchema: expectedVal.jsonSchema,\n validationLevel: expectedVal.validationLevel,\n validationAction: expectedVal.validationAction,\n },\n actual: {\n jsonSchema: liveVal.jsonSchema,\n validationLevel: liveVal.validationLevel,\n validationAction: liveVal.validationAction,\n },\n children: [],\n },\n ];\n }\n\n return [\n {\n status: 'pass',\n kind: 'validator',\n name: 'validator',\n contractPath: `storage.collections.${collName}.validator`,\n code: 'MATCH',\n message: 'Validator matches',\n expected: expectedSchema,\n actual: liveSchema,\n children: [],\n },\n ];\n}\n\nfunction diffOptions(\n collName: string,\n live: MongoSchemaCollection,\n expected: MongoSchemaCollection,\n strict: boolean,\n issues: SchemaIssue[],\n): SchemaVerificationNode[] {\n if (!live.options && !expected.options) return [];\n\n if (!expected.options && live.options) {\n const status = strict ? 'fail' : 'warn';\n issues.push({\n kind: 'type_mismatch',\n table: collName,\n actual: canonicalize(live.options),\n message: `Extra collection options on \"${collName}\"`,\n });\n return [\n {\n status,\n kind: 'options',\n name: 'options',\n contractPath: `storage.collections.${collName}.options`,\n code: 'EXTRA_OPTIONS',\n message: 'Extra collection options found',\n expected: null,\n actual: live.options,\n children: [],\n },\n ];\n }\n\n if (deepEqual(live.options, expected.options)) {\n return [\n {\n status: 'pass',\n kind: 'options',\n name: 'options',\n contractPath: `storage.collections.${collName}.options`,\n code: 'MATCH',\n message: 'Collection options match',\n expected: canonicalize(expected.options),\n actual: canonicalize(live.options),\n children: [],\n },\n ];\n }\n\n issues.push({\n kind: 'type_mismatch',\n table: collName,\n expected: canonicalize(expected.options),\n actual: canonicalize(live.options),\n message: `Collection options mismatch on \"${collName}\"`,\n });\n return [\n {\n status: 'fail',\n kind: 'options',\n name: 'options',\n contractPath: `storage.collections.${collName}.options`,\n code: 'OPTIONS_MISMATCH',\n message: 'Collection options mismatch',\n expected: expected.options,\n actual: live.options,\n children: [],\n },\n ];\n}\n","/**\n * Canonicalizes a live (introspected) `MongoSchemaIR` against the expected\n * (contract-built) IR before diffing. MongoDB applies server-side defaults\n * to several option/index families that are absent from authored contracts,\n * which would otherwise cause `verifyMongoSchema` to report false-positive\n * drift on a fresh `migrate` run.\n *\n * The normalization is contract-aware where it has to be: server defaults\n * are stripped from the live IR for fields the contract did not specify, so\n * a contract that *does* specify a value still gets compared faithfully.\n *\n * Symmetric defaults — like `changeStreamPreAndPostImages: { enabled: false }`,\n * which is equivalent to \"absent\" on both sides — are stripped from both IRs\n * so either authoring style verifies.\n */\n\nimport type {\n MongoSchemaCollection,\n MongoSchemaCollectionOptions,\n MongoSchemaIndex,\n MongoSchemaIR,\n} from '@prisma-next/mongo-schema-ir';\nimport {\n MongoSchemaCollection as MongoSchemaCollectionCtor,\n MongoSchemaCollectionOptions as MongoSchemaCollectionOptionsCtor,\n MongoSchemaIndex as MongoSchemaIndexCtor,\n MongoSchemaIR as MongoSchemaIRCtor,\n} from '@prisma-next/mongo-schema-ir';\nimport { ifDefined } from '@prisma-next/utils/defined';\n\nexport interface CanonicalizedSchemas {\n readonly live: MongoSchemaIR;\n readonly expected: MongoSchemaIR;\n}\n\nexport function canonicalizeSchemasForVerification(\n live: MongoSchemaIR,\n expected: MongoSchemaIR,\n): CanonicalizedSchemas {\n const expectedByName = new Map<string, MongoSchemaCollection>();\n for (const c of expected.collections) expectedByName.set(c.name, c);\n\n const liveByName = new Map<string, MongoSchemaCollection>();\n for (const c of live.collections) liveByName.set(c.name, c);\n\n const canonicalLive = live.collections.map((c) =>\n canonicalizeLiveCollection(c, expectedByName.get(c.name)),\n );\n const canonicalExpected = expected.collections.map((c) =>\n canonicalizeExpectedCollection(c, liveByName.get(c.name)),\n );\n\n return {\n live: new MongoSchemaIRCtor(canonicalLive),\n expected: new MongoSchemaIRCtor(canonicalExpected),\n };\n}\n\nfunction canonicalizeLiveCollection(\n liveColl: MongoSchemaCollection,\n expectedColl: MongoSchemaCollection | undefined,\n): MongoSchemaCollection {\n const expectedIndexes = expectedColl?.indexes ?? [];\n const indexes = liveColl.indexes.map((idx) =>\n canonicalizeLiveIndex(idx, findExpectedIndexCounterpart(idx, expectedIndexes)),\n );\n\n const options = liveColl.options\n ? canonicalizeLiveOptions(liveColl.options, expectedColl?.options)\n : undefined;\n\n return new MongoSchemaCollectionCtor({\n name: liveColl.name,\n indexes,\n ...ifDefined('validator', liveColl.validator),\n ...ifDefined('options', options),\n });\n}\n\nfunction canonicalizeExpectedCollection(\n expectedColl: MongoSchemaCollection,\n liveColl: MongoSchemaCollection | undefined,\n): MongoSchemaCollection {\n // Symmetric text-index key ordering: a contract-shaped text index preserves\n // the user-authored field order, but the introspected counterpart comes\n // back from MongoDB with `weights` keys in alphabetical order, so we\n // canonicalize both sides to alphabetical text-key order. The order of\n // text fields within the text block is semantically irrelevant — relevance\n // is governed by `weights`, not key order.\n const indexes = expectedColl.indexes.map(canonicalizeTextIndexKeyOrder);\n\n const options = expectedColl.options\n ? canonicalizeExpectedOptions(expectedColl.options, liveColl?.options)\n : undefined;\n\n return new MongoSchemaCollectionCtor({\n name: expectedColl.name,\n indexes,\n ...ifDefined('validator', expectedColl.validator),\n ...ifDefined('options', options),\n });\n}\n\nfunction canonicalizeTextIndexKeyOrder(index: MongoSchemaIndex): MongoSchemaIndex {\n const hasTextKey = index.keys.some((k) => k.direction === 'text');\n if (!hasTextKey) return index;\n return new MongoSchemaIndexCtor({\n keys: sortTextKeys(index.keys),\n unique: index.unique,\n ...ifDefined('sparse', index.sparse),\n ...ifDefined('expireAfterSeconds', index.expireAfterSeconds),\n ...ifDefined('partialFilterExpression', index.partialFilterExpression),\n ...ifDefined('wildcardProjection', index.wildcardProjection),\n ...ifDefined('collation', index.collation),\n ...ifDefined('weights', index.weights),\n ...ifDefined('default_language', index.default_language),\n ...ifDefined('language_override', index.language_override),\n });\n}\n\n/**\n * Returns a copy of `keys` with text-direction entries sorted alphabetically\n * while preserving the relative position of non-text entries. Compound text\n * indexes (`{a: 1, _fts: 'text', _ftsx: 1, b: 1}`) keep their scalar\n * prefix/suffix layout; only the contiguous text block is reordered.\n */\nfunction sortTextKeys(\n keys: ReadonlyArray<{\n readonly field: string;\n readonly direction: 'text' | 1 | -1 | '2dsphere' | '2d' | 'hashed';\n }>,\n): ReadonlyArray<{\n readonly field: string;\n readonly direction: 'text' | 1 | -1 | '2dsphere' | '2d' | 'hashed';\n}> {\n const textEntries = keys.filter((k) => k.direction === 'text');\n if (textEntries.length <= 1) return keys;\n const sortedText = [...textEntries].sort((a, b) => a.field.localeCompare(b.field));\n let textIdx = 0;\n return keys.map((k) => {\n if (k.direction !== 'text') return k;\n const next = sortedText[textIdx++];\n /* v8 ignore next 3 -- @preserve invariant guard: textIdx is always < sortedText.length here because we only consume sortedText for text-direction entries and sortedText is built from the same filter. */\n if (next === undefined) {\n throw new Error('sortTextKeys: text-key counts mismatched');\n }\n return next;\n });\n}\n\nfunction canonicalizeLiveIndex(\n liveIndex: MongoSchemaIndex,\n expectedIndex: MongoSchemaIndex | undefined,\n): MongoSchemaIndex {\n const projectedKeys = sortTextKeys(projectTextIndexKeys(liveIndex));\n const collation = liveIndex.collation\n ? stripUnspecifiedFields(liveIndex.collation, expectedIndex?.collation)\n : liveIndex.collation;\n\n // Text-index server defaults: when the contract did not set\n // `weights`/`default_language`/`language_override`, MongoDB applies\n // `weights = {<field>: 1, ...}` (uniform), `'english'`, and `'language'`\n // respectively. Strip them from live *only* when the live value matches\n // those defaults — preserving non-default live values lets the verifier\n // surface drift when the live index is tampered (e.g. weights tuned\n // out-of-band, custom `default_language`/`language_override`) even though\n // the contract authored neither.\n const weights =\n expectedIndex?.weights === undefined && hasDefaultTextWeights(projectedKeys, liveIndex.weights)\n ? undefined\n : liveIndex.weights;\n const default_language =\n expectedIndex?.default_language === undefined && liveIndex.default_language === 'english'\n ? undefined\n : liveIndex.default_language;\n const language_override =\n expectedIndex?.language_override === undefined && liveIndex.language_override === 'language'\n ? undefined\n : liveIndex.language_override;\n\n return new MongoSchemaIndexCtor({\n keys: projectedKeys,\n unique: liveIndex.unique,\n ...ifDefined('sparse', liveIndex.sparse),\n ...ifDefined('expireAfterSeconds', liveIndex.expireAfterSeconds),\n ...ifDefined('partialFilterExpression', liveIndex.partialFilterExpression),\n ...ifDefined('wildcardProjection', liveIndex.wildcardProjection),\n ...ifDefined('collation', collation),\n ...ifDefined('weights', weights),\n ...ifDefined('default_language', default_language),\n ...ifDefined('language_override', language_override),\n });\n}\n\n/**\n * Locate the contract-side index that corresponds to a live index for the\n * purpose of contract-aware normalization. We deliberately match by the\n * *projected* (contract-shaped) key list — so a live `_fts/_ftsx` index\n * resolves to a contract `{title: 'text', body: 'text'}` index — and pick\n * the first match. Contracts very rarely contain duplicate-key indexes; if\n * we have no counterpart we fall back to no normalization for that index.\n */\nfunction findExpectedIndexCounterpart(\n liveIndex: MongoSchemaIndex,\n expectedIndexes: ReadonlyArray<MongoSchemaIndex>,\n): MongoSchemaIndex | undefined {\n const projectedLiveKeys = sortTextKeys(projectTextIndexKeys(liveIndex));\n const liveKeySig = projectedLiveKeys.map((k) => `${k.field}:${k.direction}`).join(',');\n for (const expected of expectedIndexes) {\n const expectedKeySig = sortTextKeys(expected.keys)\n .map((k) => `${k.field}:${k.direction}`)\n .join(',');\n if (expectedKeySig === liveKeySig) return expected;\n }\n return undefined;\n}\n\n/**\n * MongoDB expands a contract-shaped text index like\n * `[{title: 'text'}, {body: 'text'}]` into its internal weighted vector\n * representation `[{_fts: 'text'}, {_ftsx: 1}]`. We project back to\n * contract-shaped keys via `weights`, iterating in whatever order MongoDB\n * returns them (alphabetical, in practice). `sortTextKeys` is applied\n * downstream to canonicalize the order on both sides, so this projection\n * does not depend on a specific iteration order.\n */\nfunction projectTextIndexKeys(liveIndex: MongoSchemaIndex): ReadonlyArray<{\n readonly field: string;\n readonly direction: 'text' | 1 | -1 | '2dsphere' | '2d' | 'hashed';\n}> {\n const isTextIndex =\n liveIndex.keys.length >= 1 &&\n liveIndex.keys.some((k) => k.field === '_fts' && k.direction === 'text');\n\n if (!isTextIndex || !liveIndex.weights) return liveIndex.keys;\n\n const textKeys = Object.keys(liveIndex.weights).map((field) => ({\n field,\n direction: 'text' as const,\n }));\n\n // Splice the projected text fields into the original `_fts/_ftsx` slot so\n // compound text indexes that mix scalar prefixes *and* suffixes — e.g.\n // `[prefix, _fts, _ftsx, suffix]` — keep their original layout. Flattening\n // scalars first would yield `[prefix, suffix, ...text]`, which `sortTextKeys`\n // (downstream) cannot recover because it only reorders text-direction\n // entries within their existing positions. MongoDB always emits exactly one\n // `_fts`/`_ftsx` pair per index, so we don't need to guard against\n // duplicates.\n type IndexKey = (typeof liveIndex.keys)[number];\n const projectedKeys: IndexKey[] = [];\n for (const key of liveIndex.keys) {\n if (key.field === '_ftsx') continue;\n if (key.field === '_fts') {\n projectedKeys.push(...textKeys);\n continue;\n }\n projectedKeys.push(key);\n }\n return projectedKeys;\n}\n\n/**\n * MongoDB's server-default `weights` for an authored-without-weights text\n * index assigns `1` to every text-direction field. Returns `true` only when\n * `liveWeights` is exactly that uniform shape (every projected text-direction\n * key weighted at `1`) so the canonicalizer leaves non-default weights —\n * including out-of-band relevance tweaks — visible to the verifier.\n *\n * `projectTextIndexKeys` derives text-direction keys from the live weights\n * map, so the count is guaranteed to match; we only have to check the value\n * shape.\n */\nfunction hasDefaultTextWeights(\n projectedKeys: ReadonlyArray<{\n readonly field: string;\n readonly direction: 'text' | 1 | -1 | '2dsphere' | '2d' | 'hashed';\n }>,\n liveWeights: MongoSchemaIndex['weights'],\n): boolean {\n if (liveWeights === undefined) return false;\n const textFields = projectedKeys.filter((k) => k.direction === 'text').map((k) => k.field);\n return textFields.every((field) => liveWeights[field] === 1);\n}\n\nfunction canonicalizeLiveOptions(\n liveOptions: MongoSchemaCollectionOptions,\n expectedOptions: MongoSchemaCollectionOptions | undefined,\n): MongoSchemaCollectionOptions | undefined {\n const collation = liveOptions.collation\n ? stripUnspecifiedFields(liveOptions.collation, expectedOptions?.collation)\n : undefined;\n\n // Timeseries: drop `bucketMaxSpanSeconds` (and any other server-applied\n // extras) when the contract did not specify them.\n const timeseries = liveOptions.timeseries\n ? (stripUnspecifiedFields(\n liveOptions.timeseries as Record<string, unknown>,\n expectedOptions?.timeseries as Record<string, unknown> | undefined,\n ) as MongoSchemaCollectionOptions['timeseries'])\n : undefined;\n\n // ClusteredIndex: drop `key`, `unique`, `v` and any other server-applied\n // extras when the contract did not specify them.\n const clusteredIndex = liveOptions.clusteredIndex\n ? (stripUnspecifiedFields(\n liveOptions.clusteredIndex as Record<string, unknown>,\n expectedOptions?.clusteredIndex as Record<string, unknown> | undefined,\n ) as MongoSchemaCollectionOptions['clusteredIndex'])\n : undefined;\n\n // changeStreamPreAndPostImages: `{enabled: false}` is equivalent to\n // \"absent\". Strip it from live so it round-trips with a contract that\n // omits the field, and is symmetric with the expected-side stripping.\n const changeStreamPreAndPostImages = isDisabledChangeStream(\n liveOptions.changeStreamPreAndPostImages,\n )\n ? undefined\n : liveOptions.changeStreamPreAndPostImages;\n\n const hasMeaningful =\n liveOptions.capped || timeseries || collation || changeStreamPreAndPostImages || clusteredIndex;\n if (!hasMeaningful) return undefined;\n\n return new MongoSchemaCollectionOptionsCtor({\n ...ifDefined('capped', liveOptions.capped),\n ...ifDefined('timeseries', timeseries),\n ...ifDefined('collation', collation),\n ...ifDefined('changeStreamPreAndPostImages', changeStreamPreAndPostImages),\n ...ifDefined('clusteredIndex', clusteredIndex),\n });\n}\n\nfunction canonicalizeExpectedOptions(\n expectedOptions: MongoSchemaCollectionOptions,\n _liveOptions: MongoSchemaCollectionOptions | undefined,\n): MongoSchemaCollectionOptions | undefined {\n // Symmetric: a contract `{enabled: false}` is equivalent to absent.\n const changeStreamPreAndPostImages = isDisabledChangeStream(\n expectedOptions.changeStreamPreAndPostImages,\n )\n ? undefined\n : expectedOptions.changeStreamPreAndPostImages;\n\n const hasMeaningful =\n expectedOptions.capped ||\n expectedOptions.timeseries ||\n expectedOptions.collation ||\n changeStreamPreAndPostImages ||\n expectedOptions.clusteredIndex;\n if (!hasMeaningful) return undefined;\n\n return new MongoSchemaCollectionOptionsCtor({\n ...ifDefined('capped', expectedOptions.capped),\n ...ifDefined('timeseries', expectedOptions.timeseries),\n ...ifDefined('collation', expectedOptions.collation),\n ...ifDefined('changeStreamPreAndPostImages', changeStreamPreAndPostImages),\n ...ifDefined('clusteredIndex', expectedOptions.clusteredIndex),\n });\n}\n\nfunction isDisabledChangeStream(value: { enabled: boolean } | undefined): boolean {\n return value !== undefined && value.enabled === false;\n}\n\n/**\n * Returns a copy of `live` containing only the keys that `expected` defines.\n * Used for option families whose individual sub-fields are server-extended\n * with platform defaults (collation, timeseries, clusteredIndex), so the\n * verifier should compare only what the contract actually authored.\n *\n * When `expected` is `undefined` — i.e. the contract authored nothing for\n * this whole option family but the live IR has it — we return `live`\n * unchanged so the verifier still sees the entire live block and can\n * surface it as drift. (Returning `undefined` here would silently strip a\n * server-attached collation/timeseries/clusteredIndex that the contract\n * never asked for, hiding real drift.)\n */\nfunction stripUnspecifiedFields(\n live: Record<string, unknown>,\n expected: Record<string, unknown> | undefined,\n): Record<string, unknown> {\n if (expected === undefined) return live;\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(expected)) {\n if (Object.hasOwn(live, key)) out[key] = live[key];\n }\n return out;\n}\n","import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';\nimport type {\n OperationContext,\n VerifyDatabaseSchemaResult,\n} from '@prisma-next/framework-components/control';\nimport { VERIFY_CODE_SCHEMA_FAILURE } from '@prisma-next/framework-components/control';\nimport type { MongoContract } from '@prisma-next/mongo-contract';\nimport type { MongoSchemaIR } from '@prisma-next/mongo-schema-ir';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport { contractToMongoSchemaIR } from '../contract-to-schema';\nimport { diffMongoSchemas } from '../schema-diff';\nimport { canonicalizeSchemasForVerification } from './canonicalize-introspection';\n\nexport interface VerifyMongoSchemaOptions {\n readonly contract: MongoContract;\n readonly schema: MongoSchemaIR;\n readonly strict: boolean;\n readonly context?: OperationContext;\n /**\n * Active framework components participating in this composition. Mongo\n * verification does not currently consult them, but the parameter exists\n * for parity with `verifySqlSchema` so callers can pass the same envelope.\n */\n readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'mongo', string>>;\n}\n\nexport function verifyMongoSchema(options: VerifyMongoSchemaOptions): VerifyDatabaseSchemaResult {\n const { contract, schema, strict, context } = options;\n const startTime = Date.now();\n\n const expectedIR = contractToMongoSchemaIR(contract);\n // Strip server-applied defaults (and authored equivalents) before diffing so\n // the verifier compares like-with-like — see `canonicalize-introspection.ts`.\n const { live: canonicalLive, expected: canonicalExpected } = canonicalizeSchemasForVerification(\n schema,\n expectedIR,\n );\n const { root, issues, counts } = diffMongoSchemas(canonicalLive, canonicalExpected, strict);\n\n const ok = counts.fail === 0;\n const profileHash = typeof contract.profileHash === 'string' ? contract.profileHash : '';\n\n return {\n ok,\n ...ifDefined('code', ok ? undefined : VERIFY_CODE_SCHEMA_FAILURE),\n summary: ok ? 'Schema matches contract' : `Schema verification found ${counts.fail} issue(s)`,\n contract: {\n storageHash: contract.storage.storageHash,\n ...(profileHash ? { profileHash } : {}),\n },\n target: { expected: contract.target },\n schema: { issues, root, counts },\n meta: {\n strict,\n ...ifDefined('contractPath', context?.contractPath),\n ...ifDefined('configPath', context?.configPath),\n },\n timings: { total: Date.now() - startTime },\n };\n}\n"],"mappings":";;;;AAeA,SAAS,YAAY,MAAuC;CAC1D,MAAM,EAAE,MAAM,OAAO,GAAG,SAAS;CACjC,OAAO;;AAGT,SAAS,aAAa,OAAqC;CACzD,OAAO,IAAI,iBAAiB;EAC1B,MAAM,MAAM;EACZ,QAAQ,MAAM;EACd,QAAQ,MAAM;EACd,oBAAoB,MAAM;EAC1B,yBAAyB,MAAM;EAC/B,oBAAoB,MAAM;EAC1B,WAAW,MAAM;EACjB,SAAS,MAAM;EACf,kBAAkB,MAAM;EACxB,mBAAmB,MAAM;EAC1B,CAAC;;AAGJ,SAAS,iBAAiB,GAAyC;CACjE,OAAO,IAAI,qBAAqB;EAC9B,YAAY,EAAE;EACd,iBAAiB,EAAE;EACnB,kBAAkB,EAAE;EACrB,CAAC;;AAGJ,SAAS,eAAe,GAAyD;CAC/E,OAAO,IAAI,6BAA6B;EACtC,GAAI,EAAE,WAAW,KAAA,KAAa,EAAE,QAAQ,EAAE,QAAQ;EAClD,GAAI,EAAE,eAAe,KAAA,KAAa,EAChC,YAAY;GACV,WAAW,EAAE,WAAW;GACxB,GAAI,EAAE,WAAW,cAAc,KAAA,KAAa,EAAE,WAAW,EAAE,WAAW,WAAW;GACjF,GAAI,EAAE,WAAW,gBAAgB,KAAA,KAAa,EAAE,aAAa,EAAE,WAAW,aAAa;GACxF,EACF;EACD,GAAI,EAAE,cAAc,KAAA,KAAa,EAC/B,WAAW,YAAY,EAAE,UAAU,EACpC;EACD,GAAI,EAAE,iCAAiC,KAAA,KAAa,EAClD,8BAA8B,EAAE,SAAS,EAAE,6BAA6B,SAAS,EAClF;EACD,GAAI,EAAE,mBAAmB,KAAA,KAAa,EAAE,gBAAgB,EAAE,gBAAgB;EAC3E,CAAC;;AAGJ,SAAS,kBAAkB,MAAc,KAA6C;CAEpF,OAAO,IAAI,sBAAsB;EAC/B;EACA,UAHe,IAAI,WAAW,EAAE,EAAE,IAAI,aAG/B;EACP,GAAI,IAAI,aAAa,QAAQ,EAAE,WAAW,iBAAiB,IAAI,UAAU,EAAE;EAC3E,GAAI,IAAI,WAAW,QAAQ,EAAE,SAAS,eAAe,IAAI,QAAQ,EAAE;EACpE,CAAC;;AAGJ,SAAgB,wBAAwB,UAA+C;CACrF,IAAI,CAAC,UACH,OAAO,IAAI,cAAc,EAAE,CAAC;CAO9B,OAAO,IAAI,cAJS,OAAO,QAAQ,SAAS,QAAQ,YAAY,CAAC,KAAK,CAAC,MAAM,SAC3E,kBAAkB,MAAM,IAAI,CAGM,CAAC;;;;ACvEvC,SAAgB,iBACd,MACA,UACA,QAKA;CACA,MAAM,SAAwB,EAAE;CAChC,MAAM,qBAA+C,EAAE;CACvD,IAAI,OAAO;CACX,IAAI,OAAO;CACX,IAAI,OAAO;CAEX,MAAM,WAAW,IAAI,IAAI,CAAC,GAAG,KAAK,iBAAiB,GAAG,SAAS,gBAAgB,CAAC;CAEhF,KAAK,MAAM,QAAQ,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE;EACvC,MAAM,WAAW,KAAK,WAAW,KAAK;EACtC,MAAM,eAAe,SAAS,WAAW,KAAK;EAE9C,IAAI,CAAC,YAAY,cAAc;GAC7B,OAAO,KAAK;IACV,MAAM;IACN,OAAO;IACP,SAAS,eAAe,KAAK;IAC9B,CAAC;GACF,mBAAmB,KAAK;IACtB,QAAQ;IACR,MAAM;IACN;IACA,cAAc,uBAAuB;IACrC,MAAM;IACN,SAAS,eAAe,KAAK;IAC7B,UAAU;IACV,QAAQ;IACR,UAAU,EAAE;IACb,CAAC;GACF;GACA;;EAGF,IAAI,YAAY,CAAC,cAAc;GAC7B,MAAM,SAAS,SAAS,SAAS;GACjC,OAAO,KAAK;IACV,MAAM;IACN,OAAO;IACP,SAAS,qBAAqB,KAAK;IACpC,CAAC;GACF,mBAAmB,KAAK;IACtB;IACA,MAAM;IACN;IACA,cAAc,uBAAuB;IACrC,MAAM;IACN,SAAS,qBAAqB,KAAK;IACnC,UAAU;IACV,QAAQ;IACR,UAAU,EAAE;IACb,CAAC;GACF,IAAI,WAAW,QAAQ;QAClB;GACL;;EAGF,MAAM,KAAK;EACX,MAAM,KAAK;EACX,MAAM,gBAAgB,YAAY,MAAM,IAAI,IAAI,QAAQ,OAAO;EAC/D,MAAM,oBAAoB,cAAc,MAAM,IAAI,IAAI,QAAQ,OAAO;EACrE,MAAM,kBAAkB,YAAY,MAAM,IAAI,IAAI,QAAQ,OAAO;EACjE,MAAM,WAAW;GAAC,GAAG;GAAe,GAAG;GAAmB,GAAG;GAAgB;EAE7E,MAAM,cAAc,SAAS,QAC1B,GAAG,MAAO,EAAE,WAAW,SAAS,SAAS,EAAE,WAAW,UAAU,MAAM,SAAS,SAAS,GACzF,OACD;EAED,KAAK,MAAM,KAAK,UACd,IAAI,EAAE,WAAW,QAAQ;OACpB,IAAI,EAAE,WAAW,QAAQ;OACzB;EAGP,IAAI,SAAS,WAAW,GACtB;EAGF,mBAAmB,KAAK;GACtB,QAAQ;GACR,MAAM;GACN;GACA,cAAc,uBAAuB;GACrC,MAAM,gBAAgB,SAAS,UAAU;GACzC,SACE,gBAAgB,SAAS,eAAe,KAAK,aAAa,eAAe,KAAK;GAChF,UAAU;GACV,QAAQ;GACR;GACD,CAAC;;CAGJ,MAAM,aAAa,OAAO,IAAI,SAAS,OAAO,IAAI,SAAS;CAC3D,MAAM,aAAa,OAAO,OAAO,OAAO,mBAAmB;CAc3D,OAAO;EAAE,MAAA;GAXP,QAAQ;GACR,MAAM;GACN,MAAM;GACN,cAAc;GACd,MAAM,eAAe,SAAS,UAAU;GACxC,SAAS,eAAe,SAAS,mBAAmB;GACpD,UAAU;GACV,QAAQ;GACR,UAAU;GAGC;EAAE;EAAQ,QAAQ;GAAE;GAAM;GAAM;GAAM;GAAY;EAAE;;AAGnE,SAAS,oBAAoB,OAAiC;CAC5D,MAAM,OAAO,MAAM,KAAK,KAAK,MAAM,GAAG,EAAE,MAAM,GAAG,EAAE,YAAY,CAAC,KAAK,IAAI;CACzE,MAAM,OAAO;EACX,MAAM,SAAS,WAAW;EAC1B,MAAM,SAAS,WAAW;EAC1B,MAAM,sBAAsB,OAAO,OAAO,MAAM,uBAAuB;EACvE,MAAM,0BAA0B,OAAO,aAAa,MAAM,wBAAwB,KAAK;EACvF,MAAM,qBAAqB,MAAM,aAAa,MAAM,mBAAmB,KAAK;EAC5E,MAAM,YAAY,OAAO,aAAa,MAAM,UAAU,KAAK;EAC3D,MAAM,UAAU,MAAM,aAAa,MAAM,QAAQ,KAAK;EACtD,MAAM,mBAAmB,MAAM,MAAM,qBAAqB;EAC1D,MAAM,oBAAoB,MAAM,MAAM,sBAAsB;EAC7D,CACE,OAAO,QAAQ,CACf,KAAK,IAAI;CACZ,OAAO,OAAO,GAAG,KAAK,GAAG,SAAS;;AAGpC,SAAS,gBAAgB,OAAiC;CACxD,OAAO,MAAM,KAAK,KAAK,MAAM,GAAG,EAAE,MAAM,GAAG,EAAE,YAAY,CAAC,KAAK,KAAK;;AAGtE,SAAS,YACP,UACA,MACA,UACA,QACA,QAC0B;CAC1B,MAAM,QAAkC,EAAE;CAC1C,MAAM,6BAAa,IAAI,KAA+B;CACtD,KAAK,MAAM,OAAO,KAAK,SAAS,WAAW,IAAI,oBAAoB,IAAI,EAAE,IAAI;CAE7E,MAAM,iCAAiB,IAAI,KAA+B;CAC1D,KAAK,MAAM,OAAO,SAAS,SAAS,eAAe,IAAI,oBAAoB,IAAI,EAAE,IAAI;CAErF,KAAK,MAAM,CAAC,KAAK,QAAQ,gBACvB,IAAI,WAAW,IAAI,IAAI,EACrB,MAAM,KAAK;EACT,QAAQ;EACR,MAAM;EACN,MAAM,gBAAgB,IAAI;EAC1B,cAAc,uBAAuB,SAAS;EAC9C,MAAM;EACN,SAAS,SAAS,gBAAgB,IAAI,CAAC;EACvC,UAAU;EACV,QAAQ;EACR,UAAU,EAAE;EACb,CAAC;MACG;EACL,OAAO,KAAK;GACV,MAAM;GACN,OAAO;GACP,mBAAmB,gBAAgB,IAAI;GACvC,SAAS,SAAS,gBAAgB,IAAI,CAAC,0BAA0B,SAAS;GAC3E,CAAC;EACF,MAAM,KAAK;GACT,QAAQ;GACR,MAAM;GACN,MAAM,gBAAgB,IAAI;GAC1B,cAAc,uBAAuB,SAAS;GAC9C,MAAM;GACN,SAAS,SAAS,gBAAgB,IAAI,CAAC;GACvC,UAAU;GACV,QAAQ;GACR,UAAU,EAAE;GACb,CAAC;;CAIN,KAAK,MAAM,CAAC,KAAK,QAAQ,YACvB,IAAI,CAAC,eAAe,IAAI,IAAI,EAAE;EAC5B,MAAM,SAAS,SAAS,SAAS;EACjC,OAAO,KAAK;GACV,MAAM;GACN,OAAO;GACP,mBAAmB,gBAAgB,IAAI;GACvC,SAAS,eAAe,gBAAgB,IAAI,CAAC,kBAAkB,SAAS;GACzE,CAAC;EACF,MAAM,KAAK;GACT;GACA,MAAM;GACN,MAAM,gBAAgB,IAAI;GAC1B,cAAc,uBAAuB,SAAS;GAC9C,MAAM;GACN,SAAS,eAAe,gBAAgB,IAAI;GAC5C,UAAU;GACV,QAAQ;GACR,UAAU,EAAE;GACb,CAAC;;CAIN,OAAO;;AAGT,SAAS,cACP,UACA,MACA,UACA,QACA,QAC0B;CAC1B,IAAI,CAAC,KAAK,aAAa,CAAC,SAAS,WAAW,OAAO,EAAE;CAErD,IAAI,SAAS,aAAa,CAAC,KAAK,WAAW;EACzC,OAAO,KAAK;GACV,MAAM;GACN,OAAO;GACP,SAAS,oCAAoC,SAAS;GACvD,CAAC;EACF,OAAO,CACL;GACE,QAAQ;GACR,MAAM;GACN,MAAM;GACN,cAAc,uBAAuB,SAAS;GAC9C,MAAM;GACN,SAAS;GACT,UAAU,aAAa,SAAS,UAAU,WAAW;GACrD,QAAQ;GACR,UAAU,EAAE;GACb,CACF;;CAGH,IAAI,CAAC,SAAS,aAAa,KAAK,WAAW;EACzC,MAAM,SAAS,SAAS,SAAS;EACjC,OAAO,KAAK;GACV,MAAM;GACN,OAAO;GACP,SAAS,kCAAkC,SAAS;GACrD,CAAC;EACF,OAAO,CACL;GACE;GACA,MAAM;GACN,MAAM;GACN,cAAc,uBAAuB,SAAS;GAC9C,MAAM;GACN,SAAS;GACT,UAAU;GACV,QAAQ,aAAa,KAAK,UAAU,WAAW;GAC/C,UAAU,EAAE;GACb,CACF;;CAGH,MAAM,UAAU,KAAK;CACrB,MAAM,cAAc,SAAS;CAC7B,MAAM,aAAa,aAAa,QAAQ,WAAW;CACnD,MAAM,iBAAiB,aAAa,YAAY,WAAW;CAE3D,IACE,eAAe,kBACf,QAAQ,oBAAoB,YAAY,mBACxC,QAAQ,qBAAqB,YAAY,kBACzC;EACA,OAAO,KAAK;GACV,MAAM;GACN,OAAO;GACP,UAAU;GACV,QAAQ;GACR,SAAS,qCAAqC,SAAS;GACxD,CAAC;EACF,OAAO,CACL;GACE,QAAQ;GACR,MAAM;GACN,MAAM;GACN,cAAc,uBAAuB,SAAS;GAC9C,MAAM;GACN,SAAS;GACT,UAAU;IACR,YAAY,YAAY;IACxB,iBAAiB,YAAY;IAC7B,kBAAkB,YAAY;IAC/B;GACD,QAAQ;IACN,YAAY,QAAQ;IACpB,iBAAiB,QAAQ;IACzB,kBAAkB,QAAQ;IAC3B;GACD,UAAU,EAAE;GACb,CACF;;CAGH,OAAO,CACL;EACE,QAAQ;EACR,MAAM;EACN,MAAM;EACN,cAAc,uBAAuB,SAAS;EAC9C,MAAM;EACN,SAAS;EACT,UAAU;EACV,QAAQ;EACR,UAAU,EAAE;EACb,CACF;;AAGH,SAAS,YACP,UACA,MACA,UACA,QACA,QAC0B;CAC1B,IAAI,CAAC,KAAK,WAAW,CAAC,SAAS,SAAS,OAAO,EAAE;CAEjD,IAAI,CAAC,SAAS,WAAW,KAAK,SAAS;EACrC,MAAM,SAAS,SAAS,SAAS;EACjC,OAAO,KAAK;GACV,MAAM;GACN,OAAO;GACP,QAAQ,aAAa,KAAK,QAAQ;GAClC,SAAS,gCAAgC,SAAS;GACnD,CAAC;EACF,OAAO,CACL;GACE;GACA,MAAM;GACN,MAAM;GACN,cAAc,uBAAuB,SAAS;GAC9C,MAAM;GACN,SAAS;GACT,UAAU;GACV,QAAQ,KAAK;GACb,UAAU,EAAE;GACb,CACF;;CAGH,IAAI,UAAU,KAAK,SAAS,SAAS,QAAQ,EAC3C,OAAO,CACL;EACE,QAAQ;EACR,MAAM;EACN,MAAM;EACN,cAAc,uBAAuB,SAAS;EAC9C,MAAM;EACN,SAAS;EACT,UAAU,aAAa,SAAS,QAAQ;EACxC,QAAQ,aAAa,KAAK,QAAQ;EAClC,UAAU,EAAE;EACb,CACF;CAGH,OAAO,KAAK;EACV,MAAM;EACN,OAAO;EACP,UAAU,aAAa,SAAS,QAAQ;EACxC,QAAQ,aAAa,KAAK,QAAQ;EAClC,SAAS,mCAAmC,SAAS;EACtD,CAAC;CACF,OAAO,CACL;EACE,QAAQ;EACR,MAAM;EACN,MAAM;EACN,cAAc,uBAAuB,SAAS;EAC9C,MAAM;EACN,SAAS;EACT,UAAU,SAAS;EACnB,QAAQ,KAAK;EACb,UAAU,EAAE;EACb,CACF;;;;AC7WH,SAAgB,mCACd,MACA,UACsB;CACtB,MAAM,iCAAiB,IAAI,KAAoC;CAC/D,KAAK,MAAM,KAAK,SAAS,aAAa,eAAe,IAAI,EAAE,MAAM,EAAE;CAEnE,MAAM,6BAAa,IAAI,KAAoC;CAC3D,KAAK,MAAM,KAAK,KAAK,aAAa,WAAW,IAAI,EAAE,MAAM,EAAE;CAE3D,MAAM,gBAAgB,KAAK,YAAY,KAAK,MAC1C,2BAA2B,GAAG,eAAe,IAAI,EAAE,KAAK,CAAC,CAC1D;CACD,MAAM,oBAAoB,SAAS,YAAY,KAAK,MAClD,+BAA+B,GAAG,WAAW,IAAI,EAAE,KAAK,CAAC,CAC1D;CAED,OAAO;EACL,MAAM,IAAIA,cAAkB,cAAc;EAC1C,UAAU,IAAIA,cAAkB,kBAAkB;EACnD;;AAGH,SAAS,2BACP,UACA,cACuB;CACvB,MAAM,kBAAkB,cAAc,WAAW,EAAE;CACnD,MAAM,UAAU,SAAS,QAAQ,KAAK,QACpC,sBAAsB,KAAK,6BAA6B,KAAK,gBAAgB,CAAC,CAC/E;CAED,MAAM,UAAU,SAAS,UACrB,wBAAwB,SAAS,SAAS,cAAc,QAAQ,GAChE,KAAA;CAEJ,OAAO,IAAIC,sBAA0B;EACnC,MAAM,SAAS;EACf;EACA,GAAG,UAAU,aAAa,SAAS,UAAU;EAC7C,GAAG,UAAU,WAAW,QAAQ;EACjC,CAAC;;AAGJ,SAAS,+BACP,cACA,UACuB;CAOvB,MAAM,UAAU,aAAa,QAAQ,IAAI,8BAA8B;CAEvE,MAAM,UAAU,aAAa,UACzB,4BAA4B,aAAa,SAAS,UAAU,QAAQ,GACpE,KAAA;CAEJ,OAAO,IAAIA,sBAA0B;EACnC,MAAM,aAAa;EACnB;EACA,GAAG,UAAU,aAAa,aAAa,UAAU;EACjD,GAAG,UAAU,WAAW,QAAQ;EACjC,CAAC;;AAGJ,SAAS,8BAA8B,OAA2C;CAEhF,IAAI,CADe,MAAM,KAAK,MAAM,MAAM,EAAE,cAAc,OAC3C,EAAE,OAAO;CACxB,OAAO,IAAIC,iBAAqB;EAC9B,MAAM,aAAa,MAAM,KAAK;EAC9B,QAAQ,MAAM;EACd,GAAG,UAAU,UAAU,MAAM,OAAO;EACpC,GAAG,UAAU,sBAAsB,MAAM,mBAAmB;EAC5D,GAAG,UAAU,2BAA2B,MAAM,wBAAwB;EACtE,GAAG,UAAU,sBAAsB,MAAM,mBAAmB;EAC5D,GAAG,UAAU,aAAa,MAAM,UAAU;EAC1C,GAAG,UAAU,WAAW,MAAM,QAAQ;EACtC,GAAG,UAAU,oBAAoB,MAAM,iBAAiB;EACxD,GAAG,UAAU,qBAAqB,MAAM,kBAAkB;EAC3D,CAAC;;;;;;;;AASJ,SAAS,aACP,MAOC;CACD,MAAM,cAAc,KAAK,QAAQ,MAAM,EAAE,cAAc,OAAO;CAC9D,IAAI,YAAY,UAAU,GAAG,OAAO;CACpC,MAAM,aAAa,CAAC,GAAG,YAAY,CAAC,MAAM,GAAG,MAAM,EAAE,MAAM,cAAc,EAAE,MAAM,CAAC;CAClF,IAAI,UAAU;CACd,OAAO,KAAK,KAAK,MAAM;EACrB,IAAI,EAAE,cAAc,QAAQ,OAAO;EACnC,MAAM,OAAO,WAAW;;EAExB,IAAI,SAAS,KAAA,GACX,MAAM,IAAI,MAAM,2CAA2C;EAE7D,OAAO;GACP;;AAGJ,SAAS,sBACP,WACA,eACkB;CAClB,MAAM,gBAAgB,aAAa,qBAAqB,UAAU,CAAC;CACnE,MAAM,YAAY,UAAU,YACxB,uBAAuB,UAAU,WAAW,eAAe,UAAU,GACrE,UAAU;CAUd,MAAM,UACJ,eAAe,YAAY,KAAA,KAAa,sBAAsB,eAAe,UAAU,QAAQ,GAC3F,KAAA,IACA,UAAU;CAChB,MAAM,mBACJ,eAAe,qBAAqB,KAAA,KAAa,UAAU,qBAAqB,YAC5E,KAAA,IACA,UAAU;CAChB,MAAM,oBACJ,eAAe,sBAAsB,KAAA,KAAa,UAAU,sBAAsB,aAC9E,KAAA,IACA,UAAU;CAEhB,OAAO,IAAIA,iBAAqB;EAC9B,MAAM;EACN,QAAQ,UAAU;EAClB,GAAG,UAAU,UAAU,UAAU,OAAO;EACxC,GAAG,UAAU,sBAAsB,UAAU,mBAAmB;EAChE,GAAG,UAAU,2BAA2B,UAAU,wBAAwB;EAC1E,GAAG,UAAU,sBAAsB,UAAU,mBAAmB;EAChE,GAAG,UAAU,aAAa,UAAU;EACpC,GAAG,UAAU,WAAW,QAAQ;EAChC,GAAG,UAAU,oBAAoB,iBAAiB;EAClD,GAAG,UAAU,qBAAqB,kBAAkB;EACrD,CAAC;;;;;;;;;;AAWJ,SAAS,6BACP,WACA,iBAC8B;CAE9B,MAAM,aADoB,aAAa,qBAAqB,UAAU,CAClC,CAAC,KAAK,MAAM,GAAG,EAAE,MAAM,GAAG,EAAE,YAAY,CAAC,KAAK,IAAI;CACtF,KAAK,MAAM,YAAY,iBAIrB,IAHuB,aAAa,SAAS,KAAK,CAC/C,KAAK,MAAM,GAAG,EAAE,MAAM,GAAG,EAAE,YAAY,CACvC,KAAK,IACU,KAAK,YAAY,OAAO;;;;;;;;;;;AAc9C,SAAS,qBAAqB,WAG3B;CAKD,IAAI,EAHF,UAAU,KAAK,UAAU,KACzB,UAAU,KAAK,MAAM,MAAM,EAAE,UAAU,UAAU,EAAE,cAAc,OAAO,KAEtD,CAAC,UAAU,SAAS,OAAO,UAAU;CAEzD,MAAM,WAAW,OAAO,KAAK,UAAU,QAAQ,CAAC,KAAK,WAAW;EAC9D;EACA,WAAW;EACZ,EAAE;CAWH,MAAM,gBAA4B,EAAE;CACpC,KAAK,MAAM,OAAO,UAAU,MAAM;EAChC,IAAI,IAAI,UAAU,SAAS;EAC3B,IAAI,IAAI,UAAU,QAAQ;GACxB,cAAc,KAAK,GAAG,SAAS;GAC/B;;EAEF,cAAc,KAAK,IAAI;;CAEzB,OAAO;;;;;;;;;;;;;AAcT,SAAS,sBACP,eAIA,aACS;CACT,IAAI,gBAAgB,KAAA,GAAW,OAAO;CAEtC,OADmB,cAAc,QAAQ,MAAM,EAAE,cAAc,OAAO,CAAC,KAAK,MAAM,EAAE,MACnE,CAAC,OAAO,UAAU,YAAY,WAAW,EAAE;;AAG9D,SAAS,wBACP,aACA,iBAC0C;CAC1C,MAAM,YAAY,YAAY,YAC1B,uBAAuB,YAAY,WAAW,iBAAiB,UAAU,GACzE,KAAA;CAIJ,MAAM,aAAa,YAAY,aAC1B,uBACC,YAAY,YACZ,iBAAiB,WAClB,GACD,KAAA;CAIJ,MAAM,iBAAiB,YAAY,iBAC9B,uBACC,YAAY,gBACZ,iBAAiB,eAClB,GACD,KAAA;CAKJ,MAAM,+BAA+B,uBACnC,YAAY,6BACb,GACG,KAAA,IACA,YAAY;CAIhB,IAAI,EADF,YAAY,UAAU,cAAc,aAAa,gCAAgC,iBAC/D,OAAO,KAAA;CAE3B,OAAO,IAAIC,6BAAiC;EAC1C,GAAG,UAAU,UAAU,YAAY,OAAO;EAC1C,GAAG,UAAU,cAAc,WAAW;EACtC,GAAG,UAAU,aAAa,UAAU;EACpC,GAAG,UAAU,gCAAgC,6BAA6B;EAC1E,GAAG,UAAU,kBAAkB,eAAe;EAC/C,CAAC;;AAGJ,SAAS,4BACP,iBACA,cAC0C;CAE1C,MAAM,+BAA+B,uBACnC,gBAAgB,6BACjB,GACG,KAAA,IACA,gBAAgB;CAQpB,IAAI,EALF,gBAAgB,UAChB,gBAAgB,cAChB,gBAAgB,aAChB,gCACA,gBAAgB,iBACE,OAAO,KAAA;CAE3B,OAAO,IAAIA,6BAAiC;EAC1C,GAAG,UAAU,UAAU,gBAAgB,OAAO;EAC9C,GAAG,UAAU,cAAc,gBAAgB,WAAW;EACtD,GAAG,UAAU,aAAa,gBAAgB,UAAU;EACpD,GAAG,UAAU,gCAAgC,6BAA6B;EAC1E,GAAG,UAAU,kBAAkB,gBAAgB,eAAe;EAC/D,CAAC;;AAGJ,SAAS,uBAAuB,OAAkD;CAChF,OAAO,UAAU,KAAA,KAAa,MAAM,YAAY;;;;;;;;;;;;;;;AAgBlD,SAAS,uBACP,MACA,UACyB;CACzB,IAAI,aAAa,KAAA,GAAW,OAAO;CACnC,MAAM,MAA+B,EAAE;CACvC,KAAK,MAAM,OAAO,OAAO,KAAK,SAAS,EACrC,IAAI,OAAO,OAAO,MAAM,IAAI,EAAE,IAAI,OAAO,KAAK;CAEhD,OAAO;;;;ACzWT,SAAgB,kBAAkB,SAA+D;CAC/F,MAAM,EAAE,UAAU,QAAQ,QAAQ,YAAY;CAC9C,MAAM,YAAY,KAAK,KAAK;CAK5B,MAAM,EAAE,MAAM,eAAe,UAAU,sBAAsB,mCAC3D,QAJiB,wBAAwB,SAK/B,CACX;CACD,MAAM,EAAE,MAAM,QAAQ,WAAW,iBAAiB,eAAe,mBAAmB,OAAO;CAE3F,MAAM,KAAK,OAAO,SAAS;CAC3B,MAAM,cAAc,OAAO,SAAS,gBAAgB,WAAW,SAAS,cAAc;CAEtF,OAAO;EACL;EACA,GAAG,UAAU,QAAQ,KAAK,KAAA,IAAY,2BAA2B;EACjE,SAAS,KAAK,4BAA4B,6BAA6B,OAAO,KAAK;EACnF,UAAU;GACR,aAAa,SAAS,QAAQ;GAC9B,GAAI,cAAc,EAAE,aAAa,GAAG,EAAE;GACvC;EACD,QAAQ,EAAE,UAAU,SAAS,QAAQ;EACrC,QAAQ;GAAE;GAAQ;GAAM;GAAQ;EAChC,MAAM;GACJ;GACA,GAAG,UAAU,gBAAgB,SAAS,aAAa;GACnD,GAAG,UAAU,cAAc,SAAS,WAAW;GAChD;EACD,SAAS,EAAE,OAAO,KAAK,KAAK,GAAG,WAAW;EAC3C"}
|