@form-engine-ts/storage-azure-table 5.1.0 → 6.0.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
@@ -42,3 +42,7 @@ and field indexes, so a page can resume inside a multi-answer entity without gap
42
42
  consume the item limit, and scanning remains bounded by `maxScanPages`. Cursor format version 1 also records the form,
43
43
  version, sorted fields, and a SHA-256 filter fingerprint. Reusing a cursor with different query context throws
44
44
  `invalid_cursor_context`.
45
+
46
+ `createLegacyAzureTableCodec()` decodes older entities using `PartitionKey`, `RowKey`, `answers`, `answeredAt`, and
47
+ `surveyVersion` into the canonical `FormSubmission` shape. Custom partition- and row-key generators can be supplied
48
+ for migration tooling.
package/dist/index.cjs CHANGED
@@ -21,12 +21,36 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  createAzureTableStorage: () => createAzureTableStorage,
24
+ createLegacyAzureTableCodec: () => createLegacyAzureTableCodec,
24
25
  defaultAzureTableSubmissionCodec: () => defaultAzureTableSubmissionCodec,
25
26
  metadataFiltersToOData: () => metadataFiltersToOData,
26
27
  submissionFilterToOData: () => submissionFilterToOData
27
28
  });
28
29
  module.exports = __toCommonJS(index_exports);
29
30
  var import_core = require("@form-engine-ts/core");
31
+ var createLegacyAzureTableCodec = (options = {}) => {
32
+ const createPartitionKey = options.partitionKeyGenerator ?? ((formId) => formId);
33
+ const createRowKey = options.rowKeyGenerator ?? ((submittedAt, submissionId) => `${submittedAt}_${submissionId}`);
34
+ return {
35
+ decode: (entity) => {
36
+ const values = entity.answers === void 0 ? {} : parseLegacyJsonObject(entity.answers, "answers");
37
+ const submittedAt = entity.answeredAt ?? entity.Timestamp;
38
+ if (submittedAt === void 0 || submittedAt.trim().length === 0) {
39
+ throw new Error("Azure Table legacy submission is missing answeredAt or Timestamp.");
40
+ }
41
+ return {
42
+ id: entity.RowKey,
43
+ formId: entity.PartitionKey,
44
+ formVersion: entity.surveyVersion ?? 1,
45
+ values,
46
+ metadata: {},
47
+ submittedAt
48
+ };
49
+ },
50
+ createPartitionKey: (formId, submissionId) => createPartitionKey(formId, submissionId),
51
+ createRowKey: (submittedAt, submissionId) => createRowKey(submittedAt, submissionId)
52
+ };
53
+ };
30
54
  var BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
31
55
  function encodeBase64(bytes) {
32
56
  let result = "";
@@ -135,6 +159,11 @@ function parseJson(value, location) {
135
159
  throw new Error(`Azure Table ${location} payload is invalid.`, { cause });
136
160
  }
137
161
  }
162
+ function parseLegacyJsonObject(value, property) {
163
+ const parsed = parseJson(value, `legacy ${property}`);
164
+ if (!isRecord(parsed)) throw new Error(`Azure Table legacy ${property} payload must be an object.`);
165
+ return parsed;
166
+ }
138
167
  function parseSubmission(value, location) {
139
168
  const parsed = typeof value === "string" ? parseJson(value, location) : value;
140
169
  if (!isRecord(parsed) || typeof parsed.id !== "string" || typeof parsed.formId !== "string" || !Number.isInteger(parsed.formVersion) || typeof parsed.locale !== "string" || typeof parsed.submittedAt !== "string" || !isRecord(parsed.values) || !Object.values(parsed.values).every(isFormValue)) {
@@ -511,6 +540,11 @@ function createAzureTableStorage(options = {}) {
511
540
  continue;
512
541
  }
513
542
  const answers = submissionTextAnswers(submission, fieldIds);
543
+ const metadata = submission.metadata === void 0 ? void 0 : Object.fromEntries(
544
+ Object.entries(submission.metadata).filter(
545
+ (entry) => entry[1] !== void 0
546
+ )
547
+ );
514
548
  for (let fieldIndex = entityIndex === entityStartIndex ? fieldStartIndex : 0; fieldIndex < answers.length; fieldIndex += 1) {
515
549
  const answer = answers[fieldIndex];
516
550
  if (answer === void 0) continue;
@@ -520,9 +554,9 @@ function createAzureTableStorage(options = {}) {
520
554
  formVersion: submission.formVersion,
521
555
  fieldId: answer.fieldId,
522
556
  text: answer.text,
523
- locale: submission.locale,
557
+ ...submission.locale === void 0 ? {} : { locale: submission.locale },
524
558
  submittedAt: submission.submittedAt,
525
- ...submission.metadata === void 0 ? {} : { metadata: submission.metadata }
559
+ ...metadata === void 0 ? {} : { metadata }
526
560
  });
527
561
  if (items.length < pageSize) continue;
528
562
  const nextFieldIndex = fieldIndex + 1;
@@ -614,6 +648,7 @@ function createAzureTableStorage(options = {}) {
614
648
  // Annotate the CommonJS export names for ESM import in node:
615
649
  0 && (module.exports = {
616
650
  createAzureTableStorage,
651
+ createLegacyAzureTableCodec,
617
652
  defaultAzureTableSubmissionCodec,
618
653
  metadataFiltersToOData,
619
654
  submissionFilterToOData
package/dist/index.d.cts CHANGED
@@ -30,6 +30,24 @@ interface AzureTableEntityCodec<T> {
30
30
  readonly serialize: (submission: T) => Record<string, unknown>;
31
31
  readonly deserialize: (entity: Record<string, unknown>) => T;
32
32
  }
33
+ interface AzureTableLegacyEntity {
34
+ readonly PartitionKey: string;
35
+ readonly RowKey: string;
36
+ readonly answers?: string;
37
+ readonly answeredAt?: string;
38
+ readonly surveyVersion?: number;
39
+ readonly Timestamp?: string;
40
+ readonly [key: string]: unknown;
41
+ }
42
+ interface AzureTableLegacyCodec {
43
+ readonly decode: (entity: AzureTableLegacyEntity) => FormSubmission;
44
+ readonly createPartitionKey: (formId: string, submissionId: string) => string;
45
+ readonly createRowKey: (submittedAt: string, submissionId: string) => string;
46
+ }
47
+ declare const createLegacyAzureTableCodec: (options?: {
48
+ readonly partitionKeyGenerator?: (formId: string, submissionId: string) => string;
49
+ readonly rowKeyGenerator?: (submittedAt: string, submissionId: string) => string;
50
+ }) => AzureTableLegacyCodec;
33
51
  interface AzureTableSubmissionCodec<T = FormSubmission> {
34
52
  readonly createEntity: (value: T) => Record<string, unknown>;
35
53
  readonly deserialize: (entity: Record<string, unknown>) => T;
@@ -86,4 +104,4 @@ declare function metadataFiltersToOData(options: SubmissionPageQueryOptions, map
86
104
  declare function submissionFilterToOData(filter: SubmissionFilter, mapping?: AzureTableFieldMapping): string | undefined;
87
105
  declare function createAzureTableStorage(options?: AzureTableStorageOptions): PagedSubmissionStorageAdapter;
88
106
 
89
- export { type AzureTableClientLike, type AzureTableEntityCodec, type AzureTableEntityIterator, type AzureTableEntityPage, type AzureTableFieldMapping, type AzureTableListOptions, type AzureTablePageSettings, type AzureTableStorageOptions, type AzureTableSubmissionCodec, type AzureTableValueCodec, type AzureTextAnswerCursorPayload, createAzureTableStorage, defaultAzureTableSubmissionCodec, metadataFiltersToOData, submissionFilterToOData };
107
+ export { type AzureTableClientLike, type AzureTableEntityCodec, type AzureTableEntityIterator, type AzureTableEntityPage, type AzureTableFieldMapping, type AzureTableLegacyCodec, type AzureTableLegacyEntity, type AzureTableListOptions, type AzureTablePageSettings, type AzureTableStorageOptions, type AzureTableSubmissionCodec, type AzureTableValueCodec, type AzureTextAnswerCursorPayload, createAzureTableStorage, createLegacyAzureTableCodec, defaultAzureTableSubmissionCodec, metadataFiltersToOData, submissionFilterToOData };
package/dist/index.d.ts CHANGED
@@ -30,6 +30,24 @@ interface AzureTableEntityCodec<T> {
30
30
  readonly serialize: (submission: T) => Record<string, unknown>;
31
31
  readonly deserialize: (entity: Record<string, unknown>) => T;
32
32
  }
33
+ interface AzureTableLegacyEntity {
34
+ readonly PartitionKey: string;
35
+ readonly RowKey: string;
36
+ readonly answers?: string;
37
+ readonly answeredAt?: string;
38
+ readonly surveyVersion?: number;
39
+ readonly Timestamp?: string;
40
+ readonly [key: string]: unknown;
41
+ }
42
+ interface AzureTableLegacyCodec {
43
+ readonly decode: (entity: AzureTableLegacyEntity) => FormSubmission;
44
+ readonly createPartitionKey: (formId: string, submissionId: string) => string;
45
+ readonly createRowKey: (submittedAt: string, submissionId: string) => string;
46
+ }
47
+ declare const createLegacyAzureTableCodec: (options?: {
48
+ readonly partitionKeyGenerator?: (formId: string, submissionId: string) => string;
49
+ readonly rowKeyGenerator?: (submittedAt: string, submissionId: string) => string;
50
+ }) => AzureTableLegacyCodec;
33
51
  interface AzureTableSubmissionCodec<T = FormSubmission> {
34
52
  readonly createEntity: (value: T) => Record<string, unknown>;
35
53
  readonly deserialize: (entity: Record<string, unknown>) => T;
@@ -86,4 +104,4 @@ declare function metadataFiltersToOData(options: SubmissionPageQueryOptions, map
86
104
  declare function submissionFilterToOData(filter: SubmissionFilter, mapping?: AzureTableFieldMapping): string | undefined;
87
105
  declare function createAzureTableStorage(options?: AzureTableStorageOptions): PagedSubmissionStorageAdapter;
88
106
 
89
- export { type AzureTableClientLike, type AzureTableEntityCodec, type AzureTableEntityIterator, type AzureTableEntityPage, type AzureTableFieldMapping, type AzureTableListOptions, type AzureTablePageSettings, type AzureTableStorageOptions, type AzureTableSubmissionCodec, type AzureTableValueCodec, type AzureTextAnswerCursorPayload, createAzureTableStorage, defaultAzureTableSubmissionCodec, metadataFiltersToOData, submissionFilterToOData };
107
+ export { type AzureTableClientLike, type AzureTableEntityCodec, type AzureTableEntityIterator, type AzureTableEntityPage, type AzureTableFieldMapping, type AzureTableLegacyCodec, type AzureTableLegacyEntity, type AzureTableListOptions, type AzureTablePageSettings, type AzureTableStorageOptions, type AzureTableSubmissionCodec, type AzureTableValueCodec, type AzureTextAnswerCursorPayload, createAzureTableStorage, createLegacyAzureTableCodec, defaultAzureTableSubmissionCodec, metadataFiltersToOData, submissionFilterToOData };
package/dist/index.js CHANGED
@@ -1,5 +1,28 @@
1
1
  // src/index.ts
2
2
  import { assertValidFormSchema, matchesSubmissionPageFilters, normalizeSubmissionPageSize } from "@form-engine-ts/core";
3
+ var createLegacyAzureTableCodec = (options = {}) => {
4
+ const createPartitionKey = options.partitionKeyGenerator ?? ((formId) => formId);
5
+ const createRowKey = options.rowKeyGenerator ?? ((submittedAt, submissionId) => `${submittedAt}_${submissionId}`);
6
+ return {
7
+ decode: (entity) => {
8
+ const values = entity.answers === void 0 ? {} : parseLegacyJsonObject(entity.answers, "answers");
9
+ const submittedAt = entity.answeredAt ?? entity.Timestamp;
10
+ if (submittedAt === void 0 || submittedAt.trim().length === 0) {
11
+ throw new Error("Azure Table legacy submission is missing answeredAt or Timestamp.");
12
+ }
13
+ return {
14
+ id: entity.RowKey,
15
+ formId: entity.PartitionKey,
16
+ formVersion: entity.surveyVersion ?? 1,
17
+ values,
18
+ metadata: {},
19
+ submittedAt
20
+ };
21
+ },
22
+ createPartitionKey: (formId, submissionId) => createPartitionKey(formId, submissionId),
23
+ createRowKey: (submittedAt, submissionId) => createRowKey(submittedAt, submissionId)
24
+ };
25
+ };
3
26
  var BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
4
27
  function encodeBase64(bytes) {
5
28
  let result = "";
@@ -108,6 +131,11 @@ function parseJson(value, location) {
108
131
  throw new Error(`Azure Table ${location} payload is invalid.`, { cause });
109
132
  }
110
133
  }
134
+ function parseLegacyJsonObject(value, property) {
135
+ const parsed = parseJson(value, `legacy ${property}`);
136
+ if (!isRecord(parsed)) throw new Error(`Azure Table legacy ${property} payload must be an object.`);
137
+ return parsed;
138
+ }
111
139
  function parseSubmission(value, location) {
112
140
  const parsed = typeof value === "string" ? parseJson(value, location) : value;
113
141
  if (!isRecord(parsed) || typeof parsed.id !== "string" || typeof parsed.formId !== "string" || !Number.isInteger(parsed.formVersion) || typeof parsed.locale !== "string" || typeof parsed.submittedAt !== "string" || !isRecord(parsed.values) || !Object.values(parsed.values).every(isFormValue)) {
@@ -484,6 +512,11 @@ function createAzureTableStorage(options = {}) {
484
512
  continue;
485
513
  }
486
514
  const answers = submissionTextAnswers(submission, fieldIds);
515
+ const metadata = submission.metadata === void 0 ? void 0 : Object.fromEntries(
516
+ Object.entries(submission.metadata).filter(
517
+ (entry) => entry[1] !== void 0
518
+ )
519
+ );
487
520
  for (let fieldIndex = entityIndex === entityStartIndex ? fieldStartIndex : 0; fieldIndex < answers.length; fieldIndex += 1) {
488
521
  const answer = answers[fieldIndex];
489
522
  if (answer === void 0) continue;
@@ -493,9 +526,9 @@ function createAzureTableStorage(options = {}) {
493
526
  formVersion: submission.formVersion,
494
527
  fieldId: answer.fieldId,
495
528
  text: answer.text,
496
- locale: submission.locale,
529
+ ...submission.locale === void 0 ? {} : { locale: submission.locale },
497
530
  submittedAt: submission.submittedAt,
498
- ...submission.metadata === void 0 ? {} : { metadata: submission.metadata }
531
+ ...metadata === void 0 ? {} : { metadata }
499
532
  });
500
533
  if (items.length < pageSize) continue;
501
534
  const nextFieldIndex = fieldIndex + 1;
@@ -586,6 +619,7 @@ function createAzureTableStorage(options = {}) {
586
619
  }
587
620
  export {
588
621
  createAzureTableStorage,
622
+ createLegacyAzureTableCodec,
589
623
  defaultAzureTableSubmissionCodec,
590
624
  metadataFiltersToOData,
591
625
  submissionFilterToOData
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@form-engine-ts/storage-azure-table",
3
- "version": "5.1.0",
3
+ "version": "6.0.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -39,7 +39,7 @@
39
39
  "typescript"
40
40
  ],
41
41
  "dependencies": {
42
- "@form-engine-ts/core": "5.1.0"
42
+ "@form-engine-ts/core": "6.0.0"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "@azure/data-tables": "^13.3.2"