@form-engine-ts/storage-mongodb 4.0.0 → 4.2.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 +5 -1
- package/dist/index.cjs +69 -27
- package/dist/index.d.cts +17 -2
- package/dist/index.d.ts +17 -2
- package/dist/index.js +69 -27
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -25,7 +25,11 @@ const submissions = await storage.listSubmissions(schema.id, schema.version, {
|
|
|
25
25
|
});
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
The caller owns the MongoDB connection lifecycle. Collection names can be customized
|
|
28
|
+
The caller owns the MongoDB connection lifecycle. Collection names can be customized with the legacy flat options or
|
|
29
|
+
with `collectionNames`. `customIndexes` adds application-owned indexes to the configured forms, versions, or response
|
|
30
|
+
collections; `createIndexes()` creates the built-in indexes first and then the custom definitions. Range boundaries are
|
|
31
|
+
inclusive, and MongoDB sorts by submission time and then ID. `clearResponses(formId)` preserves schemas and other forms;
|
|
32
|
+
`clear()` removes documents only from the configured collections and does not drop collections or indexes.
|
|
29
33
|
|
|
30
34
|
`listSubmissionPage(formId, { version, pageSize, cursor, since, until, locale })` performs bounded reads using the
|
|
31
35
|
compound `submittedAt`/response-ID cursor. Run `createIndexes()` after upgrading to create the matching compound index.
|
package/dist/index.cjs
CHANGED
|
@@ -148,23 +148,56 @@ function collectionName(value, fallback, optionName) {
|
|
|
148
148
|
if (value.trim().length === 0) throw new TypeError(`${optionName} must be a non-empty string.`);
|
|
149
149
|
return value;
|
|
150
150
|
}
|
|
151
|
+
function normalizeIndexSpecification(spec) {
|
|
152
|
+
if (typeof spec === "string") return { [spec]: 1 };
|
|
153
|
+
if (Array.isArray(spec)) return Object.fromEntries(spec.map((field) => [field, 1]));
|
|
154
|
+
if (spec instanceof Map) return spec;
|
|
155
|
+
if (!isRecord(spec) || Object.keys(spec).length === 0) throw new TypeError("Index specification must not be empty.");
|
|
156
|
+
return spec;
|
|
157
|
+
}
|
|
158
|
+
function customIndexDescriptions(definitions) {
|
|
159
|
+
return (definitions ?? []).map(({ spec, options: indexOptions }) => ({
|
|
160
|
+
...indexOptions ?? {},
|
|
161
|
+
key: normalizeIndexSpecification(spec)
|
|
162
|
+
}));
|
|
163
|
+
}
|
|
164
|
+
async function createConfiguredIndexes(collection, defaults, custom) {
|
|
165
|
+
const definitions = custom ?? [];
|
|
166
|
+
if (definitions.length === 0) {
|
|
167
|
+
await collection.createIndexes(defaults);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
if (collection.createIndex === void 0) {
|
|
171
|
+
await collection.createIndexes([...defaults, ...customIndexDescriptions(definitions)]);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
await collection.createIndexes(defaults);
|
|
175
|
+
for (const { spec, options: indexOptions } of definitions) {
|
|
176
|
+
await collection.createIndex(normalizeIndexSpecification(spec), indexOptions);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
151
179
|
function createMongoDbStorage(options) {
|
|
152
180
|
if (options?.db === void 0) throw new TypeError("db is required.");
|
|
153
|
-
const
|
|
181
|
+
const collectionNames = options.collectionNames;
|
|
182
|
+
const schemasCollectionName = collectionName(
|
|
183
|
+
collectionNames?.forms ?? options.schemasCollectionName,
|
|
184
|
+
collectionNames === void 0 ? "form_schemas" : "forms",
|
|
185
|
+
collectionNames?.forms === void 0 ? "schemasCollectionName" : "collectionNames.forms"
|
|
186
|
+
);
|
|
154
187
|
const responsesCollectionName = collectionName(
|
|
155
|
-
options.responsesCollectionName,
|
|
188
|
+
collectionNames?.formResponses ?? options.responsesCollectionName,
|
|
156
189
|
"form_responses",
|
|
157
|
-
"responsesCollectionName"
|
|
190
|
+
collectionNames?.formResponses === void 0 ? "responsesCollectionName" : "collectionNames.formResponses"
|
|
158
191
|
);
|
|
159
192
|
const versionsCollectionName = collectionName(
|
|
160
|
-
options.versionsCollectionName,
|
|
193
|
+
collectionNames?.formVersions ?? options.versionsCollectionName,
|
|
161
194
|
"form_versions",
|
|
162
|
-
"versionsCollectionName"
|
|
195
|
+
collectionNames?.formVersions === void 0 ? "versionsCollectionName" : "collectionNames.formVersions"
|
|
163
196
|
);
|
|
164
197
|
const versionStatesCollectionName = collectionName(
|
|
165
|
-
options.versionStatesCollectionName,
|
|
198
|
+
collectionNames?.formVersionStates ?? options.versionStatesCollectionName,
|
|
166
199
|
"form_version_states",
|
|
167
|
-
"versionStatesCollectionName"
|
|
200
|
+
collectionNames?.formVersionStates === void 0 ? "versionStatesCollectionName" : "collectionNames.formVersionStates"
|
|
168
201
|
);
|
|
169
202
|
const versionEventsCollectionName = "form_version_events";
|
|
170
203
|
const schemas = options.db.collection(schemasCollectionName);
|
|
@@ -481,28 +514,37 @@ function createMongoDbStorage(options) {
|
|
|
481
514
|
},
|
|
482
515
|
async createIndexes() {
|
|
483
516
|
await Promise.all([
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
517
|
+
createConfiguredIndexes(
|
|
518
|
+
submissions,
|
|
519
|
+
[
|
|
520
|
+
{ key: { formId: 1, submittedAt: 1, _id: 1 }, name: "form_responses_form_submitted_at_id" },
|
|
521
|
+
{ key: { "submission.locale": 1 }, name: "form_responses_locale" }
|
|
522
|
+
],
|
|
523
|
+
options.customIndexes?.formResponses
|
|
524
|
+
),
|
|
525
|
+
createConfiguredIndexes(
|
|
526
|
+
versions,
|
|
527
|
+
[
|
|
528
|
+
{ key: { formId: 1, version: 1 }, name: "unique_version_per_form", unique: true },
|
|
529
|
+
{
|
|
530
|
+
key: { formId: 1 },
|
|
531
|
+
name: "unique_draft_per_form",
|
|
532
|
+
unique: true,
|
|
533
|
+
partialFilterExpression: { status: "draft" }
|
|
534
|
+
},
|
|
535
|
+
{
|
|
536
|
+
key: { formId: 1 },
|
|
537
|
+
name: "unique_published_per_form",
|
|
538
|
+
unique: true,
|
|
539
|
+
partialFilterExpression: { status: "published" }
|
|
540
|
+
}
|
|
541
|
+
],
|
|
542
|
+
options.customIndexes?.formVersions
|
|
543
|
+
),
|
|
503
544
|
versionEvents.createIndexes([
|
|
504
545
|
{ key: { formId: 1, fromRevision: 1, eventIndex: 1 }, name: "form_version_events_revision" }
|
|
505
|
-
])
|
|
546
|
+
]),
|
|
547
|
+
createConfiguredIndexes(schemas, [], options.customIndexes?.forms)
|
|
506
548
|
]);
|
|
507
549
|
}
|
|
508
550
|
};
|
package/dist/index.d.cts
CHANGED
|
@@ -1,12 +1,27 @@
|
|
|
1
1
|
import { PagedSubmissionStorageAdapter, VersionedFormStorageAdapter, SubmissionFilter } from '@form-engine-ts/core';
|
|
2
|
-
import { Db, Document } from 'mongodb';
|
|
2
|
+
import { IndexSpecification, CreateIndexesOptions, Db, Document } from 'mongodb';
|
|
3
3
|
|
|
4
|
+
interface MongoCustomIndexDefinition {
|
|
5
|
+
readonly spec: IndexSpecification;
|
|
6
|
+
readonly options?: CreateIndexesOptions;
|
|
7
|
+
}
|
|
4
8
|
interface MongoDbStorageOptions {
|
|
5
9
|
readonly db: Db;
|
|
6
10
|
readonly schemasCollectionName?: string;
|
|
7
11
|
readonly responsesCollectionName?: string;
|
|
8
12
|
readonly versionsCollectionName?: string;
|
|
9
13
|
readonly versionStatesCollectionName?: string;
|
|
14
|
+
readonly collectionNames?: {
|
|
15
|
+
readonly forms?: string;
|
|
16
|
+
readonly formVersions?: string;
|
|
17
|
+
readonly formVersionStates?: string;
|
|
18
|
+
readonly formResponses?: string;
|
|
19
|
+
};
|
|
20
|
+
readonly customIndexes?: {
|
|
21
|
+
readonly forms?: readonly MongoCustomIndexDefinition[];
|
|
22
|
+
readonly formVersions?: readonly MongoCustomIndexDefinition[];
|
|
23
|
+
readonly formResponses?: readonly MongoCustomIndexDefinition[];
|
|
24
|
+
};
|
|
10
25
|
}
|
|
11
26
|
interface MongoDbStorageAdapter extends PagedSubmissionStorageAdapter, VersionedFormStorageAdapter {
|
|
12
27
|
createIndexes(): Promise<void>;
|
|
@@ -14,4 +29,4 @@ interface MongoDbStorageAdapter extends PagedSubmissionStorageAdapter, Versioned
|
|
|
14
29
|
declare function submissionFilterToMongo(filter: SubmissionFilter): Document;
|
|
15
30
|
declare function createMongoDbStorage(options: MongoDbStorageOptions): MongoDbStorageAdapter;
|
|
16
31
|
|
|
17
|
-
export { type MongoDbStorageAdapter, type MongoDbStorageOptions, createMongoDbStorage, submissionFilterToMongo };
|
|
32
|
+
export { type MongoCustomIndexDefinition, type MongoDbStorageAdapter, type MongoDbStorageOptions, createMongoDbStorage, submissionFilterToMongo };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,27 @@
|
|
|
1
1
|
import { PagedSubmissionStorageAdapter, VersionedFormStorageAdapter, SubmissionFilter } from '@form-engine-ts/core';
|
|
2
|
-
import { Db, Document } from 'mongodb';
|
|
2
|
+
import { IndexSpecification, CreateIndexesOptions, Db, Document } from 'mongodb';
|
|
3
3
|
|
|
4
|
+
interface MongoCustomIndexDefinition {
|
|
5
|
+
readonly spec: IndexSpecification;
|
|
6
|
+
readonly options?: CreateIndexesOptions;
|
|
7
|
+
}
|
|
4
8
|
interface MongoDbStorageOptions {
|
|
5
9
|
readonly db: Db;
|
|
6
10
|
readonly schemasCollectionName?: string;
|
|
7
11
|
readonly responsesCollectionName?: string;
|
|
8
12
|
readonly versionsCollectionName?: string;
|
|
9
13
|
readonly versionStatesCollectionName?: string;
|
|
14
|
+
readonly collectionNames?: {
|
|
15
|
+
readonly forms?: string;
|
|
16
|
+
readonly formVersions?: string;
|
|
17
|
+
readonly formVersionStates?: string;
|
|
18
|
+
readonly formResponses?: string;
|
|
19
|
+
};
|
|
20
|
+
readonly customIndexes?: {
|
|
21
|
+
readonly forms?: readonly MongoCustomIndexDefinition[];
|
|
22
|
+
readonly formVersions?: readonly MongoCustomIndexDefinition[];
|
|
23
|
+
readonly formResponses?: readonly MongoCustomIndexDefinition[];
|
|
24
|
+
};
|
|
10
25
|
}
|
|
11
26
|
interface MongoDbStorageAdapter extends PagedSubmissionStorageAdapter, VersionedFormStorageAdapter {
|
|
12
27
|
createIndexes(): Promise<void>;
|
|
@@ -14,4 +29,4 @@ interface MongoDbStorageAdapter extends PagedSubmissionStorageAdapter, Versioned
|
|
|
14
29
|
declare function submissionFilterToMongo(filter: SubmissionFilter): Document;
|
|
15
30
|
declare function createMongoDbStorage(options: MongoDbStorageOptions): MongoDbStorageAdapter;
|
|
16
31
|
|
|
17
|
-
export { type MongoDbStorageAdapter, type MongoDbStorageOptions, createMongoDbStorage, submissionFilterToMongo };
|
|
32
|
+
export { type MongoCustomIndexDefinition, type MongoDbStorageAdapter, type MongoDbStorageOptions, createMongoDbStorage, submissionFilterToMongo };
|
package/dist/index.js
CHANGED
|
@@ -131,23 +131,56 @@ function collectionName(value, fallback, optionName) {
|
|
|
131
131
|
if (value.trim().length === 0) throw new TypeError(`${optionName} must be a non-empty string.`);
|
|
132
132
|
return value;
|
|
133
133
|
}
|
|
134
|
+
function normalizeIndexSpecification(spec) {
|
|
135
|
+
if (typeof spec === "string") return { [spec]: 1 };
|
|
136
|
+
if (Array.isArray(spec)) return Object.fromEntries(spec.map((field) => [field, 1]));
|
|
137
|
+
if (spec instanceof Map) return spec;
|
|
138
|
+
if (!isRecord(spec) || Object.keys(spec).length === 0) throw new TypeError("Index specification must not be empty.");
|
|
139
|
+
return spec;
|
|
140
|
+
}
|
|
141
|
+
function customIndexDescriptions(definitions) {
|
|
142
|
+
return (definitions ?? []).map(({ spec, options: indexOptions }) => ({
|
|
143
|
+
...indexOptions ?? {},
|
|
144
|
+
key: normalizeIndexSpecification(spec)
|
|
145
|
+
}));
|
|
146
|
+
}
|
|
147
|
+
async function createConfiguredIndexes(collection, defaults, custom) {
|
|
148
|
+
const definitions = custom ?? [];
|
|
149
|
+
if (definitions.length === 0) {
|
|
150
|
+
await collection.createIndexes(defaults);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
if (collection.createIndex === void 0) {
|
|
154
|
+
await collection.createIndexes([...defaults, ...customIndexDescriptions(definitions)]);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
await collection.createIndexes(defaults);
|
|
158
|
+
for (const { spec, options: indexOptions } of definitions) {
|
|
159
|
+
await collection.createIndex(normalizeIndexSpecification(spec), indexOptions);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
134
162
|
function createMongoDbStorage(options) {
|
|
135
163
|
if (options?.db === void 0) throw new TypeError("db is required.");
|
|
136
|
-
const
|
|
164
|
+
const collectionNames = options.collectionNames;
|
|
165
|
+
const schemasCollectionName = collectionName(
|
|
166
|
+
collectionNames?.forms ?? options.schemasCollectionName,
|
|
167
|
+
collectionNames === void 0 ? "form_schemas" : "forms",
|
|
168
|
+
collectionNames?.forms === void 0 ? "schemasCollectionName" : "collectionNames.forms"
|
|
169
|
+
);
|
|
137
170
|
const responsesCollectionName = collectionName(
|
|
138
|
-
options.responsesCollectionName,
|
|
171
|
+
collectionNames?.formResponses ?? options.responsesCollectionName,
|
|
139
172
|
"form_responses",
|
|
140
|
-
"responsesCollectionName"
|
|
173
|
+
collectionNames?.formResponses === void 0 ? "responsesCollectionName" : "collectionNames.formResponses"
|
|
141
174
|
);
|
|
142
175
|
const versionsCollectionName = collectionName(
|
|
143
|
-
options.versionsCollectionName,
|
|
176
|
+
collectionNames?.formVersions ?? options.versionsCollectionName,
|
|
144
177
|
"form_versions",
|
|
145
|
-
"versionsCollectionName"
|
|
178
|
+
collectionNames?.formVersions === void 0 ? "versionsCollectionName" : "collectionNames.formVersions"
|
|
146
179
|
);
|
|
147
180
|
const versionStatesCollectionName = collectionName(
|
|
148
|
-
options.versionStatesCollectionName,
|
|
181
|
+
collectionNames?.formVersionStates ?? options.versionStatesCollectionName,
|
|
149
182
|
"form_version_states",
|
|
150
|
-
"versionStatesCollectionName"
|
|
183
|
+
collectionNames?.formVersionStates === void 0 ? "versionStatesCollectionName" : "collectionNames.formVersionStates"
|
|
151
184
|
);
|
|
152
185
|
const versionEventsCollectionName = "form_version_events";
|
|
153
186
|
const schemas = options.db.collection(schemasCollectionName);
|
|
@@ -464,28 +497,37 @@ function createMongoDbStorage(options) {
|
|
|
464
497
|
},
|
|
465
498
|
async createIndexes() {
|
|
466
499
|
await Promise.all([
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
500
|
+
createConfiguredIndexes(
|
|
501
|
+
submissions,
|
|
502
|
+
[
|
|
503
|
+
{ key: { formId: 1, submittedAt: 1, _id: 1 }, name: "form_responses_form_submitted_at_id" },
|
|
504
|
+
{ key: { "submission.locale": 1 }, name: "form_responses_locale" }
|
|
505
|
+
],
|
|
506
|
+
options.customIndexes?.formResponses
|
|
507
|
+
),
|
|
508
|
+
createConfiguredIndexes(
|
|
509
|
+
versions,
|
|
510
|
+
[
|
|
511
|
+
{ key: { formId: 1, version: 1 }, name: "unique_version_per_form", unique: true },
|
|
512
|
+
{
|
|
513
|
+
key: { formId: 1 },
|
|
514
|
+
name: "unique_draft_per_form",
|
|
515
|
+
unique: true,
|
|
516
|
+
partialFilterExpression: { status: "draft" }
|
|
517
|
+
},
|
|
518
|
+
{
|
|
519
|
+
key: { formId: 1 },
|
|
520
|
+
name: "unique_published_per_form",
|
|
521
|
+
unique: true,
|
|
522
|
+
partialFilterExpression: { status: "published" }
|
|
523
|
+
}
|
|
524
|
+
],
|
|
525
|
+
options.customIndexes?.formVersions
|
|
526
|
+
),
|
|
486
527
|
versionEvents.createIndexes([
|
|
487
528
|
{ key: { formId: 1, fromRevision: 1, eventIndex: 1 }, name: "form_version_events_revision" }
|
|
488
|
-
])
|
|
529
|
+
]),
|
|
530
|
+
createConfiguredIndexes(schemas, [], options.customIndexes?.forms)
|
|
489
531
|
]);
|
|
490
532
|
}
|
|
491
533
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@form-engine-ts/storage-mongodb",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.2.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": "4.
|
|
42
|
+
"@form-engine-ts/core": "4.2.0"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
45
|
"mongodb": "^6.0.0"
|