@form-engine-ts/core 2.5.1 → 2.6.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 +6 -4
- package/dist/index.cjs +60 -11
- package/dist/index.d.cts +21 -10
- package/dist/index.d.ts +21 -10
- package/dist/index.js +59 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -76,17 +76,19 @@ Results are ordered by `submittedAt`, then submission ID. Both boundaries are in
|
|
|
76
76
|
|
|
77
77
|
## Versioning, incremental analytics, and paged storage
|
|
78
78
|
|
|
79
|
-
`cloneVersionToDraft`, `publishDraft`, and `deleteDraft` implement revision-checked version transitions as
|
|
79
|
+
`cloneVersionToDraft`, asynchronous `publishDraft`, and `deleteDraft` implement revision-checked version transitions as
|
|
80
|
+
pure functions.
|
|
80
81
|
Clone/delete operations accept `expectedRevision`; cloning rejects non-published sources, publish validation failures are
|
|
81
|
-
returned as typed `validation_failed` issues, and successful publishing
|
|
82
|
-
|
|
82
|
+
returned as typed `validation_failed` issues, and successful publishing archives only a supplied actual published record,
|
|
83
|
+
preserving its schema and metadata. `createPublishTransitionPlan` returns complete records plus expected/next revisions for
|
|
84
|
+
storage adapters implementing `VersionedFormStorageAdapter` to commit atomically.
|
|
83
85
|
`createResponseAccumulator` incrementally counts choices, answered/unanswered values, and numeric summaries without retaining
|
|
84
86
|
free-text bodies. In lenient mode, mismatched responses are skipped and exposed by `addMany()` and `getReport()` instead of
|
|
85
87
|
being included silently. Independent accumulators for the same schema can be merged, and `finalize()` matches
|
|
86
88
|
`aggregateResponses`.
|
|
87
89
|
|
|
88
90
|
`exportResponsesToCsvStream` accepts an `AsyncIterable`, emits the BOM/header and one chunk per response, and supports
|
|
89
|
-
custom `CsvColumnDef` columns. Custom getters receive the submission, form version, and schema. Use
|
|
91
|
+
custom `CsvColumnDef` columns. Custom getters may be asynchronous and receive the submission, form version, and schema. Use
|
|
90
92
|
`pipeResponsesToCsvStream` to write to a Web `WritableStream` or Node-compatible writable while honoring backpressure.
|
|
91
93
|
Formula-injection neutralization applies to both default and custom columns.
|
|
92
94
|
|
package/dist/index.cjs
CHANGED
|
@@ -30,6 +30,7 @@ __export(index_exports, {
|
|
|
30
30
|
calculatePageVisibility: () => calculatePageVisibility,
|
|
31
31
|
cloneVersionToDraft: () => cloneVersionToDraft,
|
|
32
32
|
collectSchemaLocales: () => collectSchemaLocales,
|
|
33
|
+
createPublishTransitionPlan: () => createPublishTransitionPlan,
|
|
33
34
|
createResponseAccumulator: () => createResponseAccumulator,
|
|
34
35
|
createSubmission: () => createSubmission,
|
|
35
36
|
decodeSubmissionCursor: () => decodeSubmissionCursor,
|
|
@@ -1346,7 +1347,7 @@ async function* exportResponsesToCsvStream(schema, submissions, options = {}) {
|
|
|
1346
1347
|
formVersion: response.formVersion ?? schema.version,
|
|
1347
1348
|
schema
|
|
1348
1349
|
};
|
|
1349
|
-
const customCells = customColumns.map((column) => column.getValue(context));
|
|
1350
|
+
const customCells = await Promise.all(customColumns.map((column) => column.getValue(context)));
|
|
1350
1351
|
yield `\r
|
|
1351
1352
|
${[...defaultCells, ...customCells].map((value) => escapeCsvCell(value, neutralizeFormulas)).join(",")}`;
|
|
1352
1353
|
}
|
|
@@ -2091,31 +2092,39 @@ function cloneVersionToDraft(state, sourceSchema, options = {}) {
|
|
|
2091
2092
|
}
|
|
2092
2093
|
};
|
|
2093
2094
|
}
|
|
2094
|
-
function
|
|
2095
|
+
function validatePublishedRecord(state, record) {
|
|
2096
|
+
if (record === void 0) return;
|
|
2097
|
+
if (record.formId !== state.formId || record.version !== state.publishedVersion || record.status !== "published" || record.schema.id !== record.formId || record.schema.version !== record.version) {
|
|
2098
|
+
throw new TypeError("currentPublishedRecord must match the state's published version.");
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
function transitionTimestamp(options) {
|
|
2102
|
+
const timestamp = options.publishedAt ?? options.timestamp ?? "1970-01-01T00:00:00.000Z";
|
|
2103
|
+
if (!Number.isFinite(Date.parse(timestamp))) throw new TypeError("publishedAt must be a valid date string.");
|
|
2104
|
+
return timestamp;
|
|
2105
|
+
}
|
|
2106
|
+
async function publishDraft(state, draftSchema, options = {}) {
|
|
2095
2107
|
validateState(state);
|
|
2096
2108
|
const conflict = revisionConflict(state, options.expectedRevision);
|
|
2097
2109
|
if (conflict !== void 0) return conflict;
|
|
2098
2110
|
if (state.draftVersion === void 0 || draftSchema.id !== state.formId || draftSchema.version !== state.draftVersion) {
|
|
2099
2111
|
return { success: false, error: { type: "draft_not_found" } };
|
|
2100
2112
|
}
|
|
2101
|
-
|
|
2113
|
+
validatePublishedRecord(state, options.currentPublishedRecord);
|
|
2114
|
+
const validation = await options.validate?.(draftSchema);
|
|
2102
2115
|
if (validation === false || Array.isArray(validation) && validation.length > 0) {
|
|
2103
2116
|
return {
|
|
2104
2117
|
success: false,
|
|
2105
2118
|
error: { type: "validation_failed", issues: Array.isArray(validation) ? validation : [] }
|
|
2106
2119
|
};
|
|
2107
2120
|
}
|
|
2108
|
-
const timestamp = options
|
|
2109
|
-
if (!Number.isFinite(Date.parse(timestamp))) throw new TypeError("timestamp must be a valid date string.");
|
|
2121
|
+
const timestamp = transitionTimestamp(options);
|
|
2110
2122
|
const archivedVersion = state.publishedVersion;
|
|
2111
|
-
const archivedRecords =
|
|
2123
|
+
const archivedRecords = options.currentPublishedRecord === void 0 ? [] : [
|
|
2112
2124
|
{
|
|
2113
|
-
|
|
2114
|
-
version: archivedVersion,
|
|
2125
|
+
...options.currentPublishedRecord,
|
|
2115
2126
|
status: "archived",
|
|
2116
|
-
|
|
2117
|
-
createdAt: timestamp,
|
|
2118
|
-
publishedAt: timestamp,
|
|
2127
|
+
revision: options.currentPublishedRecord.revision + 1,
|
|
2119
2128
|
archivedAt: timestamp
|
|
2120
2129
|
}
|
|
2121
2130
|
];
|
|
@@ -2133,6 +2142,8 @@ function publishDraft(state, draftSchema, options = {}) {
|
|
|
2133
2142
|
version: draftSchema.version,
|
|
2134
2143
|
status: "published",
|
|
2135
2144
|
schema: draftSchema,
|
|
2145
|
+
revision: 1,
|
|
2146
|
+
...archivedVersion === void 0 ? {} : { createdFromVersion: archivedVersion },
|
|
2136
2147
|
createdAt: timestamp,
|
|
2137
2148
|
publishedAt: timestamp
|
|
2138
2149
|
},
|
|
@@ -2141,6 +2152,43 @@ function publishDraft(state, draftSchema, options = {}) {
|
|
|
2141
2152
|
}
|
|
2142
2153
|
};
|
|
2143
2154
|
}
|
|
2155
|
+
async function createPublishTransitionPlan(state, draftRecord, options = {}) {
|
|
2156
|
+
if (draftRecord.formId !== state.formId || draftRecord.version !== state.draftVersion || draftRecord.schema.id !== draftRecord.formId || draftRecord.schema.version !== draftRecord.version) {
|
|
2157
|
+
return { success: false, error: { type: "draft_not_found" } };
|
|
2158
|
+
}
|
|
2159
|
+
if (draftRecord.status !== "draft") {
|
|
2160
|
+
return { success: false, error: { type: "version_immutable", status: draftRecord.status } };
|
|
2161
|
+
}
|
|
2162
|
+
const expectedRevision = options.expectedRevision ?? state.revision;
|
|
2163
|
+
const timestamp = transitionTimestamp(options);
|
|
2164
|
+
const result = await publishDraft(state, draftRecord.schema, {
|
|
2165
|
+
...options,
|
|
2166
|
+
expectedRevision,
|
|
2167
|
+
publishedAt: timestamp
|
|
2168
|
+
});
|
|
2169
|
+
if (!result.success) return result;
|
|
2170
|
+
const publishedRecordToSave = {
|
|
2171
|
+
...draftRecord,
|
|
2172
|
+
status: "published",
|
|
2173
|
+
revision: draftRecord.revision + 1,
|
|
2174
|
+
publishedAt: timestamp
|
|
2175
|
+
};
|
|
2176
|
+
return {
|
|
2177
|
+
success: true,
|
|
2178
|
+
value: {
|
|
2179
|
+
nextState: result.value.nextState,
|
|
2180
|
+
plan: {
|
|
2181
|
+
formId: state.formId,
|
|
2182
|
+
expectedRevision,
|
|
2183
|
+
nextRevision: result.value.nextState.revision,
|
|
2184
|
+
draftToDeleteVersion: draftRecord.version,
|
|
2185
|
+
publishedRecordToSave,
|
|
2186
|
+
archivedRecordsToSave: result.value.archivedRecords,
|
|
2187
|
+
timestamp
|
|
2188
|
+
}
|
|
2189
|
+
}
|
|
2190
|
+
};
|
|
2191
|
+
}
|
|
2144
2192
|
function deleteDraft(state, options = {}) {
|
|
2145
2193
|
validateState(state);
|
|
2146
2194
|
const conflict = revisionConflict(state, options.expectedRevision);
|
|
@@ -2170,6 +2218,7 @@ function assertVersionMutable(status) {
|
|
|
2170
2218
|
calculatePageVisibility,
|
|
2171
2219
|
cloneVersionToDraft,
|
|
2172
2220
|
collectSchemaLocales,
|
|
2221
|
+
createPublishTransitionPlan,
|
|
2173
2222
|
createResponseAccumulator,
|
|
2174
2223
|
createSubmission,
|
|
2175
2224
|
decodeSubmissionCursor,
|
package/dist/index.d.cts
CHANGED
|
@@ -6,11 +6,13 @@ type Result<T, E> = {
|
|
|
6
6
|
readonly error: E;
|
|
7
7
|
};
|
|
8
8
|
type FormVersionStatus = "draft" | "published" | "archived";
|
|
9
|
-
interface FormVersionRecord {
|
|
9
|
+
interface FormVersionRecord extends ExtensibleNode {
|
|
10
10
|
readonly formId: string;
|
|
11
11
|
readonly version: number;
|
|
12
12
|
readonly status: FormVersionStatus;
|
|
13
13
|
readonly schema: FormSchema;
|
|
14
|
+
readonly revision: number;
|
|
15
|
+
readonly createdFromVersion?: number;
|
|
14
16
|
readonly createdAt: string;
|
|
15
17
|
readonly publishedAt?: string;
|
|
16
18
|
readonly archivedAt?: string;
|
|
@@ -53,8 +55,10 @@ interface CloneVersionOptions {
|
|
|
53
55
|
}
|
|
54
56
|
interface PublishDraftOptions {
|
|
55
57
|
readonly expectedRevision?: number;
|
|
56
|
-
readonly
|
|
57
|
-
|
|
58
|
+
readonly currentPublishedRecord?: FormVersionRecord;
|
|
59
|
+
readonly validate?: (schema: FormSchema) => boolean | Promise<boolean> | readonly SchemaIssue[] | Promise<readonly SchemaIssue[]>;
|
|
60
|
+
readonly publishedAt?: string;
|
|
61
|
+
/** @deprecated Use publishedAt. */
|
|
58
62
|
readonly timestamp?: string;
|
|
59
63
|
}
|
|
60
64
|
interface DeleteDraftOptions {
|
|
@@ -71,7 +75,11 @@ declare function cloneVersionToDraft(state: FormVersionState, sourceSchema: Form
|
|
|
71
75
|
readonly nextState: FormVersionState;
|
|
72
76
|
readonly draftSchema: FormSchema;
|
|
73
77
|
}, VersionTransitionError>;
|
|
74
|
-
declare function publishDraft(state: FormVersionState, draftSchema: FormSchema, options?: PublishDraftOptions): Result<PublishDraftResult, VersionTransitionError
|
|
78
|
+
declare function publishDraft(state: FormVersionState, draftSchema: FormSchema, options?: PublishDraftOptions): Promise<Result<PublishDraftResult, VersionTransitionError>>;
|
|
79
|
+
declare function createPublishTransitionPlan(state: FormVersionState, draftRecord: FormVersionRecord, options?: PublishDraftOptions): Promise<Result<{
|
|
80
|
+
readonly nextState: FormVersionState;
|
|
81
|
+
readonly plan: VersionTransitionPlan;
|
|
82
|
+
}, VersionTransitionError>>;
|
|
75
83
|
declare function deleteDraft(state: FormVersionState, options?: DeleteDraftOptions): Result<{
|
|
76
84
|
readonly nextState: FormVersionState;
|
|
77
85
|
}, VersionTransitionError>;
|
|
@@ -261,11 +269,14 @@ interface PagedSubmissionStorageAdapter extends FormStorageAdapter {
|
|
|
261
269
|
listSubmissionPage(formId: string, options?: SubmissionPageQueryOptions): Promise<SubmissionPage>;
|
|
262
270
|
}
|
|
263
271
|
interface VersionTransitionPlan {
|
|
264
|
-
readonly
|
|
272
|
+
readonly formId: string;
|
|
265
273
|
readonly expectedRevision: number;
|
|
266
|
-
readonly
|
|
267
|
-
readonly
|
|
268
|
-
readonly
|
|
274
|
+
readonly nextRevision: number;
|
|
275
|
+
readonly draftToCreate?: FormVersionRecord;
|
|
276
|
+
readonly draftToDeleteVersion?: number;
|
|
277
|
+
readonly publishedRecordToSave?: FormVersionRecord;
|
|
278
|
+
readonly archivedRecordsToSave?: readonly FormVersionRecord[];
|
|
279
|
+
readonly timestamp: string;
|
|
269
280
|
}
|
|
270
281
|
interface VersionedFormStorageAdapter extends FormStorageAdapter {
|
|
271
282
|
commitVersionTransition(plan: VersionTransitionPlan): Promise<{
|
|
@@ -377,7 +388,7 @@ interface CsvExportOptions {
|
|
|
377
388
|
}
|
|
378
389
|
interface CsvColumnDef {
|
|
379
390
|
readonly header: string;
|
|
380
|
-
readonly getValue: (context: CsvColumnContext) => string | number | boolean | null | undefined
|
|
391
|
+
readonly getValue: (context: CsvColumnContext) => string | number | boolean | null | undefined | Promise<string | number | boolean | null | undefined>;
|
|
381
392
|
}
|
|
382
393
|
interface CsvColumnContext extends FormResponse {
|
|
383
394
|
readonly submission: FormResponse;
|
|
@@ -509,4 +520,4 @@ declare function calculatePageVisibility(schema: FormSchema, currentAnswers: Rea
|
|
|
509
520
|
declare function calculateFieldVisibility(schema: FormSchema, currentAnswers: Readonly<Record<string, unknown>>): Readonly<Record<string, boolean>>;
|
|
510
521
|
declare function selectVisibleAnswers(schema: FormSchema, currentAnswers: FormValues): FormValues;
|
|
511
522
|
|
|
512
|
-
export { type AccumulatorReport, type AccumulatorResponse, type AccumulatorSkipReason, type AnswerValidationResult, type AsyncTranslationAdapter, type BaseField, type CheckboxField, type CheckboxQuestionAggregate, type ChoiceDistributionEntry, type ChoiceOption, type ChoiceQuestionAggregate, type CloneVersionOptions, type CollectedLocales, type ConditionOperator, type ConditionValue, type CreateSubmissionOptions, type CrossTabulationResult, type CsvColumnContext, type CsvColumnDef, type CsvExportOptions, type DeleteDraftOptions, type DisplayCondition, type ExtensibleNode, type FieldOption, type FieldType, type FormAnalytics, type FormEvent, type FormEventType, type FormField, type FormPage, type FormPolicy, type FormResponse, type FormSchema, type FormStorageAdapter, type FormSubmission, type FormValue, type FormValues, type FormVersionRecord, type FormVersionState, type FormVersionStatus, type JsonValue, type LocalizedText, type MultiSelectField, type NodeWritableStream, type NumberField, type NumberQuestionAggregate, type NumericSummary, type OptionAggregate, type PagedSubmissionStorageAdapter, type PopulateTranslationOptions, type PublishDraftOptions, type PublishDraftResult, type Question, type QuestionAggregate, type QuestionType, type RatingField, type ResponseAccumulator, type ResponseAccumulatorOptions, type Result, type SchemaIssue, type SchemaStructureIssue, type SchemaStructureIssueType, type SchemaTranslations, type SchemaValidationResult, type SelectField, type StorageAdapter, type StreamCsvOptions, type SubmissionCursorValue, type SubmissionPage, type SubmissionPageQueryOptions, type SubmissionQueryOptions, type TextField, type TextQuestionAggregate, type TranslationAdapter, type TranslationReport, type TranslationSlot, type ValidateFormSchemaOptions, type ValidationCode, type ValidationError, type ValidationIssue, type VersionTransitionError, type VersionTransitionPlan, type VersionedFormStorageAdapter, type WebhookConfig, type WebhookDispatchResult, aggregateResponses, assertValidFormSchema, assertVersionMutable, calculateChoiceDistribution, calculateCrossTabulation, calculateFieldVisibility, calculateNumericSummary, calculatePageVisibility, cloneVersionToDraft, collectSchemaLocales, createResponseAccumulator, createSubmission, decodeSubmissionCursor, deleteDraft, dispatchWebhook, encodeSubmissionCursor, escapeCsvCell, exportResponsesToCsv, exportResponsesToCsvStream, isDisplayConditionSatisfied, isQuestionVisible, matchesSubmissionPageFilters, normalizeSubmissionPageSize, pipeResponsesToCsvStream, populateSchemaTranslations, publishDraft, resolveFormTranslation, resolveLocalizedSchema, sanitizeSchema, selectVisibleAnswers, transformFieldType, validateAnswers, validateFormSchema, validatePageAnswers, validateSchemaStructure };
|
|
523
|
+
export { type AccumulatorReport, type AccumulatorResponse, type AccumulatorSkipReason, type AnswerValidationResult, type AsyncTranslationAdapter, type BaseField, type CheckboxField, type CheckboxQuestionAggregate, type ChoiceDistributionEntry, type ChoiceOption, type ChoiceQuestionAggregate, type CloneVersionOptions, type CollectedLocales, type ConditionOperator, type ConditionValue, type CreateSubmissionOptions, type CrossTabulationResult, type CsvColumnContext, type CsvColumnDef, type CsvExportOptions, type DeleteDraftOptions, type DisplayCondition, type ExtensibleNode, type FieldOption, type FieldType, type FormAnalytics, type FormEvent, type FormEventType, type FormField, type FormPage, type FormPolicy, type FormResponse, type FormSchema, type FormStorageAdapter, type FormSubmission, type FormValue, type FormValues, type FormVersionRecord, type FormVersionState, type FormVersionStatus, type JsonValue, type LocalizedText, type MultiSelectField, type NodeWritableStream, type NumberField, type NumberQuestionAggregate, type NumericSummary, type OptionAggregate, type PagedSubmissionStorageAdapter, type PopulateTranslationOptions, type PublishDraftOptions, type PublishDraftResult, type Question, type QuestionAggregate, type QuestionType, type RatingField, type ResponseAccumulator, type ResponseAccumulatorOptions, type Result, type SchemaIssue, type SchemaStructureIssue, type SchemaStructureIssueType, type SchemaTranslations, type SchemaValidationResult, type SelectField, type StorageAdapter, type StreamCsvOptions, type SubmissionCursorValue, type SubmissionPage, type SubmissionPageQueryOptions, type SubmissionQueryOptions, type TextField, type TextQuestionAggregate, type TranslationAdapter, type TranslationReport, type TranslationSlot, type ValidateFormSchemaOptions, type ValidationCode, type ValidationError, type ValidationIssue, type VersionTransitionError, type VersionTransitionPlan, type VersionedFormStorageAdapter, type WebhookConfig, type WebhookDispatchResult, aggregateResponses, assertValidFormSchema, assertVersionMutable, calculateChoiceDistribution, calculateCrossTabulation, calculateFieldVisibility, calculateNumericSummary, calculatePageVisibility, cloneVersionToDraft, collectSchemaLocales, createPublishTransitionPlan, createResponseAccumulator, createSubmission, decodeSubmissionCursor, deleteDraft, dispatchWebhook, encodeSubmissionCursor, escapeCsvCell, exportResponsesToCsv, exportResponsesToCsvStream, isDisplayConditionSatisfied, isQuestionVisible, matchesSubmissionPageFilters, normalizeSubmissionPageSize, pipeResponsesToCsvStream, populateSchemaTranslations, publishDraft, resolveFormTranslation, resolveLocalizedSchema, sanitizeSchema, selectVisibleAnswers, transformFieldType, validateAnswers, validateFormSchema, validatePageAnswers, validateSchemaStructure };
|
package/dist/index.d.ts
CHANGED
|
@@ -6,11 +6,13 @@ type Result<T, E> = {
|
|
|
6
6
|
readonly error: E;
|
|
7
7
|
};
|
|
8
8
|
type FormVersionStatus = "draft" | "published" | "archived";
|
|
9
|
-
interface FormVersionRecord {
|
|
9
|
+
interface FormVersionRecord extends ExtensibleNode {
|
|
10
10
|
readonly formId: string;
|
|
11
11
|
readonly version: number;
|
|
12
12
|
readonly status: FormVersionStatus;
|
|
13
13
|
readonly schema: FormSchema;
|
|
14
|
+
readonly revision: number;
|
|
15
|
+
readonly createdFromVersion?: number;
|
|
14
16
|
readonly createdAt: string;
|
|
15
17
|
readonly publishedAt?: string;
|
|
16
18
|
readonly archivedAt?: string;
|
|
@@ -53,8 +55,10 @@ interface CloneVersionOptions {
|
|
|
53
55
|
}
|
|
54
56
|
interface PublishDraftOptions {
|
|
55
57
|
readonly expectedRevision?: number;
|
|
56
|
-
readonly
|
|
57
|
-
|
|
58
|
+
readonly currentPublishedRecord?: FormVersionRecord;
|
|
59
|
+
readonly validate?: (schema: FormSchema) => boolean | Promise<boolean> | readonly SchemaIssue[] | Promise<readonly SchemaIssue[]>;
|
|
60
|
+
readonly publishedAt?: string;
|
|
61
|
+
/** @deprecated Use publishedAt. */
|
|
58
62
|
readonly timestamp?: string;
|
|
59
63
|
}
|
|
60
64
|
interface DeleteDraftOptions {
|
|
@@ -71,7 +75,11 @@ declare function cloneVersionToDraft(state: FormVersionState, sourceSchema: Form
|
|
|
71
75
|
readonly nextState: FormVersionState;
|
|
72
76
|
readonly draftSchema: FormSchema;
|
|
73
77
|
}, VersionTransitionError>;
|
|
74
|
-
declare function publishDraft(state: FormVersionState, draftSchema: FormSchema, options?: PublishDraftOptions): Result<PublishDraftResult, VersionTransitionError
|
|
78
|
+
declare function publishDraft(state: FormVersionState, draftSchema: FormSchema, options?: PublishDraftOptions): Promise<Result<PublishDraftResult, VersionTransitionError>>;
|
|
79
|
+
declare function createPublishTransitionPlan(state: FormVersionState, draftRecord: FormVersionRecord, options?: PublishDraftOptions): Promise<Result<{
|
|
80
|
+
readonly nextState: FormVersionState;
|
|
81
|
+
readonly plan: VersionTransitionPlan;
|
|
82
|
+
}, VersionTransitionError>>;
|
|
75
83
|
declare function deleteDraft(state: FormVersionState, options?: DeleteDraftOptions): Result<{
|
|
76
84
|
readonly nextState: FormVersionState;
|
|
77
85
|
}, VersionTransitionError>;
|
|
@@ -261,11 +269,14 @@ interface PagedSubmissionStorageAdapter extends FormStorageAdapter {
|
|
|
261
269
|
listSubmissionPage(formId: string, options?: SubmissionPageQueryOptions): Promise<SubmissionPage>;
|
|
262
270
|
}
|
|
263
271
|
interface VersionTransitionPlan {
|
|
264
|
-
readonly
|
|
272
|
+
readonly formId: string;
|
|
265
273
|
readonly expectedRevision: number;
|
|
266
|
-
readonly
|
|
267
|
-
readonly
|
|
268
|
-
readonly
|
|
274
|
+
readonly nextRevision: number;
|
|
275
|
+
readonly draftToCreate?: FormVersionRecord;
|
|
276
|
+
readonly draftToDeleteVersion?: number;
|
|
277
|
+
readonly publishedRecordToSave?: FormVersionRecord;
|
|
278
|
+
readonly archivedRecordsToSave?: readonly FormVersionRecord[];
|
|
279
|
+
readonly timestamp: string;
|
|
269
280
|
}
|
|
270
281
|
interface VersionedFormStorageAdapter extends FormStorageAdapter {
|
|
271
282
|
commitVersionTransition(plan: VersionTransitionPlan): Promise<{
|
|
@@ -377,7 +388,7 @@ interface CsvExportOptions {
|
|
|
377
388
|
}
|
|
378
389
|
interface CsvColumnDef {
|
|
379
390
|
readonly header: string;
|
|
380
|
-
readonly getValue: (context: CsvColumnContext) => string | number | boolean | null | undefined
|
|
391
|
+
readonly getValue: (context: CsvColumnContext) => string | number | boolean | null | undefined | Promise<string | number | boolean | null | undefined>;
|
|
381
392
|
}
|
|
382
393
|
interface CsvColumnContext extends FormResponse {
|
|
383
394
|
readonly submission: FormResponse;
|
|
@@ -509,4 +520,4 @@ declare function calculatePageVisibility(schema: FormSchema, currentAnswers: Rea
|
|
|
509
520
|
declare function calculateFieldVisibility(schema: FormSchema, currentAnswers: Readonly<Record<string, unknown>>): Readonly<Record<string, boolean>>;
|
|
510
521
|
declare function selectVisibleAnswers(schema: FormSchema, currentAnswers: FormValues): FormValues;
|
|
511
522
|
|
|
512
|
-
export { type AccumulatorReport, type AccumulatorResponse, type AccumulatorSkipReason, type AnswerValidationResult, type AsyncTranslationAdapter, type BaseField, type CheckboxField, type CheckboxQuestionAggregate, type ChoiceDistributionEntry, type ChoiceOption, type ChoiceQuestionAggregate, type CloneVersionOptions, type CollectedLocales, type ConditionOperator, type ConditionValue, type CreateSubmissionOptions, type CrossTabulationResult, type CsvColumnContext, type CsvColumnDef, type CsvExportOptions, type DeleteDraftOptions, type DisplayCondition, type ExtensibleNode, type FieldOption, type FieldType, type FormAnalytics, type FormEvent, type FormEventType, type FormField, type FormPage, type FormPolicy, type FormResponse, type FormSchema, type FormStorageAdapter, type FormSubmission, type FormValue, type FormValues, type FormVersionRecord, type FormVersionState, type FormVersionStatus, type JsonValue, type LocalizedText, type MultiSelectField, type NodeWritableStream, type NumberField, type NumberQuestionAggregate, type NumericSummary, type OptionAggregate, type PagedSubmissionStorageAdapter, type PopulateTranslationOptions, type PublishDraftOptions, type PublishDraftResult, type Question, type QuestionAggregate, type QuestionType, type RatingField, type ResponseAccumulator, type ResponseAccumulatorOptions, type Result, type SchemaIssue, type SchemaStructureIssue, type SchemaStructureIssueType, type SchemaTranslations, type SchemaValidationResult, type SelectField, type StorageAdapter, type StreamCsvOptions, type SubmissionCursorValue, type SubmissionPage, type SubmissionPageQueryOptions, type SubmissionQueryOptions, type TextField, type TextQuestionAggregate, type TranslationAdapter, type TranslationReport, type TranslationSlot, type ValidateFormSchemaOptions, type ValidationCode, type ValidationError, type ValidationIssue, type VersionTransitionError, type VersionTransitionPlan, type VersionedFormStorageAdapter, type WebhookConfig, type WebhookDispatchResult, aggregateResponses, assertValidFormSchema, assertVersionMutable, calculateChoiceDistribution, calculateCrossTabulation, calculateFieldVisibility, calculateNumericSummary, calculatePageVisibility, cloneVersionToDraft, collectSchemaLocales, createResponseAccumulator, createSubmission, decodeSubmissionCursor, deleteDraft, dispatchWebhook, encodeSubmissionCursor, escapeCsvCell, exportResponsesToCsv, exportResponsesToCsvStream, isDisplayConditionSatisfied, isQuestionVisible, matchesSubmissionPageFilters, normalizeSubmissionPageSize, pipeResponsesToCsvStream, populateSchemaTranslations, publishDraft, resolveFormTranslation, resolveLocalizedSchema, sanitizeSchema, selectVisibleAnswers, transformFieldType, validateAnswers, validateFormSchema, validatePageAnswers, validateSchemaStructure };
|
|
523
|
+
export { type AccumulatorReport, type AccumulatorResponse, type AccumulatorSkipReason, type AnswerValidationResult, type AsyncTranslationAdapter, type BaseField, type CheckboxField, type CheckboxQuestionAggregate, type ChoiceDistributionEntry, type ChoiceOption, type ChoiceQuestionAggregate, type CloneVersionOptions, type CollectedLocales, type ConditionOperator, type ConditionValue, type CreateSubmissionOptions, type CrossTabulationResult, type CsvColumnContext, type CsvColumnDef, type CsvExportOptions, type DeleteDraftOptions, type DisplayCondition, type ExtensibleNode, type FieldOption, type FieldType, type FormAnalytics, type FormEvent, type FormEventType, type FormField, type FormPage, type FormPolicy, type FormResponse, type FormSchema, type FormStorageAdapter, type FormSubmission, type FormValue, type FormValues, type FormVersionRecord, type FormVersionState, type FormVersionStatus, type JsonValue, type LocalizedText, type MultiSelectField, type NodeWritableStream, type NumberField, type NumberQuestionAggregate, type NumericSummary, type OptionAggregate, type PagedSubmissionStorageAdapter, type PopulateTranslationOptions, type PublishDraftOptions, type PublishDraftResult, type Question, type QuestionAggregate, type QuestionType, type RatingField, type ResponseAccumulator, type ResponseAccumulatorOptions, type Result, type SchemaIssue, type SchemaStructureIssue, type SchemaStructureIssueType, type SchemaTranslations, type SchemaValidationResult, type SelectField, type StorageAdapter, type StreamCsvOptions, type SubmissionCursorValue, type SubmissionPage, type SubmissionPageQueryOptions, type SubmissionQueryOptions, type TextField, type TextQuestionAggregate, type TranslationAdapter, type TranslationReport, type TranslationSlot, type ValidateFormSchemaOptions, type ValidationCode, type ValidationError, type ValidationIssue, type VersionTransitionError, type VersionTransitionPlan, type VersionedFormStorageAdapter, type WebhookConfig, type WebhookDispatchResult, aggregateResponses, assertValidFormSchema, assertVersionMutable, calculateChoiceDistribution, calculateCrossTabulation, calculateFieldVisibility, calculateNumericSummary, calculatePageVisibility, cloneVersionToDraft, collectSchemaLocales, createPublishTransitionPlan, createResponseAccumulator, createSubmission, decodeSubmissionCursor, deleteDraft, dispatchWebhook, encodeSubmissionCursor, escapeCsvCell, exportResponsesToCsv, exportResponsesToCsvStream, isDisplayConditionSatisfied, isQuestionVisible, matchesSubmissionPageFilters, normalizeSubmissionPageSize, pipeResponsesToCsvStream, populateSchemaTranslations, publishDraft, resolveFormTranslation, resolveLocalizedSchema, sanitizeSchema, selectVisibleAnswers, transformFieldType, validateAnswers, validateFormSchema, validatePageAnswers, validateSchemaStructure };
|
package/dist/index.js
CHANGED
|
@@ -1286,7 +1286,7 @@ async function* exportResponsesToCsvStream(schema, submissions, options = {}) {
|
|
|
1286
1286
|
formVersion: response.formVersion ?? schema.version,
|
|
1287
1287
|
schema
|
|
1288
1288
|
};
|
|
1289
|
-
const customCells = customColumns.map((column) => column.getValue(context));
|
|
1289
|
+
const customCells = await Promise.all(customColumns.map((column) => column.getValue(context)));
|
|
1290
1290
|
yield `\r
|
|
1291
1291
|
${[...defaultCells, ...customCells].map((value) => escapeCsvCell(value, neutralizeFormulas)).join(",")}`;
|
|
1292
1292
|
}
|
|
@@ -2031,31 +2031,39 @@ function cloneVersionToDraft(state, sourceSchema, options = {}) {
|
|
|
2031
2031
|
}
|
|
2032
2032
|
};
|
|
2033
2033
|
}
|
|
2034
|
-
function
|
|
2034
|
+
function validatePublishedRecord(state, record) {
|
|
2035
|
+
if (record === void 0) return;
|
|
2036
|
+
if (record.formId !== state.formId || record.version !== state.publishedVersion || record.status !== "published" || record.schema.id !== record.formId || record.schema.version !== record.version) {
|
|
2037
|
+
throw new TypeError("currentPublishedRecord must match the state's published version.");
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
function transitionTimestamp(options) {
|
|
2041
|
+
const timestamp = options.publishedAt ?? options.timestamp ?? "1970-01-01T00:00:00.000Z";
|
|
2042
|
+
if (!Number.isFinite(Date.parse(timestamp))) throw new TypeError("publishedAt must be a valid date string.");
|
|
2043
|
+
return timestamp;
|
|
2044
|
+
}
|
|
2045
|
+
async function publishDraft(state, draftSchema, options = {}) {
|
|
2035
2046
|
validateState(state);
|
|
2036
2047
|
const conflict = revisionConflict(state, options.expectedRevision);
|
|
2037
2048
|
if (conflict !== void 0) return conflict;
|
|
2038
2049
|
if (state.draftVersion === void 0 || draftSchema.id !== state.formId || draftSchema.version !== state.draftVersion) {
|
|
2039
2050
|
return { success: false, error: { type: "draft_not_found" } };
|
|
2040
2051
|
}
|
|
2041
|
-
|
|
2052
|
+
validatePublishedRecord(state, options.currentPublishedRecord);
|
|
2053
|
+
const validation = await options.validate?.(draftSchema);
|
|
2042
2054
|
if (validation === false || Array.isArray(validation) && validation.length > 0) {
|
|
2043
2055
|
return {
|
|
2044
2056
|
success: false,
|
|
2045
2057
|
error: { type: "validation_failed", issues: Array.isArray(validation) ? validation : [] }
|
|
2046
2058
|
};
|
|
2047
2059
|
}
|
|
2048
|
-
const timestamp = options
|
|
2049
|
-
if (!Number.isFinite(Date.parse(timestamp))) throw new TypeError("timestamp must be a valid date string.");
|
|
2060
|
+
const timestamp = transitionTimestamp(options);
|
|
2050
2061
|
const archivedVersion = state.publishedVersion;
|
|
2051
|
-
const archivedRecords =
|
|
2062
|
+
const archivedRecords = options.currentPublishedRecord === void 0 ? [] : [
|
|
2052
2063
|
{
|
|
2053
|
-
|
|
2054
|
-
version: archivedVersion,
|
|
2064
|
+
...options.currentPublishedRecord,
|
|
2055
2065
|
status: "archived",
|
|
2056
|
-
|
|
2057
|
-
createdAt: timestamp,
|
|
2058
|
-
publishedAt: timestamp,
|
|
2066
|
+
revision: options.currentPublishedRecord.revision + 1,
|
|
2059
2067
|
archivedAt: timestamp
|
|
2060
2068
|
}
|
|
2061
2069
|
];
|
|
@@ -2073,6 +2081,8 @@ function publishDraft(state, draftSchema, options = {}) {
|
|
|
2073
2081
|
version: draftSchema.version,
|
|
2074
2082
|
status: "published",
|
|
2075
2083
|
schema: draftSchema,
|
|
2084
|
+
revision: 1,
|
|
2085
|
+
...archivedVersion === void 0 ? {} : { createdFromVersion: archivedVersion },
|
|
2076
2086
|
createdAt: timestamp,
|
|
2077
2087
|
publishedAt: timestamp
|
|
2078
2088
|
},
|
|
@@ -2081,6 +2091,43 @@ function publishDraft(state, draftSchema, options = {}) {
|
|
|
2081
2091
|
}
|
|
2082
2092
|
};
|
|
2083
2093
|
}
|
|
2094
|
+
async function createPublishTransitionPlan(state, draftRecord, options = {}) {
|
|
2095
|
+
if (draftRecord.formId !== state.formId || draftRecord.version !== state.draftVersion || draftRecord.schema.id !== draftRecord.formId || draftRecord.schema.version !== draftRecord.version) {
|
|
2096
|
+
return { success: false, error: { type: "draft_not_found" } };
|
|
2097
|
+
}
|
|
2098
|
+
if (draftRecord.status !== "draft") {
|
|
2099
|
+
return { success: false, error: { type: "version_immutable", status: draftRecord.status } };
|
|
2100
|
+
}
|
|
2101
|
+
const expectedRevision = options.expectedRevision ?? state.revision;
|
|
2102
|
+
const timestamp = transitionTimestamp(options);
|
|
2103
|
+
const result = await publishDraft(state, draftRecord.schema, {
|
|
2104
|
+
...options,
|
|
2105
|
+
expectedRevision,
|
|
2106
|
+
publishedAt: timestamp
|
|
2107
|
+
});
|
|
2108
|
+
if (!result.success) return result;
|
|
2109
|
+
const publishedRecordToSave = {
|
|
2110
|
+
...draftRecord,
|
|
2111
|
+
status: "published",
|
|
2112
|
+
revision: draftRecord.revision + 1,
|
|
2113
|
+
publishedAt: timestamp
|
|
2114
|
+
};
|
|
2115
|
+
return {
|
|
2116
|
+
success: true,
|
|
2117
|
+
value: {
|
|
2118
|
+
nextState: result.value.nextState,
|
|
2119
|
+
plan: {
|
|
2120
|
+
formId: state.formId,
|
|
2121
|
+
expectedRevision,
|
|
2122
|
+
nextRevision: result.value.nextState.revision,
|
|
2123
|
+
draftToDeleteVersion: draftRecord.version,
|
|
2124
|
+
publishedRecordToSave,
|
|
2125
|
+
archivedRecordsToSave: result.value.archivedRecords,
|
|
2126
|
+
timestamp
|
|
2127
|
+
}
|
|
2128
|
+
}
|
|
2129
|
+
};
|
|
2130
|
+
}
|
|
2084
2131
|
function deleteDraft(state, options = {}) {
|
|
2085
2132
|
validateState(state);
|
|
2086
2133
|
const conflict = revisionConflict(state, options.expectedRevision);
|
|
@@ -2109,6 +2156,7 @@ export {
|
|
|
2109
2156
|
calculatePageVisibility,
|
|
2110
2157
|
cloneVersionToDraft,
|
|
2111
2158
|
collectSchemaLocales,
|
|
2159
|
+
createPublishTransitionPlan,
|
|
2112
2160
|
createResponseAccumulator,
|
|
2113
2161
|
createSubmission,
|
|
2114
2162
|
decodeSubmissionCursor,
|