@form-engine-ts/storage-mongodb 2.7.0 → 2.9.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/README.md CHANGED
@@ -33,8 +33,12 @@ Metadata filters and the generic submission-filter AST are translated to MongoDB
33
33
  Legacy predicate filters remain supported and are applied client-side. `listTextAnswerPage` returns stable cursor pages
34
34
  of individual text/textarea answers without loading every answer body at once.
35
35
 
36
- Version records are stored in `form_versions`, with unique `(formId, version)` and `(formId, status)` indexes. Transition
37
- state lives in `form_version_states`. `commitVersionTransition(plan)` compares `expectedRevision` atomically and returns
36
+ Version records are stored in `form_versions`, with a unique `(formId, version)` index. Partial unique indexes allow at
37
+ most one Draft and one Published record per form, while any number of Archived records remain available. Transition state
38
+ lives in `form_version_states`. `commitVersionTransition(plan)` compares `expectedRevision` atomically and returns
38
39
  `revision_conflict` to losing concurrent publishers; on a real MongoDB client, the state and record changes are committed
39
40
  in one transaction. Clone, publish, and draft-delete plans persist complete version state, affected records, and audit
40
41
  events. State/record/list reads and typed commit errors are exposed through the full `VersionedFormStorageAdapter` API.
42
+ Version commits require transaction support and return `transaction_unsupported` before any write when it is unavailable.
43
+ MongoDB duplicate-key failures are mapped to `draft_already_exists` or `revision_conflict` rather than a generic storage
44
+ error.
package/dist/index.cjs CHANGED
@@ -102,11 +102,29 @@ function parseVersionRecordDocument(document) {
102
102
  }
103
103
  return cloneJson(document.record);
104
104
  }
105
- function isCasConflict(error) {
105
+ function mongoErrorMessage(error) {
106
+ return error instanceof Error ? error.message : isRecord(error) ? String(error.message ?? "") : "";
107
+ }
108
+ function isDuplicateKeyError(error) {
109
+ return isRecord(error) && error.code === 11e3;
110
+ }
111
+ function duplicateIndexName(error) {
112
+ if (!isDuplicateKeyError(error)) return void 0;
113
+ return /unique_(?:draft|published|version)_per_form/u.exec(mongoErrorMessage(error))?.[0];
114
+ }
115
+ function isRevisionConflict(error) {
106
116
  if (!isRecord(error)) return false;
107
- if (error.code === 11e3 || error.code === 112 || error.code === 251) return true;
117
+ if (error.code === 11e3) return duplicateIndexName(error) !== "unique_draft_per_form";
118
+ if (error.code === 112 || error.code === 251) return true;
108
119
  return typeof error.hasErrorLabel === "function" && error.hasErrorLabel("TransientTransactionError") === true;
109
120
  }
121
+ function isTransactionUnsupported(error) {
122
+ if (!isRecord(error)) return false;
123
+ if (error.code === 20 || error.code === 263 || error.code === 303) return true;
124
+ return /transaction numbers are only allowed|transactions? (?:are|is) not supported|replica set member|mongos/iu.test(
125
+ mongoErrorMessage(error)
126
+ );
127
+ }
110
128
  function parseSchemaDocument(document) {
111
129
  try {
112
130
  (0, import_core.assertValidFormSchema)(document.schema);
@@ -165,6 +183,18 @@ function createMongoDbStorage(options) {
165
183
  }
166
184
  };
167
185
  };
186
+ const storageDraftAlreadyExists = async (plan) => {
187
+ const state = await versionStates.findOne({ _id: plan.formId });
188
+ const record = await versions.findOne({ formId: plan.formId, status: "draft" });
189
+ const currentDraftVersion = record?.version ?? state?.draftVersion ?? plan.draftToCreate?.version;
190
+ return currentDraftVersion === void 0 ? storageRevisionConflict(plan) : { success: false, error: { type: "draft_already_exists", currentDraftVersion } };
191
+ };
192
+ const mapCommitError = async (error, plan) => {
193
+ if (isTransactionUnsupported(error)) return { success: false, error: { type: "transaction_unsupported" } };
194
+ if (duplicateIndexName(error) === "unique_draft_per_form") return storageDraftAlreadyExists(plan);
195
+ if (isRevisionConflict(error)) return storageRevisionConflict(plan);
196
+ return { success: false, error: { type: "storage_error", cause: error } };
197
+ };
168
198
  const upsertVersionRecord = async (record, session) => {
169
199
  const stored = cloneJson(record);
170
200
  await versions.updateOne(
@@ -217,8 +247,8 @@ function createMongoDbStorage(options) {
217
247
  );
218
248
  }
219
249
  if (plan.draftToCreate !== void 0) await upsertVersionRecord(plan.draftToCreate, session);
220
- if (plan.publishedRecordToSave !== void 0) await upsertVersionRecord(plan.publishedRecordToSave, session);
221
250
  for (const record of plan.archivedRecordsToSave ?? []) await upsertVersionRecord(record, session);
251
+ if (plan.publishedRecordToSave !== void 0) await upsertVersionRecord(plan.publishedRecordToSave, session);
222
252
  for (const [eventIndex, event] of plan.events.entries()) {
223
253
  await versionEvents.updateOne(
224
254
  { _id: versionEventDocumentId(plan.formId, plan.expectedRevision, eventIndex) },
@@ -311,12 +341,16 @@ function createMongoDbStorage(options) {
311
341
  ...hasMore && last !== void 0 ? { nextCursor: (0, import_core.encodeSubmissionCursor)({ submittedAt: last.submittedAt, responseId: last.id }) } : {}
312
342
  };
313
343
  },
314
- async listTextAnswerPage(formId, fieldId, options2 = {}) {
315
- if (fieldId.trim().length === 0) throw new TypeError("fieldId must not be empty.");
344
+ async listTextAnswerPage(formId, fieldIdOrOptions, providedOptions) {
345
+ const options2 = typeof fieldIdOrOptions === "string" ? providedOptions ?? {} : fieldIdOrOptions ?? {};
346
+ const requestedFieldIds = typeof fieldIdOrOptions === "string" ? [fieldIdOrOptions] : options2.fieldIds === void 0 ? void 0 : [...new Set(options2.fieldIds)];
347
+ if (requestedFieldIds?.some((fieldId) => fieldId.trim().length === 0)) {
348
+ throw new TypeError("fieldIds must not contain empty values.");
349
+ }
316
350
  const pageSize = (0, import_core.normalizeSubmissionPageSize)(options2.pageSize);
317
351
  const cursor = options2.cursor === void 0 ? void 0 : (0, import_core.decodeTextAnswerCursor)(options2.cursor);
318
- if (cursor !== void 0 && cursor.fieldId !== fieldId) {
319
- throw new TypeError("Text answer cursor fieldId does not match the query.");
352
+ if (cursor !== void 0 && requestedFieldIds !== void 0 && !requestedFieldIds.includes(cursor.fieldId)) {
353
+ throw new TypeError("Text answer cursor fieldId does not match the query fields.");
320
354
  }
321
355
  const submittedAt = options2.since === void 0 && options2.until === void 0 ? void 0 : {
322
356
  ...options2.since === void 0 ? {} : { $gte: options2.since },
@@ -327,8 +361,8 @@ function createMongoDbStorage(options) {
327
361
  ...options2.version === void 0 ? {} : { formVersion: options2.version },
328
362
  ...options2.locale === void 0 ? {} : { "submission.locale": options2.locale },
329
363
  ...submittedAt === void 0 ? {} : { submittedAt },
330
- ...cursor === void 0 ? {} : { _id: { $gt: cursor.responseId } },
331
- [`submission.values.${fieldId}`]: { $type: "string" }
364
+ ...cursor === void 0 ? {} : { _id: { $gte: cursor.responseId } },
365
+ ...requestedFieldIds?.length === 1 ? { [`submission.values.${requestedFieldIds[0]}`]: { $type: "string" } } : {}
332
366
  };
333
367
  const serverFilters = [baseFilter];
334
368
  if (options2.filter !== void 0 && typeof options2.filter !== "function") {
@@ -339,22 +373,27 @@ function createMongoDbStorage(options) {
339
373
  }
340
374
  const filter = serverFilters.length === 1 ? baseFilter : { $and: serverFilters };
341
375
  const sorted = submissions.find(filter).sort({ _id: 1 });
342
- const documents = typeof options2.filter === "function" ? await sorted.toArray() : await sorted.limit(pageSize + 1).toArray();
376
+ const documents = await sorted.toArray();
343
377
  const candidates = documents.map(parseSubmissionDocument).filter((submission) => (0, import_core.matchesSubmissionPageFilters)(submission, options2)).flatMap((submission) => {
344
- const text = submission.values[fieldId];
345
- if (typeof text !== "string") return [];
346
- return [
347
- {
348
- responseId: submission.id,
349
- formId: submission.formId,
350
- formVersion: submission.formVersion,
351
- fieldId,
352
- text,
353
- locale: submission.locale,
354
- submittedAt: submission.submittedAt,
355
- ...submission.metadata === void 0 ? {} : { metadata: submission.metadata }
378
+ const entries = requestedFieldIds === void 0 ? Object.entries(submission.values) : requestedFieldIds.map((fieldId) => [fieldId, submission.values[fieldId]]);
379
+ return entries.flatMap(([fieldId, text]) => {
380
+ if (typeof text !== "string" || text.length === 0) return [];
381
+ if (cursor !== void 0 && (submission.id < cursor.responseId || submission.id === cursor.responseId && fieldId <= cursor.fieldId)) {
382
+ return [];
356
383
  }
357
- ];
384
+ return [
385
+ {
386
+ responseId: submission.id,
387
+ formId: submission.formId,
388
+ formVersion: submission.formVersion,
389
+ fieldId,
390
+ text,
391
+ locale: submission.locale,
392
+ submittedAt: submission.submittedAt,
393
+ ...submission.metadata === void 0 ? {} : { metadata: submission.metadata }
394
+ }
395
+ ];
396
+ });
358
397
  });
359
398
  const hasMore = candidates.length > pageSize;
360
399
  const items = candidates.slice(0, pageSize);
@@ -362,7 +401,7 @@ function createMongoDbStorage(options) {
362
401
  return {
363
402
  items,
364
403
  hasMore,
365
- ...hasMore && last !== void 0 ? { nextCursor: (0, import_core.encodeTextAnswerCursor)({ responseId: last.responseId, fieldId }) } : {}
404
+ ...hasMore && last !== void 0 ? { nextCursor: (0, import_core.encodeTextAnswerCursor)({ responseId: last.responseId, fieldId: last.fieldId }) } : {}
366
405
  };
367
406
  },
368
407
  async deleteSubmission(submissionId) {
@@ -413,16 +452,18 @@ function createMongoDbStorage(options) {
413
452
  }
414
453
  const client = options.db.client;
415
454
  if (client === void 0 || typeof client.startSession !== "function") {
416
- try {
417
- return await applyVersionTransition(plan);
418
- } catch (error) {
419
- if (isCasConflict(error)) {
420
- return storageRevisionConflict(plan);
421
- }
422
- return { success: false, error: { type: "storage_error", cause: error } };
423
- }
455
+ return { success: false, error: { type: "transaction_unsupported" } };
456
+ }
457
+ let session;
458
+ try {
459
+ session = client.startSession();
460
+ } catch (error) {
461
+ return isTransactionUnsupported(error) ? { success: false, error: { type: "transaction_unsupported" } } : { success: false, error: { type: "storage_error", cause: error } };
462
+ }
463
+ if (typeof session.withTransaction !== "function") {
464
+ await session.endSession();
465
+ return { success: false, error: { type: "transaction_unsupported" } };
424
466
  }
425
- const session = client.startSession();
426
467
  try {
427
468
  let result = {
428
469
  success: false,
@@ -433,10 +474,7 @@ function createMongoDbStorage(options) {
433
474
  });
434
475
  return result;
435
476
  } catch (error) {
436
- if (isCasConflict(error)) {
437
- return storageRevisionConflict(plan);
438
- }
439
- return { success: false, error: { type: "storage_error", cause: error } };
477
+ return mapCommitError(error, plan);
440
478
  } finally {
441
479
  await session.endSession();
442
480
  }
@@ -448,8 +486,19 @@ function createMongoDbStorage(options) {
448
486
  { key: { "submission.locale": 1 }, name: "form_responses_locale" }
449
487
  ]),
450
488
  versions.createIndexes([
451
- { key: { formId: 1, version: 1 }, name: "form_versions_form_version", unique: true },
452
- { key: { formId: 1, status: 1 }, name: "form_versions_form_status" }
489
+ { key: { formId: 1, version: 1 }, name: "unique_version_per_form", unique: true },
490
+ {
491
+ key: { formId: 1 },
492
+ name: "unique_draft_per_form",
493
+ unique: true,
494
+ partialFilterExpression: { status: "draft" }
495
+ },
496
+ {
497
+ key: { formId: 1 },
498
+ name: "unique_published_per_form",
499
+ unique: true,
500
+ partialFilterExpression: { status: "published" }
501
+ }
453
502
  ]),
454
503
  versionEvents.createIndexes([
455
504
  { key: { formId: 1, fromRevision: 1, eventIndex: 1 }, name: "form_version_events_revision" }
package/dist/index.js CHANGED
@@ -85,11 +85,29 @@ function parseVersionRecordDocument(document) {
85
85
  }
86
86
  return cloneJson(document.record);
87
87
  }
88
- function isCasConflict(error) {
88
+ function mongoErrorMessage(error) {
89
+ return error instanceof Error ? error.message : isRecord(error) ? String(error.message ?? "") : "";
90
+ }
91
+ function isDuplicateKeyError(error) {
92
+ return isRecord(error) && error.code === 11e3;
93
+ }
94
+ function duplicateIndexName(error) {
95
+ if (!isDuplicateKeyError(error)) return void 0;
96
+ return /unique_(?:draft|published|version)_per_form/u.exec(mongoErrorMessage(error))?.[0];
97
+ }
98
+ function isRevisionConflict(error) {
89
99
  if (!isRecord(error)) return false;
90
- if (error.code === 11e3 || error.code === 112 || error.code === 251) return true;
100
+ if (error.code === 11e3) return duplicateIndexName(error) !== "unique_draft_per_form";
101
+ if (error.code === 112 || error.code === 251) return true;
91
102
  return typeof error.hasErrorLabel === "function" && error.hasErrorLabel("TransientTransactionError") === true;
92
103
  }
104
+ function isTransactionUnsupported(error) {
105
+ if (!isRecord(error)) return false;
106
+ if (error.code === 20 || error.code === 263 || error.code === 303) return true;
107
+ return /transaction numbers are only allowed|transactions? (?:are|is) not supported|replica set member|mongos/iu.test(
108
+ mongoErrorMessage(error)
109
+ );
110
+ }
93
111
  function parseSchemaDocument(document) {
94
112
  try {
95
113
  assertValidFormSchema(document.schema);
@@ -148,6 +166,18 @@ function createMongoDbStorage(options) {
148
166
  }
149
167
  };
150
168
  };
169
+ const storageDraftAlreadyExists = async (plan) => {
170
+ const state = await versionStates.findOne({ _id: plan.formId });
171
+ const record = await versions.findOne({ formId: plan.formId, status: "draft" });
172
+ const currentDraftVersion = record?.version ?? state?.draftVersion ?? plan.draftToCreate?.version;
173
+ return currentDraftVersion === void 0 ? storageRevisionConflict(plan) : { success: false, error: { type: "draft_already_exists", currentDraftVersion } };
174
+ };
175
+ const mapCommitError = async (error, plan) => {
176
+ if (isTransactionUnsupported(error)) return { success: false, error: { type: "transaction_unsupported" } };
177
+ if (duplicateIndexName(error) === "unique_draft_per_form") return storageDraftAlreadyExists(plan);
178
+ if (isRevisionConflict(error)) return storageRevisionConflict(plan);
179
+ return { success: false, error: { type: "storage_error", cause: error } };
180
+ };
151
181
  const upsertVersionRecord = async (record, session) => {
152
182
  const stored = cloneJson(record);
153
183
  await versions.updateOne(
@@ -200,8 +230,8 @@ function createMongoDbStorage(options) {
200
230
  );
201
231
  }
202
232
  if (plan.draftToCreate !== void 0) await upsertVersionRecord(plan.draftToCreate, session);
203
- if (plan.publishedRecordToSave !== void 0) await upsertVersionRecord(plan.publishedRecordToSave, session);
204
233
  for (const record of plan.archivedRecordsToSave ?? []) await upsertVersionRecord(record, session);
234
+ if (plan.publishedRecordToSave !== void 0) await upsertVersionRecord(plan.publishedRecordToSave, session);
205
235
  for (const [eventIndex, event] of plan.events.entries()) {
206
236
  await versionEvents.updateOne(
207
237
  { _id: versionEventDocumentId(plan.formId, plan.expectedRevision, eventIndex) },
@@ -294,12 +324,16 @@ function createMongoDbStorage(options) {
294
324
  ...hasMore && last !== void 0 ? { nextCursor: encodeSubmissionCursor({ submittedAt: last.submittedAt, responseId: last.id }) } : {}
295
325
  };
296
326
  },
297
- async listTextAnswerPage(formId, fieldId, options2 = {}) {
298
- if (fieldId.trim().length === 0) throw new TypeError("fieldId must not be empty.");
327
+ async listTextAnswerPage(formId, fieldIdOrOptions, providedOptions) {
328
+ const options2 = typeof fieldIdOrOptions === "string" ? providedOptions ?? {} : fieldIdOrOptions ?? {};
329
+ const requestedFieldIds = typeof fieldIdOrOptions === "string" ? [fieldIdOrOptions] : options2.fieldIds === void 0 ? void 0 : [...new Set(options2.fieldIds)];
330
+ if (requestedFieldIds?.some((fieldId) => fieldId.trim().length === 0)) {
331
+ throw new TypeError("fieldIds must not contain empty values.");
332
+ }
299
333
  const pageSize = normalizeSubmissionPageSize(options2.pageSize);
300
334
  const cursor = options2.cursor === void 0 ? void 0 : decodeTextAnswerCursor(options2.cursor);
301
- if (cursor !== void 0 && cursor.fieldId !== fieldId) {
302
- throw new TypeError("Text answer cursor fieldId does not match the query.");
335
+ if (cursor !== void 0 && requestedFieldIds !== void 0 && !requestedFieldIds.includes(cursor.fieldId)) {
336
+ throw new TypeError("Text answer cursor fieldId does not match the query fields.");
303
337
  }
304
338
  const submittedAt = options2.since === void 0 && options2.until === void 0 ? void 0 : {
305
339
  ...options2.since === void 0 ? {} : { $gte: options2.since },
@@ -310,8 +344,8 @@ function createMongoDbStorage(options) {
310
344
  ...options2.version === void 0 ? {} : { formVersion: options2.version },
311
345
  ...options2.locale === void 0 ? {} : { "submission.locale": options2.locale },
312
346
  ...submittedAt === void 0 ? {} : { submittedAt },
313
- ...cursor === void 0 ? {} : { _id: { $gt: cursor.responseId } },
314
- [`submission.values.${fieldId}`]: { $type: "string" }
347
+ ...cursor === void 0 ? {} : { _id: { $gte: cursor.responseId } },
348
+ ...requestedFieldIds?.length === 1 ? { [`submission.values.${requestedFieldIds[0]}`]: { $type: "string" } } : {}
315
349
  };
316
350
  const serverFilters = [baseFilter];
317
351
  if (options2.filter !== void 0 && typeof options2.filter !== "function") {
@@ -322,22 +356,27 @@ function createMongoDbStorage(options) {
322
356
  }
323
357
  const filter = serverFilters.length === 1 ? baseFilter : { $and: serverFilters };
324
358
  const sorted = submissions.find(filter).sort({ _id: 1 });
325
- const documents = typeof options2.filter === "function" ? await sorted.toArray() : await sorted.limit(pageSize + 1).toArray();
359
+ const documents = await sorted.toArray();
326
360
  const candidates = documents.map(parseSubmissionDocument).filter((submission) => matchesSubmissionPageFilters(submission, options2)).flatMap((submission) => {
327
- const text = submission.values[fieldId];
328
- if (typeof text !== "string") return [];
329
- return [
330
- {
331
- responseId: submission.id,
332
- formId: submission.formId,
333
- formVersion: submission.formVersion,
334
- fieldId,
335
- text,
336
- locale: submission.locale,
337
- submittedAt: submission.submittedAt,
338
- ...submission.metadata === void 0 ? {} : { metadata: submission.metadata }
361
+ const entries = requestedFieldIds === void 0 ? Object.entries(submission.values) : requestedFieldIds.map((fieldId) => [fieldId, submission.values[fieldId]]);
362
+ return entries.flatMap(([fieldId, text]) => {
363
+ if (typeof text !== "string" || text.length === 0) return [];
364
+ if (cursor !== void 0 && (submission.id < cursor.responseId || submission.id === cursor.responseId && fieldId <= cursor.fieldId)) {
365
+ return [];
339
366
  }
340
- ];
367
+ return [
368
+ {
369
+ responseId: submission.id,
370
+ formId: submission.formId,
371
+ formVersion: submission.formVersion,
372
+ fieldId,
373
+ text,
374
+ locale: submission.locale,
375
+ submittedAt: submission.submittedAt,
376
+ ...submission.metadata === void 0 ? {} : { metadata: submission.metadata }
377
+ }
378
+ ];
379
+ });
341
380
  });
342
381
  const hasMore = candidates.length > pageSize;
343
382
  const items = candidates.slice(0, pageSize);
@@ -345,7 +384,7 @@ function createMongoDbStorage(options) {
345
384
  return {
346
385
  items,
347
386
  hasMore,
348
- ...hasMore && last !== void 0 ? { nextCursor: encodeTextAnswerCursor({ responseId: last.responseId, fieldId }) } : {}
387
+ ...hasMore && last !== void 0 ? { nextCursor: encodeTextAnswerCursor({ responseId: last.responseId, fieldId: last.fieldId }) } : {}
349
388
  };
350
389
  },
351
390
  async deleteSubmission(submissionId) {
@@ -396,16 +435,18 @@ function createMongoDbStorage(options) {
396
435
  }
397
436
  const client = options.db.client;
398
437
  if (client === void 0 || typeof client.startSession !== "function") {
399
- try {
400
- return await applyVersionTransition(plan);
401
- } catch (error) {
402
- if (isCasConflict(error)) {
403
- return storageRevisionConflict(plan);
404
- }
405
- return { success: false, error: { type: "storage_error", cause: error } };
406
- }
438
+ return { success: false, error: { type: "transaction_unsupported" } };
439
+ }
440
+ let session;
441
+ try {
442
+ session = client.startSession();
443
+ } catch (error) {
444
+ return isTransactionUnsupported(error) ? { success: false, error: { type: "transaction_unsupported" } } : { success: false, error: { type: "storage_error", cause: error } };
445
+ }
446
+ if (typeof session.withTransaction !== "function") {
447
+ await session.endSession();
448
+ return { success: false, error: { type: "transaction_unsupported" } };
407
449
  }
408
- const session = client.startSession();
409
450
  try {
410
451
  let result = {
411
452
  success: false,
@@ -416,10 +457,7 @@ function createMongoDbStorage(options) {
416
457
  });
417
458
  return result;
418
459
  } catch (error) {
419
- if (isCasConflict(error)) {
420
- return storageRevisionConflict(plan);
421
- }
422
- return { success: false, error: { type: "storage_error", cause: error } };
460
+ return mapCommitError(error, plan);
423
461
  } finally {
424
462
  await session.endSession();
425
463
  }
@@ -431,8 +469,19 @@ function createMongoDbStorage(options) {
431
469
  { key: { "submission.locale": 1 }, name: "form_responses_locale" }
432
470
  ]),
433
471
  versions.createIndexes([
434
- { key: { formId: 1, version: 1 }, name: "form_versions_form_version", unique: true },
435
- { key: { formId: 1, status: 1 }, name: "form_versions_form_status" }
472
+ { key: { formId: 1, version: 1 }, name: "unique_version_per_form", unique: true },
473
+ {
474
+ key: { formId: 1 },
475
+ name: "unique_draft_per_form",
476
+ unique: true,
477
+ partialFilterExpression: { status: "draft" }
478
+ },
479
+ {
480
+ key: { formId: 1 },
481
+ name: "unique_published_per_form",
482
+ unique: true,
483
+ partialFilterExpression: { status: "published" }
484
+ }
436
485
  ]),
437
486
  versionEvents.createIndexes([
438
487
  { key: { formId: 1, fromRevision: 1, eventIndex: 1 }, name: "form_version_events_revision" }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@form-engine-ts/storage-mongodb",
3
- "version": "2.7.0",
3
+ "version": "2.9.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -39,7 +39,7 @@
39
39
  "typescript"
40
40
  ],
41
41
  "dependencies": {
42
- "@form-engine-ts/core": "2.7.0"
42
+ "@form-engine-ts/core": "2.9.0"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "mongodb": "^6.0.0"