@nxgt/mongo 0.1.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.
@@ -0,0 +1,21 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/definition/define-collection.ts", "../src/definition/fields.ts", "../src/definition/json-schema.ts", "../src/errors/data-error.ts", "../src/errors/to-data-error.ts", "../src/pagination/cursor.ts", "../src/pagination/page.ts", "../src/sync/index-diff.ts", "../src/sync/validator-diff.ts", "../src/sync/sync-collection.ts", "../src/repository/create-repository.ts", "../src/transaction/with-transaction.ts"],
4
+ "sourcesContent": [
5
+ "import type { IndexDescription, ObjectId } from 'mongodb';\nimport type { z } from 'zod';\n\n/** What MongoDB does with a document that fails the validator. */\nexport type ValidationAction = 'error' | 'warn';\n\n/**\n * Which documents the validator applies to. `off` writes no validator at all;\n * `moderate` exempts documents that were already invalid from updates.\n */\nexport type ValidationLevel = 'off' | 'moderate' | 'strict';\n\nexport interface ValidationConfig {\n\t/** Default `'strict'`. */\n\tlevel?: ValidationLevel;\n\t/** Default `'error'`. `'warn'` logs and lets the write through. */\n\taction?: ValidationAction;\n}\n\n/** What `defineCollection` takes. */\nexport interface CollectionConfig<Schema extends z.ZodObject> {\n\t/** The collection's name on the server. */\n\tname: string;\n\t/**\n\t * The documents, as they are stored: `z.output` is what a read gives back,\n\t * `z.input` what a write takes. It must have an `_id`.\n\t */\n\tschema: Schema;\n\t/** The indexes `sync` creates, as the driver describes them. */\n\tindexes?: readonly IndexDescription[];\n\t/** The `$jsonSchema` validator `sync` writes from the schema. */\n\tvalidation?: ValidationConfig;\n}\n\n/** A collection, as `defineCollection` returns it: frozen, with its defaults. */\nexport interface CollectionDefinition<Schema extends z.ZodObject = z.ZodObject>\n\textends Readonly<CollectionConfig<Schema>> {\n\treadonly indexes: readonly IndexDescription[];\n\treadonly validation: Required<ValidationConfig>;\n}\n\n/** Any definition, whatever its documents. */\nexport type AnyCollectionDefinition = CollectionDefinition<any>;\n\n/** The documents of a definition, as they are read back. */\nexport type DocumentOf<Def> = Def extends { schema: infer Schema }\n\t? Schema extends z.ZodType\n\t\t? z.output<Schema>\n\t\t: never\n\t: never;\n\n/** What a write takes: the documents before their defaults are filled. */\nexport type NewDocumentOf<Def> = Def extends { schema: infer Schema }\n\t? Schema extends z.ZodType\n\t\t? z.input<Schema>\n\t\t: never\n\t: never;\n\n/** The type of `_id`. */\nexport type IdOf<Def> =\n\tDocumentOf<Def> extends { _id: infer Id } ? Id : ObjectId;\n\n/** A field of the documents, as a top-level key. */\nexport type FieldOf<Def> = keyof DocumentOf<Def> & string;\n\n/**\n * Defines a collection: its name, the Zod schema of its documents, its\n * indexes, and how its validator is applied.\n *\n * The schema is the one source: it types every read and write, and `sync`\n * derives the collection's `$jsonSchema` validator from it.\n *\n * ```ts\n * export const users = defineCollection({\n * \tname: 'users',\n * \tschema: z.object({\n * \t\t_id: id(),\n * \t\temail: z.email(),\n * \t\t...timestamps(),\n * \t\t...softDelete(),\n * \t}),\n * \tindexes: [{ key: { email: 1 }, unique: true, name: 'users_email_unique' }],\n * });\n * ```\n */\nexport function defineCollection<Schema extends z.ZodObject>(\n\tconfig: CollectionConfig<Schema>,\n): CollectionDefinition<Schema> {\n\tif (!('_id' in config.schema.shape)) {\n\t\tthrow new TypeError(\n\t\t\t`defineCollection: \"${config.name}\"'s schema has no _id. Add ` +\n\t\t\t\t'`_id: id()`, which fills a new ObjectId on create, or declare the ' +\n\t\t\t\t'key your documents use.',\n\t\t);\n\t}\n\treturn Object.freeze({\n\t\t...config,\n\t\tindexes: Object.freeze([...(config.indexes ?? [])]),\n\t\tvalidation: Object.freeze({\n\t\t\tlevel: config.validation?.level ?? 'strict',\n\t\t\taction: config.validation?.action ?? 'error',\n\t\t}),\n\t}) as CollectionDefinition<Schema>;\n}\n\n/** Which of the fields the repository knows about a definition's schema has. */\nexport function stampsOf(definition: AnyCollectionDefinition): {\n\tcreatedAt: boolean;\n\tupdatedAt: boolean;\n\tdeletedAt: boolean;\n\tversion: boolean;\n\tcreatedBy: boolean;\n\tupdatedBy: boolean;\n\tdeletedBy: boolean;\n} {\n\tconst shape = definition.schema.shape as Record<string, unknown>;\n\tconst has = (name: string) => name in shape;\n\treturn {\n\t\tcreatedAt: has('createdAt'),\n\t\tupdatedAt: has('updatedAt'),\n\t\tdeletedAt: has('deletedAt'),\n\t\tversion: has('version'),\n\t\tcreatedBy: has('createdBy'),\n\t\tupdatedBy: has('updatedBy'),\n\t\tdeletedBy: has('deletedBy'),\n\t};\n}\n",
6
+ "import { ObjectId } from 'mongodb';\nimport { z } from 'zod';\n\n/** An `ObjectId`, read without `instanceof`: two copies of the driver. */\nfunction isObjectId(value: unknown): value is ObjectId {\n\treturn (\n\t\ttypeof value === 'object' &&\n\t\tvalue !== null &&\n\t\t(value as { _bsontype?: unknown })._bsontype === 'ObjectId'\n\t);\n}\n\n/**\n * An `ObjectId`, declared to MongoDB as `bsonType: 'objectId'`. JSON Schema\n * has no type for one, so the metadata is how the validator learns of it.\n */\nexport function objectId() {\n\treturn z\n\t\t.custom<ObjectId>(isObjectId, { error: 'must be an ObjectId' })\n\t\t.meta({ bsonType: 'objectId' });\n}\n\n/**\n * `_id`, filled with a new `ObjectId` when a document is created: optional to\n * write, always there once read.\n */\nexport function id() {\n\treturn objectId().default(() => new ObjectId());\n}\n\n/**\n * `createdAt` and `updatedAt`, filled on create. A repository sets `updatedAt`\n * on every update.\n */\nexport function timestamps() {\n\treturn {\n\t\tcreatedAt: z.date().default(() => new Date()),\n\t\tupdatedAt: z.date().default(() => new Date()),\n\t};\n}\n\n/**\n * `deletedAt`, `null` while the document is live. A repository on a collection\n * with it soft-deletes, and leaves deleted documents out of every read.\n */\nexport function softDelete() {\n\treturn { deletedAt: z.date().nullable().default(null) };\n}\n\n/**\n * `version`, raised by one on every update. A repository with it takes\n * `expectedVersion` and throws `OptimisticLockError` when it no longer\n * matches.\n */\nexport function optimisticLock() {\n\treturn { version: z.int().nonnegative().default(0) };\n}\n\n/**\n * `createdBy`, `updatedBy` and `deletedBy`, stamped from the actor a\n * repository was given with `as(actor)`. The actor's own type is the schema\n * passed in, an `ObjectId` by default.\n */\nexport function actors<Actor extends z.ZodType = ReturnType<typeof objectId>>(\n\tactor: Actor = objectId() as unknown as Actor,\n) {\n\treturn {\n\t\tcreatedBy: actor.nullable().default(null),\n\t\tupdatedBy: actor.nullable().default(null),\n\t\tdeletedBy: actor.nullable().default(null),\n\t};\n}\n\n/** The fields the repository gives a meaning to, by name. */\nexport const STAMP_FIELDS = {\n\tid: '_id',\n\tcreatedAt: 'createdAt',\n\tupdatedAt: 'updatedAt',\n\tdeletedAt: 'deletedAt',\n\tversion: 'version',\n\tcreatedBy: 'createdBy',\n\tupdatedBy: 'updatedBy',\n\tdeletedBy: 'deletedBy',\n} as const;\n",
7
+ "import { z } from 'zod';\n\n/**\n * Every keyword MongoDB's `$jsonSchema` knows. It **rejects** a document that\n * uses any other, rather than ignoring it, so anything not in here is dropped\n * on the way out.\n */\nexport const MONGO_JSON_SCHEMA_KEYWORDS: ReadonlySet<string> = new Set([\n\t'additionalItems',\n\t'additionalProperties',\n\t'allOf',\n\t'anyOf',\n\t'bsonType',\n\t'dependencies',\n\t'description',\n\t'enum',\n\t'exclusiveMaximum',\n\t'exclusiveMinimum',\n\t'items',\n\t'maxItems',\n\t'maxLength',\n\t'maxProperties',\n\t'maximum',\n\t'minItems',\n\t'minLength',\n\t'minProperties',\n\t'minimum',\n\t'multipleOf',\n\t'not',\n\t'oneOf',\n\t'pattern',\n\t'patternProperties',\n\t'properties',\n\t'required',\n\t'title',\n\t'type',\n\t'uniqueItems',\n]);\n\n/** Keywords whose value is a map of names to schemas, not a schema. */\nconst SCHEMA_MAPS = new Set([\n\t'properties',\n\t'patternProperties',\n\t'dependencies',\n]);\n\ntype Node = Record<string, unknown>;\n\nfunction isRecord(value: unknown): value is Node {\n\treturn typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * A JavaScript number reaches BSON as an `int` when it is a whole number that\n * fits in 32 bits, and as a `double` otherwise — never as a `long`, unless the\n * caller wrapped it. `type: \"integer\"`, which MongoDB has no equivalent for,\n * therefore becomes the three types a whole number can arrive as, with\n * `multipleOf: 1` to refuse a fractional double.\n */\nconst INTEGER_BSON_TYPES = ['int', 'long', 'double'];\n\nfunction convertIntegerType(node: Node): void {\n\tconst type = node.type;\n\tif (type === 'integer') {\n\t\tdelete node.type;\n\t\tnode.bsonType = [...INTEGER_BSON_TYPES];\n\t\tnode.multipleOf ??= 1;\n\t\treturn;\n\t}\n\tif (Array.isArray(type) && type.includes('integer')) {\n\t\tdelete node.type;\n\t\tnode.bsonType = [\n\t\t\t...type.filter((one) => one !== 'integer'),\n\t\t\t...INTEGER_BSON_TYPES,\n\t\t];\n\t\tnode.multipleOf ??= 1;\n\t}\n}\n\nfunction refName(ref: string): string {\n\treturn ref.replace(/^#\\/(definitions|\\$defs)\\//, '');\n}\n\nfunction inline(value: unknown, defs: Node, stack: string[]): unknown {\n\tif (Array.isArray(value)) {\n\t\treturn value.map((one) => inline(one, defs, stack));\n\t}\n\tif (!isRecord(value)) return value;\n\n\tif (typeof value.$ref === 'string') {\n\t\tconst name = refName(value.$ref);\n\t\tif (stack.includes(name)) {\n\t\t\tthrow new TypeError(\n\t\t\t\t`toMongoJsonSchema: \"${name}\" refers to itself. MongoDB's $jsonSchema ` +\n\t\t\t\t\t'has no $ref, so a recursive schema cannot be a validator. Give the ' +\n\t\t\t\t\t'collection no validator, or model the field as an object with no ' +\n\t\t\t\t\t'schema of its own.',\n\t\t\t);\n\t\t}\n\t\tconst target = defs[name];\n\t\tif (!isRecord(target)) {\n\t\t\tthrow new TypeError(\n\t\t\t\t`toMongoJsonSchema: cannot resolve ${value.$ref}, which zod emitted`,\n\t\t\t);\n\t\t}\n\t\tconst { $ref: _ref, ...siblings } = value;\n\t\treturn {\n\t\t\t...(inline(target, defs, [...stack, name]) as Node),\n\t\t\t...(inline(siblings, defs, stack) as Node),\n\t\t};\n\t}\n\n\tconst out: Node = {};\n\tfor (const [key, inner] of Object.entries(value)) {\n\t\tif (!MONGO_JSON_SCHEMA_KEYWORDS.has(key)) continue;\n\t\tif (SCHEMA_MAPS.has(key) && isRecord(inner)) {\n\t\t\tconst mapped: Node = {};\n\t\t\tfor (const [name, schema] of Object.entries(inner)) {\n\t\t\t\tmapped[name] = inline(schema, defs, stack);\n\t\t\t}\n\t\t\tout[key] = mapped;\n\t\t\tcontinue;\n\t\t}\n\t\tout[key] = inline(inner, defs, stack);\n\t}\n\tconvertIntegerType(out);\n\treturn out;\n}\n\n/**\n * A Zod schema as a MongoDB `$jsonSchema`, ready for a collection's validator.\n *\n * `z.toJSONSchema` alone is not one: MongoDB rejects `$schema`, `$ref`,\n * `definitions`, `default`, `format` and `id`, has no `integer` type, and\n * treats a keyword it does not know as an error rather than ignoring it. This\n * resolves every `$ref` by inlining it, keeps only the keywords MongoDB\n * knows, and maps `integer`.\n *\n * `Date` and `ObjectId` have no JSON Schema type: they are declared with\n * `bsonType`, which `date()` and `objectId()` already carry in their metadata.\n * Any schema can do the same with `.meta({ bsonType: 'decimal' })`.\n *\n * ```ts\n * toMongoJsonSchema(z.object({ _id: objectId(), email: z.string() }));\n * // { type: 'object', properties: { … }, required: ['_id', 'email'], … }\n * ```\n */\nexport function toMongoJsonSchema(schema: z.ZodType): Record<string, unknown> {\n\tconst json = z.toJSONSchema(schema, {\n\t\ttarget: 'draft-4',\n\t\tio: 'output',\n\t\t// A `Date` is unrepresentable in JSON Schema; the override below gives\n\t\t// it a bsonType instead, and `{}` is what it starts from.\n\t\tunrepresentable: 'any',\n\t\toverride: (ctx) => {\n\t\t\tconst type = (ctx.zodSchema as { _zod: { def: { type: string } } })._zod\n\t\t\t\t.def.type;\n\t\t\tif (type === 'date' && ctx.jsonSchema.bsonType === undefined) {\n\t\t\t\tctx.jsonSchema.bsonType = 'date';\n\t\t\t}\n\t\t},\n\t}) as Node;\n\n\tconst definitions = isRecord(json.definitions)\n\t\t? json.definitions\n\t\t: isRecord(json.$defs)\n\t\t\t? json.$defs\n\t\t\t: {};\n\treturn inline(json, definitions, []) as Record<string, unknown>;\n}\n",
8
+ "/** What went wrong, as a string a caller can switch on. */\nexport type DataErrorCode =\n\t| 'DATABASE'\n\t| 'NOT_FOUND'\n\t| 'CONFLICT'\n\t| 'VALIDATION'\n\t| 'OPTIMISTIC_LOCK'\n\t| 'INVALID_CURSOR';\n\n/** One reason a document failed the collection's `$jsonSchema` validator. */\nexport interface ValidationIssue {\n\t/** The dotted path of the field, empty for the document itself. */\n\tpath: string;\n\t/** The rule it broke: `bsonType`, `required`, `minimum`… */\n\treason: string;\n\t/** What the schema asked for, as MongoDB reports it. */\n\tspecifiedAs?: unknown;\n\t/** The value that was refused, when the server names it. */\n\tconsideredValue?: unknown;\n\t/** Its BSON type, when the server names it: `string`, `int`, `double`… */\n\tconsideredType?: string;\n\t/** The schema's `description` for the field, when it has one. */\n\tdescription?: string;\n}\n\nexport interface DataErrorOptions {\n\tcollection?: string | undefined;\n\t/** The `_id` a method by id was given. */\n\tid?: unknown;\n\t/** MongoDB's numeric error code: 11000, 121, 26… */\n\tserverCode?: number | undefined;\n\t/** MongoDB's `codeName`, which write errors do not carry. */\n\tserverCodeName?: string | undefined;\n\t/** The index a conflict names, when the server names one. */\n\tindex?: string | undefined;\n\t/** The fields the error is about: an index's keys, or a validator's paths. */\n\tkeys?: string[];\n\t/** Those fields' values, when the server gives them. */\n\tvalues?: Record<string, unknown> | undefined;\n\tissues?: ValidationIssue[];\n\texpectedVersion?: number | undefined;\n\tactualVersion?: number | undefined;\n\tcause?: unknown;\n}\n\n/**\n * What this package throws. Every method turns a driver error into one of\n * these, so an application catches `ConflictError` instead of reading `11000`\n * off an error whose shape changes with the operation that produced it.\n *\n * A driver error that is none of them reaches the caller as it is.\n */\nexport class DataError extends Error {\n\toverride name = 'DataError';\n\treadonly code: DataErrorCode = 'DATABASE';\n\treadonly collection: string | undefined;\n\treadonly id: unknown;\n\treadonly serverCode: number | undefined;\n\treadonly serverCodeName: string | undefined;\n\treadonly index: string | undefined;\n\treadonly keys: string[];\n\treadonly values: Record<string, unknown> | undefined;\n\treadonly issues: ValidationIssue[];\n\treadonly expectedVersion: number | undefined;\n\treadonly actualVersion: number | undefined;\n\n\tconstructor(message = 'Database error', options: DataErrorOptions = {}) {\n\t\tsuper(\n\t\t\tmessage,\n\t\t\toptions.cause === undefined ? undefined : { cause: options.cause },\n\t\t);\n\t\tthis.collection = options.collection;\n\t\tthis.id = options.id;\n\t\tthis.serverCode = options.serverCode;\n\t\tthis.serverCodeName = options.serverCodeName;\n\t\tthis.index = options.index;\n\t\tthis.keys = options.keys ?? [];\n\t\tthis.values = options.values;\n\t\tthis.issues = options.issues ?? [];\n\t\tthis.expectedVersion = options.expectedVersion;\n\t\tthis.actualVersion = options.actualVersion;\n\t}\n}\n\n/** No document matched, where one was required. */\nexport class NotFoundError extends DataError {\n\toverride name = 'NotFoundError';\n\toverride readonly code = 'NOT_FOUND' as const;\n\n\tconstructor(message = 'Not found', options: DataErrorOptions = {}) {\n\t\tsuper(message, options);\n\t}\n}\n\n/** A unique index refused the write: MongoDB's `E11000`. */\nexport class ConflictError extends DataError {\n\toverride name = 'ConflictError';\n\toverride readonly code = 'CONFLICT' as const;\n\n\tconstructor(message = 'Duplicate key', options: DataErrorOptions = {}) {\n\t\tsuper(message, { serverCode: 11000, ...options });\n\t}\n}\n\n/** The collection's `$jsonSchema` validator refused the document: code 121. */\nexport class ValidationError extends DataError {\n\toverride name = 'ValidationError';\n\toverride readonly code = 'VALIDATION' as const;\n\n\tconstructor(\n\t\tmessage = 'Document failed validation',\n\t\toptions: DataErrorOptions = {},\n\t) {\n\t\tsuper(message, { serverCode: 121, ...options });\n\t}\n}\n\n/**\n * The document changed since it was read: its `version` is no longer the one\n * the update expected, and nothing was written.\n */\nexport class OptimisticLockError extends DataError {\n\toverride name = 'OptimisticLockError';\n\toverride readonly code = 'OPTIMISTIC_LOCK' as const;\n\n\tconstructor(message = 'Version conflict', options: DataErrorOptions = {}) {\n\t\tsuper(message, options);\n\t}\n}\n\n/** A cursor this package did not write, or one for another ordering. */\nexport class InvalidCursorError extends DataError {\n\toverride name = 'InvalidCursorError';\n\toverride readonly code = 'INVALID_CURSOR' as const;\n\n\tconstructor(message = 'Invalid cursor', options: DataErrorOptions = {}) {\n\t\tsuper(message, options);\n\t}\n}\n",
9
+ "import {\n\tConflictError,\n\tDataError,\n\ttype DataErrorOptions,\n\tValidationError,\n\ttype ValidationIssue,\n} from './data-error';\n\ntype Record_ = Record<string, unknown>;\n\nfunction isRecord(value: unknown): value is Record_ {\n\treturn typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction asArray(value: unknown): unknown[] {\n\tif (Array.isArray(value)) return value;\n\treturn value === undefined || value === null ? [] : [value];\n}\n\nfunction text(value: unknown): string | undefined {\n\treturn typeof value === 'string' ? value : undefined;\n}\n\n/**\n * The index a duplicate key names. The message is the only place it is:\n * `E11000 duplicate key error collection: db.users index: users_email_unique\n * dup key: { email: \"a@b.c\" }`.\n */\nfunction indexFromMessage(message: string | undefined): string | undefined {\n\treturn message?.match(/index:\\s*(\\S+)\\s+dup key/)?.[1];\n}\n\n/**\n * The keys of a duplicate key. `keyPattern` carries them for a single write;\n * a bulk write carries neither it nor `keyValue`, and the message is all there\n * is: `dup key: { email: \"a@b.c\", tenant: 1 }`.\n */\nfunction keysOfDuplicate(error: Record_): {\n\tkeys: string[];\n\tvalues: Record_ | undefined;\n} {\n\tconst pattern = error.keyPattern;\n\tif (isRecord(pattern)) {\n\t\tconst values = isRecord(error.keyValue) ? error.keyValue : undefined;\n\t\treturn { keys: Object.keys(pattern), values };\n\t}\n\tconst inMessage = text(error.errmsg)?.match(/dup key:\\s*\\{([^}]*)\\}/)?.[1];\n\tif (!inMessage) return { keys: [], values: undefined };\n\tconst keys = [...inMessage.matchAll(/([\\w.$]+)\\s*:/g)].map(\n\t\t(match) => match[1] as string,\n\t);\n\treturn { keys, values: undefined };\n}\n\n/** The first write error of a bulk result, which may be one object or a list. */\nfunction firstWriteError(error: Record_): Record_ | undefined {\n\tfor (const write of asArray(error.writeErrors)) {\n\t\t// The driver wraps each one; its fields sit on `err` there.\n\t\tconst inner = isRecord(write) && isRecord(write.err) ? write.err : write;\n\t\tif (isRecord(inner)) return inner;\n\t}\n\treturn undefined;\n}\n\n/**\n * The issues of a `$jsonSchema` failure, from `errInfo.details`. The server\n * nests them: `schemaRulesNotSatisfied` holds `propertiesNotSatisfied`, whose\n * `details` hold either leaf rules or another level of properties.\n */\nfunction issuesOf(details: unknown, path: string[] = []): ValidationIssue[] {\n\tconst issues: ValidationIssue[] = [];\n\tfor (const rule of asArray(details)) {\n\t\tif (!isRecord(rule)) continue;\n\n\t\tif (rule.propertiesNotSatisfied !== undefined) {\n\t\t\tfor (const property of asArray(rule.propertiesNotSatisfied)) {\n\t\t\t\tif (!isRecord(property)) continue;\n\t\t\t\tconst name = text(property.propertyName) ?? '';\n\t\t\t\tconst nested = issuesOf(property.details, [...path, name]);\n\t\t\t\tconst description = text(property.description);\n\t\t\t\tissues.push(\n\t\t\t\t\t...(description === undefined\n\t\t\t\t\t\t? nested\n\t\t\t\t\t\t: nested.map((issue) => ({ description, ...issue }))),\n\t\t\t\t);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (rule.missingProperties !== undefined) {\n\t\t\tfor (const missing of asArray(rule.missingProperties)) {\n\t\t\t\tissues.push({\n\t\t\t\t\tpath: [...path, String(missing)].join('.'),\n\t\t\t\t\treason: 'required',\n\t\t\t\t\tspecifiedAs: rule.specifiedAs,\n\t\t\t\t});\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (rule.schemaRulesNotSatisfied !== undefined) {\n\t\t\tissues.push(...issuesOf(rule.schemaRulesNotSatisfied, path));\n\t\t\tcontinue;\n\t\t}\n\n\t\tissues.push({\n\t\t\tpath: path.join('.'),\n\t\t\treason: text(rule.reason) ?? text(rule.operatorName) ?? 'invalid',\n\t\t\t...(rule.specifiedAs === undefined\n\t\t\t\t? {}\n\t\t\t\t: { specifiedAs: rule.specifiedAs }),\n\t\t\t...(rule.consideredValue === undefined\n\t\t\t\t? {}\n\t\t\t\t: { consideredValue: rule.consideredValue }),\n\t\t\t...(text(rule.consideredType) === undefined\n\t\t\t\t? {}\n\t\t\t\t: { consideredType: text(rule.consideredType) }),\n\t\t});\n\t}\n\treturn issues;\n}\n\n/**\n * A MongoDB error as one of this package's, or the error itself when it is\n * none of them.\n *\n * It reads the error's fields rather than its class: a duplicate key arrives\n * as a `MongoServerError` with `keyPattern` from `insertOne`, and as a\n * `MongoBulkWriteError` whose `writeErrors` carry neither `keyPattern` nor\n * `keyValue` from `insertMany` and `bulkWrite`. Both become a `ConflictError`\n * with the same fields. Reading fields also survives two copies of the driver\n * in one tree, where `instanceof` does not.\n */\nexport function toDataError(\n\terror: unknown,\n\tcontext: { collection?: string | undefined } = {},\n): unknown {\n\tif (error instanceof DataError) return error;\n\tif (!isRecord(error)) return error;\n\n\t// A bulk write carries the detail on its write errors, and only a message\n\t// at the top; a single write carries everything at the top.\n\tconst source = firstWriteError(error) ?? error;\n\tconst code =\n\t\ttypeof source.code === 'number'\n\t\t\t? source.code\n\t\t\t: typeof error.code === 'number'\n\t\t\t\t? error.code\n\t\t\t\t: undefined;\n\tif (typeof code !== 'number') return error;\n\n\tconst message =\n\t\ttext(source.errmsg) ??\n\t\ttext(source.message) ??\n\t\ttext((error as { message?: unknown }).message) ??\n\t\t'';\n\tconst common: DataErrorOptions = {\n\t\tcollection: context.collection,\n\t\tserverCode: code,\n\t\tserverCodeName: text(error.codeName) ?? text(source.codeName),\n\t\tcause: error,\n\t};\n\n\tif (code === 11000) {\n\t\tconst { keys, values } = keysOfDuplicate(source);\n\t\tconst index = indexFromMessage(message);\n\t\tconst named =\n\t\t\tkeys.length > 0 ? keys.join(', ') : (index ?? 'a unique index');\n\t\treturn new ConflictError(\n\t\t\t`Duplicate key on ${named}${\n\t\t\t\tcontext.collection ? ` in \"${context.collection}\"` : ''\n\t\t\t}`,\n\t\t\t{ ...common, index, keys, values },\n\t\t);\n\t}\n\n\tif (code === 121) {\n\t\tconst errInfo = isRecord(source.errInfo) ? source.errInfo : undefined;\n\t\tconst issues = issuesOf(errInfo?.details);\n\t\treturn new ValidationError(\n\t\t\t`Document failed validation${\n\t\t\t\tcontext.collection ? ` in \"${context.collection}\"` : ''\n\t\t\t}${issues.length > 0 ? `: ${issues.map((i) => `${i.path} ${i.reason}`).join(', ')}` : ''}`,\n\t\t\t{ ...common, issues, keys: issues.map((issue) => issue.path) },\n\t\t);\n\t}\n\n\treturn new DataError(message || `MongoDB error ${code}`, common);\n}\n",
10
+ "import { ObjectId } from 'mongodb';\nimport { InvalidCursorError } from '../errors/data-error';\n\n/** What a cursor holds: the ordering values of the last document of a page. */\nexport interface CursorPayload {\n\t/** The ordering it was written for: `<field>:<asc|desc>`. */\n\treadonly key: string;\n\treadonly values: readonly unknown[];\n}\n\n/** An `ObjectId`, read without `instanceof`: two copies of the driver. */\nfunction isObjectId(value: unknown): value is ObjectId {\n\treturn (\n\t\ttypeof value === 'object' &&\n\t\tvalue !== null &&\n\t\t(value as { _bsontype?: unknown })._bsontype === 'ObjectId'\n\t);\n}\n\n// JSON has no Date, no bigint and no ObjectId, and a cursor must give back the\n// very value it was written from: a Date compares with a date field, an\n// ObjectId with `_id`. `JSON.stringify` has already called `toJSON` by the time\n// the replacer runs, so the raw value is read off `this`.\nfunction replacer(this: Record<string, unknown>, key: string, value: unknown) {\n\tconst raw = this[key];\n\tif (raw instanceof Date) return { $date: raw.toISOString() };\n\tif (typeof raw === 'bigint') return { $bigint: raw.toString() };\n\tif (isObjectId(raw)) return { $oid: raw.toHexString() };\n\treturn value;\n}\n\nfunction reviver(_key: string, value: unknown): unknown {\n\tif (value && typeof value === 'object' && !Array.isArray(value)) {\n\t\tconst keys = Object.keys(value);\n\t\tif (keys.length === 1) {\n\t\t\tconst tagged = value as {\n\t\t\t\t$date?: unknown;\n\t\t\t\t$bigint?: unknown;\n\t\t\t\t$oid?: unknown;\n\t\t\t};\n\t\t\tif (typeof tagged.$date === 'string') return new Date(tagged.$date);\n\t\t\tif (typeof tagged.$bigint === 'string') return BigInt(tagged.$bigint);\n\t\t\tif (typeof tagged.$oid === 'string') return new ObjectId(tagged.$oid);\n\t\t}\n\t}\n\treturn value;\n}\n\nfunction toBase64Url(text: string): string {\n\tlet binary = '';\n\tfor (const byte of new TextEncoder().encode(text)) {\n\t\tbinary += String.fromCharCode(byte);\n\t}\n\treturn btoa(binary)\n\t\t.replace(/\\+/g, '-')\n\t\t.replace(/\\//g, '_')\n\t\t.replace(/=+$/, '');\n}\n\nfunction fromBase64Url(text: string): string {\n\tconst base64 = text.replace(/-/g, '+').replace(/_/g, '/');\n\tconst binary = atob(base64 + '='.repeat((4 - (base64.length % 4)) % 4));\n\treturn new TextDecoder().decode(\n\t\tUint8Array.from(binary, (char) => char.charCodeAt(0)),\n\t);\n}\n\n/**\n * Writes an opaque, URL-safe cursor. `Date`, `bigint` and `ObjectId` values\n * survive the round trip. It is encoded, not signed: a client can read it,\n * and forge one.\n */\nexport function encodeCursor(payload: CursorPayload): string {\n\treturn toBase64Url(JSON.stringify([payload.key, payload.values], replacer));\n}\n\n/**\n * Reads a cursor `encodeCursor` wrote. Throws `InvalidCursorError` for\n * anything else, and for a cursor written for another ordering when\n * `expectedKey` is given.\n */\nexport function decodeCursor(\n\tcursor: string,\n\texpectedKey?: string,\n): CursorPayload {\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(fromBase64Url(cursor), reviver);\n\t} catch (cause) {\n\t\tthrow new InvalidCursorError('Invalid cursor: it cannot be decoded', {\n\t\t\tcause,\n\t\t});\n\t}\n\tif (\n\t\t!Array.isArray(parsed) ||\n\t\tparsed.length !== 2 ||\n\t\ttypeof parsed[0] !== 'string' ||\n\t\t!Array.isArray(parsed[1])\n\t) {\n\t\tthrow new InvalidCursorError('Invalid cursor: unexpected shape');\n\t}\n\tconst [key, values] = parsed as [string, unknown[]];\n\tif (expectedKey !== undefined && key !== expectedKey) {\n\t\tthrow new InvalidCursorError(\n\t\t\t`Invalid cursor: it was written for the ordering ${key}, not ${expectedKey}`,\n\t\t);\n\t}\n\treturn { key, values };\n}\n",
11
+ "/** One page of an offset pagination. */\nexport interface Page<T> {\n\titems: T[];\n\t/** Every document that matches, across all pages. */\n\ttotal: number;\n\t/** 1-based. */\n\tpage: number;\n\tpageSize: number;\n\t/** `Math.ceil(total / pageSize)`: 0 when nothing matches. */\n\tpageCount: number;\n}\n\n/** One page of a cursor pagination. */\nexport interface CursorPage<T> {\n\titems: T[];\n\t/** Pass it as `after` for the next page; `null` on the last one. */\n\tnextCursor: string | null;\n}\n\nexport interface PageOptions {\n\t/** 1-based. Default `1`. */\n\tpage?: number;\n\t/** Default `20`, at most `maxPageSize`. */\n\tpageSize?: number;\n}\n\nexport interface PageWindow {\n\tpage: number;\n\tpageSize: number;\n\tlimit: number;\n\tskip: number;\n}\n\nexport const DEFAULT_PAGE_SIZE = 20;\nexport const DEFAULT_MAX_PAGE_SIZE = 100;\n\nfunction positiveInteger(name: string, value: number): number {\n\tif (!Number.isInteger(value) || value < 1) {\n\t\tthrow new RangeError(\n\t\t\t`${name} must be an integer of at least 1, not ${value}`,\n\t\t);\n\t}\n\treturn value;\n}\n\n/**\n * Checks `page` and `pageSize` and turns them into a `limit` and a `skip`.\n * A `pageSize` above `maxPageSize` is lowered to it; one that is not a\n * positive integer throws a `RangeError`.\n */\nexport function pageWindow(\n\toptions: PageOptions = {},\n\tmaxPageSize = DEFAULT_MAX_PAGE_SIZE,\n): PageWindow {\n\tconst page = positiveInteger('page', options.page ?? 1);\n\tconst pageSize = Math.min(\n\t\tpositiveInteger('pageSize', options.pageSize ?? DEFAULT_PAGE_SIZE),\n\t\tmaxPageSize,\n\t);\n\treturn { page, pageSize, limit: pageSize, skip: (page - 1) * pageSize };\n}\n\n/** Assembles a `Page` from its documents and the total. */\nexport function toPage<T>(\n\titems: T[],\n\ttotal: number,\n\twindow: PageWindow,\n): Page<T> {\n\treturn {\n\t\titems,\n\t\ttotal,\n\t\tpage: window.page,\n\t\tpageSize: window.pageSize,\n\t\tpageCount: Math.ceil(total / window.pageSize),\n\t};\n}\n\n/** Checks a cursor page's `limit`, as `pageWindow` checks a `pageSize`. */\nexport function cursorLimit(\n\tlimit: number | undefined,\n\tmaxPageSize = DEFAULT_MAX_PAGE_SIZE,\n): number {\n\treturn Math.min(\n\t\tpositiveInteger('limit', limit ?? DEFAULT_PAGE_SIZE),\n\t\tmaxPageSize,\n\t);\n}\n",
12
+ "import type { IndexDescription, IndexDescriptionInfo } from 'mongodb';\n\n/**\n * What MongoDB fills a collation in with. It reads an index's collation back\n * canonical — every field, plus the ICU `version` — so a wanted collation is\n * compared against its own defaults, and `version` is left out: it changes\n * with the server's ICU, and recreating every index over it would be absurd.\n */\nconst COLLATION_DEFAULTS: Record<string, unknown> = {\n\tcaseLevel: false,\n\tcaseFirst: 'off',\n\tstrength: 3,\n\tnumericOrdering: false,\n\talternate: 'non-ignorable',\n\tmaxVariable: 'punct',\n\tnormalization: false,\n\tbackwards: false,\n};\n\n/**\n * Options the server does not read back when they are false, but does when\n * they were sent explicitly. Compared against the default either way.\n */\nconst OPTION_DEFAULTS: Record<string, unknown> = {\n\tunique: false,\n\tsparse: false,\n\thidden: false,\n\tbackground: false,\n};\n\n/** Never part of an index's identity: the server's own bookkeeping. */\nconst IGNORED = new Set(['v', 'ns', 'key', 'name']);\n\ntype Fields = Record<string, unknown>;\n\nfunction keyOf(index: IndexDescription | IndexDescriptionInfo): Fields {\n\tconst key = index.key;\n\treturn key instanceof Map ? Object.fromEntries(key) : { ...key };\n}\n\n/**\n * The name MongoDB gives an index that names none: every field and direction,\n * joined by `_`.\n */\nexport function indexNameOf(key: Fields): string {\n\treturn Object.entries(key)\n\t\t.map(([field, direction]) => `${field}_${String(direction)}`)\n\t\t.join('_');\n}\n\nfunction canonicalCollation(value: unknown): unknown {\n\tif (typeof value !== 'object' || value === null) return value;\n\tconst collation = value as Fields;\n\tconst out: Fields = {};\n\tfor (const [field, fallback] of Object.entries(COLLATION_DEFAULTS)) {\n\t\tout[field] = collation[field] ?? fallback;\n\t}\n\tout.locale = collation.locale;\n\t// `version` is the server's ICU version, never something to sync on.\n\treturn out;\n}\n\n/** An index reduced to what makes two of them the same. */\nexport interface NormalizedIndex {\n\tname: string;\n\t/** In order: a compound index on `{a, b}` is not one on `{b, a}`. */\n\tkey: Fields;\n\toptions: Fields;\n}\n\nexport function normalizeIndex(\n\tindex: IndexDescription | IndexDescriptionInfo,\n): NormalizedIndex {\n\tconst key = keyOf(index);\n\tconst options: Fields = {};\n\tfor (const [name, value] of Object.entries(index)) {\n\t\tif (IGNORED.has(name) || value === undefined) continue;\n\t\tif (name === 'collation') {\n\t\t\toptions.collation = canonicalCollation(value);\n\t\t\tcontinue;\n\t\t}\n\t\tif (OPTION_DEFAULTS[name] === value) continue;\n\t\toptions[name] = value;\n\t}\n\treturn { name: index.name ?? indexNameOf(key), key, options };\n}\n\nfunction canonical(value: unknown): string {\n\treturn JSON.stringify(value, (_name, inner) =>\n\t\tinner && typeof inner === 'object' && !Array.isArray(inner)\n\t\t\t? Object.fromEntries(\n\t\t\t\t\tObject.entries(inner).sort(([a], [b]) => (a < b ? -1 : 1)),\n\t\t\t\t)\n\t\t\t: inner,\n\t);\n}\n\n/** Are two indexes the same index, with the same options? */\nexport function indexMatches(\n\twanted: IndexDescription,\n\tlive: IndexDescriptionInfo,\n): boolean {\n\tconst a = normalizeIndex(wanted);\n\tconst b = normalizeIndex(live);\n\treturn (\n\t\t// The key's order counts, so it is compared as it was written.\n\t\tJSON.stringify(Object.entries(a.key)) ===\n\t\t\tJSON.stringify(Object.entries(b.key)) &&\n\t\tcanonical(a.options) === canonical(b.options)\n\t);\n}\n\nexport interface IndexDiff {\n\t/** Not on the server yet. */\n\tcreate: IndexDescription[];\n\t/**\n\t * There under this name, with other options: MongoDB refuses to change\n\t * one, so it is dropped and created again.\n\t */\n\trecreate: IndexDescription[];\n\t/** Already as the definition wants it. */\n\tunchanged: string[];\n\t/** On the server and in no definition. `_id_` is never one. */\n\textra: string[];\n}\n\n/**\n * What an index sync has to do. Indexes are matched by name, which is what\n * MongoDB keys them on: the same name with other options is error 86, and the\n * same key under another name is error 85.\n */\nexport function diffIndexes(\n\twanted: readonly IndexDescription[],\n\tlive: readonly IndexDescriptionInfo[],\n): IndexDiff {\n\tconst byName = new Map(\n\t\tlive.map((index) => [normalizeIndex(index).name, index]),\n\t);\n\tconst diff: IndexDiff = {\n\t\tcreate: [],\n\t\trecreate: [],\n\t\tunchanged: [],\n\t\textra: [],\n\t};\n\tconst named = new Set<string>();\n\n\tfor (const index of wanted) {\n\t\tconst name = normalizeIndex(index).name;\n\t\tnamed.add(name);\n\t\tconst existing = byName.get(name);\n\t\tif (!existing) diff.create.push({ ...index, name });\n\t\telse if (indexMatches(index, existing)) diff.unchanged.push(name);\n\t\telse diff.recreate.push({ ...index, name });\n\t}\n\n\tfor (const name of byName.keys()) {\n\t\t// The `_id_` index is created with the collection and cannot be dropped.\n\t\tif (name !== '_id_' && !named.has(name)) diff.extra.push(name);\n\t}\n\treturn diff;\n}\n",
13
+ "import type {\n\tValidationAction,\n\tValidationLevel,\n} from '../definition/define-collection';\n\n/** A collection's validation, as `listCollections` reports it in `options`. */\nexport interface LiveValidation {\n\tvalidator?: Record<string, unknown>;\n\tvalidationLevel?: string;\n\tvalidationAction?: string;\n}\n\n/** The validation a definition asks for. `validator` is absent for `off`. */\nexport interface WantedValidation {\n\tvalidator: Record<string, unknown> | undefined;\n\tlevel: ValidationLevel;\n\taction: ValidationAction;\n}\n\nfunction canonical(value: unknown): string {\n\treturn JSON.stringify(value ?? null, (_name, inner) =>\n\t\tinner && typeof inner === 'object' && !Array.isArray(inner)\n\t\t\t? Object.fromEntries(\n\t\t\t\t\tObject.entries(inner).sort(([a], [b]) => (a < b ? -1 : 1)),\n\t\t\t\t)\n\t\t\t: inner,\n\t);\n}\n\n/** Has a collection a validator at all? An empty one is no validator. */\nexport function hasValidator(live: LiveValidation): boolean {\n\treturn live.validator !== undefined && Object.keys(live.validator).length > 0;\n}\n\n/**\n * Does the collection already validate the way the definition says?\n *\n * MongoDB reads a `$jsonSchema` back exactly as it was sent, so the two are\n * compared whole. What it does not read back is a validator that was removed:\n * `collMod` with `validator: {}` leaves **no `validator` key at all**, while\n * `validationLevel` and `validationAction` stay behind. And a collection\n * created without one reports `options: {}`, where the level is `strict` and\n * the action `error` by default.\n */\nexport function validationMatches(\n\twanted: WantedValidation,\n\tlive: LiveValidation,\n): boolean {\n\tif (wanted.validator === undefined) return !hasValidator(live);\n\tif (!hasValidator(live)) return false;\n\treturn (\n\t\tcanonical(live.validator) === canonical(wanted.validator) &&\n\t\t(live.validationLevel ?? 'strict') === wanted.level &&\n\t\t(live.validationAction ?? 'error') === wanted.action\n\t);\n}\n",
14
+ "import type {\n\tClientSession,\n\tDb,\n\tDocument,\n\tIndexDescription,\n\tIndexDescriptionInfo,\n} from 'mongodb';\nimport type { AnyCollectionDefinition } from '../definition/define-collection';\nimport { toMongoJsonSchema } from '../definition/json-schema';\nimport { DataError } from '../errors/data-error';\nimport { toDataError } from '../errors/to-data-error';\nimport { diffIndexes, normalizeIndex } from './index-diff';\nimport {\n\thasValidator,\n\ttype LiveValidation,\n\tvalidationMatches,\n\ttype WantedValidation,\n} from './validator-diff';\n\nexport interface SyncOptions {\n\t/**\n\t * Compare and report, but send nothing: no collection is created, no\n\t * validator written, no index touched. For a check in CI, or a look before\n\t * a deploy.\n\t */\n\tdryRun?: boolean;\n\t/**\n\t * Drop the indexes the server has and no definition names. Off by default:\n\t * an index someone added on purpose is not this package's to remove.\n\t * `_id_` is never dropped, and cannot be.\n\t */\n\tdropUnknownIndexes?: boolean;\n\t/**\n\t * A session for the reads. MongoDB does not allow `collMod` or an index\n\t * build inside a transaction, so do not pass one that is in a transaction.\n\t */\n\tsession?: ClientSession;\n}\n\n/** What `sync` found and did to one collection. */\nexport interface SyncReport {\n\tname: string;\n\t/** The collection did not exist, and was created. */\n\tcreated: boolean;\n\t/** What the `$jsonSchema` validator needed. */\n\tvalidator: 'unchanged' | 'created' | 'updated' | 'removed';\n\tindexes: {\n\t\tcreated: string[];\n\t\t/** There with other options: MongoDB cannot change one, so it is dropped and built again. */\n\t\trecreated: string[];\n\t\tdropped: string[];\n\t\tunchanged: string[];\n\t};\n\tdryRun: boolean;\n}\n\nfunction serverCode(error: unknown): number | undefined {\n\tconst code = (error as { code?: unknown } | null)?.code;\n\treturn typeof code === 'number' ? code : undefined;\n}\n\nasync function collectionOptions(\n\tdb: Db,\n\tname: string,\n\tsession: ClientSession | undefined,\n): Promise<LiveValidation | undefined> {\n\t// `nameOnly: false` is what types the answer as the whole entry: without\n\t// it the driver's overload gives back a name and a type alone.\n\tconst [info] = await db\n\t\t.listCollections(\n\t\t\t{ name },\n\t\t\t{ ...(session ? { session } : {}), nameOnly: false },\n\t\t)\n\t\t.toArray();\n\treturn info ? ((info.options ?? {}) as LiveValidation) : undefined;\n}\n\n/** The indexes of a collection, or none when it does not exist yet. */\nasync function liveIndexes(\n\tdb: Db,\n\tname: string,\n\tsession: ClientSession | undefined,\n): Promise<IndexDescriptionInfo[]> {\n\ttry {\n\t\treturn await db.collection(name).indexes({ session });\n\t} catch (error) {\n\t\t// NamespaceNotFound: nothing is there, so nothing is indexed.\n\t\tif (serverCode(error) === 26) return [];\n\t\tthrow error;\n\t}\n}\n\nfunction validationFor(definition: AnyCollectionDefinition): WantedValidation {\n\tconst { level, action } = definition.validation;\n\treturn {\n\t\tvalidator:\n\t\t\tlevel === 'off'\n\t\t\t\t? undefined\n\t\t\t\t: { $jsonSchema: toMongoJsonSchema(definition.schema) },\n\t\tlevel,\n\t\taction,\n\t};\n}\n\nfunction creationOptions(wanted: WantedValidation): Document {\n\treturn wanted.validator === undefined\n\t\t? {}\n\t\t: {\n\t\t\t\tvalidator: wanted.validator,\n\t\t\t\tvalidationLevel: wanted.level,\n\t\t\t\tvalidationAction: wanted.action,\n\t\t\t};\n}\n\nasync function writeValidation(\n\tdb: Db,\n\tname: string,\n\twanted: WantedValidation,\n\tsession: ClientSession | undefined,\n): Promise<void> {\n\ttry {\n\t\tawait db.command(\n\t\t\t{\n\t\t\t\tcollMod: name,\n\t\t\t\t// An empty validator is how one is removed: the key then goes\n\t\t\t\t// away entirely, and the level and the action stay behind.\n\t\t\t\tvalidator: wanted.validator ?? {},\n\t\t\t\t...(wanted.validator === undefined\n\t\t\t\t\t? {}\n\t\t\t\t\t: { validationLevel: wanted.level, validationAction: wanted.action }),\n\t\t\t},\n\t\t\tsession ? { session } : undefined,\n\t\t);\n\t} catch (error) {\n\t\tif (serverCode(error) === 13) {\n\t\t\tthrow new DataError(\n\t\t\t\t`sync: not allowed to run collMod on \"${name}\". Writing a validator ` +\n\t\t\t\t\t'needs the `collMod` action, which `readWrite` does not grant and ' +\n\t\t\t\t\t'`dbAdmin` does: sync with a role that has it, not with the ' +\n\t\t\t\t\t'application’s own user.',\n\t\t\t\t{ collection: name, serverCode: 13, cause: error },\n\t\t\t);\n\t\t}\n\t\tthrow toDataError(error, { collection: name });\n\t}\n}\n\n/**\n * Brings one collection in line with its definition, and says what it changed:\n *\n * 1. creates the collection, with its validator, when it is missing;\n * 2. writes the validator with `collMod` when it differs from the definition's;\n * 3. creates the indexes that are missing, and rebuilds those whose options\n * changed — MongoDB refuses to alter an index in place.\n *\n * Run it twice and the second run sends nothing.\n *\n * ```ts\n * const report = await syncCollection(db, users);\n * // { created: true, validator: 'created', indexes: { created: ['users_email_unique'], … } }\n * ```\n *\n * It is a deployment step, not a request-time one: `collMod` needs the\n * `dbAdmin` role, and neither it nor an index build may run in a transaction.\n */\nexport async function syncCollection(\n\tdb: Db,\n\tdefinition: AnyCollectionDefinition,\n\toptions: SyncOptions = {},\n): Promise<SyncReport> {\n\tconst { name } = definition;\n\tconst dryRun = options.dryRun ?? false;\n\tconst session = options.session;\n\tconst wanted = validationFor(definition);\n\n\tlet live = await collectionOptions(db, name, session);\n\tlet created = false;\n\tlet validator: SyncReport['validator'] = 'unchanged';\n\n\tif (!live) {\n\t\tcreated = true;\n\t\tif (wanted.validator !== undefined) validator = 'created';\n\t\tif (!dryRun) {\n\t\t\ttry {\n\t\t\t\tawait db.createCollection(name, {\n\t\t\t\t\t...creationOptions(wanted),\n\t\t\t\t\t...(session ? { session } : {}),\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\t// NamespaceExists: another sync created it between the lookup and\n\t\t\t\t// the creation. Go on with theirs, which is compared below.\n\t\t\t\tif (serverCode(error) !== 48) {\n\t\t\t\t\tthrow toDataError(error, { collection: name });\n\t\t\t\t}\n\t\t\t\tcreated = false;\n\t\t\t\tvalidator = 'unchanged';\n\t\t\t\tlive = await collectionOptions(db, name, session);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (live && !validationMatches(wanted, live)) {\n\t\tvalidator =\n\t\t\twanted.validator === undefined\n\t\t\t\t? 'removed'\n\t\t\t\t: hasValidator(live)\n\t\t\t\t\t? 'updated'\n\t\t\t\t\t: 'created';\n\t\tif (!dryRun) await writeValidation(db, name, wanted, session);\n\t}\n\n\tconst existing =\n\t\tdryRun && created ? [] : await liveIndexes(db, name, session);\n\tconst diff = diffIndexes(definition.indexes, existing);\n\tconst dropped = options.dropUnknownIndexes ? diff.extra : [];\n\tconst build: IndexDescription[] = [...diff.create, ...diff.recreate];\n\n\tif (!dryRun) {\n\t\tconst collection = db.collection(name);\n\t\tfor (const index of [\n\t\t\t...diff.recreate.map((i) => normalizeIndex(i).name),\n\t\t\t...dropped,\n\t\t]) {\n\t\t\tawait collection.dropIndex(index, session ? { session } : undefined);\n\t\t}\n\t\tif (build.length > 0) {\n\t\t\ttry {\n\t\t\t\tawait collection.createIndexes(\n\t\t\t\t\tbuild,\n\t\t\t\t\tsession ? { session } : undefined,\n\t\t\t\t);\n\t\t\t} catch (error) {\n\t\t\t\tthrow toDataError(error, { collection: name });\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {\n\t\tname,\n\t\tcreated,\n\t\tvalidator,\n\t\tindexes: {\n\t\t\tcreated: diff.create.map((index) => normalizeIndex(index).name),\n\t\t\trecreated: diff.recreate.map((index) => normalizeIndex(index).name),\n\t\t\tdropped,\n\t\t\tunchanged: diff.unchanged,\n\t\t},\n\t\tdryRun,\n\t};\n}\n\n/**\n * `syncCollection` for each definition, one after the other, in the order\n * given. The first that throws stops the rest.\n */\nexport async function syncCollections(\n\tdb: Db,\n\tdefinitions: readonly AnyCollectionDefinition[],\n\toptions: SyncOptions = {},\n): Promise<SyncReport[]> {\n\tconst reports: SyncReport[] = [];\n\tfor (const definition of definitions) {\n\t\treports.push(await syncCollection(db, definition, options));\n\t}\n\treturn reports;\n}\n",
15
+ "import type { ClientSession, Db, Document } from 'mongodb';\nimport type { z } from 'zod';\nimport {\n\ttype AnyCollectionDefinition,\n\ttype CollectionDefinition,\n\tstampsOf,\n} from '../definition/define-collection';\nimport {\n\tDataError,\n\tNotFoundError,\n\tOptimisticLockError,\n} from '../errors/data-error';\nimport { toDataError } from '../errors/to-data-error';\nimport { decodeCursor, encodeCursor } from '../pagination/cursor';\nimport {\n\ttype CursorPage,\n\tcursorLimit,\n\tDEFAULT_MAX_PAGE_SIZE,\n\ttype Page,\n\tpageWindow,\n\ttoPage,\n} from '../pagination/page';\nimport { type SyncOptions, syncCollection } from '../sync/sync-collection';\nimport type { OrderDirection, Repository, RepositoryOptions } from './types';\n\ntype Fields = Record<string, unknown>;\n\nfunction isRecord(value: unknown): value is Fields {\n\treturn typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/** Does this patch speak in MongoDB's operators rather than in fields? */\nfunction isUpdateFilter(patch: Fields): boolean {\n\treturn Object.keys(patch).some((key) => key.startsWith('$'));\n}\n\n/** `a` and `b`, without letting one's `$or` swallow the other's. */\nfunction mergeFilters(a: Fields | undefined, b: Fields | undefined): Fields {\n\tconst left = a && Object.keys(a).length > 0 ? a : undefined;\n\tconst right = b && Object.keys(b).length > 0 ? b : undefined;\n\tif (!left) return right ?? {};\n\tif (!right) return left;\n\treturn { $and: [left, right] };\n}\n\n/**\n * A repository over one collection: typed reads and writes by `_id` or by\n * filter, pagination, soft delete, optimistic locking, audit stamps, and\n * MongoDB errors turned into this package's.\n *\n * ```ts\n * const users = createRepository(db, usersCollection);\n * const ada = await users.create({ email: 'ada@example.com' });\n * await users.update(ada._id, { name: 'Ada' }, { expectedVersion: ada.version });\n * ```\n *\n * Every operation runs in the repository's session, which `with(session)`\n * sets: MongoDB has no ambient session, so a write inside a transaction that\n * was not given one is not part of it and is not rolled back.\n */\nexport function createRepository<Schema extends z.ZodObject>(\n\tdb: Db,\n\tdefinition: CollectionDefinition<Schema>,\n\toptions: RepositoryOptions = {},\n): Repository<CollectionDefinition<Schema>> {\n\treturn build(db, definition, options) as unknown as Repository<\n\t\tCollectionDefinition<Schema>\n\t>;\n}\n\nfunction build(\n\tdb: Db,\n\tdefinition: AnyCollectionDefinition,\n\toptions: RepositoryOptions,\n) {\n\tconst name = definition.name;\n\t// The driver types a collection by its documents; this body works on any\n\t// collection, and the public type above is what callers see.\n\tconst collection = db.collection<any>(name);\n\tconst shape = definition.schema.shape as Record<string, z.ZodType>;\n\tconst stamps = stampsOf(definition);\n\tconst session = options.session;\n\tconst actor = options.actor;\n\tconst maxPageSize = options.maxPageSize ?? DEFAULT_MAX_PAGE_SIZE;\n\tconst parses = (options.validate ?? 'parse') === 'parse';\n\tconst softDeletes = options.softDelete ?? stamps.deletedAt;\n\tconst touches = options.touchUpdatedAt ?? stamps.updatedAt;\n\tconst locks = options.optimisticLock ?? stamps.version;\n\n\tif (options.softDelete === true && !stamps.deletedAt) {\n\t\tthrow new TypeError(\n\t\t\t`createRepository: softDelete needs a \"deletedAt\" field, and \"${name}\" has none`,\n\t\t);\n\t}\n\tif (options.optimisticLock === true && !stamps.version) {\n\t\tthrow new TypeError(\n\t\t\t`createRepository: optimisticLock needs a \"version\" field, and \"${name}\" has none`,\n\t\t);\n\t}\n\n\tconst run = async <T>(fn: () => Promise<T>): Promise<T> => {\n\t\ttry {\n\t\t\treturn await fn();\n\t\t} catch (error) {\n\t\t\tthrow toDataError(error, { collection: name });\n\t\t}\n\t};\n\n\tconst sessionOption = session ? { session } : {};\n\n\t/** The filter that leaves soft-deleted documents out. */\n\tconst live = (withDeleted?: boolean): Fields | undefined =>\n\t\tsoftDeletes && !withDeleted ? { deletedAt: null } : undefined;\n\n\tconst scoped = (filter: unknown, withDeleted?: boolean): Fields =>\n\t\tmergeFilters(isRecord(filter) ? filter : undefined, live(withDeleted));\n\n\tconst notFound = (id: unknown) =>\n\t\tnew NotFoundError(`No document in \"${name}\" with _id ${String(id)}`, {\n\t\t\tcollection: name,\n\t\t\tid,\n\t\t});\n\n\tconst requireFilter = (method: string, filter: unknown): void => {\n\t\tif (!isRecord(filter) || Object.keys(filter).length === 0) {\n\t\t\tthrow new TypeError(\n\t\t\t\t`${method} needs a filter. Pass \\`{ _id: { $exists: true } }\\` to target every document of \"${name}\".`,\n\t\t\t);\n\t\t}\n\t};\n\n\t/** The document to insert: checked against the schema, defaults filled. */\n\tconst toDocument = (values: unknown): Fields => {\n\t\tconst stamped: Fields = { ...(values as Fields) };\n\t\tif (actor !== undefined) {\n\t\t\tif (stamps.createdBy && stamped.createdBy === undefined) {\n\t\t\t\tstamped.createdBy = actor;\n\t\t\t}\n\t\t\tif (stamps.updatedBy && stamped.updatedBy === undefined) {\n\t\t\t\tstamped.updatedBy = actor;\n\t\t\t}\n\t\t}\n\t\treturn parses ? (definition.schema.parse(stamped) as Fields) : stamped;\n\t};\n\n\t/**\n\t * The update to send: a patch of fields becomes `$set`, checked field by\n\t * field against the schema, with the stamps this repository keeps. A patch\n\t * that already speaks in operators is sent as it is, with the stamps added.\n\t */\n\tconst toUpdate = (patch: unknown): Fields => {\n\t\tif (!isRecord(patch)) {\n\t\t\tthrow new TypeError(\n\t\t\t\t`update: expected the document's fields or MongoDB's operators, not ${String(patch)}`,\n\t\t\t);\n\t\t}\n\t\tconst update: Fields = isUpdateFilter(patch) ? { ...patch } : {};\n\t\tconst set: Fields = isRecord(update.$set) ? { ...update.$set } : {};\n\n\t\tif (!isUpdateFilter(patch)) {\n\t\t\tfor (const [field, value] of Object.entries(patch)) {\n\t\t\t\tif (value === undefined) continue;\n\t\t\t\tconst schema = shape[field];\n\t\t\t\tif (!schema) {\n\t\t\t\t\tthrow new TypeError(\n\t\t\t\t\t\t`update: \"${name}\" has no field \"${field}\" in its schema`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tset[field] = parses ? schema.parse(value) : value;\n\t\t\t}\n\t\t}\n\n\t\tif (touches && set.updatedAt === undefined) set.updatedAt = new Date();\n\t\tif (\n\t\t\tactor !== undefined &&\n\t\t\tstamps.updatedBy &&\n\t\t\tset.updatedBy === undefined\n\t\t) {\n\t\t\tset.updatedBy = actor;\n\t\t}\n\t\tif (Object.keys(set).length > 0) update.$set = set;\n\n\t\tif (locks) {\n\t\t\tconst inc = isRecord(update.$inc) ? { ...update.$inc } : {};\n\t\t\tinc.version = (inc.version as number | undefined) ?? 1;\n\t\t\tupdate.$inc = inc;\n\t\t}\n\t\treturn update;\n\t};\n\n\tconst findOne = async (filter: Fields, projection?: unknown) =>\n\t\trun(async () =>\n\t\t\tcollection.findOne(filter, {\n\t\t\t\t...sessionOption,\n\t\t\t\t...(projection ? { projection } : {}),\n\t\t\t}),\n\t\t);\n\n\tasync function findById(id: unknown, opts: { withDeleted?: boolean } = {}) {\n\t\tconst found = await findOne(scoped({ _id: id }, opts.withDeleted));\n\t\treturn found ?? undefined;\n\t}\n\n\tasync function getById(id: unknown, opts: { withDeleted?: boolean } = {}) {\n\t\tconst found = await findById(id, opts);\n\t\tif (!found) throw notFound(id);\n\t\treturn found;\n\t}\n\n\tasync function findMany(opts: Fields = {}): Promise<Fields[]> {\n\t\treturn run(async () => {\n\t\t\tlet cursor = collection.find(\n\t\t\t\tscoped(opts.filter, opts.withDeleted as boolean | undefined),\n\t\t\t\t{\n\t\t\t\t\t...sessionOption,\n\t\t\t\t\t...(opts.projection ? { projection: opts.projection } : {}),\n\t\t\t\t},\n\t\t\t);\n\t\t\tif (opts.sort !== undefined) cursor = cursor.sort(opts.sort as never);\n\t\t\tif (opts.skip !== undefined) cursor = cursor.skip(opts.skip as number);\n\t\t\tif (opts.limit !== undefined) cursor = cursor.limit(opts.limit as number);\n\t\t\treturn cursor.toArray();\n\t\t});\n\t}\n\n\tasync function countDocuments(\n\t\tfilter?: unknown,\n\t\topts: { withDeleted?: boolean } = {},\n\t) {\n\t\treturn run(async () =>\n\t\t\tcollection.countDocuments(scoped(filter, opts.withDeleted), {\n\t\t\t\t...sessionOption,\n\t\t\t}),\n\t\t);\n\t}\n\n\t/**\n\t * `findOneAndUpdate` answers `null` for a document that is not there, one\n\t * that is soft-deleted, and one whose version moved. Only a second read\n\t * tells them apart.\n\t */\n\tasync function updatedOrThrow(\n\t\tid: unknown,\n\t\tfilter: Fields,\n\t\tupdate: Fields,\n\t\texpectedVersion: number | undefined,\n\t): Promise<Fields> {\n\t\tconst updated = await run(async () =>\n\t\t\tcollection.findOneAndUpdate(filter, update, {\n\t\t\t\t...sessionOption,\n\t\t\t\treturnDocument: 'after',\n\t\t\t}),\n\t\t);\n\t\tif (updated) return updated as Fields;\n\n\t\tif (expectedVersion !== undefined) {\n\t\t\tconst current = await findOne({ _id: id });\n\t\t\tif (current) {\n\t\t\t\tthrow new OptimisticLockError(\n\t\t\t\t\t`Document ${String(id)} of \"${name}\" is at version ${String(\n\t\t\t\t\t\tcurrent.version,\n\t\t\t\t\t)}, not ${expectedVersion}: it changed since it was read`,\n\t\t\t\t\t{\n\t\t\t\t\t\tcollection: name,\n\t\t\t\t\t\tid,\n\t\t\t\t\t\texpectedVersion,\n\t\t\t\t\t\tactualVersion:\n\t\t\t\t\t\t\ttypeof current.version === 'number' ? current.version : undefined,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tthrow notFound(id);\n\t}\n\n\tasync function hardDelete(id: unknown): Promise<Fields> {\n\t\tconst deleted = await run(async () =>\n\t\t\tcollection.findOneAndDelete({ _id: id }, { ...sessionOption }),\n\t\t);\n\t\tif (!deleted) throw notFound(id);\n\t\treturn deleted as Fields;\n\t}\n\n\tasync function hardDeleteMany(filter: unknown): Promise<number> {\n\t\trequireFilter('hardDeleteMany', filter);\n\t\treturn run(async () => {\n\t\t\tconst result = await collection.deleteMany(filter as Fields, {\n\t\t\t\t...sessionOption,\n\t\t\t});\n\t\t\treturn result.deletedCount;\n\t\t});\n\t}\n\n\tconst repository = {\n\t\tdefinition,\n\t\tdb,\n\t\tcollection,\n\t\tsession,\n\n\t\twith: (other: ClientSession | undefined) =>\n\t\t\tbuild(db, definition, { ...options, session: other }),\n\t\tas: (who: unknown) => build(db, definition, { ...options, actor: who }),\n\t\tsync: (syncOptions: SyncOptions = {}) =>\n\t\t\tsyncCollection(db, definition, { ...sessionOption, ...syncOptions }),\n\n\t\tfindById,\n\t\tgetById,\n\n\t\tasync findFirst(filter?: unknown, opts: Fields = {}) {\n\t\t\tconst [first] = await findMany({ ...opts, filter, limit: 1 });\n\t\t\treturn first;\n\t\t},\n\n\t\tfindMany,\n\n\t\tasync create(values: unknown) {\n\t\t\tconst document = toDocument(values);\n\t\t\treturn run(async () => {\n\t\t\t\tawait collection.insertOne(document as Document, { ...sessionOption });\n\t\t\t\treturn document;\n\t\t\t});\n\t\t},\n\n\t\tasync createMany(values: readonly unknown[]) {\n\t\t\tif (values.length === 0) return [];\n\t\t\tconst documents = values.map(toDocument);\n\t\t\treturn run(async () => {\n\t\t\t\tawait collection.insertMany(documents as Document[], {\n\t\t\t\t\t...sessionOption,\n\t\t\t\t});\n\t\t\t\treturn documents;\n\t\t\t});\n\t\t},\n\n\t\tasync update(id: unknown, patch: unknown, opts: Fields = {}) {\n\t\t\tconst expectedVersion = opts.expectedVersion as number | undefined;\n\t\t\tif (expectedVersion !== undefined && !locks) {\n\t\t\t\tthrow new TypeError(\n\t\t\t\t\t`update: expectedVersion needs a \"version\" field, and \"${name}\" has none`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst update = toUpdate(patch);\n\t\t\tconst filter = mergeFilters(\n\t\t\t\t{\n\t\t\t\t\t_id: id,\n\t\t\t\t\t...(expectedVersion === undefined\n\t\t\t\t\t\t? {}\n\t\t\t\t\t\t: { version: expectedVersion }),\n\t\t\t\t},\n\t\t\t\tlive(),\n\t\t\t);\n\t\t\treturn updatedOrThrow(id, filter, update, expectedVersion);\n\t\t},\n\n\t\tasync updateMany(filter: unknown, patch: unknown) {\n\t\t\trequireFilter('updateMany', filter);\n\t\t\tconst update = toUpdate(patch);\n\t\t\treturn run(async () => {\n\t\t\t\tconst result = await collection.updateMany(scoped(filter), update, {\n\t\t\t\t\t...sessionOption,\n\t\t\t\t});\n\t\t\t\treturn result.modifiedCount;\n\t\t\t});\n\t\t},\n\n\t\tasync delete(id: unknown) {\n\t\t\tif (!softDeletes) return hardDelete(id);\n\t\t\tconst set: Fields = { deletedAt: new Date() };\n\t\t\tif (actor !== undefined && stamps.deletedBy) set.deletedBy = actor;\n\t\t\tconst update: Fields = { $set: set };\n\t\t\tif (locks) update.$inc = { version: 1 };\n\t\t\treturn updatedOrThrow(\n\t\t\t\tid,\n\t\t\t\tmergeFilters({ _id: id }, live()),\n\t\t\t\tupdate,\n\t\t\t\tundefined,\n\t\t\t);\n\t\t},\n\n\t\tasync deleteMany(filter: unknown) {\n\t\t\trequireFilter('deleteMany', filter);\n\t\t\tif (!softDeletes) return hardDeleteMany(filter);\n\t\t\tconst set: Fields = { deletedAt: new Date() };\n\t\t\tif (actor !== undefined && stamps.deletedBy) set.deletedBy = actor;\n\t\t\tconst update: Fields = { $set: set };\n\t\t\tif (locks) update.$inc = { version: 1 };\n\t\t\treturn run(async () => {\n\t\t\t\tconst result = await collection.updateMany(scoped(filter), update, {\n\t\t\t\t\t...sessionOption,\n\t\t\t\t});\n\t\t\t\treturn result.modifiedCount;\n\t\t\t});\n\t\t},\n\n\t\thardDelete,\n\t\thardDeleteMany,\n\n\t\tasync restore(id: unknown) {\n\t\t\tif (!stamps.deletedAt) {\n\t\t\t\tthrow new TypeError(`restore: \"${name}\" has no soft delete`);\n\t\t\t}\n\t\t\tconst set: Fields = { deletedAt: null };\n\t\t\tif (stamps.deletedBy) set.deletedBy = null;\n\t\t\tif (touches) set.updatedAt = new Date();\n\t\t\tconst update: Fields = { $set: set };\n\t\t\tif (locks) update.$inc = { version: 1 };\n\t\t\treturn updatedOrThrow(id, { _id: id }, update, undefined);\n\t\t},\n\n\t\tcount: countDocuments,\n\n\t\tasync exists(filter: unknown, opts: { withDeleted?: boolean } = {}) {\n\t\t\tconst found = await findOne(scoped(filter, opts.withDeleted), { _id: 1 });\n\t\t\treturn found !== null && found !== undefined;\n\t\t},\n\n\t\tasync paginate(opts: Fields = {}): Promise<Page<Fields>> {\n\t\t\tconst window = pageWindow(opts, maxPageSize);\n\t\t\tconst [items, total] = await Promise.all([\n\t\t\t\tfindMany({\n\t\t\t\t\tfilter: opts.filter,\n\t\t\t\t\tsort: opts.sort ?? { _id: 1 },\n\t\t\t\t\tlimit: window.limit,\n\t\t\t\t\tskip: window.skip,\n\t\t\t\t\twithDeleted: opts.withDeleted,\n\t\t\t\t}),\n\t\t\t\tcountDocuments(opts.filter, {\n\t\t\t\t\twithDeleted: opts.withDeleted as boolean | undefined,\n\t\t\t\t}),\n\t\t\t]);\n\t\t\treturn toPage(items, total, window);\n\t\t},\n\n\t\tasync paginateByCursor(opts: Fields = {}): Promise<CursorPage<Fields>> {\n\t\t\tconst sortField = (opts.orderBy as string | undefined) ?? '_id';\n\t\t\tif (!shape[sortField] && sortField !== '_id') {\n\t\t\t\tthrow new TypeError(\n\t\t\t\t\t`paginateByCursor: \"${name}\" has no field \"${sortField}\" in its schema`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst direction = (opts.direction as OrderDirection | undefined) ?? 'asc';\n\t\t\tconst fields = sortField === '_id' ? ['_id'] : [sortField, '_id'];\n\t\t\tconst cursorKey = `${sortField}:${direction}`;\n\t\t\tconst limit = cursorLimit(opts.limit as number | undefined, maxPageSize);\n\t\t\tconst past = direction === 'asc' ? '$gt' : '$lt';\n\n\t\t\tlet after: Fields | undefined;\n\t\t\tif (opts.after) {\n\t\t\t\tconst { values } = decodeCursor(opts.after as string, cursorKey);\n\t\t\t\tif (values.length !== fields.length) {\n\t\t\t\t\tthrow new DataError(\n\t\t\t\t\t\t`Invalid cursor: expected ${fields.length} value(s), got ${values.length}`,\n\t\t\t\t\t\t{ collection: name },\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\t// `a > x OR (a = x AND b > y)`, the keyset of the ordering.\n\t\t\t\tafter = {\n\t\t\t\t\t$or: fields.map((field, index) => ({\n\t\t\t\t\t\t...Object.fromEntries(\n\t\t\t\t\t\t\tfields\n\t\t\t\t\t\t\t\t.slice(0, index)\n\t\t\t\t\t\t\t\t.map((previous, i) => [previous, values[i]]),\n\t\t\t\t\t\t),\n\t\t\t\t\t\t[field]: { [past]: values[index] },\n\t\t\t\t\t})),\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst sort = Object.fromEntries(\n\t\t\t\tfields.map((field) => [field, direction === 'asc' ? 1 : -1]),\n\t\t\t);\n\t\t\tconst documents = await findMany({\n\t\t\t\tfilter: mergeFilters(\n\t\t\t\t\tisRecord(opts.filter) ? opts.filter : undefined,\n\t\t\t\t\tafter,\n\t\t\t\t),\n\t\t\t\tsort,\n\t\t\t\tlimit: limit + 1,\n\t\t\t\twithDeleted: opts.withDeleted,\n\t\t\t});\n\n\t\t\tconst items = documents.slice(0, limit);\n\t\t\tconst last = items.at(-1);\n\t\t\tif (documents.length <= limit || !last) {\n\t\t\t\treturn { items, nextCursor: null };\n\t\t\t}\n\n\t\t\tconst values = fields.map((field) => {\n\t\t\t\tconst value = last[field];\n\t\t\t\tif (value === null || value === undefined) {\n\t\t\t\t\tthrow new TypeError(\n\t\t\t\t\t\t`paginateByCursor: \"${field}\" is null in a document of \"${name}\". ` +\n\t\t\t\t\t\t\t'Page along a field every document has.',\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn value;\n\t\t\t});\n\t\t\treturn { items, nextCursor: encodeCursor({ key: cursorKey, values }) };\n\t\t},\n\t};\n\n\treturn repository;\n}\n",
16
+ "import type { ClientSession, MongoClient, TransactionOptions } from 'mongodb';\nimport { toDataError } from '../errors/to-data-error';\n\n/** What a transaction can be started from: a client, or a session. */\nexport type TransactionHost = MongoClient | ClientSession;\n\n/** A session, read without `instanceof`: two copies of the driver. */\nfunction isSession(host: TransactionHost): host is ClientSession {\n\treturn typeof (host as ClientSession).inTransaction === 'function';\n}\n\n/**\n * Runs `fn` in a transaction: committed when it resolves, aborted when it\n * throws, and a MongoDB error turned into a `DataError` on the way out. The\n * session is the argument, and **every operation inside has to be given it**:\n * MongoDB has no ambient session, so an operation without one runs outside the\n * transaction and is not rolled back. `repository.with(session)` is how a\n * repository takes it.\n *\n * ```ts\n * await withTransaction(client, async (session) => {\n * \tconst team = await teams.with(session).create({ name: 'Core' });\n * \tawait users.with(session).update(userId, { teamId: team._id });\n * });\n * ```\n *\n * Given a session that is already in a transaction, it **joins** it: `fn` runs\n * with that session and nothing is committed until the outer one commits.\n * MongoDB has no savepoints, so an inner failure cannot be rolled back on its\n * own, and starting a second transaction on one session throws\n * `MongoTransactionError`.\n *\n * Two things the driver does that are easy to be surprised by:\n *\n * - it **retries `fn`** from the start on a `TransientTransactionError`, and\n * the commit alone on an `UnknownTransactionCommitResult`, until 120 seconds\n * have passed. `fn` must therefore be safe to run twice.\n * - `session.abortTransaction()` inside `fn` ends the transaction without\n * throwing: `withTransaction` then resolves.\n */\nexport async function withTransaction<T>(\n\thost: TransactionHost,\n\tfn: (session: ClientSession) => Promise<T>,\n\toptions?: TransactionOptions,\n): Promise<T> {\n\ttry {\n\t\tif (isSession(host)) {\n\t\t\tif (host.inTransaction()) {\n\t\t\t\tif (options) {\n\t\t\t\t\tthrow new TypeError(\n\t\t\t\t\t\t'withTransaction: this session is already in a transaction, which ' +\n\t\t\t\t\t\t\t'this call joins. MongoDB has no savepoints, so the read ' +\n\t\t\t\t\t\t\t'concern, the write concern and the read preference are the ' +\n\t\t\t\t\t\t\t'outer transaction’s.',\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn await fn(host);\n\t\t\t}\n\t\t\treturn await host.withTransaction(fn, options);\n\t\t}\n\n\t\tconst session = host.startSession();\n\t\ttry {\n\t\t\treturn await session.withTransaction(fn, options);\n\t\t} finally {\n\t\t\tawait session.endSession();\n\t\t}\n\t} catch (error) {\n\t\tthrow toDataError(error);\n\t}\n}\n"
17
+ ],
18
+ "mappings": "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",
19
+ "debugId": "44372BBBC1BF50A464756E2164756E21",
20
+ "names": []
21
+ }
@@ -0,0 +1,19 @@
1
+ /** What a cursor holds: the ordering values of the last document of a page. */
2
+ export interface CursorPayload {
3
+ /** The ordering it was written for: `<field>:<asc|desc>`. */
4
+ readonly key: string;
5
+ readonly values: readonly unknown[];
6
+ }
7
+ /**
8
+ * Writes an opaque, URL-safe cursor. `Date`, `bigint` and `ObjectId` values
9
+ * survive the round trip. It is encoded, not signed: a client can read it,
10
+ * and forge one.
11
+ */
12
+ export declare function encodeCursor(payload: CursorPayload): string;
13
+ /**
14
+ * Reads a cursor `encodeCursor` wrote. Throws `InvalidCursorError` for
15
+ * anything else, and for a cursor written for another ordering when
16
+ * `expectedKey` is given.
17
+ */
18
+ export declare function decodeCursor(cursor: string, expectedKey?: string): CursorPayload;
19
+ //# sourceMappingURL=cursor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../../src/pagination/cursor.ts"],"names":[],"mappings":"AAGA,+EAA+E;AAC/E,MAAM,WAAW,aAAa;IAC7B,6DAA6D;IAC7D,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,CAAC;CACpC;AA2DD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAE3D;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAC3B,MAAM,EAAE,MAAM,EACd,WAAW,CAAC,EAAE,MAAM,GAClB,aAAa,CAwBf"}
@@ -0,0 +1,42 @@
1
+ /** One page of an offset pagination. */
2
+ export interface Page<T> {
3
+ items: T[];
4
+ /** Every document that matches, across all pages. */
5
+ total: number;
6
+ /** 1-based. */
7
+ page: number;
8
+ pageSize: number;
9
+ /** `Math.ceil(total / pageSize)`: 0 when nothing matches. */
10
+ pageCount: number;
11
+ }
12
+ /** One page of a cursor pagination. */
13
+ export interface CursorPage<T> {
14
+ items: T[];
15
+ /** Pass it as `after` for the next page; `null` on the last one. */
16
+ nextCursor: string | null;
17
+ }
18
+ export interface PageOptions {
19
+ /** 1-based. Default `1`. */
20
+ page?: number;
21
+ /** Default `20`, at most `maxPageSize`. */
22
+ pageSize?: number;
23
+ }
24
+ export interface PageWindow {
25
+ page: number;
26
+ pageSize: number;
27
+ limit: number;
28
+ skip: number;
29
+ }
30
+ export declare const DEFAULT_PAGE_SIZE = 20;
31
+ export declare const DEFAULT_MAX_PAGE_SIZE = 100;
32
+ /**
33
+ * Checks `page` and `pageSize` and turns them into a `limit` and a `skip`.
34
+ * A `pageSize` above `maxPageSize` is lowered to it; one that is not a
35
+ * positive integer throws a `RangeError`.
36
+ */
37
+ export declare function pageWindow(options?: PageOptions, maxPageSize?: number): PageWindow;
38
+ /** Assembles a `Page` from its documents and the total. */
39
+ export declare function toPage<T>(items: T[], total: number, window: PageWindow): Page<T>;
40
+ /** Checks a cursor page's `limit`, as `pageWindow` checks a `pageSize`. */
41
+ export declare function cursorLimit(limit: number | undefined, maxPageSize?: number): number;
42
+ //# sourceMappingURL=page.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page.d.ts","sourceRoot":"","sources":["../../src/pagination/page.ts"],"names":[],"mappings":"AAAA,wCAAwC;AACxC,MAAM,WAAW,IAAI,CAAC,CAAC;IACtB,KAAK,EAAE,CAAC,EAAE,CAAC;IACX,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,eAAe;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,6DAA6D;IAC7D,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,uCAAuC;AACvC,MAAM,WAAW,UAAU,CAAC,CAAC;IAC5B,KAAK,EAAE,CAAC,EAAE,CAAC;IACX,oEAAoE;IACpE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,WAAW;IAC3B,4BAA4B;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,UAAU;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACb;AAED,eAAO,MAAM,iBAAiB,KAAK,CAAC;AACpC,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAWzC;;;;GAIG;AACH,wBAAgB,UAAU,CACzB,OAAO,GAAE,WAAgB,EACzB,WAAW,SAAwB,GACjC,UAAU,CAOZ;AAED,2DAA2D;AAC3D,wBAAgB,MAAM,CAAC,CAAC,EACvB,KAAK,EAAE,CAAC,EAAE,EACV,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,UAAU,GAChB,IAAI,CAAC,CAAC,CAAC,CAQT;AAED,2EAA2E;AAC3E,wBAAgB,WAAW,CAC1B,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,WAAW,SAAwB,GACjC,MAAM,CAKR"}
@@ -0,0 +1,21 @@
1
+ import type { Db } from 'mongodb';
2
+ import type { z } from 'zod';
3
+ import { type CollectionDefinition } from '../definition/define-collection';
4
+ import type { Repository, RepositoryOptions } from './types';
5
+ /**
6
+ * A repository over one collection: typed reads and writes by `_id` or by
7
+ * filter, pagination, soft delete, optimistic locking, audit stamps, and
8
+ * MongoDB errors turned into this package's.
9
+ *
10
+ * ```ts
11
+ * const users = createRepository(db, usersCollection);
12
+ * const ada = await users.create({ email: 'ada@example.com' });
13
+ * await users.update(ada._id, { name: 'Ada' }, { expectedVersion: ada.version });
14
+ * ```
15
+ *
16
+ * Every operation runs in the repository's session, which `with(session)`
17
+ * sets: MongoDB has no ambient session, so a write inside a transaction that
18
+ * was not given one is not part of it and is not rolled back.
19
+ */
20
+ export declare function createRepository<Schema extends z.ZodObject>(db: Db, definition: CollectionDefinition<Schema>, options?: RepositoryOptions): Repository<CollectionDefinition<Schema>>;
21
+ //# sourceMappingURL=create-repository.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-repository.d.ts","sourceRoot":"","sources":["../../src/repository/create-repository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,EAAE,EAAY,MAAM,SAAS,CAAC;AAC3D,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,EAEN,KAAK,oBAAoB,EAEzB,MAAM,iCAAiC,CAAC;AAiBzC,OAAO,KAAK,EAAkB,UAAU,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAsB7E;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,SAAS,CAAC,CAAC,SAAS,EAC1D,EAAE,EAAE,EAAE,EACN,UAAU,EAAE,oBAAoB,CAAC,MAAM,CAAC,EACxC,OAAO,GAAE,iBAAsB,GAC7B,UAAU,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC,CAI1C"}
@@ -0,0 +1,145 @@
1
+ import type { ClientSession, Collection, Db, Filter, Sort, UpdateFilter } from 'mongodb';
2
+ import type { DocumentOf, FieldOf, IdOf, NewDocumentOf } from '../definition/define-collection';
3
+ import type { CursorPage, Page, PageOptions } from '../pagination/page';
4
+ import type { SyncOptions, SyncReport } from '../sync/sync-collection';
5
+ export type OrderDirection = 'asc' | 'desc';
6
+ export interface ReadOptions {
7
+ /** Include soft-deleted documents. Ignored without a `deletedAt` field. */
8
+ withDeleted?: boolean;
9
+ }
10
+ export interface FindFirstOptions<Def> extends ReadOptions {
11
+ sort?: Sort;
12
+ projection?: Record<FieldOf<Def>, 0 | 1> | Record<string, unknown>;
13
+ }
14
+ export interface FindManyOptions<Def> extends FindFirstOptions<Def> {
15
+ filter?: Filter<DocumentOf<Def>>;
16
+ limit?: number;
17
+ skip?: number;
18
+ }
19
+ export interface PaginateOptions<Def> extends PageOptions, ReadOptions {
20
+ filter?: Filter<DocumentOf<Def>>;
21
+ /** Default `{ _id: 1 }`, so that pages are stable. */
22
+ sort?: Sort;
23
+ }
24
+ export interface CursorPaginateOptions<Def> extends ReadOptions {
25
+ /** The `nextCursor` of the previous page. Omit it for the first page. */
26
+ after?: string | null | undefined;
27
+ /** Documents per page. Default `20`, at most `maxPageSize`. */
28
+ limit?: number;
29
+ filter?: Filter<DocumentOf<Def>>;
30
+ /**
31
+ * The field to page along. Default `_id`. Any other is followed by `_id`,
32
+ * which breaks its ties, and must be set on every document.
33
+ */
34
+ orderBy?: FieldOf<Def>;
35
+ /** Default `'asc'`. */
36
+ direction?: OrderDirection;
37
+ }
38
+ /**
39
+ * What an update writes: the document's own fields, checked against the
40
+ * schema, or MongoDB's operators for anything they cannot say.
41
+ *
42
+ * The driver's `UpdateFilter` is intersected with `Document`, so it accepts
43
+ * any key whatsoever; a plain patch is what gets checked.
44
+ */
45
+ export type Patch<Def> = Partial<DocumentOf<Def>> | (UpdateFilter<DocumentOf<Def>> & {
46
+ [K in keyof DocumentOf<Def>]?: never;
47
+ });
48
+ export interface UpdateOptions {
49
+ /**
50
+ * Only update the document while its `version` is still this one. When it
51
+ * is not, nothing is written and `OptimisticLockError` is thrown with the
52
+ * version the document has now.
53
+ */
54
+ expectedVersion?: number;
55
+ }
56
+ export interface RepositoryOptions {
57
+ /**
58
+ * Soft delete through the `deletedAt` field. Default: on when the schema
59
+ * has one. `false` makes `delete` a real delete.
60
+ */
61
+ softDelete?: boolean;
62
+ /**
63
+ * Set `updatedAt` on every update that does not set it. Default: on when
64
+ * the schema has the field.
65
+ */
66
+ touchUpdatedAt?: boolean;
67
+ /**
68
+ * Raise `version` by one on every update. Default: on when the schema has
69
+ * the field. `expectedVersion` needs it.
70
+ */
71
+ optimisticLock?: boolean;
72
+ /**
73
+ * Check documents against the schema before writing them, which is also
74
+ * what fills their defaults. Default `'parse'`. `'off'` sends them as they
75
+ * are — and then nothing fills `_id`, `createdAt` or `version`.
76
+ */
77
+ validate?: 'parse' | 'off';
78
+ /** The largest `pageSize` or `limit` a page may ask for. Default `100`. */
79
+ maxPageSize?: number;
80
+ /** The session every operation runs in. `with(session)` is how it is set. */
81
+ session?: ClientSession;
82
+ /** Who is writing, stamped into `createdBy`, `updatedBy` and `deletedBy`. */
83
+ actor?: unknown;
84
+ }
85
+ /**
86
+ * A repository over one collection: typed reads and writes by `_id` or by
87
+ * filter, pagination, soft delete, optimistic locking, audit stamps, and
88
+ * MongoDB errors turned into this package's.
89
+ */
90
+ export interface Repository<Def> {
91
+ readonly definition: Def;
92
+ readonly db: Db;
93
+ /** The driver's collection, for anything this does not wrap. */
94
+ readonly collection: Collection<DocumentOf<Def> & Document>;
95
+ /** The session every operation of this repository runs in, if any. */
96
+ readonly session: ClientSession | undefined;
97
+ /**
98
+ * The same repository, bound to a session. MongoDB has no ambient
99
+ * session: without this, an operation inside a transaction runs outside
100
+ * it.
101
+ */
102
+ with(session: ClientSession | undefined): Repository<Def>;
103
+ /** The same repository, stamping this actor into the `*By` fields. */
104
+ as(actor: unknown): Repository<Def>;
105
+ /** Creates the collection, its validator and its indexes. See `syncCollection`. */
106
+ sync(options?: SyncOptions): Promise<SyncReport>;
107
+ /** The document with this `_id`, or `undefined`. */
108
+ findById(id: IdOf<Def>, options?: ReadOptions): Promise<DocumentOf<Def> | undefined>;
109
+ /** The document with this `_id`. Throws `NotFoundError`. */
110
+ getById(id: IdOf<Def>, options?: ReadOptions): Promise<DocumentOf<Def>>;
111
+ /** The first document that matches, or `undefined`. */
112
+ findFirst(filter?: Filter<DocumentOf<Def>>, options?: FindFirstOptions<Def>): Promise<DocumentOf<Def> | undefined>;
113
+ /** Every document that matches. */
114
+ findMany(options?: FindManyOptions<Def>): Promise<DocumentOf<Def>[]>;
115
+ /** Checks the document against the schema, fills its defaults, inserts it. */
116
+ create(values: NewDocumentOf<Def>): Promise<DocumentOf<Def>>;
117
+ /** The same, in one insert. `[]` sends nothing. */
118
+ createMany(values: readonly NewDocumentOf<Def>[]): Promise<DocumentOf<Def>[]>;
119
+ /** Updates the document with this `_id` and returns it. Throws `NotFoundError`. */
120
+ update(id: IdOf<Def>, patch: Patch<Def>, options?: UpdateOptions): Promise<DocumentOf<Def>>;
121
+ /** Updates every document that matches, and returns how many changed. */
122
+ updateMany(filter: Filter<DocumentOf<Def>>, patch: Patch<Def>): Promise<number>;
123
+ /**
124
+ * Deletes the document with this `_id` and returns it: a soft delete on a
125
+ * collection with `deletedAt`. Throws `NotFoundError`.
126
+ */
127
+ delete(id: IdOf<Def>): Promise<DocumentOf<Def>>;
128
+ /** Deletes every document that matches, and returns how many. */
129
+ deleteMany(filter: Filter<DocumentOf<Def>>): Promise<number>;
130
+ /** A real delete, of a live or a soft-deleted document. */
131
+ hardDelete(id: IdOf<Def>): Promise<DocumentOf<Def>>;
132
+ /** A real delete of every document that matches, soft-deleted ones included. */
133
+ hardDeleteMany(filter: Filter<DocumentOf<Def>>): Promise<number>;
134
+ /** Clears `deletedAt` and returns the document. Throws `NotFoundError`. */
135
+ restore(id: IdOf<Def>): Promise<DocumentOf<Def>>;
136
+ /** How many documents match. */
137
+ count(filter?: Filter<DocumentOf<Def>>, options?: ReadOptions): Promise<number>;
138
+ /** Whether any document matches. */
139
+ exists(filter: Filter<DocumentOf<Def>>, options?: ReadOptions): Promise<boolean>;
140
+ /** One page of the documents that match, and how many there are. */
141
+ paginate(options?: PaginateOptions<Def>): Promise<Page<DocumentOf<Def>>>;
142
+ /** One page of the documents that match, after a cursor. */
143
+ paginateByCursor(options?: CursorPaginateOptions<Def>): Promise<CursorPage<DocumentOf<Def>>>;
144
+ }
145
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/repository/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import type { IndexDescription, IndexDescriptionInfo } from 'mongodb';
2
+ type Fields = Record<string, unknown>;
3
+ /**
4
+ * The name MongoDB gives an index that names none: every field and direction,
5
+ * joined by `_`.
6
+ */
7
+ export declare function indexNameOf(key: Fields): string;
8
+ /** An index reduced to what makes two of them the same. */
9
+ export interface NormalizedIndex {
10
+ name: string;
11
+ /** In order: a compound index on `{a, b}` is not one on `{b, a}`. */
12
+ key: Fields;
13
+ options: Fields;
14
+ }
15
+ export declare function normalizeIndex(index: IndexDescription | IndexDescriptionInfo): NormalizedIndex;
16
+ /** Are two indexes the same index, with the same options? */
17
+ export declare function indexMatches(wanted: IndexDescription, live: IndexDescriptionInfo): boolean;
18
+ export interface IndexDiff {
19
+ /** Not on the server yet. */
20
+ create: IndexDescription[];
21
+ /**
22
+ * There under this name, with other options: MongoDB refuses to change
23
+ * one, so it is dropped and created again.
24
+ */
25
+ recreate: IndexDescription[];
26
+ /** Already as the definition wants it. */
27
+ unchanged: string[];
28
+ /** On the server and in no definition. `_id_` is never one. */
29
+ extra: string[];
30
+ }
31
+ /**
32
+ * What an index sync has to do. Indexes are matched by name, which is what
33
+ * MongoDB keys them on: the same name with other options is error 86, and the
34
+ * same key under another name is error 85.
35
+ */
36
+ export declare function diffIndexes(wanted: readonly IndexDescription[], live: readonly IndexDescriptionInfo[]): IndexDiff;
37
+ export {};
38
+ //# sourceMappingURL=index-diff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-diff.d.ts","sourceRoot":"","sources":["../../src/sync/index-diff.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAiCtE,KAAK,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAOtC;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI/C;AAcD,2DAA2D;AAC3D,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,qEAAqE;IACrE,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,cAAc,CAC7B,KAAK,EAAE,gBAAgB,GAAG,oBAAoB,GAC5C,eAAe,CAajB;AAYD,6DAA6D;AAC7D,wBAAgB,YAAY,CAC3B,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,oBAAoB,GACxB,OAAO,CAST;AAED,MAAM,WAAW,SAAS;IACzB,6BAA6B;IAC7B,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B;;;OAGG;IACH,QAAQ,EAAE,gBAAgB,EAAE,CAAC;IAC7B,0CAA0C;IAC1C,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,+DAA+D;IAC/D,KAAK,EAAE,MAAM,EAAE,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAC1B,MAAM,EAAE,SAAS,gBAAgB,EAAE,EACnC,IAAI,EAAE,SAAS,oBAAoB,EAAE,GACnC,SAAS,CA0BX"}
@@ -0,0 +1,62 @@
1
+ import type { ClientSession, Db } from 'mongodb';
2
+ import type { AnyCollectionDefinition } from '../definition/define-collection';
3
+ export interface SyncOptions {
4
+ /**
5
+ * Compare and report, but send nothing: no collection is created, no
6
+ * validator written, no index touched. For a check in CI, or a look before
7
+ * a deploy.
8
+ */
9
+ dryRun?: boolean;
10
+ /**
11
+ * Drop the indexes the server has and no definition names. Off by default:
12
+ * an index someone added on purpose is not this package's to remove.
13
+ * `_id_` is never dropped, and cannot be.
14
+ */
15
+ dropUnknownIndexes?: boolean;
16
+ /**
17
+ * A session for the reads. MongoDB does not allow `collMod` or an index
18
+ * build inside a transaction, so do not pass one that is in a transaction.
19
+ */
20
+ session?: ClientSession;
21
+ }
22
+ /** What `sync` found and did to one collection. */
23
+ export interface SyncReport {
24
+ name: string;
25
+ /** The collection did not exist, and was created. */
26
+ created: boolean;
27
+ /** What the `$jsonSchema` validator needed. */
28
+ validator: 'unchanged' | 'created' | 'updated' | 'removed';
29
+ indexes: {
30
+ created: string[];
31
+ /** There with other options: MongoDB cannot change one, so it is dropped and built again. */
32
+ recreated: string[];
33
+ dropped: string[];
34
+ unchanged: string[];
35
+ };
36
+ dryRun: boolean;
37
+ }
38
+ /**
39
+ * Brings one collection in line with its definition, and says what it changed:
40
+ *
41
+ * 1. creates the collection, with its validator, when it is missing;
42
+ * 2. writes the validator with `collMod` when it differs from the definition's;
43
+ * 3. creates the indexes that are missing, and rebuilds those whose options
44
+ * changed — MongoDB refuses to alter an index in place.
45
+ *
46
+ * Run it twice and the second run sends nothing.
47
+ *
48
+ * ```ts
49
+ * const report = await syncCollection(db, users);
50
+ * // { created: true, validator: 'created', indexes: { created: ['users_email_unique'], … } }
51
+ * ```
52
+ *
53
+ * It is a deployment step, not a request-time one: `collMod` needs the
54
+ * `dbAdmin` role, and neither it nor an index build may run in a transaction.
55
+ */
56
+ export declare function syncCollection(db: Db, definition: AnyCollectionDefinition, options?: SyncOptions): Promise<SyncReport>;
57
+ /**
58
+ * `syncCollection` for each definition, one after the other, in the order
59
+ * given. The first that throws stops the rest.
60
+ */
61
+ export declare function syncCollections(db: Db, definitions: readonly AnyCollectionDefinition[], options?: SyncOptions): Promise<SyncReport[]>;
62
+ //# sourceMappingURL=sync-collection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-collection.d.ts","sourceRoot":"","sources":["../../src/sync/sync-collection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,aAAa,EACb,EAAE,EAIF,MAAM,SAAS,CAAC;AACjB,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAY/E,MAAM,WAAW,WAAW;IAC3B;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;;;OAGG;IACH,OAAO,CAAC,EAAE,aAAa,CAAC;CACxB;AAED,mDAAmD;AACnD,MAAM,WAAW,UAAU;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,qDAAqD;IACrD,OAAO,EAAE,OAAO,CAAC;IACjB,+CAA+C;IAC/C,SAAS,EAAE,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;IAC3D,OAAO,EAAE;QACR,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,6FAA6F;QAC7F,SAAS,EAAE,MAAM,EAAE,CAAC;QACpB,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,SAAS,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;IACF,MAAM,EAAE,OAAO,CAAC;CAChB;AA6FD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,cAAc,CACnC,EAAE,EAAE,EAAE,EACN,UAAU,EAAE,uBAAuB,EACnC,OAAO,GAAE,WAAgB,GACvB,OAAO,CAAC,UAAU,CAAC,CAgFrB;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACpC,EAAE,EAAE,EAAE,EACN,WAAW,EAAE,SAAS,uBAAuB,EAAE,EAC/C,OAAO,GAAE,WAAgB,GACvB,OAAO,CAAC,UAAU,EAAE,CAAC,CAMvB"}