@nxgt/mongo 0.1.0 → 0.3.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/index.js CHANGED
@@ -25,162 +25,7 @@ function stampsOf(definition) {
25
25
  deletedBy: has("deletedBy")
26
26
  };
27
27
  }
28
- // src/definition/fields.ts
29
- import { ObjectId } from "mongodb";
30
- import { z } from "zod";
31
- function isObjectId(value) {
32
- return typeof value === "object" && value !== null && value._bsontype === "ObjectId";
33
- }
34
- function objectId() {
35
- return z.custom(isObjectId, { error: "must be an ObjectId" }).meta({ bsonType: "objectId" });
36
- }
37
- function id() {
38
- return objectId().default(() => new ObjectId);
39
- }
40
- function timestamps() {
41
- return {
42
- createdAt: z.date().default(() => new Date),
43
- updatedAt: z.date().default(() => new Date)
44
- };
45
- }
46
- function softDelete() {
47
- return { deletedAt: z.date().nullable().default(null) };
48
- }
49
- function optimisticLock() {
50
- return { version: z.int().nonnegative().default(0) };
51
- }
52
- function actors(actor = objectId()) {
53
- return {
54
- createdBy: actor.nullable().default(null),
55
- updatedBy: actor.nullable().default(null),
56
- deletedBy: actor.nullable().default(null)
57
- };
58
- }
59
- var STAMP_FIELDS = {
60
- id: "_id",
61
- createdAt: "createdAt",
62
- updatedAt: "updatedAt",
63
- deletedAt: "deletedAt",
64
- version: "version",
65
- createdBy: "createdBy",
66
- updatedBy: "updatedBy",
67
- deletedBy: "deletedBy"
68
- };
69
- // src/definition/json-schema.ts
70
- import { z as z2 } from "zod";
71
- var MONGO_JSON_SCHEMA_KEYWORDS = new Set([
72
- "additionalItems",
73
- "additionalProperties",
74
- "allOf",
75
- "anyOf",
76
- "bsonType",
77
- "dependencies",
78
- "description",
79
- "enum",
80
- "exclusiveMaximum",
81
- "exclusiveMinimum",
82
- "items",
83
- "maxItems",
84
- "maxLength",
85
- "maxProperties",
86
- "maximum",
87
- "minItems",
88
- "minLength",
89
- "minProperties",
90
- "minimum",
91
- "multipleOf",
92
- "not",
93
- "oneOf",
94
- "pattern",
95
- "patternProperties",
96
- "properties",
97
- "required",
98
- "title",
99
- "type",
100
- "uniqueItems"
101
- ]);
102
- var SCHEMA_MAPS = new Set([
103
- "properties",
104
- "patternProperties",
105
- "dependencies"
106
- ]);
107
- function isRecord(value) {
108
- return typeof value === "object" && value !== null && !Array.isArray(value);
109
- }
110
- var INTEGER_BSON_TYPES = ["int", "long", "double"];
111
- function convertIntegerType(node) {
112
- const type = node.type;
113
- if (type === "integer") {
114
- delete node.type;
115
- node.bsonType = [...INTEGER_BSON_TYPES];
116
- node.multipleOf ??= 1;
117
- return;
118
- }
119
- if (Array.isArray(type) && type.includes("integer")) {
120
- delete node.type;
121
- node.bsonType = [
122
- ...type.filter((one) => one !== "integer"),
123
- ...INTEGER_BSON_TYPES
124
- ];
125
- node.multipleOf ??= 1;
126
- }
127
- }
128
- function refName(ref) {
129
- return ref.replace(/^#\/(definitions|\$defs)\//, "");
130
- }
131
- function inline(value, defs, stack) {
132
- if (Array.isArray(value)) {
133
- return value.map((one) => inline(one, defs, stack));
134
- }
135
- if (!isRecord(value))
136
- return value;
137
- if (typeof value.$ref === "string") {
138
- const name = refName(value.$ref);
139
- if (stack.includes(name)) {
140
- throw new TypeError(`toMongoJsonSchema: "${name}" refers to itself. MongoDB's $jsonSchema ` + "has no $ref, so a recursive schema cannot be a validator. Give the " + "collection no validator, or model the field as an object with no " + "schema of its own.");
141
- }
142
- const target = defs[name];
143
- if (!isRecord(target)) {
144
- throw new TypeError(`toMongoJsonSchema: cannot resolve ${value.$ref}, which zod emitted`);
145
- }
146
- const { $ref: _ref, ...siblings } = value;
147
- return {
148
- ...inline(target, defs, [...stack, name]),
149
- ...inline(siblings, defs, stack)
150
- };
151
- }
152
- const out = {};
153
- for (const [key, inner] of Object.entries(value)) {
154
- if (!MONGO_JSON_SCHEMA_KEYWORDS.has(key))
155
- continue;
156
- if (SCHEMA_MAPS.has(key) && isRecord(inner)) {
157
- const mapped = {};
158
- for (const [name, schema] of Object.entries(inner)) {
159
- mapped[name] = inline(schema, defs, stack);
160
- }
161
- out[key] = mapped;
162
- continue;
163
- }
164
- out[key] = inline(inner, defs, stack);
165
- }
166
- convertIntegerType(out);
167
- return out;
168
- }
169
- function toMongoJsonSchema(schema) {
170
- const json = z2.toJSONSchema(schema, {
171
- target: "draft-4",
172
- io: "output",
173
- unrepresentable: "any",
174
- override: (ctx) => {
175
- const type = ctx.zodSchema._zod.def.type;
176
- if (type === "date" && ctx.jsonSchema.bsonType === undefined) {
177
- ctx.jsonSchema.bsonType = "date";
178
- }
179
- }
180
- });
181
- const definitions = isRecord(json.definitions) ? json.definitions : isRecord(json.$defs) ? json.$defs : {};
182
- return inline(json, definitions, []);
183
- }
28
+
184
29
  // src/errors/data-error.ts
185
30
  class DataError extends Error {
186
31
  constructor(message = "Database error", options = {}) {
@@ -232,6 +77,14 @@ class OptimisticLockError extends DataError {
232
77
  }
233
78
  }
234
79
 
80
+ class InvalidIdError extends DataError {
81
+ constructor(message = "Invalid id", options = {}) {
82
+ super(message, options);
83
+ this.name = "InvalidIdError";
84
+ this.code = "INVALID_ID";
85
+ }
86
+ }
87
+
235
88
  class InvalidCursorError extends DataError {
236
89
  constructor(message = "Invalid cursor", options = {}) {
237
90
  super(message, options);
@@ -239,8 +92,9 @@ class InvalidCursorError extends DataError {
239
92
  this.code = "INVALID_CURSOR";
240
93
  }
241
94
  }
95
+
242
96
  // src/errors/to-data-error.ts
243
- function isRecord2(value) {
97
+ function isRecord(value) {
244
98
  return typeof value === "object" && value !== null && !Array.isArray(value);
245
99
  }
246
100
  function asArray(value) {
@@ -256,8 +110,8 @@ function indexFromMessage(message) {
256
110
  }
257
111
  function keysOfDuplicate(error) {
258
112
  const pattern = error.keyPattern;
259
- if (isRecord2(pattern)) {
260
- const values = isRecord2(error.keyValue) ? error.keyValue : undefined;
113
+ if (isRecord(pattern)) {
114
+ const values = isRecord(error.keyValue) ? error.keyValue : undefined;
261
115
  return { keys: Object.keys(pattern), values };
262
116
  }
263
117
  const inMessage = text(error.errmsg)?.match(/dup key:\s*\{([^}]*)\}/)?.[1];
@@ -268,8 +122,8 @@ function keysOfDuplicate(error) {
268
122
  }
269
123
  function firstWriteError(error) {
270
124
  for (const write of asArray(error.writeErrors)) {
271
- const inner = isRecord2(write) && isRecord2(write.err) ? write.err : write;
272
- if (isRecord2(inner))
125
+ const inner = isRecord(write) && isRecord(write.err) ? write.err : write;
126
+ if (isRecord(inner))
273
127
  return inner;
274
128
  }
275
129
  return;
@@ -277,11 +131,11 @@ function firstWriteError(error) {
277
131
  function issuesOf(details, path = []) {
278
132
  const issues = [];
279
133
  for (const rule of asArray(details)) {
280
- if (!isRecord2(rule))
134
+ if (!isRecord(rule))
281
135
  continue;
282
136
  if (rule.propertiesNotSatisfied !== undefined) {
283
137
  for (const property of asArray(rule.propertiesNotSatisfied)) {
284
- if (!isRecord2(property))
138
+ if (!isRecord(property))
285
139
  continue;
286
140
  const name = text(property.propertyName) ?? "";
287
141
  const nested = issuesOf(property.details, [...path, name]);
@@ -317,7 +171,7 @@ function issuesOf(details, path = []) {
317
171
  function toDataError(error, context = {}) {
318
172
  if (error instanceof DataError)
319
173
  return error;
320
- if (!isRecord2(error))
174
+ if (!isRecord(error))
321
175
  return error;
322
176
  const source = firstWriteError(error) ?? error;
323
177
  const code = typeof source.code === "number" ? source.code : typeof error.code === "number" ? error.code : undefined;
@@ -337,15 +191,16 @@ function toDataError(error, context = {}) {
337
191
  return new ConflictError(`Duplicate key on ${named}${context.collection ? ` in "${context.collection}"` : ""}`, { ...common, index, keys, values });
338
192
  }
339
193
  if (code === 121) {
340
- const errInfo = isRecord2(source.errInfo) ? source.errInfo : undefined;
194
+ const errInfo = isRecord(source.errInfo) ? source.errInfo : undefined;
341
195
  const issues = issuesOf(errInfo?.details);
342
196
  return new ValidationError(`Document failed validation${context.collection ? ` in "${context.collection}"` : ""}${issues.length > 0 ? `: ${issues.map((i) => `${i.path} ${i.reason}`).join(", ")}` : ""}`, { ...common, issues, keys: issues.map((issue) => issue.path) });
343
197
  }
344
198
  return new DataError(message || `MongoDB error ${code}`, common);
345
199
  }
200
+
346
201
  // src/pagination/cursor.ts
347
- import { ObjectId as ObjectId2 } from "mongodb";
348
- function isObjectId2(value) {
202
+ import { ObjectId } from "mongodb";
203
+ function isObjectId(value) {
349
204
  return typeof value === "object" && value !== null && value._bsontype === "ObjectId";
350
205
  }
351
206
  function replacer(key, value) {
@@ -354,7 +209,7 @@ function replacer(key, value) {
354
209
  return { $date: raw.toISOString() };
355
210
  if (typeof raw === "bigint")
356
211
  return { $bigint: raw.toString() };
357
- if (isObjectId2(raw))
212
+ if (isObjectId(raw))
358
213
  return { $oid: raw.toHexString() };
359
214
  return value;
360
215
  }
@@ -368,7 +223,7 @@ function reviver(_key, value) {
368
223
  if (typeof tagged.$bigint === "string")
369
224
  return BigInt(tagged.$bigint);
370
225
  if (typeof tagged.$oid === "string")
371
- return new ObjectId2(tagged.$oid);
226
+ return new ObjectId(tagged.$oid);
372
227
  }
373
228
  }
374
229
  return value;
@@ -406,6 +261,7 @@ function decodeCursor(cursor, expectedKey) {
406
261
  }
407
262
  return { key, values };
408
263
  }
264
+
409
265
  // src/pagination/page.ts
410
266
  var DEFAULT_PAGE_SIZE = 20;
411
267
  var DEFAULT_MAX_PAGE_SIZE = 100;
@@ -432,6 +288,123 @@ function toPage(items, total, window) {
432
288
  function cursorLimit(limit, maxPageSize = DEFAULT_MAX_PAGE_SIZE) {
433
289
  return Math.min(positiveInteger("limit", limit ?? DEFAULT_PAGE_SIZE), maxPageSize);
434
290
  }
291
+
292
+ // src/definition/json-schema.ts
293
+ import { z } from "zod";
294
+ var MONGO_JSON_SCHEMA_KEYWORDS = new Set([
295
+ "additionalItems",
296
+ "additionalProperties",
297
+ "allOf",
298
+ "anyOf",
299
+ "bsonType",
300
+ "dependencies",
301
+ "description",
302
+ "enum",
303
+ "exclusiveMaximum",
304
+ "exclusiveMinimum",
305
+ "items",
306
+ "maxItems",
307
+ "maxLength",
308
+ "maxProperties",
309
+ "maximum",
310
+ "minItems",
311
+ "minLength",
312
+ "minProperties",
313
+ "minimum",
314
+ "multipleOf",
315
+ "not",
316
+ "oneOf",
317
+ "pattern",
318
+ "patternProperties",
319
+ "properties",
320
+ "required",
321
+ "title",
322
+ "type",
323
+ "uniqueItems"
324
+ ]);
325
+ var SCHEMA_MAPS = new Set([
326
+ "properties",
327
+ "patternProperties",
328
+ "dependencies"
329
+ ]);
330
+ function isRecord2(value) {
331
+ return typeof value === "object" && value !== null && !Array.isArray(value);
332
+ }
333
+ var INTEGER_BSON_TYPES = ["int", "long", "double"];
334
+ function convertIntegerType(node) {
335
+ const type = node.type;
336
+ if (type === "integer") {
337
+ delete node.type;
338
+ node.bsonType = [...INTEGER_BSON_TYPES];
339
+ node.multipleOf ??= 1;
340
+ return;
341
+ }
342
+ if (Array.isArray(type) && type.includes("integer")) {
343
+ delete node.type;
344
+ node.bsonType = [
345
+ ...type.filter((one) => one !== "integer"),
346
+ ...INTEGER_BSON_TYPES
347
+ ];
348
+ node.multipleOf ??= 1;
349
+ }
350
+ }
351
+ function refName(ref) {
352
+ return ref.replace(/^#\/(definitions|\$defs)\//, "");
353
+ }
354
+ function inline(value, defs, stack) {
355
+ if (Array.isArray(value)) {
356
+ return value.map((one) => inline(one, defs, stack));
357
+ }
358
+ if (!isRecord2(value))
359
+ return value;
360
+ if (typeof value.$ref === "string") {
361
+ const name = refName(value.$ref);
362
+ if (stack.includes(name)) {
363
+ throw new TypeError(`toMongoJsonSchema: "${name}" refers to itself. MongoDB's $jsonSchema ` + "has no $ref, so a recursive schema cannot be a validator. Give the " + "collection no validator, or model the field as an object with no " + "schema of its own.");
364
+ }
365
+ const target = defs[name];
366
+ if (!isRecord2(target)) {
367
+ throw new TypeError(`toMongoJsonSchema: cannot resolve ${value.$ref}, which zod emitted`);
368
+ }
369
+ const { $ref: _ref, ...siblings } = value;
370
+ return {
371
+ ...inline(target, defs, [...stack, name]),
372
+ ...inline(siblings, defs, stack)
373
+ };
374
+ }
375
+ const out = {};
376
+ for (const [key, inner] of Object.entries(value)) {
377
+ if (!MONGO_JSON_SCHEMA_KEYWORDS.has(key))
378
+ continue;
379
+ if (SCHEMA_MAPS.has(key) && isRecord2(inner)) {
380
+ const mapped = {};
381
+ for (const [name, schema] of Object.entries(inner)) {
382
+ mapped[name] = inline(schema, defs, stack);
383
+ }
384
+ out[key] = mapped;
385
+ continue;
386
+ }
387
+ out[key] = inline(inner, defs, stack);
388
+ }
389
+ convertIntegerType(out);
390
+ return out;
391
+ }
392
+ function toMongoJsonSchema(schema) {
393
+ const json = z.toJSONSchema(schema, {
394
+ target: "draft-4",
395
+ io: "output",
396
+ unrepresentable: "any",
397
+ override: (ctx) => {
398
+ const type = ctx.zodSchema._zod.def.type;
399
+ if (type === "date" && ctx.jsonSchema.bsonType === undefined) {
400
+ ctx.jsonSchema.bsonType = "date";
401
+ }
402
+ }
403
+ });
404
+ const definitions = isRecord2(json.definitions) ? json.definitions : isRecord2(json.$defs) ? json.$defs : {};
405
+ return inline(json, definitions, []);
406
+ }
407
+
435
408
  // src/sync/index-diff.ts
436
409
  var COLLATION_DEFAULTS = {
437
410
  caseLevel: false,
@@ -655,7 +628,7 @@ async function syncCollections(db, definitions, options = {}) {
655
628
  return reports;
656
629
  }
657
630
 
658
- // src/repository/create-repository.ts
631
+ // src/collection/get-collection.ts
659
632
  function isRecord3(value) {
660
633
  return typeof value === "object" && value !== null && !Array.isArray(value);
661
634
  }
@@ -671,13 +644,25 @@ function mergeFilters(a, b) {
671
644
  return left;
672
645
  return { $and: [left, right] };
673
646
  }
674
- function createRepository(db, definition, options = {}) {
647
+ function databaseOf(source, name) {
648
+ const client = source;
649
+ if (typeof client.db === "function")
650
+ return client.db(name);
651
+ const db = source;
652
+ if (name !== undefined && db.databaseName !== name) {
653
+ throw new TypeError(`getCollection: given a Db for "${db.databaseName}", and a db option of ` + `"${name}". Pass the client, or the database you mean.`);
654
+ }
655
+ return db;
656
+ }
657
+ function getCollection(source, definition, options = {}) {
658
+ const db = databaseOf(source, options.db);
675
659
  return build(db, definition, options);
676
660
  }
677
661
  function build(db, definition, options) {
678
662
  const name = definition.name;
679
663
  const collection = db.collection(name);
680
664
  const shape = definition.schema.shape;
665
+ const hasOwnId = "id" in shape;
681
666
  const stamps = stampsOf(definition);
682
667
  const session = options.session;
683
668
  const actor = options.actor;
@@ -687,10 +672,10 @@ function build(db, definition, options) {
687
672
  const touches = options.touchUpdatedAt ?? stamps.updatedAt;
688
673
  const locks = options.optimisticLock ?? stamps.version;
689
674
  if (options.softDelete === true && !stamps.deletedAt) {
690
- throw new TypeError(`createRepository: softDelete needs a "deletedAt" field, and "${name}" has none`);
675
+ throw new TypeError(`getCollection: softDelete needs a "deletedAt" field, and "${name}" has none`);
691
676
  }
692
677
  if (options.optimisticLock === true && !stamps.version) {
693
- throw new TypeError(`createRepository: optimisticLock needs a "version" field, and "${name}" has none`);
678
+ throw new TypeError(`getCollection: optimisticLock needs a "version" field, and "${name}" has none`);
694
679
  }
695
680
  const run = async (fn) => {
696
681
  try {
@@ -702,6 +687,17 @@ function build(db, definition, options) {
702
687
  const sessionOption = session ? { session } : {};
703
688
  const live = (withDeleted) => softDeletes && !withDeleted ? { deletedAt: null } : undefined;
704
689
  const scoped = (filter, withDeleted) => mergeFilters(isRecord3(filter) ? filter : undefined, live(withDeleted));
690
+ const withId = (document) => {
691
+ if (hasOwnId || !isRecord3(document) || document._id === undefined || Object.hasOwn(document, "id")) {
692
+ return document;
693
+ }
694
+ Object.defineProperty(document, "id", {
695
+ get: () => String(document._id),
696
+ enumerable: true,
697
+ configurable: true
698
+ });
699
+ return document;
700
+ };
705
701
  const notFound = (id) => new NotFoundError(`No document in "${name}" with _id ${String(id)}`, {
706
702
  collection: name,
707
703
  id
@@ -713,6 +709,8 @@ function build(db, definition, options) {
713
709
  };
714
710
  const toDocument = (values) => {
715
711
  const stamped = { ...values };
712
+ if (!hasOwnId)
713
+ delete stamped.id;
716
714
  if (actor !== undefined) {
717
715
  if (stamps.createdBy && stamped.createdBy === undefined) {
718
716
  stamped.createdBy = actor;
@@ -754,10 +752,13 @@ function build(db, definition, options) {
754
752
  }
755
753
  return update;
756
754
  };
757
- const findOne = async (filter, projection) => run(async () => collection.findOne(filter, {
758
- ...sessionOption,
759
- ...projection ? { projection } : {}
760
- }));
755
+ const findOne = async (filter, projection) => run(async () => {
756
+ const found = await collection.findOne(filter, {
757
+ ...sessionOption,
758
+ ...projection ? { projection } : {}
759
+ });
760
+ return found === null ? null : withId(found);
761
+ });
761
762
  async function findById(id, opts = {}) {
762
763
  const found = await findOne(scoped({ _id: id }, opts.withDeleted));
763
764
  return found ?? undefined;
@@ -780,7 +781,8 @@ function build(db, definition, options) {
780
781
  cursor = cursor.skip(opts.skip);
781
782
  if (opts.limit !== undefined)
782
783
  cursor = cursor.limit(opts.limit);
783
- return cursor.toArray();
784
+ const found = await cursor.toArray();
785
+ return found.map((document) => withId(document));
784
786
  });
785
787
  }
786
788
  async function countDocuments(filter, opts = {}) {
@@ -794,7 +796,7 @@ function build(db, definition, options) {
794
796
  returnDocument: "after"
795
797
  }));
796
798
  if (updated)
797
- return updated;
799
+ return withId(updated);
798
800
  if (expectedVersion !== undefined) {
799
801
  const current = await findOne({ _id: id });
800
802
  if (current) {
@@ -812,7 +814,7 @@ function build(db, definition, options) {
812
814
  const deleted = await run(async () => collection.findOneAndDelete({ _id: id }, { ...sessionOption }));
813
815
  if (!deleted)
814
816
  throw notFound(id);
815
- return deleted;
817
+ return withId(deleted);
816
818
  }
817
819
  async function hardDeleteMany(filter) {
818
820
  requireFilter("hardDeleteMany", filter);
@@ -823,12 +825,12 @@ function build(db, definition, options) {
823
825
  return result.deletedCount;
824
826
  });
825
827
  }
826
- const repository = {
828
+ const api = {
827
829
  definition,
828
830
  db,
829
- collection,
831
+ raw: collection,
830
832
  session,
831
- with: (other) => build(db, definition, { ...options, session: other }),
833
+ withSession: (other) => build(db, definition, { ...options, session: other }),
832
834
  as: (who) => build(db, definition, { ...options, actor: who }),
833
835
  sync: (syncOptions = {}) => syncCollection(db, definition, { ...sessionOption, ...syncOptions }),
834
836
  findById,
@@ -842,7 +844,7 @@ function build(db, definition, options) {
842
844
  const document = toDocument(values);
843
845
  return run(async () => {
844
846
  await collection.insertOne(document, { ...sessionOption });
845
- return document;
847
+ return withId(document);
846
848
  });
847
849
  },
848
850
  async createMany(values) {
@@ -853,7 +855,7 @@ function build(db, definition, options) {
853
855
  await collection.insertMany(documents, {
854
856
  ...sessionOption
855
857
  });
856
- return documents;
858
+ return documents.map((document) => withId(document));
857
859
  });
858
860
  },
859
861
  async update(id, patch, opts = {}) {
@@ -988,8 +990,102 @@ function build(db, definition, options) {
988
990
  return { items, nextCursor: encodeCursor({ key: cursorKey, values }) };
989
991
  }
990
992
  };
991
- return repository;
993
+ return new Proxy(api, {
994
+ get(target, key, receiver) {
995
+ if (Reflect.has(target, key))
996
+ return Reflect.get(target, key, receiver);
997
+ const value = collection[key];
998
+ return typeof value === "function" ? value.bind(collection) : value;
999
+ },
1000
+ has(target, key) {
1001
+ return Reflect.has(target, key) || key in collection;
1002
+ }
1003
+ });
992
1004
  }
1005
+ // src/definition/fields.ts
1006
+ import { ObjectId as ObjectId3 } from "mongodb";
1007
+ import { z as z3 } from "zod";
1008
+
1009
+ // src/definition/object-id.ts
1010
+ import { ObjectId as ObjectId2 } from "mongodb";
1011
+ import { z as z2 } from "zod";
1012
+ var HEX_24 = /^[0-9a-fA-F]{24}$/;
1013
+ function isObjectId2(value) {
1014
+ return typeof value === "object" && value !== null && value._bsontype === "ObjectId";
1015
+ }
1016
+ function isObjectIdString(value) {
1017
+ return typeof value === "string" && HEX_24.test(value);
1018
+ }
1019
+ function isValidObjectId(value) {
1020
+ return isObjectId2(value) || isObjectIdString(value);
1021
+ }
1022
+ function tryObjectId(value) {
1023
+ if (isObjectId2(value))
1024
+ return value;
1025
+ if (isObjectIdString(value))
1026
+ return ObjectId2.createFromHexString(value);
1027
+ return;
1028
+ }
1029
+ function describe(value) {
1030
+ if (value === null)
1031
+ return "null";
1032
+ if (value === undefined)
1033
+ return "undefined";
1034
+ if (typeof value === "string")
1035
+ return `the string ${JSON.stringify(value)}`;
1036
+ return `a ${typeof value}`;
1037
+ }
1038
+ function toObjectId(value, field = "_id") {
1039
+ const made = tryObjectId(value);
1040
+ if (made)
1041
+ return made;
1042
+ throw new InvalidIdError(`${field}: expected an ObjectId or its 24-character hex string, got ${describe(value)}`, { id: value, keys: [field] });
1043
+ }
1044
+ function toObjectIds(values, field = "_id") {
1045
+ return [...values].map((value) => toObjectId(value, field));
1046
+ }
1047
+ function objectIdParam() {
1048
+ return z2.custom(isValidObjectId, {
1049
+ error: "must be an ObjectId or its 24-character hex string"
1050
+ }).transform((value) => toObjectId(value));
1051
+ }
1052
+
1053
+ // src/definition/fields.ts
1054
+ function objectId() {
1055
+ return z3.custom(isObjectId2, { error: "must be an ObjectId" }).meta({ bsonType: "objectId" });
1056
+ }
1057
+ function id() {
1058
+ return objectId().default(() => new ObjectId3);
1059
+ }
1060
+ function timestamps() {
1061
+ return {
1062
+ createdAt: z3.date().default(() => new Date),
1063
+ updatedAt: z3.date().default(() => new Date)
1064
+ };
1065
+ }
1066
+ function softDelete() {
1067
+ return { deletedAt: z3.date().nullable().default(null) };
1068
+ }
1069
+ function optimisticLock() {
1070
+ return { version: z3.int().nonnegative().default(0) };
1071
+ }
1072
+ function actors(actor = objectId()) {
1073
+ return {
1074
+ createdBy: actor.nullable().default(null),
1075
+ updatedBy: actor.nullable().default(null),
1076
+ deletedBy: actor.nullable().default(null)
1077
+ };
1078
+ }
1079
+ var STAMP_FIELDS = {
1080
+ id: "_id",
1081
+ createdAt: "createdAt",
1082
+ updatedAt: "updatedAt",
1083
+ deletedAt: "deletedAt",
1084
+ version: "version",
1085
+ createdBy: "createdBy",
1086
+ updatedBy: "updatedBy",
1087
+ deletedBy: "deletedBy"
1088
+ };
993
1089
  // src/transaction/with-transaction.ts
994
1090
  function isSession(host) {
995
1091
  return typeof host.inTransaction === "function";
@@ -1021,24 +1117,29 @@ export {
1021
1117
  DEFAULT_PAGE_SIZE,
1022
1118
  DataError,
1023
1119
  InvalidCursorError,
1120
+ InvalidIdError,
1024
1121
  MONGO_JSON_SCHEMA_KEYWORDS,
1025
1122
  NotFoundError,
1026
1123
  OptimisticLockError,
1027
1124
  STAMP_FIELDS,
1028
1125
  ValidationError,
1029
1126
  actors,
1030
- createRepository,
1031
1127
  cursorLimit,
1032
1128
  decodeCursor,
1033
1129
  defineCollection,
1034
1130
  diffIndexes,
1035
1131
  encodeCursor,
1132
+ getCollection,
1036
1133
  hasValidator,
1037
1134
  id,
1038
1135
  indexMatches,
1039
1136
  indexNameOf,
1137
+ isObjectId2 as isObjectId,
1138
+ isObjectIdString,
1139
+ isValidObjectId,
1040
1140
  normalizeIndex,
1041
1141
  objectId,
1142
+ objectIdParam,
1042
1143
  optimisticLock,
1043
1144
  pageWindow,
1044
1145
  softDelete,
@@ -1048,10 +1149,13 @@ export {
1048
1149
  timestamps,
1049
1150
  toDataError,
1050
1151
  toMongoJsonSchema,
1152
+ toObjectId,
1153
+ toObjectIds,
1051
1154
  toPage,
1155
+ tryObjectId,
1052
1156
  validationMatches,
1053
1157
  withTransaction
1054
1158
  };
1055
1159
 
1056
- //# debugId=44372BBBC1BF50A464756E2164756E21
1160
+ //# debugId=3B847E76D4AAF66F64756E2164756E21
1057
1161
  //# sourceMappingURL=index.js.map