@form-engine-ts/core 5.0.0 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -22,6 +22,9 @@ var index_exports = {};
22
22
  __export(index_exports, {
23
23
  DEFAULT_FIELD_TYPE_DEFINITIONS: () => DEFAULT_FIELD_TYPE_DEFINITIONS,
24
24
  EN_MESSAGES: () => EN_MESSAGES,
25
+ FormSubmissionError: () => FormSubmissionError,
26
+ FormSubmissionMetadataSchema: () => FormSubmissionMetadataSchema,
27
+ FormSubmissionWireSchema: () => FormSubmissionWireSchema,
25
28
  JA_MESSAGES: () => JA_MESSAGES,
26
29
  aggregateResponses: () => aggregateResponses,
27
30
  applyTransitionPlan: () => applyTransitionPlan,
@@ -77,6 +80,7 @@ __export(index_exports, {
77
80
  resolveLocalizedSchema: () => resolveLocalizedSchema,
78
81
  sanitizeSchema: () => sanitizeSchema,
79
82
  selectVisibleAnswers: () => selectVisibleAnswers,
83
+ toFormSubmissionWire: () => toFormSubmissionWire,
80
84
  transformFieldType: () => transformFieldType,
81
85
  validateAnswers: () => validateAnswers,
82
86
  validateFormSchema: () => validateFormSchema,
@@ -1686,15 +1690,28 @@ function serializeUnknown(value) {
1686
1690
  if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value;
1687
1691
  return JSON.stringify(value);
1688
1692
  }
1693
+ function submissionWithMetadata(submission) {
1694
+ return { ...submission, metadata: submission.metadata ?? {} };
1695
+ }
1689
1696
  async function* exportResponsesToCsvStream(schema, submissions, options = {}) {
1690
1697
  assertValidFormSchema(schema);
1691
1698
  const includeDefaultColumns = options.includeDefaultColumns ?? true;
1692
1699
  const customColumns = options.columns ?? [];
1700
+ const contractColumns = options.customColumns ?? [];
1701
+ const includeLocale = options.includeLocale ?? true;
1702
+ const includePiiStatus = options.includePiiStatus ?? false;
1693
1703
  const headers = [
1694
- ...includeDefaultColumns ? ["submissionId", "submittedAt", "locale", ...schema.fields.map((field) => field.id)] : [],
1695
- ...customColumns.map((column) => column.header)
1704
+ ...includeDefaultColumns ? [
1705
+ "submissionId",
1706
+ "submittedAt",
1707
+ ...includeLocale ? ["locale"] : [],
1708
+ ...includePiiStatus ? ["piiStatus"] : [],
1709
+ ...schema.fields.map((field) => field.id)
1710
+ ] : [],
1711
+ ...customColumns.map((column) => column.header),
1712
+ ...contractColumns.map((column) => column.header)
1696
1713
  ];
1697
- const neutralizeFormulas = options.neutralizeFormulas ?? true;
1714
+ const neutralizeFormulas = options.preventFormulaInjection ?? options.neutralizeFormulas ?? true;
1698
1715
  const header = headers.map((value) => escapeCsvCell(value, neutralizeFormulas)).join(",");
1699
1716
  yield `${options.withBom ?? true ? "\uFEFF" : ""}${header}`;
1700
1717
  for await (const submission of submissions) {
@@ -1703,10 +1720,12 @@ async function* exportResponsesToCsvStream(schema, submissions, options = {}) {
1703
1720
  const response = asFormResponse(submission);
1704
1721
  const answers = response.answers;
1705
1722
  const visible = selectVisibleAnswers(schema, answers);
1723
+ const piiStatus = response.metadata?.piiConfirmed === true ? "confirmed" : "unconfirmed";
1706
1724
  const defaultCells = includeDefaultColumns ? [
1707
1725
  response.responseId,
1708
1726
  response.submittedAt,
1709
- response.sourceLocale ?? "",
1727
+ ...includeLocale ? [response.sourceLocale ?? ""] : [],
1728
+ ...includePiiStatus ? [piiStatus] : [],
1710
1729
  ...schema.fields.map((field) => serializeUnknown(visible[field.id]))
1711
1730
  ] : [];
1712
1731
  const context = {
@@ -1716,8 +1735,9 @@ async function* exportResponsesToCsvStream(schema, submissions, options = {}) {
1716
1735
  schema
1717
1736
  };
1718
1737
  const customCells = await Promise.all(customColumns.map((column) => column.getValue(context)));
1738
+ const contractCells = "values" in submission ? contractColumns.map((column) => column.getValue(submissionWithMetadata(submission), schema)) : contractColumns.map(() => void 0);
1719
1739
  yield `\r
1720
- ${[...defaultCells, ...customCells].map((value) => escapeCsvCell(value, neutralizeFormulas)).join(",")}`;
1740
+ ${[...defaultCells, ...customCells, ...contractCells].map((value) => escapeCsvCell(value, neutralizeFormulas)).join(",")}`;
1721
1741
  }
1722
1742
  }
1723
1743
  function isWebWritableStream(writable) {
@@ -1774,23 +1794,51 @@ function exportResponsesToCsv(schema, responses, options = {}) {
1774
1794
  throw new TypeError(`Submission ${response.id} does not match ${schema.id}@${schema.version}.`);
1775
1795
  }
1776
1796
  }
1797
+ const includeLocale = options.includeLocale ?? true;
1798
+ const includePiiStatus = options.includePiiStatus ?? false;
1799
+ const customColumns = options.customColumns ?? [];
1800
+ const headers = [
1801
+ "submissionId",
1802
+ "submittedAt",
1803
+ ...includeLocale ? ["locale"] : [],
1804
+ ...includePiiStatus ? ["piiStatus"] : [],
1805
+ ...schema.fields.map((field) => field.id),
1806
+ ...customColumns.map((column) => column.header)
1807
+ ];
1777
1808
  const rows = [
1778
- ["submissionId", "submittedAt", "locale", ...schema.fields.map((field) => field.id)],
1809
+ headers,
1779
1810
  ...responses.map((response) => {
1780
1811
  const visible = selectVisibleAnswers(schema, response.values);
1812
+ const piiStatus = response.metadata?.piiConfirmed === true ? "confirmed" : "unconfirmed";
1813
+ const submissionForCustomColumns = submissionWithMetadata(response);
1781
1814
  return [
1782
1815
  response.id,
1783
1816
  response.submittedAt,
1784
- response.locale,
1785
- ...schema.fields.map((field) => serializeValue(visible[field.id]))
1817
+ ...includeLocale ? [response.locale] : [],
1818
+ ...includePiiStatus ? [piiStatus] : [],
1819
+ ...schema.fields.map((field) => serializeValue(visible[field.id])),
1820
+ ...customColumns.map((column) => column.getValue(submissionForCustomColumns, schema))
1786
1821
  ];
1787
1822
  })
1788
1823
  ];
1789
- const neutralizeFormulas = options.neutralizeFormulas ?? true;
1824
+ const neutralizeFormulas = options.preventFormulaInjection ?? options.neutralizeFormulas ?? true;
1790
1825
  const csv = rows.map((row) => row.map((cell) => escapeCsvCell(cell, neutralizeFormulas)).join(",")).join("\r\n");
1791
- return options.withBom ?? true ? `\uFEFF${csv}` : csv;
1826
+ return options.useBom ?? options.withBom ?? true ? `\uFEFF${csv}` : csv;
1792
1827
  }
1793
1828
 
1829
+ // src/errors.ts
1830
+ var FormSubmissionError = class extends Error {
1831
+ payload;
1832
+ constructor(payload) {
1833
+ super(payload.messageKey);
1834
+ this.name = "FormSubmissionError";
1835
+ this.payload = payload;
1836
+ }
1837
+ toJSON() {
1838
+ return this.payload;
1839
+ }
1840
+ };
1841
+
1794
1842
  // src/events.ts
1795
1843
  function bytesToHex(bytes) {
1796
1844
  return [...new Uint8Array(bytes)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
@@ -2113,7 +2161,20 @@ var EN_MESSAGES = Object.freeze({
2113
2161
  "workspace.errors.maxLocalesExceeded": "The maximum number of locales ({{max}}) has been reached.",
2114
2162
  "workspace.errors.readOnly": "This workspace is read-only.",
2115
2163
  "workspace.errors.adapterNotConfigured": "A translation adapter is not configured.",
2116
- "workspace.errors.translationFailed": "Translation failed."
2164
+ "workspace.errors.translationFailed": "Translation failed.",
2165
+ "workspace.header.title": "Translation workspace",
2166
+ "workspace.header.sourceLocale": "Source language",
2167
+ "workspace.header.targetLocale": "Target language",
2168
+ "workspace.header.translateAll": "Translate all",
2169
+ "workspace.header.progress": "{{translated}}/{{total}} translated ({{percent}}%)",
2170
+ "workspace.slot.sourceText": "Source",
2171
+ "workspace.slot.translatedText": "Translation",
2172
+ "workspace.slot.translateSingle": "Translate this slot",
2173
+ "workspace.slot.revertManual": "Revert manual translation",
2174
+ "workspace.confirm.removeLocaleTitle": "Remove language?",
2175
+ "workspace.confirm.removeLocaleMessage": "This will remove {{locale}} and its translations.",
2176
+ "workspace.empty.noTargetLocales": "No target languages are configured.",
2177
+ "workspace.empty.noSlotsToTranslate": "There are no translation slots for this language."
2117
2178
  });
2118
2179
 
2119
2180
  // src/i18n/catalogs/ja.ts
@@ -2285,7 +2346,20 @@ var JA_MESSAGES = Object.freeze({
2285
2346
  "workspace.errors.maxLocalesExceeded": "\u767B\u9332\u53EF\u80FD\u306A\u6700\u5927\u8A00\u8A9E\u6570 ({{max}}) \u306B\u9054\u3057\u307E\u3057\u305F\u3002",
2286
2347
  "workspace.errors.readOnly": "\u8AAD\u307F\u53D6\u308A\u5C02\u7528\u30E2\u30FC\u30C9\u306E\u305F\u3081\u5909\u66F4\u3067\u304D\u307E\u305B\u3093\u3002",
2287
2348
  "workspace.errors.adapterNotConfigured": "\u7FFB\u8A33\u30A2\u30C0\u30D7\u30BF\u30FC\u304C\u8A2D\u5B9A\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002",
2288
- "workspace.errors.translationFailed": "\u7FFB\u8A33\u51E6\u7406\u306B\u5931\u6557\u3057\u307E\u3057\u305F\u3002"
2349
+ "workspace.errors.translationFailed": "\u7FFB\u8A33\u51E6\u7406\u306B\u5931\u6557\u3057\u307E\u3057\u305F\u3002",
2350
+ "workspace.header.title": "\u591A\u8A00\u8A9E\u7FFB\u8A33\u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9",
2351
+ "workspace.header.sourceLocale": "\u5143\u8A00\u8A9E",
2352
+ "workspace.header.targetLocale": "\u7FFB\u8A33\u8A00\u8A9E",
2353
+ "workspace.header.translateAll": "\u4E00\u62EC\u81EA\u52D5\u7FFB\u8A33",
2354
+ "workspace.header.progress": "{{translated}}/{{total}} \u7FFB\u8A33\u6E08\u307F ({{percent}}%)",
2355
+ "workspace.slot.sourceText": "\u539F\u6587",
2356
+ "workspace.slot.translatedText": "\u7FFB\u8A33\u6587",
2357
+ "workspace.slot.translateSingle": "\u3053\u306E\u9805\u76EE\u3092\u7FFB\u8A33",
2358
+ "workspace.slot.revertManual": "\u624B\u52D5\u7FFB\u8A33\u3092\u5143\u306B\u623B\u3059",
2359
+ "workspace.confirm.removeLocaleTitle": "\u8A00\u8A9E\u3092\u524A\u9664\u3057\u307E\u3059\u304B\uFF1F",
2360
+ "workspace.confirm.removeLocaleMessage": "{{locale}} \u3068\u305D\u306E\u7FFB\u8A33\u3092\u524A\u9664\u3057\u307E\u3059\u3002",
2361
+ "workspace.empty.noTargetLocales": "\u7FFB\u8A33\u5148\u306E\u8A00\u8A9E\u304C\u8A2D\u5B9A\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002",
2362
+ "workspace.empty.noSlotsToTranslate": "\u3053\u306E\u8A00\u8A9E\u306B\u306F\u7FFB\u8A33\u9805\u76EE\u304C\u3042\u308A\u307E\u305B\u3093\u3002"
2289
2363
  });
2290
2364
 
2291
2365
  // src/i18n/translator.ts
@@ -2574,6 +2648,19 @@ function matchesSubmissionPageFilters(submission, options) {
2574
2648
  );
2575
2649
  }
2576
2650
 
2651
+ // src/schemas/submission.zod.ts
2652
+ var import_zod = require("zod");
2653
+ var FormSubmissionMetadataSchema = import_zod.z.record(import_zod.z.string(), import_zod.z.unknown());
2654
+ var FormSubmissionWireSchema = import_zod.z.object({
2655
+ id: import_zod.z.string().min(1),
2656
+ formId: import_zod.z.string().min(1),
2657
+ formVersion: import_zod.z.number().int().positive(),
2658
+ values: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()),
2659
+ metadata: FormSubmissionMetadataSchema,
2660
+ submittedAt: import_zod.z.string().datetime(),
2661
+ schemaRevision: import_zod.z.number().int().optional()
2662
+ });
2663
+
2577
2664
  // src/validation.ts
2578
2665
  var DEFAULT_MESSAGES = {
2579
2666
  required: "validation.required",
@@ -2754,6 +2841,19 @@ function toFormValues(answers) {
2754
2841
  function isFormValue(value) {
2755
2842
  return value === void 0 || typeof value === "string" || typeof value === "number" || typeof value === "boolean" || Array.isArray(value) && value.every((item) => typeof item === "string");
2756
2843
  }
2844
+ function toFormSubmissionWire(submission) {
2845
+ const { id, formId, formVersion, values, answers, metadata, submittedAt, schemaRevision } = submission;
2846
+ const targetValues = values ?? answers ?? {};
2847
+ return {
2848
+ id,
2849
+ formId,
2850
+ formVersion,
2851
+ values: { ...targetValues },
2852
+ metadata: { ...metadata ?? {} },
2853
+ submittedAt,
2854
+ ...schemaRevision === void 0 ? {} : { schemaRevision }
2855
+ };
2856
+ }
2757
2857
  function createSubmission(schemaOrInput, values, options) {
2758
2858
  if ("answers" in schemaOrInput) {
2759
2859
  const input = schemaOrInput;
@@ -3177,18 +3277,20 @@ var migrateSchemaTranslationMetadata = (schema, migratorOrOptions) => {
3177
3277
  };
3178
3278
  };
3179
3279
  var removeLocaleFromSchema = (schema, localeToRemove) => {
3180
- if (localeToRemove === schema.defaultLocale) {
3280
+ const normalizedLocaleToRemove = normalizeLocale(localeToRemove) ?? localeToRemove;
3281
+ const normalizedDefaultLocale = schema.defaultLocale === void 0 ? void 0 : normalizeLocale(schema.defaultLocale) ?? schema.defaultLocale;
3282
+ if (normalizedLocaleToRemove === normalizedDefaultLocale) {
3181
3283
  throw new Error(`Cannot remove defaultLocale: ${localeToRemove}`);
3182
3284
  }
3183
- const form = removeLocalizedNodeLocale(schema, localeToRemove);
3285
+ const form = removeLocalizedNodeLocale(schema, normalizedLocaleToRemove);
3184
3286
  const fields = schema.fields.map((field) => {
3185
- const localizedField = removeLocalizedNodeLocale(field, localeToRemove);
3287
+ const localizedField = removeLocalizedNodeLocale(field, normalizedLocaleToRemove);
3186
3288
  if (!("options" in localizedField)) return localizedField;
3187
3289
  return {
3188
3290
  ...localizedField,
3189
3291
  options: localizedField.options.map((option) => {
3190
- const translations = removeLocaleRecord(option.translations, localeToRemove);
3191
- const translationMetadata = removeLocaleRecord(option.translationMetadata, localeToRemove);
3292
+ const translations = removeLocaleRecord(option.translations, normalizedLocaleToRemove);
3293
+ const translationMetadata = removeLocaleRecord(option.translationMetadata, normalizedLocaleToRemove);
3192
3294
  const { translations: _translations, translationMetadata: _translationMetadata, ...base } = option;
3193
3295
  return {
3194
3296
  ...base,
@@ -3198,10 +3300,12 @@ var removeLocaleFromSchema = (schema, localeToRemove) => {
3198
3300
  })
3199
3301
  };
3200
3302
  });
3201
- const pages = schema.pages?.map((page) => removeLocalizedNodeLocale(page, localeToRemove));
3303
+ const pages = schema.pages?.map((page) => removeLocalizedNodeLocale(page, normalizedLocaleToRemove));
3202
3304
  return {
3203
3305
  ...form,
3204
- supportedLocales: (schema.supportedLocales ?? []).filter((locale) => locale !== localeToRemove),
3306
+ supportedLocales: (schema.supportedLocales ?? []).filter(
3307
+ (locale) => (normalizeLocale(locale) ?? locale) !== normalizedLocaleToRemove
3308
+ ),
3205
3309
  fields,
3206
3310
  ...pages === void 0 ? {} : { pages }
3207
3311
  };
@@ -3660,6 +3764,9 @@ async function commitVersionTransition(options) {
3660
3764
  0 && (module.exports = {
3661
3765
  DEFAULT_FIELD_TYPE_DEFINITIONS,
3662
3766
  EN_MESSAGES,
3767
+ FormSubmissionError,
3768
+ FormSubmissionMetadataSchema,
3769
+ FormSubmissionWireSchema,
3663
3770
  JA_MESSAGES,
3664
3771
  aggregateResponses,
3665
3772
  applyTransitionPlan,
@@ -3715,6 +3822,7 @@ async function commitVersionTransition(options) {
3715
3822
  resolveLocalizedSchema,
3716
3823
  sanitizeSchema,
3717
3824
  selectVisibleAnswers,
3825
+ toFormSubmissionWire,
3718
3826
  transformFieldType,
3719
3827
  validateAnswers,
3720
3828
  validateFormSchema,
package/dist/index.d.cts CHANGED
@@ -1,8 +1,22 @@
1
+ import { z } from 'zod';
2
+
3
+ type AggregationSkipReason = "missing_field" | "type_mismatch" | "invalid_option" | "unsupported_version" | "locale_mismatch" | "pii_unconfirmed";
4
+ interface AggregationReport {
5
+ readonly totalProcessed: number;
6
+ readonly aggregatedCount: number;
7
+ readonly skippedItems: readonly {
8
+ readonly submissionId: string;
9
+ readonly fieldId: string;
10
+ readonly reason: AggregationSkipReason;
11
+ }[];
12
+ }
13
+
1
14
  type KnownBuilderTranslationKey = "builder.formTitle" | "builder.formDescription" | "builder.completionMessage" | "builder.addQuestion" | "builder.actions.addField" | "builder.actions.deleteField" | "builder.actions.moveUp" | "builder.actions.moveDown" | "builder.actions.add" | "builder.actions.delete" | "builder.actions.edit" | "builder.actions.settings" | "builder.actions.translate" | "builder.actions.close" | "builder.actions.dragHandle" | "builder.fields.selectType" | "builder.fields.typeText" | "builder.fields.typeTextarea" | "builder.fields.typeNumber" | "builder.fields.typeRadio" | "builder.fields.typeCheckbox" | "builder.fields.typeSelect" | "builder.fields.typeRating" | "builder.fields.typeMultiSelect" | "builder.fieldType.text" | "builder.fieldType.textarea" | "builder.fieldType.number" | "builder.fieldType.radio" | "builder.fieldType.checkbox" | "builder.fieldType.select" | "builder.fieldType.rating" | "builder.fieldType.multi-select" | "builder.fieldTypeDescription.text" | "builder.fieldTypeDescription.textarea" | "builder.fieldTypeDescription.number" | "builder.fieldTypeDescription.radio" | "builder.fieldTypeDescription.checkbox" | "builder.fieldTypeDescription.select" | "builder.fieldTypeDescription.rating" | "builder.fieldTypeDescription.multi-select" | "builder.fieldCategory.text" | "builder.fieldCategory.choice" | "builder.fieldCategory.number" | "builder.fieldCategory.advanced" | "builder.required" | "builder.options" | "builder.localization.title" | "builder.localization" | "builder.localization.addLocale" | "builder.localization.selectLocaleToAdd" | "builder.localization.defaultLocale" | "builder.localization.translateAll" | "builder.localization.noLocalesConfigured" | "builder.localization.localesConfiguredSummary" | "builder.localization.allLocalesAdded" | "builder.localization.maxLocalesReached" | "builder.submissionSettings.title" | "builder.submissionSettings.showConfirmation" | "builder.submissionSettings.renderMode" | "builder.formBuilder" | "builder.basicSettings" | "builder.description" | "builder.moveUp" | "builder.moveDown" | "builder.delete" | "builder.deleteAction" | "builder.questionTitle" | "builder.questionTitlePlaceholder" | "builder.newQuestionTitle" | "builder.type" | "builder.minimum" | "builder.maximum" | "builder.minimumLength" | "builder.maximumLength" | "builder.pattern" | "builder.step" | "builder.optionLabel" | "builder.optionLabelPlaceholder" | "builder.newOptionLabel" | "builder.remove" | "builder.addOption" | "builder.displayCondition" | "builder.alwaysVisible" | "builder.conditionOperator" | "builder.conditionValue" | "builder.conditionTrue" | "builder.conditionFalse" | "builder.pages" | "builder.enablePages" | "builder.addPage" | "builder.newPage" | "builder.pageTitle" | "builder.pageDescription" | "builder.pageQuestion" | "builder.questionPage" | "builder.pageCondition" | "builder.unassigned" | "builder.defaultLocale" | "builder.supportedLocales" | "builder.addLocale" | "builder.editLocale" | "builder.autoTranslate" | "builder.translating" | "builder.translationLocale" | "builder.selectLocale" | "builder.selectLocaleToAdd" | "builder.translation" | "builder.translatedFormTitle" | "builder.translatedFormDescription" | "builder.translatedCompletionMessage" | "builder.translatedQuestionTitle" | "builder.translatedDescription" | "builder.translationUnavailable" | "builder.operator.equals" | "builder.operator.not_equals" | "builder.operator.contains" | "builder.operator.not_empty" | "builder.showConfirmationBeforeSubmit" | "builder.confirmationRenderMode";
2
15
  type BuilderTranslationKey = KnownBuilderTranslationKey;
3
16
  type RendererTranslationKey = "renderer.submitButton" | "renderer.submittingButton" | "renderer.retryButton" | "renderer.requiredField" | "renderer.alreadySubmittedTitle" | "renderer.alreadySubmittedMessage" | "renderer.serverErrorSummary" | "renderer.confirmSensitiveDataTitle" | "renderer.confirmSensitiveDataMessage" | "renderer.confirmButton" | "renderer.cancelButton" | "form.submit" | "form.submitting" | "form.back" | "form.next" | "form.step" | "form.draftRestored" | "form.submissionBlocked" | "form.confirmSensitiveData" | "form.confirmSubmission" | "form.cancelSubmission" | "form.yes" | "form.no" | "form.alreadySubmitted" | "form.submitAnother" | "validation.required" | "validation.invalidOption" | "validation.invalidType" | "validation.max" | "validation.maxLength" | "validation.maxSelections" | "validation.min" | "validation.minLength" | "validation.minSelections" | "validation.pattern" | "validation.sensitiveData" | "validation.step" | "validation.unknownField";
4
17
  type TranslationWorkspaceTranslationKey = "workspace.title" | "workspace.status.missing" | "workspace.status.translated" | "workspace.status.stale" | "workspace.status.manual" | "workspace.status.manualStale" | "workspace.errors.localeNotAllowed" | "workspace.errors.maxLocalesExceeded" | "workspace.errors.readOnly" | "workspace.errors.adapterNotConfigured" | "workspace.errors.translationFailed";
5
- type FormEngineTranslationKey = KnownBuilderTranslationKey | RendererTranslationKey | TranslationWorkspaceTranslationKey;
18
+ type TranslationWorkspaceDetailedKey = "workspace.header.title" | "workspace.header.sourceLocale" | "workspace.header.targetLocale" | "workspace.header.translateAll" | "workspace.header.progress" | "workspace.slot.sourceText" | "workspace.slot.translatedText" | "workspace.slot.translateSingle" | "workspace.slot.revertManual" | "workspace.confirm.removeLocaleTitle" | "workspace.confirm.removeLocaleMessage" | "workspace.empty.noTargetLocales" | "workspace.empty.noSlotsToTranslate";
19
+ type FormEngineTranslationKey = KnownBuilderTranslationKey | RendererTranslationKey | TranslationWorkspaceTranslationKey | TranslationWorkspaceDetailedKey;
6
20
  type FormEngineMessages = Partial<Record<FormEngineTranslationKey, string>>;
7
21
 
8
22
  type Result<T, E> = {
@@ -199,6 +213,18 @@ type JsonValue = string | number | boolean | null | readonly JsonValue[] | {
199
213
  interface BaseSubmissionMetadata {
200
214
  readonly [key: string]: JsonValue | undefined;
201
215
  }
216
+ /** A contract or tenant-managed locale and its translation capabilities. */
217
+ interface LocaleOption {
218
+ /** Canonical BCP 47 locale tag. */
219
+ readonly locale: string;
220
+ /** Human-readable locale name. */
221
+ readonly label: string;
222
+ /** Whether automatic translation is allowed for this locale. Defaults to true. */
223
+ readonly translatable?: boolean;
224
+ /** Whether the locale may be removed from the form. Defaults to true. */
225
+ readonly removable?: boolean;
226
+ readonly metadata?: Readonly<Record<string, unknown>>;
227
+ }
202
228
  /** Arbitrary, JSON-serializable data preserved by every form-engine operation. */
203
229
  interface ExtensibleNode {
204
230
  readonly metadata?: Readonly<Record<string, JsonValue>>;
@@ -360,6 +386,16 @@ type FormSubmission<TMeta extends BaseSubmissionMetadata | undefined = undefined
360
386
  } : {
361
387
  readonly metadata: TMeta;
362
388
  });
389
+ /** Clean network and persistence representation of a form submission. */
390
+ interface FormSubmissionWire<TMeta extends BaseSubmissionMetadata = BaseSubmissionMetadata> {
391
+ readonly id: string;
392
+ readonly formId: string;
393
+ readonly formVersion: number;
394
+ readonly values: Record<string, unknown>;
395
+ readonly metadata: TMeta;
396
+ readonly submittedAt: string;
397
+ readonly schemaRevision?: number;
398
+ }
363
399
  interface CreateSubmissionInput<TMeta extends BaseSubmissionMetadata = BaseSubmissionMetadata> {
364
400
  readonly id?: string;
365
401
  readonly formId: string;
@@ -600,9 +636,21 @@ interface ResponseAccumulatorOptions {
600
636
  }
601
637
  declare function createResponseAccumulator(schema: FormSchema, options?: ResponseAccumulatorOptions): ResponseAccumulator;
602
638
  declare function escapeCsvCell(value: string | number | boolean | null | undefined, neutralizeFormulas?: boolean): string;
603
- interface CsvExportOptions {
639
+ interface CsvColumnDefinition<TMeta extends BaseSubmissionMetadata = BaseSubmissionMetadata> {
640
+ readonly key: string;
641
+ readonly header: string;
642
+ readonly getValue: (submission: FormSubmission<TMeta>, schema: FormSchema) => string | number | boolean | null | undefined;
643
+ }
644
+ interface CsvExportOptions<TMeta extends BaseSubmissionMetadata = BaseSubmissionMetadata> {
604
645
  readonly withBom?: boolean;
605
646
  readonly neutralizeFormulas?: boolean;
647
+ /** Alias for withBom used by the public export contract. */
648
+ readonly useBom?: boolean;
649
+ /** Alias for neutralizeFormulas used by the public export contract. */
650
+ readonly preventFormulaInjection?: boolean;
651
+ readonly customColumns?: readonly CsvColumnDefinition<TMeta>[];
652
+ readonly includePiiStatus?: boolean;
653
+ readonly includeLocale?: boolean;
606
654
  }
607
655
  interface CsvColumnDef {
608
656
  readonly header: string;
@@ -629,6 +677,45 @@ interface NodeWritableStream {
629
677
  declare function pipeResponsesToCsvStream(schema: FormSchema, submissions: AsyncIterable<AccumulatorResponse>, writable: WritableStream<Uint8Array> | NodeWritableStream, options?: StreamCsvOptions): Promise<void>;
630
678
  declare function exportResponsesToCsv(schema: FormSchema, responses: readonly FormSubmission[], options?: CsvExportOptions): string;
631
679
 
680
+ interface SensitiveDataFinding {
681
+ readonly fieldId: string;
682
+ readonly type: string;
683
+ readonly start?: number;
684
+ readonly end?: number;
685
+ readonly matchedText?: string;
686
+ readonly maskedText?: string;
687
+ }
688
+ interface PrivacyEngine {
689
+ detect(schema: FormSchema, values: Record<string, unknown>): readonly SensitiveDataFinding[];
690
+ }
691
+ interface SubmissionValidationResult {
692
+ readonly valid: boolean;
693
+ readonly fieldErrors: Readonly<Record<string, string>>;
694
+ readonly formErrors: readonly string[];
695
+ readonly piiFindings?: readonly SensitiveDataFinding[];
696
+ }
697
+ declare function validateAnswers(schema: FormSchema, values: FormValues): AnswerValidationResult;
698
+ declare function validatePageAnswers(schema: FormSchema, pageIndex: number, values: FormValues): AnswerValidationResult;
699
+ declare function validateSubmission<TMeta extends BaseSubmissionMetadata | undefined = undefined>(schema: FormSchema, submission: FormSubmission<TMeta>, options?: {
700
+ readonly privacyEngine?: PrivacyEngine;
701
+ }): SubmissionValidationResult;
702
+
703
+ interface FormSubmissionSerializedError {
704
+ readonly code: "VALIDATION_FAILED" | "PII_CONFIRMATION_REQUIRED" | "SUBMISSION_BLOCKED" | "STORAGE_ERROR";
705
+ readonly messageKey: FormEngineTranslationKey | string;
706
+ readonly messageParams?: Readonly<Record<string, unknown>>;
707
+ readonly fieldErrors: Readonly<Record<string, string>>;
708
+ readonly formErrors: readonly string[];
709
+ readonly piiFindings?: readonly SensitiveDataFinding[];
710
+ readonly piiWarningAcknowledged?: boolean;
711
+ }
712
+ /** Error with a stable, JSON-serializable payload for RPC boundaries. */
713
+ declare class FormSubmissionError extends Error {
714
+ readonly payload: FormSubmissionSerializedError;
715
+ constructor(payload: FormSubmissionSerializedError);
716
+ toJSON(): FormSubmissionSerializedError;
717
+ }
718
+
632
719
  type FormEventType = "response.submitted" | "schema.updated";
633
720
  interface FormEvent<T = unknown> {
634
721
  readonly id: string;
@@ -772,12 +859,28 @@ interface ValidateFormSchemaOptions {
772
859
  declare function validateFormSchema(input: unknown, options?: ValidateFormSchemaOptions): SchemaValidationResult;
773
860
  declare function assertValidFormSchema(input: unknown): asserts input is FormSchema;
774
861
 
862
+ /** Runtime schema for the JSON metadata carried by a submission wire payload. */
863
+ declare const FormSubmissionMetadataSchema: z.ZodRecord<z.ZodString, z.ZodUnknown>;
864
+ /** Runtime schema for the clean, alias-free submission wire format. */
865
+ declare const FormSubmissionWireSchema: z.ZodObject<{
866
+ id: z.ZodString;
867
+ formId: z.ZodString;
868
+ formVersion: z.ZodNumber;
869
+ values: z.ZodRecord<z.ZodString, z.ZodUnknown>;
870
+ metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;
871
+ submittedAt: z.ZodString;
872
+ schemaRevision: z.ZodOptional<z.ZodNumber>;
873
+ }, z.core.$strip>;
874
+ type FormSubmissionWireSchemaType = z.infer<typeof FormSubmissionWireSchema>;
875
+
775
876
  interface CreateSubmissionOptions<TMeta extends BaseSubmissionMetadata = BaseSubmissionMetadata> extends ExtensibleNode {
776
877
  readonly id: string;
777
878
  readonly locale: string;
778
879
  readonly submittedAt: string;
779
880
  readonly metadata?: TMeta & Readonly<Record<string, JsonValue>>;
780
881
  }
882
+ declare function toFormSubmissionWire<TMeta extends BaseSubmissionMetadata>(submission: FormSubmission<TMeta>): FormSubmissionWire<TMeta>;
883
+ declare function toFormSubmissionWire(submission: FormSubmission): FormSubmissionWire;
781
884
  declare function createSubmission(schema: FormSchema, values: FormValues, options: CreateSubmissionOptions): FormSubmission;
782
885
  declare function createSubmission<TMeta extends BaseSubmissionMetadata = BaseSubmissionMetadata>(schema: FormSchema, values: FormValues, options: CreateSubmissionOptions<TMeta>): FormSubmission<TMeta>;
783
886
  declare function createSubmission<TMeta extends BaseSubmissionMetadata = BaseSubmissionMetadata>(input: CreateSubmissionInput<TMeta>): FormSubmission<TMeta>;
@@ -897,29 +1000,6 @@ declare function populateSchemaTranslations(schema: FormSchema, targetLocales: r
897
1000
  }>;
898
1001
  declare function resolveFormTranslation(schema: FormSchema, adapter: AsyncTranslationAdapter, targetLocale: string, sourceLocale?: string): Promise<FormSchema>;
899
1002
 
900
- interface SensitiveDataFinding {
901
- readonly fieldId: string;
902
- readonly type: string;
903
- readonly start?: number;
904
- readonly end?: number;
905
- readonly matchedText?: string;
906
- readonly maskedText?: string;
907
- }
908
- interface PrivacyEngine {
909
- detect(schema: FormSchema, values: Record<string, unknown>): readonly SensitiveDataFinding[];
910
- }
911
- interface SubmissionValidationResult {
912
- readonly valid: boolean;
913
- readonly fieldErrors: Readonly<Record<string, string>>;
914
- readonly formErrors: readonly string[];
915
- readonly piiFindings?: readonly SensitiveDataFinding[];
916
- }
917
- declare function validateAnswers(schema: FormSchema, values: FormValues): AnswerValidationResult;
918
- declare function validatePageAnswers(schema: FormSchema, pageIndex: number, values: FormValues): AnswerValidationResult;
919
- declare function validateSubmission<TMeta extends BaseSubmissionMetadata | undefined = undefined>(schema: FormSchema, submission: FormSubmission<TMeta>, options?: {
920
- readonly privacyEngine?: PrivacyEngine;
921
- }): SubmissionValidationResult;
922
-
923
1003
  declare function isDisplayConditionGroupSatisfied(group: DisplayConditionGroup, currentAnswers: Readonly<Record<string, unknown>>): boolean;
924
1004
  declare function isQuestionVisible(question: FormField, currentAnswers: Readonly<Record<string, unknown>>): boolean;
925
1005
  declare function isDisplayConditionSatisfied(condition: DisplayCondition | undefined, currentAnswers: Readonly<Record<string, unknown>>): boolean;
@@ -927,4 +1007,4 @@ declare function calculatePageVisibility(schema: FormSchema, currentAnswers: Rea
927
1007
  declare function calculateFieldVisibility(schema: FormSchema, currentAnswers: Readonly<Record<string, unknown>>): Readonly<Record<string, boolean>>;
928
1008
  declare function selectVisibleAnswers(schema: FormSchema, currentAnswers: FormValues): FormValues;
929
1009
 
930
- export { type AccumulatorReport, type AccumulatorResponse, type AccumulatorSkipReason, type AnswerValidationResult, type AsyncTranslationAdapter, type BaseField, type BaseFieldConstraintRule, type BaseSubmissionMetadata, type BuilderTranslationKey, type CanonicalTranslationMetadata, type CheckboxField, type CheckboxQuestionAggregate, type ChoiceDistributionEntry, type ChoiceFieldConstraintRule, type ChoiceOption, type ChoiceQuestionAggregate, type CloneVersionOptions, type CollectedLocales, type CommitVersionTransitionOptions, type ConditionOperator, type ConditionValue, type CreateSubmissionInput, type CreateSubmissionOptions, type CrossTabulationResult, type CsvColumnContext, type CsvColumnDef, type CsvExportOptions, type CursorPagingOptions, DEFAULT_FIELD_TYPE_DEFINITIONS, type DeleteDraftOptions, type DisplayCondition, type DisplayConditionGroup, type DisplayRule, EN_MESSAGES, type ExtensibleNode, type FieldConstraintRule, type FieldDisplayCondition, type FieldOption, type FieldType, type FieldTypeDefinition, type FormAnalytics, type FormEngineMessages, type FormEngineTranslationKey, type FormEngineTranslator, type FormEngineTranslatorOptions, type FormEvent, type FormEventType, type FormField, type FormPage, type FormPolicy, type FormResponse, type FormSchema, type FormStorageAdapter, type FormSubmission, type FormSubmissionSettings, type FormValue, type FormValues, type FormVersionRecord, type FormVersionState, type FormVersionStatus, type FormVersionTransitionPlan, JA_MESSAGES, type JsonValue, type KnownBuilderTranslationKey, type LegacyTranslationMetadata, type LocalizedText, type MigrateSchemaTranslationMetadataOptions, type MultiSelectField, type NodeWritableStream, type NumberField, type NumberQuestionAggregate, type NumericSummary, type OptionAggregate, type PagedSubmissionStorageAdapter, type PaginatedResult, type PaginationIteratorOptions, type PopulateTranslationOptions, type PopulateTranslationsOptions, type PrivacyEngine, type PublishDraftOptions, type PublishDraftResult, type Question, type QuestionAggregate, type QuestionType, type RatingField, type RatingFieldConstraintRule, type RendererTranslationKey, type ResponseAccumulator, type ResponseAccumulatorOptions, type Result, type SanitizeSchemaOptions, type SchemaIssue, type SchemaStructureIssue, type SchemaStructureIssueType, type SchemaTranslations, type SchemaValidationResult, type SelectField, type SensitiveDataFinding, type StorageAdapter, type StorageCommitError, type StorageCursor, type StorageFilterCriteria, type StreamCsvOptions, type SubmissionCursorPayload, type SubmissionCursorValue, type SubmissionFilter, type SubmissionPage, type SubmissionPageQueryOptions, type SubmissionQueryOptions, type SubmissionValidationResult, type TextAnswerCursorPayload, type TextAnswerCursorValue, type TextAnswerItem, type TextAnswerPage, type TextAnswerPageQueryOptions, type TextField, type TextFieldConstraintRule, type TextQuestionAggregate, type TranslationAdapter, type TranslationMetadataMigrator, type TranslationMigrationContext, type TranslationProviderError, type TranslationReport, type TranslationSlot, type TranslationStatus, type TranslationWorkspaceTranslationKey, type ValidateFormSchemaOptions, type ValidationCode, type ValidationError, type ValidationIssue, type VersionTransitionContext, type VersionTransitionError, type VersionTransitionEvent, type VersionTransitionPlan, type VersionedFormStorageAdapter, type WebhookConfig, type WebhookDispatchResult, aggregateResponses, applyTransitionPlan, assertValidFormSchema, assertVersionMutable, calculateChoiceDistribution, calculateCrossTabulation, calculateFieldVisibility, calculateNumericSummary, calculatePageVisibility, cloneVersionToDraft, collectSchemaLocales, collectTranslationSlots, commitVersionTransition, computeSourceTextHash, createCloneTransitionPlan, createDeleteDraftTransitionPlan, createFormEngineTranslator, createPublishTransitionPlan, createResponseAccumulator, createSubmission, decodeStorageSubmissionCursor, decodeStorageTextAnswerCursor, decodeSubmissionCursor, decodeTextAnswerCursor, deleteDraft, dispatchWebhook, encodeStorageSubmissionCursor, encodeStorageTextAnswerCursor, encodeSubmissionCursor, encodeTextAnswerCursor, escapeCsvCell, exportResponsesToCsv, exportResponsesToCsvStream, getTranslationStatus, isDisplayConditionGroupSatisfied, isDisplayConditionSatisfied, isManualTranslationMetadata, isQuestionVisible, iterateSubmissionPages, jsonValuesEqual, matchesSubmissionFilter, matchesSubmissionPageFilters, migrateSchemaTranslationMetadata, normalizeLocale, normalizeSubmissionPageSize, paginateWithFilter, pipeResponsesToCsvStream, populateSchemaTranslations, publishDraft, removeLocaleFromSchema, resolveFormTranslation, resolveLocalizedSchema, sanitizeSchema, selectVisibleAnswers, transformFieldType, validateAnswers, validateFormSchema, validatePageAnswers, validateSchemaStructure, validateSubmission };
1010
+ export { type AccumulatorReport, type AccumulatorResponse, type AccumulatorSkipReason, type AggregationReport, type AggregationSkipReason, type AnswerValidationResult, type AsyncTranslationAdapter, type BaseField, type BaseFieldConstraintRule, type BaseSubmissionMetadata, type BuilderTranslationKey, type CanonicalTranslationMetadata, type CheckboxField, type CheckboxQuestionAggregate, type ChoiceDistributionEntry, type ChoiceFieldConstraintRule, type ChoiceOption, type ChoiceQuestionAggregate, type CloneVersionOptions, type CollectedLocales, type CommitVersionTransitionOptions, type ConditionOperator, type ConditionValue, type CreateSubmissionInput, type CreateSubmissionOptions, type CrossTabulationResult, type CsvColumnContext, type CsvColumnDef, type CsvColumnDefinition, type CsvExportOptions, type CursorPagingOptions, DEFAULT_FIELD_TYPE_DEFINITIONS, type DeleteDraftOptions, type DisplayCondition, type DisplayConditionGroup, type DisplayRule, EN_MESSAGES, type ExtensibleNode, type FieldConstraintRule, type FieldDisplayCondition, type FieldOption, type FieldType, type FieldTypeDefinition, type FormAnalytics, type FormEngineMessages, type FormEngineTranslationKey, type FormEngineTranslator, type FormEngineTranslatorOptions, type FormEvent, type FormEventType, type FormField, type FormPage, type FormPolicy, type FormResponse, type FormSchema, type FormStorageAdapter, type FormSubmission, FormSubmissionError, FormSubmissionMetadataSchema, type FormSubmissionSerializedError, type FormSubmissionSettings, type FormSubmissionWire, FormSubmissionWireSchema, type FormSubmissionWireSchemaType, type FormValue, type FormValues, type FormVersionRecord, type FormVersionState, type FormVersionStatus, type FormVersionTransitionPlan, JA_MESSAGES, type JsonValue, type KnownBuilderTranslationKey, type LegacyTranslationMetadata, type LocaleOption, type LocalizedText, type MigrateSchemaTranslationMetadataOptions, type MultiSelectField, type NodeWritableStream, type NumberField, type NumberQuestionAggregate, type NumericSummary, type OptionAggregate, type PagedSubmissionStorageAdapter, type PaginatedResult, type PaginationIteratorOptions, type PopulateTranslationOptions, type PopulateTranslationsOptions, type PrivacyEngine, type PublishDraftOptions, type PublishDraftResult, type Question, type QuestionAggregate, type QuestionType, type RatingField, type RatingFieldConstraintRule, type RendererTranslationKey, type ResponseAccumulator, type ResponseAccumulatorOptions, type Result, type SanitizeSchemaOptions, type SchemaIssue, type SchemaStructureIssue, type SchemaStructureIssueType, type SchemaTranslations, type SchemaValidationResult, type SelectField, type SensitiveDataFinding, type StorageAdapter, type StorageCommitError, type StorageCursor, type StorageFilterCriteria, type StreamCsvOptions, type SubmissionCursorPayload, type SubmissionCursorValue, type SubmissionFilter, type SubmissionPage, type SubmissionPageQueryOptions, type SubmissionQueryOptions, type SubmissionValidationResult, type TextAnswerCursorPayload, type TextAnswerCursorValue, type TextAnswerItem, type TextAnswerPage, type TextAnswerPageQueryOptions, type TextField, type TextFieldConstraintRule, type TextQuestionAggregate, type TranslationAdapter, type TranslationMetadataMigrator, type TranslationMigrationContext, type TranslationProviderError, type TranslationReport, type TranslationSlot, type TranslationStatus, type TranslationWorkspaceDetailedKey, type TranslationWorkspaceTranslationKey, type ValidateFormSchemaOptions, type ValidationCode, type ValidationError, type ValidationIssue, type VersionTransitionContext, type VersionTransitionError, type VersionTransitionEvent, type VersionTransitionPlan, type VersionedFormStorageAdapter, type WebhookConfig, type WebhookDispatchResult, aggregateResponses, applyTransitionPlan, assertValidFormSchema, assertVersionMutable, calculateChoiceDistribution, calculateCrossTabulation, calculateFieldVisibility, calculateNumericSummary, calculatePageVisibility, cloneVersionToDraft, collectSchemaLocales, collectTranslationSlots, commitVersionTransition, computeSourceTextHash, createCloneTransitionPlan, createDeleteDraftTransitionPlan, createFormEngineTranslator, createPublishTransitionPlan, createResponseAccumulator, createSubmission, decodeStorageSubmissionCursor, decodeStorageTextAnswerCursor, decodeSubmissionCursor, decodeTextAnswerCursor, deleteDraft, dispatchWebhook, encodeStorageSubmissionCursor, encodeStorageTextAnswerCursor, encodeSubmissionCursor, encodeTextAnswerCursor, escapeCsvCell, exportResponsesToCsv, exportResponsesToCsvStream, getTranslationStatus, isDisplayConditionGroupSatisfied, isDisplayConditionSatisfied, isManualTranslationMetadata, isQuestionVisible, iterateSubmissionPages, jsonValuesEqual, matchesSubmissionFilter, matchesSubmissionPageFilters, migrateSchemaTranslationMetadata, normalizeLocale, normalizeSubmissionPageSize, paginateWithFilter, pipeResponsesToCsvStream, populateSchemaTranslations, publishDraft, removeLocaleFromSchema, resolveFormTranslation, resolveLocalizedSchema, sanitizeSchema, selectVisibleAnswers, toFormSubmissionWire, transformFieldType, validateAnswers, validateFormSchema, validatePageAnswers, validateSchemaStructure, validateSubmission };
package/dist/index.d.ts CHANGED
@@ -1,8 +1,22 @@
1
+ import { z } from 'zod';
2
+
3
+ type AggregationSkipReason = "missing_field" | "type_mismatch" | "invalid_option" | "unsupported_version" | "locale_mismatch" | "pii_unconfirmed";
4
+ interface AggregationReport {
5
+ readonly totalProcessed: number;
6
+ readonly aggregatedCount: number;
7
+ readonly skippedItems: readonly {
8
+ readonly submissionId: string;
9
+ readonly fieldId: string;
10
+ readonly reason: AggregationSkipReason;
11
+ }[];
12
+ }
13
+
1
14
  type KnownBuilderTranslationKey = "builder.formTitle" | "builder.formDescription" | "builder.completionMessage" | "builder.addQuestion" | "builder.actions.addField" | "builder.actions.deleteField" | "builder.actions.moveUp" | "builder.actions.moveDown" | "builder.actions.add" | "builder.actions.delete" | "builder.actions.edit" | "builder.actions.settings" | "builder.actions.translate" | "builder.actions.close" | "builder.actions.dragHandle" | "builder.fields.selectType" | "builder.fields.typeText" | "builder.fields.typeTextarea" | "builder.fields.typeNumber" | "builder.fields.typeRadio" | "builder.fields.typeCheckbox" | "builder.fields.typeSelect" | "builder.fields.typeRating" | "builder.fields.typeMultiSelect" | "builder.fieldType.text" | "builder.fieldType.textarea" | "builder.fieldType.number" | "builder.fieldType.radio" | "builder.fieldType.checkbox" | "builder.fieldType.select" | "builder.fieldType.rating" | "builder.fieldType.multi-select" | "builder.fieldTypeDescription.text" | "builder.fieldTypeDescription.textarea" | "builder.fieldTypeDescription.number" | "builder.fieldTypeDescription.radio" | "builder.fieldTypeDescription.checkbox" | "builder.fieldTypeDescription.select" | "builder.fieldTypeDescription.rating" | "builder.fieldTypeDescription.multi-select" | "builder.fieldCategory.text" | "builder.fieldCategory.choice" | "builder.fieldCategory.number" | "builder.fieldCategory.advanced" | "builder.required" | "builder.options" | "builder.localization.title" | "builder.localization" | "builder.localization.addLocale" | "builder.localization.selectLocaleToAdd" | "builder.localization.defaultLocale" | "builder.localization.translateAll" | "builder.localization.noLocalesConfigured" | "builder.localization.localesConfiguredSummary" | "builder.localization.allLocalesAdded" | "builder.localization.maxLocalesReached" | "builder.submissionSettings.title" | "builder.submissionSettings.showConfirmation" | "builder.submissionSettings.renderMode" | "builder.formBuilder" | "builder.basicSettings" | "builder.description" | "builder.moveUp" | "builder.moveDown" | "builder.delete" | "builder.deleteAction" | "builder.questionTitle" | "builder.questionTitlePlaceholder" | "builder.newQuestionTitle" | "builder.type" | "builder.minimum" | "builder.maximum" | "builder.minimumLength" | "builder.maximumLength" | "builder.pattern" | "builder.step" | "builder.optionLabel" | "builder.optionLabelPlaceholder" | "builder.newOptionLabel" | "builder.remove" | "builder.addOption" | "builder.displayCondition" | "builder.alwaysVisible" | "builder.conditionOperator" | "builder.conditionValue" | "builder.conditionTrue" | "builder.conditionFalse" | "builder.pages" | "builder.enablePages" | "builder.addPage" | "builder.newPage" | "builder.pageTitle" | "builder.pageDescription" | "builder.pageQuestion" | "builder.questionPage" | "builder.pageCondition" | "builder.unassigned" | "builder.defaultLocale" | "builder.supportedLocales" | "builder.addLocale" | "builder.editLocale" | "builder.autoTranslate" | "builder.translating" | "builder.translationLocale" | "builder.selectLocale" | "builder.selectLocaleToAdd" | "builder.translation" | "builder.translatedFormTitle" | "builder.translatedFormDescription" | "builder.translatedCompletionMessage" | "builder.translatedQuestionTitle" | "builder.translatedDescription" | "builder.translationUnavailable" | "builder.operator.equals" | "builder.operator.not_equals" | "builder.operator.contains" | "builder.operator.not_empty" | "builder.showConfirmationBeforeSubmit" | "builder.confirmationRenderMode";
2
15
  type BuilderTranslationKey = KnownBuilderTranslationKey;
3
16
  type RendererTranslationKey = "renderer.submitButton" | "renderer.submittingButton" | "renderer.retryButton" | "renderer.requiredField" | "renderer.alreadySubmittedTitle" | "renderer.alreadySubmittedMessage" | "renderer.serverErrorSummary" | "renderer.confirmSensitiveDataTitle" | "renderer.confirmSensitiveDataMessage" | "renderer.confirmButton" | "renderer.cancelButton" | "form.submit" | "form.submitting" | "form.back" | "form.next" | "form.step" | "form.draftRestored" | "form.submissionBlocked" | "form.confirmSensitiveData" | "form.confirmSubmission" | "form.cancelSubmission" | "form.yes" | "form.no" | "form.alreadySubmitted" | "form.submitAnother" | "validation.required" | "validation.invalidOption" | "validation.invalidType" | "validation.max" | "validation.maxLength" | "validation.maxSelections" | "validation.min" | "validation.minLength" | "validation.minSelections" | "validation.pattern" | "validation.sensitiveData" | "validation.step" | "validation.unknownField";
4
17
  type TranslationWorkspaceTranslationKey = "workspace.title" | "workspace.status.missing" | "workspace.status.translated" | "workspace.status.stale" | "workspace.status.manual" | "workspace.status.manualStale" | "workspace.errors.localeNotAllowed" | "workspace.errors.maxLocalesExceeded" | "workspace.errors.readOnly" | "workspace.errors.adapterNotConfigured" | "workspace.errors.translationFailed";
5
- type FormEngineTranslationKey = KnownBuilderTranslationKey | RendererTranslationKey | TranslationWorkspaceTranslationKey;
18
+ type TranslationWorkspaceDetailedKey = "workspace.header.title" | "workspace.header.sourceLocale" | "workspace.header.targetLocale" | "workspace.header.translateAll" | "workspace.header.progress" | "workspace.slot.sourceText" | "workspace.slot.translatedText" | "workspace.slot.translateSingle" | "workspace.slot.revertManual" | "workspace.confirm.removeLocaleTitle" | "workspace.confirm.removeLocaleMessage" | "workspace.empty.noTargetLocales" | "workspace.empty.noSlotsToTranslate";
19
+ type FormEngineTranslationKey = KnownBuilderTranslationKey | RendererTranslationKey | TranslationWorkspaceTranslationKey | TranslationWorkspaceDetailedKey;
6
20
  type FormEngineMessages = Partial<Record<FormEngineTranslationKey, string>>;
7
21
 
8
22
  type Result<T, E> = {
@@ -199,6 +213,18 @@ type JsonValue = string | number | boolean | null | readonly JsonValue[] | {
199
213
  interface BaseSubmissionMetadata {
200
214
  readonly [key: string]: JsonValue | undefined;
201
215
  }
216
+ /** A contract or tenant-managed locale and its translation capabilities. */
217
+ interface LocaleOption {
218
+ /** Canonical BCP 47 locale tag. */
219
+ readonly locale: string;
220
+ /** Human-readable locale name. */
221
+ readonly label: string;
222
+ /** Whether automatic translation is allowed for this locale. Defaults to true. */
223
+ readonly translatable?: boolean;
224
+ /** Whether the locale may be removed from the form. Defaults to true. */
225
+ readonly removable?: boolean;
226
+ readonly metadata?: Readonly<Record<string, unknown>>;
227
+ }
202
228
  /** Arbitrary, JSON-serializable data preserved by every form-engine operation. */
203
229
  interface ExtensibleNode {
204
230
  readonly metadata?: Readonly<Record<string, JsonValue>>;
@@ -360,6 +386,16 @@ type FormSubmission<TMeta extends BaseSubmissionMetadata | undefined = undefined
360
386
  } : {
361
387
  readonly metadata: TMeta;
362
388
  });
389
+ /** Clean network and persistence representation of a form submission. */
390
+ interface FormSubmissionWire<TMeta extends BaseSubmissionMetadata = BaseSubmissionMetadata> {
391
+ readonly id: string;
392
+ readonly formId: string;
393
+ readonly formVersion: number;
394
+ readonly values: Record<string, unknown>;
395
+ readonly metadata: TMeta;
396
+ readonly submittedAt: string;
397
+ readonly schemaRevision?: number;
398
+ }
363
399
  interface CreateSubmissionInput<TMeta extends BaseSubmissionMetadata = BaseSubmissionMetadata> {
364
400
  readonly id?: string;
365
401
  readonly formId: string;
@@ -600,9 +636,21 @@ interface ResponseAccumulatorOptions {
600
636
  }
601
637
  declare function createResponseAccumulator(schema: FormSchema, options?: ResponseAccumulatorOptions): ResponseAccumulator;
602
638
  declare function escapeCsvCell(value: string | number | boolean | null | undefined, neutralizeFormulas?: boolean): string;
603
- interface CsvExportOptions {
639
+ interface CsvColumnDefinition<TMeta extends BaseSubmissionMetadata = BaseSubmissionMetadata> {
640
+ readonly key: string;
641
+ readonly header: string;
642
+ readonly getValue: (submission: FormSubmission<TMeta>, schema: FormSchema) => string | number | boolean | null | undefined;
643
+ }
644
+ interface CsvExportOptions<TMeta extends BaseSubmissionMetadata = BaseSubmissionMetadata> {
604
645
  readonly withBom?: boolean;
605
646
  readonly neutralizeFormulas?: boolean;
647
+ /** Alias for withBom used by the public export contract. */
648
+ readonly useBom?: boolean;
649
+ /** Alias for neutralizeFormulas used by the public export contract. */
650
+ readonly preventFormulaInjection?: boolean;
651
+ readonly customColumns?: readonly CsvColumnDefinition<TMeta>[];
652
+ readonly includePiiStatus?: boolean;
653
+ readonly includeLocale?: boolean;
606
654
  }
607
655
  interface CsvColumnDef {
608
656
  readonly header: string;
@@ -629,6 +677,45 @@ interface NodeWritableStream {
629
677
  declare function pipeResponsesToCsvStream(schema: FormSchema, submissions: AsyncIterable<AccumulatorResponse>, writable: WritableStream<Uint8Array> | NodeWritableStream, options?: StreamCsvOptions): Promise<void>;
630
678
  declare function exportResponsesToCsv(schema: FormSchema, responses: readonly FormSubmission[], options?: CsvExportOptions): string;
631
679
 
680
+ interface SensitiveDataFinding {
681
+ readonly fieldId: string;
682
+ readonly type: string;
683
+ readonly start?: number;
684
+ readonly end?: number;
685
+ readonly matchedText?: string;
686
+ readonly maskedText?: string;
687
+ }
688
+ interface PrivacyEngine {
689
+ detect(schema: FormSchema, values: Record<string, unknown>): readonly SensitiveDataFinding[];
690
+ }
691
+ interface SubmissionValidationResult {
692
+ readonly valid: boolean;
693
+ readonly fieldErrors: Readonly<Record<string, string>>;
694
+ readonly formErrors: readonly string[];
695
+ readonly piiFindings?: readonly SensitiveDataFinding[];
696
+ }
697
+ declare function validateAnswers(schema: FormSchema, values: FormValues): AnswerValidationResult;
698
+ declare function validatePageAnswers(schema: FormSchema, pageIndex: number, values: FormValues): AnswerValidationResult;
699
+ declare function validateSubmission<TMeta extends BaseSubmissionMetadata | undefined = undefined>(schema: FormSchema, submission: FormSubmission<TMeta>, options?: {
700
+ readonly privacyEngine?: PrivacyEngine;
701
+ }): SubmissionValidationResult;
702
+
703
+ interface FormSubmissionSerializedError {
704
+ readonly code: "VALIDATION_FAILED" | "PII_CONFIRMATION_REQUIRED" | "SUBMISSION_BLOCKED" | "STORAGE_ERROR";
705
+ readonly messageKey: FormEngineTranslationKey | string;
706
+ readonly messageParams?: Readonly<Record<string, unknown>>;
707
+ readonly fieldErrors: Readonly<Record<string, string>>;
708
+ readonly formErrors: readonly string[];
709
+ readonly piiFindings?: readonly SensitiveDataFinding[];
710
+ readonly piiWarningAcknowledged?: boolean;
711
+ }
712
+ /** Error with a stable, JSON-serializable payload for RPC boundaries. */
713
+ declare class FormSubmissionError extends Error {
714
+ readonly payload: FormSubmissionSerializedError;
715
+ constructor(payload: FormSubmissionSerializedError);
716
+ toJSON(): FormSubmissionSerializedError;
717
+ }
718
+
632
719
  type FormEventType = "response.submitted" | "schema.updated";
633
720
  interface FormEvent<T = unknown> {
634
721
  readonly id: string;
@@ -772,12 +859,28 @@ interface ValidateFormSchemaOptions {
772
859
  declare function validateFormSchema(input: unknown, options?: ValidateFormSchemaOptions): SchemaValidationResult;
773
860
  declare function assertValidFormSchema(input: unknown): asserts input is FormSchema;
774
861
 
862
+ /** Runtime schema for the JSON metadata carried by a submission wire payload. */
863
+ declare const FormSubmissionMetadataSchema: z.ZodRecord<z.ZodString, z.ZodUnknown>;
864
+ /** Runtime schema for the clean, alias-free submission wire format. */
865
+ declare const FormSubmissionWireSchema: z.ZodObject<{
866
+ id: z.ZodString;
867
+ formId: z.ZodString;
868
+ formVersion: z.ZodNumber;
869
+ values: z.ZodRecord<z.ZodString, z.ZodUnknown>;
870
+ metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;
871
+ submittedAt: z.ZodString;
872
+ schemaRevision: z.ZodOptional<z.ZodNumber>;
873
+ }, z.core.$strip>;
874
+ type FormSubmissionWireSchemaType = z.infer<typeof FormSubmissionWireSchema>;
875
+
775
876
  interface CreateSubmissionOptions<TMeta extends BaseSubmissionMetadata = BaseSubmissionMetadata> extends ExtensibleNode {
776
877
  readonly id: string;
777
878
  readonly locale: string;
778
879
  readonly submittedAt: string;
779
880
  readonly metadata?: TMeta & Readonly<Record<string, JsonValue>>;
780
881
  }
882
+ declare function toFormSubmissionWire<TMeta extends BaseSubmissionMetadata>(submission: FormSubmission<TMeta>): FormSubmissionWire<TMeta>;
883
+ declare function toFormSubmissionWire(submission: FormSubmission): FormSubmissionWire;
781
884
  declare function createSubmission(schema: FormSchema, values: FormValues, options: CreateSubmissionOptions): FormSubmission;
782
885
  declare function createSubmission<TMeta extends BaseSubmissionMetadata = BaseSubmissionMetadata>(schema: FormSchema, values: FormValues, options: CreateSubmissionOptions<TMeta>): FormSubmission<TMeta>;
783
886
  declare function createSubmission<TMeta extends BaseSubmissionMetadata = BaseSubmissionMetadata>(input: CreateSubmissionInput<TMeta>): FormSubmission<TMeta>;
@@ -897,29 +1000,6 @@ declare function populateSchemaTranslations(schema: FormSchema, targetLocales: r
897
1000
  }>;
898
1001
  declare function resolveFormTranslation(schema: FormSchema, adapter: AsyncTranslationAdapter, targetLocale: string, sourceLocale?: string): Promise<FormSchema>;
899
1002
 
900
- interface SensitiveDataFinding {
901
- readonly fieldId: string;
902
- readonly type: string;
903
- readonly start?: number;
904
- readonly end?: number;
905
- readonly matchedText?: string;
906
- readonly maskedText?: string;
907
- }
908
- interface PrivacyEngine {
909
- detect(schema: FormSchema, values: Record<string, unknown>): readonly SensitiveDataFinding[];
910
- }
911
- interface SubmissionValidationResult {
912
- readonly valid: boolean;
913
- readonly fieldErrors: Readonly<Record<string, string>>;
914
- readonly formErrors: readonly string[];
915
- readonly piiFindings?: readonly SensitiveDataFinding[];
916
- }
917
- declare function validateAnswers(schema: FormSchema, values: FormValues): AnswerValidationResult;
918
- declare function validatePageAnswers(schema: FormSchema, pageIndex: number, values: FormValues): AnswerValidationResult;
919
- declare function validateSubmission<TMeta extends BaseSubmissionMetadata | undefined = undefined>(schema: FormSchema, submission: FormSubmission<TMeta>, options?: {
920
- readonly privacyEngine?: PrivacyEngine;
921
- }): SubmissionValidationResult;
922
-
923
1003
  declare function isDisplayConditionGroupSatisfied(group: DisplayConditionGroup, currentAnswers: Readonly<Record<string, unknown>>): boolean;
924
1004
  declare function isQuestionVisible(question: FormField, currentAnswers: Readonly<Record<string, unknown>>): boolean;
925
1005
  declare function isDisplayConditionSatisfied(condition: DisplayCondition | undefined, currentAnswers: Readonly<Record<string, unknown>>): boolean;
@@ -927,4 +1007,4 @@ declare function calculatePageVisibility(schema: FormSchema, currentAnswers: Rea
927
1007
  declare function calculateFieldVisibility(schema: FormSchema, currentAnswers: Readonly<Record<string, unknown>>): Readonly<Record<string, boolean>>;
928
1008
  declare function selectVisibleAnswers(schema: FormSchema, currentAnswers: FormValues): FormValues;
929
1009
 
930
- export { type AccumulatorReport, type AccumulatorResponse, type AccumulatorSkipReason, type AnswerValidationResult, type AsyncTranslationAdapter, type BaseField, type BaseFieldConstraintRule, type BaseSubmissionMetadata, type BuilderTranslationKey, type CanonicalTranslationMetadata, type CheckboxField, type CheckboxQuestionAggregate, type ChoiceDistributionEntry, type ChoiceFieldConstraintRule, type ChoiceOption, type ChoiceQuestionAggregate, type CloneVersionOptions, type CollectedLocales, type CommitVersionTransitionOptions, type ConditionOperator, type ConditionValue, type CreateSubmissionInput, type CreateSubmissionOptions, type CrossTabulationResult, type CsvColumnContext, type CsvColumnDef, type CsvExportOptions, type CursorPagingOptions, DEFAULT_FIELD_TYPE_DEFINITIONS, type DeleteDraftOptions, type DisplayCondition, type DisplayConditionGroup, type DisplayRule, EN_MESSAGES, type ExtensibleNode, type FieldConstraintRule, type FieldDisplayCondition, type FieldOption, type FieldType, type FieldTypeDefinition, type FormAnalytics, type FormEngineMessages, type FormEngineTranslationKey, type FormEngineTranslator, type FormEngineTranslatorOptions, type FormEvent, type FormEventType, type FormField, type FormPage, type FormPolicy, type FormResponse, type FormSchema, type FormStorageAdapter, type FormSubmission, type FormSubmissionSettings, type FormValue, type FormValues, type FormVersionRecord, type FormVersionState, type FormVersionStatus, type FormVersionTransitionPlan, JA_MESSAGES, type JsonValue, type KnownBuilderTranslationKey, type LegacyTranslationMetadata, type LocalizedText, type MigrateSchemaTranslationMetadataOptions, type MultiSelectField, type NodeWritableStream, type NumberField, type NumberQuestionAggregate, type NumericSummary, type OptionAggregate, type PagedSubmissionStorageAdapter, type PaginatedResult, type PaginationIteratorOptions, type PopulateTranslationOptions, type PopulateTranslationsOptions, type PrivacyEngine, type PublishDraftOptions, type PublishDraftResult, type Question, type QuestionAggregate, type QuestionType, type RatingField, type RatingFieldConstraintRule, type RendererTranslationKey, type ResponseAccumulator, type ResponseAccumulatorOptions, type Result, type SanitizeSchemaOptions, type SchemaIssue, type SchemaStructureIssue, type SchemaStructureIssueType, type SchemaTranslations, type SchemaValidationResult, type SelectField, type SensitiveDataFinding, type StorageAdapter, type StorageCommitError, type StorageCursor, type StorageFilterCriteria, type StreamCsvOptions, type SubmissionCursorPayload, type SubmissionCursorValue, type SubmissionFilter, type SubmissionPage, type SubmissionPageQueryOptions, type SubmissionQueryOptions, type SubmissionValidationResult, type TextAnswerCursorPayload, type TextAnswerCursorValue, type TextAnswerItem, type TextAnswerPage, type TextAnswerPageQueryOptions, type TextField, type TextFieldConstraintRule, type TextQuestionAggregate, type TranslationAdapter, type TranslationMetadataMigrator, type TranslationMigrationContext, type TranslationProviderError, type TranslationReport, type TranslationSlot, type TranslationStatus, type TranslationWorkspaceTranslationKey, type ValidateFormSchemaOptions, type ValidationCode, type ValidationError, type ValidationIssue, type VersionTransitionContext, type VersionTransitionError, type VersionTransitionEvent, type VersionTransitionPlan, type VersionedFormStorageAdapter, type WebhookConfig, type WebhookDispatchResult, aggregateResponses, applyTransitionPlan, assertValidFormSchema, assertVersionMutable, calculateChoiceDistribution, calculateCrossTabulation, calculateFieldVisibility, calculateNumericSummary, calculatePageVisibility, cloneVersionToDraft, collectSchemaLocales, collectTranslationSlots, commitVersionTransition, computeSourceTextHash, createCloneTransitionPlan, createDeleteDraftTransitionPlan, createFormEngineTranslator, createPublishTransitionPlan, createResponseAccumulator, createSubmission, decodeStorageSubmissionCursor, decodeStorageTextAnswerCursor, decodeSubmissionCursor, decodeTextAnswerCursor, deleteDraft, dispatchWebhook, encodeStorageSubmissionCursor, encodeStorageTextAnswerCursor, encodeSubmissionCursor, encodeTextAnswerCursor, escapeCsvCell, exportResponsesToCsv, exportResponsesToCsvStream, getTranslationStatus, isDisplayConditionGroupSatisfied, isDisplayConditionSatisfied, isManualTranslationMetadata, isQuestionVisible, iterateSubmissionPages, jsonValuesEqual, matchesSubmissionFilter, matchesSubmissionPageFilters, migrateSchemaTranslationMetadata, normalizeLocale, normalizeSubmissionPageSize, paginateWithFilter, pipeResponsesToCsvStream, populateSchemaTranslations, publishDraft, removeLocaleFromSchema, resolveFormTranslation, resolveLocalizedSchema, sanitizeSchema, selectVisibleAnswers, transformFieldType, validateAnswers, validateFormSchema, validatePageAnswers, validateSchemaStructure, validateSubmission };
1010
+ export { type AccumulatorReport, type AccumulatorResponse, type AccumulatorSkipReason, type AggregationReport, type AggregationSkipReason, type AnswerValidationResult, type AsyncTranslationAdapter, type BaseField, type BaseFieldConstraintRule, type BaseSubmissionMetadata, type BuilderTranslationKey, type CanonicalTranslationMetadata, type CheckboxField, type CheckboxQuestionAggregate, type ChoiceDistributionEntry, type ChoiceFieldConstraintRule, type ChoiceOption, type ChoiceQuestionAggregate, type CloneVersionOptions, type CollectedLocales, type CommitVersionTransitionOptions, type ConditionOperator, type ConditionValue, type CreateSubmissionInput, type CreateSubmissionOptions, type CrossTabulationResult, type CsvColumnContext, type CsvColumnDef, type CsvColumnDefinition, type CsvExportOptions, type CursorPagingOptions, DEFAULT_FIELD_TYPE_DEFINITIONS, type DeleteDraftOptions, type DisplayCondition, type DisplayConditionGroup, type DisplayRule, EN_MESSAGES, type ExtensibleNode, type FieldConstraintRule, type FieldDisplayCondition, type FieldOption, type FieldType, type FieldTypeDefinition, type FormAnalytics, type FormEngineMessages, type FormEngineTranslationKey, type FormEngineTranslator, type FormEngineTranslatorOptions, type FormEvent, type FormEventType, type FormField, type FormPage, type FormPolicy, type FormResponse, type FormSchema, type FormStorageAdapter, type FormSubmission, FormSubmissionError, FormSubmissionMetadataSchema, type FormSubmissionSerializedError, type FormSubmissionSettings, type FormSubmissionWire, FormSubmissionWireSchema, type FormSubmissionWireSchemaType, type FormValue, type FormValues, type FormVersionRecord, type FormVersionState, type FormVersionStatus, type FormVersionTransitionPlan, JA_MESSAGES, type JsonValue, type KnownBuilderTranslationKey, type LegacyTranslationMetadata, type LocaleOption, type LocalizedText, type MigrateSchemaTranslationMetadataOptions, type MultiSelectField, type NodeWritableStream, type NumberField, type NumberQuestionAggregate, type NumericSummary, type OptionAggregate, type PagedSubmissionStorageAdapter, type PaginatedResult, type PaginationIteratorOptions, type PopulateTranslationOptions, type PopulateTranslationsOptions, type PrivacyEngine, type PublishDraftOptions, type PublishDraftResult, type Question, type QuestionAggregate, type QuestionType, type RatingField, type RatingFieldConstraintRule, type RendererTranslationKey, type ResponseAccumulator, type ResponseAccumulatorOptions, type Result, type SanitizeSchemaOptions, type SchemaIssue, type SchemaStructureIssue, type SchemaStructureIssueType, type SchemaTranslations, type SchemaValidationResult, type SelectField, type SensitiveDataFinding, type StorageAdapter, type StorageCommitError, type StorageCursor, type StorageFilterCriteria, type StreamCsvOptions, type SubmissionCursorPayload, type SubmissionCursorValue, type SubmissionFilter, type SubmissionPage, type SubmissionPageQueryOptions, type SubmissionQueryOptions, type SubmissionValidationResult, type TextAnswerCursorPayload, type TextAnswerCursorValue, type TextAnswerItem, type TextAnswerPage, type TextAnswerPageQueryOptions, type TextField, type TextFieldConstraintRule, type TextQuestionAggregate, type TranslationAdapter, type TranslationMetadataMigrator, type TranslationMigrationContext, type TranslationProviderError, type TranslationReport, type TranslationSlot, type TranslationStatus, type TranslationWorkspaceDetailedKey, type TranslationWorkspaceTranslationKey, type ValidateFormSchemaOptions, type ValidationCode, type ValidationError, type ValidationIssue, type VersionTransitionContext, type VersionTransitionError, type VersionTransitionEvent, type VersionTransitionPlan, type VersionedFormStorageAdapter, type WebhookConfig, type WebhookDispatchResult, aggregateResponses, applyTransitionPlan, assertValidFormSchema, assertVersionMutable, calculateChoiceDistribution, calculateCrossTabulation, calculateFieldVisibility, calculateNumericSummary, calculatePageVisibility, cloneVersionToDraft, collectSchemaLocales, collectTranslationSlots, commitVersionTransition, computeSourceTextHash, createCloneTransitionPlan, createDeleteDraftTransitionPlan, createFormEngineTranslator, createPublishTransitionPlan, createResponseAccumulator, createSubmission, decodeStorageSubmissionCursor, decodeStorageTextAnswerCursor, decodeSubmissionCursor, decodeTextAnswerCursor, deleteDraft, dispatchWebhook, encodeStorageSubmissionCursor, encodeStorageTextAnswerCursor, encodeSubmissionCursor, encodeTextAnswerCursor, escapeCsvCell, exportResponsesToCsv, exportResponsesToCsvStream, getTranslationStatus, isDisplayConditionGroupSatisfied, isDisplayConditionSatisfied, isManualTranslationMetadata, isQuestionVisible, iterateSubmissionPages, jsonValuesEqual, matchesSubmissionFilter, matchesSubmissionPageFilters, migrateSchemaTranslationMetadata, normalizeLocale, normalizeSubmissionPageSize, paginateWithFilter, pipeResponsesToCsvStream, populateSchemaTranslations, publishDraft, removeLocaleFromSchema, resolveFormTranslation, resolveLocalizedSchema, sanitizeSchema, selectVisibleAnswers, toFormSubmissionWire, transformFieldType, validateAnswers, validateFormSchema, validatePageAnswers, validateSchemaStructure, validateSubmission };
package/dist/index.js CHANGED
@@ -1598,15 +1598,28 @@ function serializeUnknown(value) {
1598
1598
  if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value;
1599
1599
  return JSON.stringify(value);
1600
1600
  }
1601
+ function submissionWithMetadata(submission) {
1602
+ return { ...submission, metadata: submission.metadata ?? {} };
1603
+ }
1601
1604
  async function* exportResponsesToCsvStream(schema, submissions, options = {}) {
1602
1605
  assertValidFormSchema(schema);
1603
1606
  const includeDefaultColumns = options.includeDefaultColumns ?? true;
1604
1607
  const customColumns = options.columns ?? [];
1608
+ const contractColumns = options.customColumns ?? [];
1609
+ const includeLocale = options.includeLocale ?? true;
1610
+ const includePiiStatus = options.includePiiStatus ?? false;
1605
1611
  const headers = [
1606
- ...includeDefaultColumns ? ["submissionId", "submittedAt", "locale", ...schema.fields.map((field) => field.id)] : [],
1607
- ...customColumns.map((column) => column.header)
1612
+ ...includeDefaultColumns ? [
1613
+ "submissionId",
1614
+ "submittedAt",
1615
+ ...includeLocale ? ["locale"] : [],
1616
+ ...includePiiStatus ? ["piiStatus"] : [],
1617
+ ...schema.fields.map((field) => field.id)
1618
+ ] : [],
1619
+ ...customColumns.map((column) => column.header),
1620
+ ...contractColumns.map((column) => column.header)
1608
1621
  ];
1609
- const neutralizeFormulas = options.neutralizeFormulas ?? true;
1622
+ const neutralizeFormulas = options.preventFormulaInjection ?? options.neutralizeFormulas ?? true;
1610
1623
  const header = headers.map((value) => escapeCsvCell(value, neutralizeFormulas)).join(",");
1611
1624
  yield `${options.withBom ?? true ? "\uFEFF" : ""}${header}`;
1612
1625
  for await (const submission of submissions) {
@@ -1615,10 +1628,12 @@ async function* exportResponsesToCsvStream(schema, submissions, options = {}) {
1615
1628
  const response = asFormResponse(submission);
1616
1629
  const answers = response.answers;
1617
1630
  const visible = selectVisibleAnswers(schema, answers);
1631
+ const piiStatus = response.metadata?.piiConfirmed === true ? "confirmed" : "unconfirmed";
1618
1632
  const defaultCells = includeDefaultColumns ? [
1619
1633
  response.responseId,
1620
1634
  response.submittedAt,
1621
- response.sourceLocale ?? "",
1635
+ ...includeLocale ? [response.sourceLocale ?? ""] : [],
1636
+ ...includePiiStatus ? [piiStatus] : [],
1622
1637
  ...schema.fields.map((field) => serializeUnknown(visible[field.id]))
1623
1638
  ] : [];
1624
1639
  const context = {
@@ -1628,8 +1643,9 @@ async function* exportResponsesToCsvStream(schema, submissions, options = {}) {
1628
1643
  schema
1629
1644
  };
1630
1645
  const customCells = await Promise.all(customColumns.map((column) => column.getValue(context)));
1646
+ const contractCells = "values" in submission ? contractColumns.map((column) => column.getValue(submissionWithMetadata(submission), schema)) : contractColumns.map(() => void 0);
1631
1647
  yield `\r
1632
- ${[...defaultCells, ...customCells].map((value) => escapeCsvCell(value, neutralizeFormulas)).join(",")}`;
1648
+ ${[...defaultCells, ...customCells, ...contractCells].map((value) => escapeCsvCell(value, neutralizeFormulas)).join(",")}`;
1633
1649
  }
1634
1650
  }
1635
1651
  function isWebWritableStream(writable) {
@@ -1686,23 +1702,51 @@ function exportResponsesToCsv(schema, responses, options = {}) {
1686
1702
  throw new TypeError(`Submission ${response.id} does not match ${schema.id}@${schema.version}.`);
1687
1703
  }
1688
1704
  }
1705
+ const includeLocale = options.includeLocale ?? true;
1706
+ const includePiiStatus = options.includePiiStatus ?? false;
1707
+ const customColumns = options.customColumns ?? [];
1708
+ const headers = [
1709
+ "submissionId",
1710
+ "submittedAt",
1711
+ ...includeLocale ? ["locale"] : [],
1712
+ ...includePiiStatus ? ["piiStatus"] : [],
1713
+ ...schema.fields.map((field) => field.id),
1714
+ ...customColumns.map((column) => column.header)
1715
+ ];
1689
1716
  const rows = [
1690
- ["submissionId", "submittedAt", "locale", ...schema.fields.map((field) => field.id)],
1717
+ headers,
1691
1718
  ...responses.map((response) => {
1692
1719
  const visible = selectVisibleAnswers(schema, response.values);
1720
+ const piiStatus = response.metadata?.piiConfirmed === true ? "confirmed" : "unconfirmed";
1721
+ const submissionForCustomColumns = submissionWithMetadata(response);
1693
1722
  return [
1694
1723
  response.id,
1695
1724
  response.submittedAt,
1696
- response.locale,
1697
- ...schema.fields.map((field) => serializeValue(visible[field.id]))
1725
+ ...includeLocale ? [response.locale] : [],
1726
+ ...includePiiStatus ? [piiStatus] : [],
1727
+ ...schema.fields.map((field) => serializeValue(visible[field.id])),
1728
+ ...customColumns.map((column) => column.getValue(submissionForCustomColumns, schema))
1698
1729
  ];
1699
1730
  })
1700
1731
  ];
1701
- const neutralizeFormulas = options.neutralizeFormulas ?? true;
1732
+ const neutralizeFormulas = options.preventFormulaInjection ?? options.neutralizeFormulas ?? true;
1702
1733
  const csv = rows.map((row) => row.map((cell) => escapeCsvCell(cell, neutralizeFormulas)).join(",")).join("\r\n");
1703
- return options.withBom ?? true ? `\uFEFF${csv}` : csv;
1734
+ return options.useBom ?? options.withBom ?? true ? `\uFEFF${csv}` : csv;
1704
1735
  }
1705
1736
 
1737
+ // src/errors.ts
1738
+ var FormSubmissionError = class extends Error {
1739
+ payload;
1740
+ constructor(payload) {
1741
+ super(payload.messageKey);
1742
+ this.name = "FormSubmissionError";
1743
+ this.payload = payload;
1744
+ }
1745
+ toJSON() {
1746
+ return this.payload;
1747
+ }
1748
+ };
1749
+
1706
1750
  // src/events.ts
1707
1751
  function bytesToHex(bytes) {
1708
1752
  return [...new Uint8Array(bytes)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
@@ -2025,7 +2069,20 @@ var EN_MESSAGES = Object.freeze({
2025
2069
  "workspace.errors.maxLocalesExceeded": "The maximum number of locales ({{max}}) has been reached.",
2026
2070
  "workspace.errors.readOnly": "This workspace is read-only.",
2027
2071
  "workspace.errors.adapterNotConfigured": "A translation adapter is not configured.",
2028
- "workspace.errors.translationFailed": "Translation failed."
2072
+ "workspace.errors.translationFailed": "Translation failed.",
2073
+ "workspace.header.title": "Translation workspace",
2074
+ "workspace.header.sourceLocale": "Source language",
2075
+ "workspace.header.targetLocale": "Target language",
2076
+ "workspace.header.translateAll": "Translate all",
2077
+ "workspace.header.progress": "{{translated}}/{{total}} translated ({{percent}}%)",
2078
+ "workspace.slot.sourceText": "Source",
2079
+ "workspace.slot.translatedText": "Translation",
2080
+ "workspace.slot.translateSingle": "Translate this slot",
2081
+ "workspace.slot.revertManual": "Revert manual translation",
2082
+ "workspace.confirm.removeLocaleTitle": "Remove language?",
2083
+ "workspace.confirm.removeLocaleMessage": "This will remove {{locale}} and its translations.",
2084
+ "workspace.empty.noTargetLocales": "No target languages are configured.",
2085
+ "workspace.empty.noSlotsToTranslate": "There are no translation slots for this language."
2029
2086
  });
2030
2087
 
2031
2088
  // src/i18n/catalogs/ja.ts
@@ -2197,7 +2254,20 @@ var JA_MESSAGES = Object.freeze({
2197
2254
  "workspace.errors.maxLocalesExceeded": "\u767B\u9332\u53EF\u80FD\u306A\u6700\u5927\u8A00\u8A9E\u6570 ({{max}}) \u306B\u9054\u3057\u307E\u3057\u305F\u3002",
2198
2255
  "workspace.errors.readOnly": "\u8AAD\u307F\u53D6\u308A\u5C02\u7528\u30E2\u30FC\u30C9\u306E\u305F\u3081\u5909\u66F4\u3067\u304D\u307E\u305B\u3093\u3002",
2199
2256
  "workspace.errors.adapterNotConfigured": "\u7FFB\u8A33\u30A2\u30C0\u30D7\u30BF\u30FC\u304C\u8A2D\u5B9A\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002",
2200
- "workspace.errors.translationFailed": "\u7FFB\u8A33\u51E6\u7406\u306B\u5931\u6557\u3057\u307E\u3057\u305F\u3002"
2257
+ "workspace.errors.translationFailed": "\u7FFB\u8A33\u51E6\u7406\u306B\u5931\u6557\u3057\u307E\u3057\u305F\u3002",
2258
+ "workspace.header.title": "\u591A\u8A00\u8A9E\u7FFB\u8A33\u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9",
2259
+ "workspace.header.sourceLocale": "\u5143\u8A00\u8A9E",
2260
+ "workspace.header.targetLocale": "\u7FFB\u8A33\u8A00\u8A9E",
2261
+ "workspace.header.translateAll": "\u4E00\u62EC\u81EA\u52D5\u7FFB\u8A33",
2262
+ "workspace.header.progress": "{{translated}}/{{total}} \u7FFB\u8A33\u6E08\u307F ({{percent}}%)",
2263
+ "workspace.slot.sourceText": "\u539F\u6587",
2264
+ "workspace.slot.translatedText": "\u7FFB\u8A33\u6587",
2265
+ "workspace.slot.translateSingle": "\u3053\u306E\u9805\u76EE\u3092\u7FFB\u8A33",
2266
+ "workspace.slot.revertManual": "\u624B\u52D5\u7FFB\u8A33\u3092\u5143\u306B\u623B\u3059",
2267
+ "workspace.confirm.removeLocaleTitle": "\u8A00\u8A9E\u3092\u524A\u9664\u3057\u307E\u3059\u304B\uFF1F",
2268
+ "workspace.confirm.removeLocaleMessage": "{{locale}} \u3068\u305D\u306E\u7FFB\u8A33\u3092\u524A\u9664\u3057\u307E\u3059\u3002",
2269
+ "workspace.empty.noTargetLocales": "\u7FFB\u8A33\u5148\u306E\u8A00\u8A9E\u304C\u8A2D\u5B9A\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002",
2270
+ "workspace.empty.noSlotsToTranslate": "\u3053\u306E\u8A00\u8A9E\u306B\u306F\u7FFB\u8A33\u9805\u76EE\u304C\u3042\u308A\u307E\u305B\u3093\u3002"
2201
2271
  });
2202
2272
 
2203
2273
  // src/i18n/translator.ts
@@ -2486,6 +2556,19 @@ function matchesSubmissionPageFilters(submission, options) {
2486
2556
  );
2487
2557
  }
2488
2558
 
2559
+ // src/schemas/submission.zod.ts
2560
+ import { z } from "zod";
2561
+ var FormSubmissionMetadataSchema = z.record(z.string(), z.unknown());
2562
+ var FormSubmissionWireSchema = z.object({
2563
+ id: z.string().min(1),
2564
+ formId: z.string().min(1),
2565
+ formVersion: z.number().int().positive(),
2566
+ values: z.record(z.string(), z.unknown()),
2567
+ metadata: FormSubmissionMetadataSchema,
2568
+ submittedAt: z.string().datetime(),
2569
+ schemaRevision: z.number().int().optional()
2570
+ });
2571
+
2489
2572
  // src/validation.ts
2490
2573
  var DEFAULT_MESSAGES = {
2491
2574
  required: "validation.required",
@@ -2666,6 +2749,19 @@ function toFormValues(answers) {
2666
2749
  function isFormValue(value) {
2667
2750
  return value === void 0 || typeof value === "string" || typeof value === "number" || typeof value === "boolean" || Array.isArray(value) && value.every((item) => typeof item === "string");
2668
2751
  }
2752
+ function toFormSubmissionWire(submission) {
2753
+ const { id, formId, formVersion, values, answers, metadata, submittedAt, schemaRevision } = submission;
2754
+ const targetValues = values ?? answers ?? {};
2755
+ return {
2756
+ id,
2757
+ formId,
2758
+ formVersion,
2759
+ values: { ...targetValues },
2760
+ metadata: { ...metadata ?? {} },
2761
+ submittedAt,
2762
+ ...schemaRevision === void 0 ? {} : { schemaRevision }
2763
+ };
2764
+ }
2669
2765
  function createSubmission(schemaOrInput, values, options) {
2670
2766
  if ("answers" in schemaOrInput) {
2671
2767
  const input = schemaOrInput;
@@ -3089,18 +3185,20 @@ var migrateSchemaTranslationMetadata = (schema, migratorOrOptions) => {
3089
3185
  };
3090
3186
  };
3091
3187
  var removeLocaleFromSchema = (schema, localeToRemove) => {
3092
- if (localeToRemove === schema.defaultLocale) {
3188
+ const normalizedLocaleToRemove = normalizeLocale(localeToRemove) ?? localeToRemove;
3189
+ const normalizedDefaultLocale = schema.defaultLocale === void 0 ? void 0 : normalizeLocale(schema.defaultLocale) ?? schema.defaultLocale;
3190
+ if (normalizedLocaleToRemove === normalizedDefaultLocale) {
3093
3191
  throw new Error(`Cannot remove defaultLocale: ${localeToRemove}`);
3094
3192
  }
3095
- const form = removeLocalizedNodeLocale(schema, localeToRemove);
3193
+ const form = removeLocalizedNodeLocale(schema, normalizedLocaleToRemove);
3096
3194
  const fields = schema.fields.map((field) => {
3097
- const localizedField = removeLocalizedNodeLocale(field, localeToRemove);
3195
+ const localizedField = removeLocalizedNodeLocale(field, normalizedLocaleToRemove);
3098
3196
  if (!("options" in localizedField)) return localizedField;
3099
3197
  return {
3100
3198
  ...localizedField,
3101
3199
  options: localizedField.options.map((option) => {
3102
- const translations = removeLocaleRecord(option.translations, localeToRemove);
3103
- const translationMetadata = removeLocaleRecord(option.translationMetadata, localeToRemove);
3200
+ const translations = removeLocaleRecord(option.translations, normalizedLocaleToRemove);
3201
+ const translationMetadata = removeLocaleRecord(option.translationMetadata, normalizedLocaleToRemove);
3104
3202
  const { translations: _translations, translationMetadata: _translationMetadata, ...base } = option;
3105
3203
  return {
3106
3204
  ...base,
@@ -3110,10 +3208,12 @@ var removeLocaleFromSchema = (schema, localeToRemove) => {
3110
3208
  })
3111
3209
  };
3112
3210
  });
3113
- const pages = schema.pages?.map((page) => removeLocalizedNodeLocale(page, localeToRemove));
3211
+ const pages = schema.pages?.map((page) => removeLocalizedNodeLocale(page, normalizedLocaleToRemove));
3114
3212
  return {
3115
3213
  ...form,
3116
- supportedLocales: (schema.supportedLocales ?? []).filter((locale) => locale !== localeToRemove),
3214
+ supportedLocales: (schema.supportedLocales ?? []).filter(
3215
+ (locale) => (normalizeLocale(locale) ?? locale) !== normalizedLocaleToRemove
3216
+ ),
3117
3217
  fields,
3118
3218
  ...pages === void 0 ? {} : { pages }
3119
3219
  };
@@ -3571,6 +3671,9 @@ async function commitVersionTransition(options) {
3571
3671
  export {
3572
3672
  DEFAULT_FIELD_TYPE_DEFINITIONS,
3573
3673
  EN_MESSAGES,
3674
+ FormSubmissionError,
3675
+ FormSubmissionMetadataSchema,
3676
+ FormSubmissionWireSchema,
3574
3677
  JA_MESSAGES,
3575
3678
  aggregateResponses,
3576
3679
  applyTransitionPlan,
@@ -3626,6 +3729,7 @@ export {
3626
3729
  resolveLocalizedSchema,
3627
3730
  sanitizeSchema,
3628
3731
  selectVisibleAnswers,
3732
+ toFormSubmissionWire,
3629
3733
  transformFieldType,
3630
3734
  validateAnswers,
3631
3735
  validateFormSchema,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@form-engine-ts/core",
3
- "version": "5.0.0",
3
+ "version": "5.1.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -38,6 +38,9 @@
38
38
  "validation",
39
39
  "typescript"
40
40
  ],
41
+ "dependencies": {
42
+ "zod": "^4.0.0"
43
+ },
41
44
  "scripts": {
42
45
  "build": "tsup src/index.ts --format esm,cjs --dts --clean",
43
46
  "check": "biome check . && tsc --noEmit",