@form-engine-ts/core 2.8.0 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -104,4 +104,9 @@ may push supported nodes to their native query language while preserving identic
104
104
  implement `listTextAnswerPage` expose stable cursor pagination over individual text answers.
105
105
  `TextAnswerPageQueryOptions.fieldIds` can select multiple free-text fields for item-level paging.
106
106
 
107
+ `iterateSubmissionPages(adapter, formId, query, options)` safely traverses every page as an async generator. It supports
108
+ `pageSize`, `maxItems`, and `AbortSignal`, continues through empty pages with a next cursor, and rejects missing or cyclic
109
+ cursors instead of looping indefinitely. Publish transition validation also rejects mismatched form IDs, non-Published
110
+ records, and an unexpected current record with typed errors.
111
+
107
112
  See the [project documentation](https://github.com/nitta-a/form-engine-ts#readme) for the complete schema and API guide.
package/dist/index.cjs CHANGED
@@ -46,6 +46,7 @@ __export(index_exports, {
46
46
  exportResponsesToCsvStream: () => exportResponsesToCsvStream,
47
47
  isDisplayConditionSatisfied: () => isDisplayConditionSatisfied,
48
48
  isQuestionVisible: () => isQuestionVisible,
49
+ iterateSubmissionPages: () => iterateSubmissionPages,
49
50
  jsonValuesEqual: () => jsonValuesEqual,
50
51
  matchesSubmissionFilter: () => matchesSubmissionFilter,
51
52
  matchesSubmissionPageFilters: () => matchesSubmissionPageFilters,
@@ -1523,6 +1524,54 @@ function transformFieldType(field, nextType) {
1523
1524
  }
1524
1525
 
1525
1526
  // src/pagination.ts
1527
+ function asFormResponse2(submission) {
1528
+ return {
1529
+ responseId: submission.id,
1530
+ formId: submission.formId,
1531
+ formVersion: submission.formVersion,
1532
+ sourceLocale: submission.locale,
1533
+ answers: submission.values,
1534
+ submittedAt: submission.submittedAt,
1535
+ ...submission.metadata === void 0 ? {} : { metadata: submission.metadata }
1536
+ };
1537
+ }
1538
+ async function* iterateSubmissionPages(adapter, formId, queryOptions = {}, options = {}) {
1539
+ const pageSize = normalizeSubmissionPageSize(options.pageSize ?? queryOptions.pageSize);
1540
+ const maxItems = options.maxItems ?? Number.POSITIVE_INFINITY;
1541
+ if (maxItems !== Number.POSITIVE_INFINITY && (!Number.isSafeInteger(maxItems) || maxItems < 0)) {
1542
+ throw new TypeError("maxItems must be a non-negative safe integer.");
1543
+ }
1544
+ if (maxItems === 0) return;
1545
+ const seenCursors = /* @__PURE__ */ new Set();
1546
+ let cursor = queryOptions.cursor;
1547
+ const { cursor: _initialCursor, pageSize: _initialPageSize, ...baseQueryOptions } = queryOptions;
1548
+ if (cursor !== void 0) seenCursors.add(cursor);
1549
+ let emitted = 0;
1550
+ while (emitted < maxItems) {
1551
+ options.signal?.throwIfAborted();
1552
+ const remaining = maxItems - emitted;
1553
+ const requestedPageSize = Number.isFinite(remaining) ? Math.min(pageSize, remaining) : pageSize;
1554
+ const page = await adapter.listSubmissionPage(formId, {
1555
+ ...baseQueryOptions,
1556
+ pageSize: requestedPageSize,
1557
+ ...cursor === void 0 ? {} : { cursor }
1558
+ });
1559
+ options.signal?.throwIfAborted();
1560
+ const available = Number.isFinite(remaining) ? page.items.slice(0, remaining) : page.items;
1561
+ if (available.length > 0) {
1562
+ emitted += available.length;
1563
+ yield available.map(asFormResponse2);
1564
+ }
1565
+ if (!page.hasMore || emitted >= maxItems) return;
1566
+ const nextCursor = page.nextCursor;
1567
+ if (nextCursor === void 0 || nextCursor.length === 0) {
1568
+ throw new Error("Submission pagination returned hasMore without a next cursor.");
1569
+ }
1570
+ if (seenCursors.has(nextCursor)) throw new Error(`Submission pagination cursor cycle detected: ${nextCursor}`);
1571
+ seenCursors.add(nextCursor);
1572
+ cursor = nextCursor;
1573
+ }
1574
+ }
1526
1575
  var BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1527
1576
  function encodeBase64(bytes) {
1528
1577
  let result = "";
@@ -2203,14 +2252,23 @@ function createCloneTransitionPlan(state, sourceRecord, options = {}) {
2203
2252
  };
2204
2253
  }
2205
2254
  function validatePublishedRecord(state, record) {
2206
- if (state.publishedVersion !== void 0 && record?.version !== state.publishedVersion) {
2255
+ if (state.publishedVersion === void 0) {
2256
+ return record === void 0 ? void 0 : { success: false, error: { type: "unexpected_published_record" } };
2257
+ }
2258
+ if (record !== void 0 && record.formId !== state.formId) {
2259
+ return { success: false, error: { type: "form_id_mismatch" } };
2260
+ }
2261
+ if (record !== void 0 && record.status !== "published") {
2262
+ return { success: false, error: { type: "invalid_published_status" } };
2263
+ }
2264
+ if (record?.version !== state.publishedVersion) {
2207
2265
  return {
2208
2266
  success: false,
2209
2267
  error: { type: "missing_published_record", expectedVersion: state.publishedVersion }
2210
2268
  };
2211
2269
  }
2212
2270
  if (record === void 0) return void 0;
2213
- if (record.formId !== state.formId || record.status !== "published" || record.schema.id !== record.formId || record.schema.version !== record.version) {
2271
+ if (record.schema.id !== record.formId || record.schema.version !== record.version) {
2214
2272
  throw new TypeError("currentPublishedRecord must match the state's published version.");
2215
2273
  }
2216
2274
  return void 0;
@@ -2386,6 +2444,7 @@ function assertVersionMutable(status) {
2386
2444
  exportResponsesToCsvStream,
2387
2445
  isDisplayConditionSatisfied,
2388
2446
  isQuestionVisible,
2447
+ iterateSubmissionPages,
2389
2448
  jsonValuesEqual,
2390
2449
  matchesSubmissionFilter,
2391
2450
  matchesSubmissionPageFilters,
package/dist/index.d.cts CHANGED
@@ -40,6 +40,12 @@ type VersionTransitionError = {
40
40
  } | {
41
41
  readonly type: "missing_published_record";
42
42
  readonly expectedVersion: number;
43
+ } | {
44
+ readonly type: "form_id_mismatch";
45
+ } | {
46
+ readonly type: "invalid_published_status";
47
+ } | {
48
+ readonly type: "unexpected_published_record";
43
49
  } | {
44
50
  readonly type: "revision_conflict";
45
51
  readonly expectedRevision: number;
@@ -354,6 +360,8 @@ type StorageCommitError = {
354
360
  } | {
355
361
  readonly type: "draft_already_exists";
356
362
  readonly currentDraftVersion: number;
363
+ } | {
364
+ readonly type: "transaction_unsupported";
357
365
  } | {
358
366
  readonly type: "invalid_transition";
359
367
  readonly message: string;
@@ -523,6 +531,12 @@ declare function dispatchWebhook<T>(event: FormEvent<T>, config: WebhookConfig,
523
531
  */
524
532
  declare function transformFieldType(field: FormField, nextType: QuestionType): FormField;
525
533
 
534
+ interface PaginationIteratorOptions {
535
+ readonly pageSize?: number;
536
+ readonly maxItems?: number;
537
+ readonly signal?: AbortSignal;
538
+ }
539
+ declare function iterateSubmissionPages(adapter: PagedSubmissionStorageAdapter, formId: string, queryOptions?: SubmissionPageQueryOptions, options?: PaginationIteratorOptions): AsyncIterable<readonly FormResponse[]>;
526
540
  interface SubmissionCursorValue {
527
541
  readonly submittedAt: string;
528
542
  readonly responseId: string;
@@ -613,4 +627,4 @@ declare function calculatePageVisibility(schema: FormSchema, currentAnswers: Rea
613
627
  declare function calculateFieldVisibility(schema: FormSchema, currentAnswers: Readonly<Record<string, unknown>>): Readonly<Record<string, boolean>>;
614
628
  declare function selectVisibleAnswers(schema: FormSchema, currentAnswers: FormValues): FormValues;
615
629
 
616
- 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 StorageCommitError, type StreamCsvOptions, type SubmissionCursorValue, type SubmissionFilter, type SubmissionPage, type SubmissionPageQueryOptions, type SubmissionQueryOptions, type TextAnswerCursorValue, type TextAnswerItem, type TextAnswerPage, type TextAnswerPageQueryOptions, type TextField, type TextQuestionAggregate, type TranslationAdapter, type TranslationReport, type TranslationSlot, type ValidateFormSchemaOptions, type ValidationCode, type ValidationError, type ValidationIssue, type VersionTransitionError, type VersionTransitionEvent, type VersionTransitionPlan, type VersionedFormStorageAdapter, type WebhookConfig, type WebhookDispatchResult, aggregateResponses, assertValidFormSchema, assertVersionMutable, calculateChoiceDistribution, calculateCrossTabulation, calculateFieldVisibility, calculateNumericSummary, calculatePageVisibility, cloneVersionToDraft, collectSchemaLocales, createCloneTransitionPlan, createDeleteDraftTransitionPlan, createPublishTransitionPlan, createResponseAccumulator, createSubmission, decodeSubmissionCursor, decodeTextAnswerCursor, deleteDraft, dispatchWebhook, encodeSubmissionCursor, encodeTextAnswerCursor, escapeCsvCell, exportResponsesToCsv, exportResponsesToCsvStream, isDisplayConditionSatisfied, isQuestionVisible, jsonValuesEqual, matchesSubmissionFilter, matchesSubmissionPageFilters, normalizeSubmissionPageSize, pipeResponsesToCsvStream, populateSchemaTranslations, publishDraft, resolveFormTranslation, resolveLocalizedSchema, sanitizeSchema, selectVisibleAnswers, transformFieldType, validateAnswers, validateFormSchema, validatePageAnswers, validateSchemaStructure };
630
+ 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 PaginationIteratorOptions, 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 StorageCommitError, type StreamCsvOptions, type SubmissionCursorValue, type SubmissionFilter, type SubmissionPage, type SubmissionPageQueryOptions, type SubmissionQueryOptions, type TextAnswerCursorValue, type TextAnswerItem, type TextAnswerPage, type TextAnswerPageQueryOptions, type TextField, type TextQuestionAggregate, type TranslationAdapter, type TranslationReport, type TranslationSlot, type ValidateFormSchemaOptions, type ValidationCode, type ValidationError, type ValidationIssue, type VersionTransitionError, type VersionTransitionEvent, type VersionTransitionPlan, type VersionedFormStorageAdapter, type WebhookConfig, type WebhookDispatchResult, aggregateResponses, assertValidFormSchema, assertVersionMutable, calculateChoiceDistribution, calculateCrossTabulation, calculateFieldVisibility, calculateNumericSummary, calculatePageVisibility, cloneVersionToDraft, collectSchemaLocales, createCloneTransitionPlan, createDeleteDraftTransitionPlan, createPublishTransitionPlan, createResponseAccumulator, createSubmission, decodeSubmissionCursor, decodeTextAnswerCursor, deleteDraft, dispatchWebhook, encodeSubmissionCursor, encodeTextAnswerCursor, escapeCsvCell, exportResponsesToCsv, exportResponsesToCsvStream, isDisplayConditionSatisfied, isQuestionVisible, iterateSubmissionPages, jsonValuesEqual, matchesSubmissionFilter, matchesSubmissionPageFilters, normalizeSubmissionPageSize, pipeResponsesToCsvStream, populateSchemaTranslations, publishDraft, resolveFormTranslation, resolveLocalizedSchema, sanitizeSchema, selectVisibleAnswers, transformFieldType, validateAnswers, validateFormSchema, validatePageAnswers, validateSchemaStructure };
package/dist/index.d.ts CHANGED
@@ -40,6 +40,12 @@ type VersionTransitionError = {
40
40
  } | {
41
41
  readonly type: "missing_published_record";
42
42
  readonly expectedVersion: number;
43
+ } | {
44
+ readonly type: "form_id_mismatch";
45
+ } | {
46
+ readonly type: "invalid_published_status";
47
+ } | {
48
+ readonly type: "unexpected_published_record";
43
49
  } | {
44
50
  readonly type: "revision_conflict";
45
51
  readonly expectedRevision: number;
@@ -354,6 +360,8 @@ type StorageCommitError = {
354
360
  } | {
355
361
  readonly type: "draft_already_exists";
356
362
  readonly currentDraftVersion: number;
363
+ } | {
364
+ readonly type: "transaction_unsupported";
357
365
  } | {
358
366
  readonly type: "invalid_transition";
359
367
  readonly message: string;
@@ -523,6 +531,12 @@ declare function dispatchWebhook<T>(event: FormEvent<T>, config: WebhookConfig,
523
531
  */
524
532
  declare function transformFieldType(field: FormField, nextType: QuestionType): FormField;
525
533
 
534
+ interface PaginationIteratorOptions {
535
+ readonly pageSize?: number;
536
+ readonly maxItems?: number;
537
+ readonly signal?: AbortSignal;
538
+ }
539
+ declare function iterateSubmissionPages(adapter: PagedSubmissionStorageAdapter, formId: string, queryOptions?: SubmissionPageQueryOptions, options?: PaginationIteratorOptions): AsyncIterable<readonly FormResponse[]>;
526
540
  interface SubmissionCursorValue {
527
541
  readonly submittedAt: string;
528
542
  readonly responseId: string;
@@ -613,4 +627,4 @@ declare function calculatePageVisibility(schema: FormSchema, currentAnswers: Rea
613
627
  declare function calculateFieldVisibility(schema: FormSchema, currentAnswers: Readonly<Record<string, unknown>>): Readonly<Record<string, boolean>>;
614
628
  declare function selectVisibleAnswers(schema: FormSchema, currentAnswers: FormValues): FormValues;
615
629
 
616
- 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 StorageCommitError, type StreamCsvOptions, type SubmissionCursorValue, type SubmissionFilter, type SubmissionPage, type SubmissionPageQueryOptions, type SubmissionQueryOptions, type TextAnswerCursorValue, type TextAnswerItem, type TextAnswerPage, type TextAnswerPageQueryOptions, type TextField, type TextQuestionAggregate, type TranslationAdapter, type TranslationReport, type TranslationSlot, type ValidateFormSchemaOptions, type ValidationCode, type ValidationError, type ValidationIssue, type VersionTransitionError, type VersionTransitionEvent, type VersionTransitionPlan, type VersionedFormStorageAdapter, type WebhookConfig, type WebhookDispatchResult, aggregateResponses, assertValidFormSchema, assertVersionMutable, calculateChoiceDistribution, calculateCrossTabulation, calculateFieldVisibility, calculateNumericSummary, calculatePageVisibility, cloneVersionToDraft, collectSchemaLocales, createCloneTransitionPlan, createDeleteDraftTransitionPlan, createPublishTransitionPlan, createResponseAccumulator, createSubmission, decodeSubmissionCursor, decodeTextAnswerCursor, deleteDraft, dispatchWebhook, encodeSubmissionCursor, encodeTextAnswerCursor, escapeCsvCell, exportResponsesToCsv, exportResponsesToCsvStream, isDisplayConditionSatisfied, isQuestionVisible, jsonValuesEqual, matchesSubmissionFilter, matchesSubmissionPageFilters, normalizeSubmissionPageSize, pipeResponsesToCsvStream, populateSchemaTranslations, publishDraft, resolveFormTranslation, resolveLocalizedSchema, sanitizeSchema, selectVisibleAnswers, transformFieldType, validateAnswers, validateFormSchema, validatePageAnswers, validateSchemaStructure };
630
+ 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 PaginationIteratorOptions, 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 StorageCommitError, type StreamCsvOptions, type SubmissionCursorValue, type SubmissionFilter, type SubmissionPage, type SubmissionPageQueryOptions, type SubmissionQueryOptions, type TextAnswerCursorValue, type TextAnswerItem, type TextAnswerPage, type TextAnswerPageQueryOptions, type TextField, type TextQuestionAggregate, type TranslationAdapter, type TranslationReport, type TranslationSlot, type ValidateFormSchemaOptions, type ValidationCode, type ValidationError, type ValidationIssue, type VersionTransitionError, type VersionTransitionEvent, type VersionTransitionPlan, type VersionedFormStorageAdapter, type WebhookConfig, type WebhookDispatchResult, aggregateResponses, assertValidFormSchema, assertVersionMutable, calculateChoiceDistribution, calculateCrossTabulation, calculateFieldVisibility, calculateNumericSummary, calculatePageVisibility, cloneVersionToDraft, collectSchemaLocales, createCloneTransitionPlan, createDeleteDraftTransitionPlan, createPublishTransitionPlan, createResponseAccumulator, createSubmission, decodeSubmissionCursor, decodeTextAnswerCursor, deleteDraft, dispatchWebhook, encodeSubmissionCursor, encodeTextAnswerCursor, escapeCsvCell, exportResponsesToCsv, exportResponsesToCsvStream, isDisplayConditionSatisfied, isQuestionVisible, iterateSubmissionPages, jsonValuesEqual, matchesSubmissionFilter, matchesSubmissionPageFilters, normalizeSubmissionPageSize, pipeResponsesToCsvStream, populateSchemaTranslations, publishDraft, resolveFormTranslation, resolveLocalizedSchema, sanitizeSchema, selectVisibleAnswers, transformFieldType, validateAnswers, validateFormSchema, validatePageAnswers, validateSchemaStructure };
package/dist/index.js CHANGED
@@ -1456,6 +1456,54 @@ function transformFieldType(field, nextType) {
1456
1456
  }
1457
1457
 
1458
1458
  // src/pagination.ts
1459
+ function asFormResponse2(submission) {
1460
+ return {
1461
+ responseId: submission.id,
1462
+ formId: submission.formId,
1463
+ formVersion: submission.formVersion,
1464
+ sourceLocale: submission.locale,
1465
+ answers: submission.values,
1466
+ submittedAt: submission.submittedAt,
1467
+ ...submission.metadata === void 0 ? {} : { metadata: submission.metadata }
1468
+ };
1469
+ }
1470
+ async function* iterateSubmissionPages(adapter, formId, queryOptions = {}, options = {}) {
1471
+ const pageSize = normalizeSubmissionPageSize(options.pageSize ?? queryOptions.pageSize);
1472
+ const maxItems = options.maxItems ?? Number.POSITIVE_INFINITY;
1473
+ if (maxItems !== Number.POSITIVE_INFINITY && (!Number.isSafeInteger(maxItems) || maxItems < 0)) {
1474
+ throw new TypeError("maxItems must be a non-negative safe integer.");
1475
+ }
1476
+ if (maxItems === 0) return;
1477
+ const seenCursors = /* @__PURE__ */ new Set();
1478
+ let cursor = queryOptions.cursor;
1479
+ const { cursor: _initialCursor, pageSize: _initialPageSize, ...baseQueryOptions } = queryOptions;
1480
+ if (cursor !== void 0) seenCursors.add(cursor);
1481
+ let emitted = 0;
1482
+ while (emitted < maxItems) {
1483
+ options.signal?.throwIfAborted();
1484
+ const remaining = maxItems - emitted;
1485
+ const requestedPageSize = Number.isFinite(remaining) ? Math.min(pageSize, remaining) : pageSize;
1486
+ const page = await adapter.listSubmissionPage(formId, {
1487
+ ...baseQueryOptions,
1488
+ pageSize: requestedPageSize,
1489
+ ...cursor === void 0 ? {} : { cursor }
1490
+ });
1491
+ options.signal?.throwIfAborted();
1492
+ const available = Number.isFinite(remaining) ? page.items.slice(0, remaining) : page.items;
1493
+ if (available.length > 0) {
1494
+ emitted += available.length;
1495
+ yield available.map(asFormResponse2);
1496
+ }
1497
+ if (!page.hasMore || emitted >= maxItems) return;
1498
+ const nextCursor = page.nextCursor;
1499
+ if (nextCursor === void 0 || nextCursor.length === 0) {
1500
+ throw new Error("Submission pagination returned hasMore without a next cursor.");
1501
+ }
1502
+ if (seenCursors.has(nextCursor)) throw new Error(`Submission pagination cursor cycle detected: ${nextCursor}`);
1503
+ seenCursors.add(nextCursor);
1504
+ cursor = nextCursor;
1505
+ }
1506
+ }
1459
1507
  var BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1460
1508
  function encodeBase64(bytes) {
1461
1509
  let result = "";
@@ -2136,14 +2184,23 @@ function createCloneTransitionPlan(state, sourceRecord, options = {}) {
2136
2184
  };
2137
2185
  }
2138
2186
  function validatePublishedRecord(state, record) {
2139
- if (state.publishedVersion !== void 0 && record?.version !== state.publishedVersion) {
2187
+ if (state.publishedVersion === void 0) {
2188
+ return record === void 0 ? void 0 : { success: false, error: { type: "unexpected_published_record" } };
2189
+ }
2190
+ if (record !== void 0 && record.formId !== state.formId) {
2191
+ return { success: false, error: { type: "form_id_mismatch" } };
2192
+ }
2193
+ if (record !== void 0 && record.status !== "published") {
2194
+ return { success: false, error: { type: "invalid_published_status" } };
2195
+ }
2196
+ if (record?.version !== state.publishedVersion) {
2140
2197
  return {
2141
2198
  success: false,
2142
2199
  error: { type: "missing_published_record", expectedVersion: state.publishedVersion }
2143
2200
  };
2144
2201
  }
2145
2202
  if (record === void 0) return void 0;
2146
- if (record.formId !== state.formId || record.status !== "published" || record.schema.id !== record.formId || record.schema.version !== record.version) {
2203
+ if (record.schema.id !== record.formId || record.schema.version !== record.version) {
2147
2204
  throw new TypeError("currentPublishedRecord must match the state's published version.");
2148
2205
  }
2149
2206
  return void 0;
@@ -2318,6 +2375,7 @@ export {
2318
2375
  exportResponsesToCsvStream,
2319
2376
  isDisplayConditionSatisfied,
2320
2377
  isQuestionVisible,
2378
+ iterateSubmissionPages,
2321
2379
  jsonValuesEqual,
2322
2380
  matchesSubmissionFilter,
2323
2381
  matchesSubmissionPageFilters,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@form-engine-ts/core",
3
- "version": "2.8.0",
3
+ "version": "2.9.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },