oneentry 1.0.162 → 1.0.163

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.
Files changed (65) hide show
  1. package/README.md +18 -0
  2. package/changelog.md +59 -0
  3. package/dist/admins/adminsSchemas.d.ts +6 -22
  4. package/dist/admins/adminsSchemas.js +2 -9
  5. package/dist/attribute-sets/attributeSetsInterfaces.d.ts +6 -6
  6. package/dist/auth-provider/authProvidersInterfaces.d.ts +4 -4
  7. package/dist/base/attributes.d.ts +105 -0
  8. package/dist/base/attributes.js +172 -0
  9. package/dist/base/utils.d.ts +95 -2
  10. package/dist/base/validation.d.ts +29 -0
  11. package/dist/base/validation.js +93 -1
  12. package/dist/blocks/blocksInterfaces.d.ts +1 -1
  13. package/dist/blocks/blocksSchemas.d.ts +6 -6
  14. package/dist/blocks/blocksSchemas.js +2 -1
  15. package/dist/discounts/discountsInterfaces.d.ts +1 -1
  16. package/dist/forms-data/formsDataApi.d.ts +16 -6
  17. package/dist/forms-data/formsDataApi.js +14 -4
  18. package/dist/forms-data/formsDataInterfaces.d.ts +48 -15
  19. package/dist/index.d.ts +1 -0
  20. package/dist/index.js +9 -1
  21. package/dist/integration-collections/integrationCollectionsInterfaces.d.ts +6 -6
  22. package/dist/menus/menusInterfaces.d.ts +1 -1
  23. package/dist/menus/menusSchemas.js +2 -1
  24. package/dist/orders/ordersInterfaces.d.ts +1 -1
  25. package/dist/pages/pagesSchemas.d.ts +3 -3
  26. package/dist/pages/pagesSchemas.js +2 -1
  27. package/dist/product-statuses/productStatusesInterfaces.d.ts +2 -2
  28. package/dist/products/productsSchemas.d.ts +5 -5
  29. package/dist/products/productsSchemas.js +3 -2
  30. package/dist/templates/templatesInterfaces.d.ts +1 -1
  31. package/dist/templates/templatesSchemas.d.ts +3 -3
  32. package/dist/templates/templatesSchemas.js +2 -1
  33. package/dist/templates-preview/templatesPreviewInterfaces.d.ts +2 -2
  34. package/esm/admins/adminsSchemas.d.ts +6 -22
  35. package/esm/admins/adminsSchemas.js +2 -9
  36. package/esm/attribute-sets/attributeSetsInterfaces.d.ts +6 -6
  37. package/esm/auth-provider/authProvidersInterfaces.d.ts +4 -4
  38. package/esm/base/attributes.d.ts +105 -0
  39. package/esm/base/attributes.js +163 -0
  40. package/esm/base/utils.d.ts +95 -2
  41. package/esm/base/validation.d.ts +29 -0
  42. package/esm/base/validation.js +92 -0
  43. package/esm/blocks/blocksInterfaces.d.ts +1 -1
  44. package/esm/blocks/blocksSchemas.d.ts +6 -6
  45. package/esm/blocks/blocksSchemas.js +2 -1
  46. package/esm/discounts/discountsInterfaces.d.ts +1 -1
  47. package/esm/forms-data/formsDataApi.d.ts +16 -6
  48. package/esm/forms-data/formsDataApi.js +14 -4
  49. package/esm/forms-data/formsDataInterfaces.d.ts +48 -15
  50. package/esm/index.d.ts +1 -0
  51. package/esm/index.js +1 -0
  52. package/esm/integration-collections/integrationCollectionsInterfaces.d.ts +6 -6
  53. package/esm/menus/menusInterfaces.d.ts +1 -1
  54. package/esm/menus/menusSchemas.js +2 -1
  55. package/esm/orders/ordersInterfaces.d.ts +1 -1
  56. package/esm/pages/pagesSchemas.d.ts +3 -3
  57. package/esm/pages/pagesSchemas.js +2 -1
  58. package/esm/product-statuses/productStatusesInterfaces.d.ts +2 -2
  59. package/esm/products/productsSchemas.d.ts +5 -5
  60. package/esm/products/productsSchemas.js +3 -2
  61. package/esm/templates/templatesInterfaces.d.ts +1 -1
  62. package/esm/templates/templatesSchemas.d.ts +3 -3
  63. package/esm/templates/templatesSchemas.js +2 -1
  64. package/esm/templates-preview/templatesPreviewInterfaces.d.ts +2 -2
  65. package/package.json +2 -2
@@ -266,10 +266,19 @@ interface ITimeIntervalRange {
266
266
  * @property {string} type - Attribute data type (e.g. "string", "integer", "list", "file", "image").
267
267
  * @property {unknown} value - Attribute value — actual TS type depends on `type`, and the SDK normalizes it to the same shape in every module: `string` for "string"/"text"; `number | null` for "integer"/"float"/"real"; the file object itself for a single-file "image"/"file" and an array of them for several; an array for "list" and "groupOfImages". An attribute with no value is always `null`. Example: "Admins text".
268
268
  * @property {number} [position] - Sort position of the value inside its set; the containing collection is returned sorted by it. Example: 0.
269
- * @property {Record<string, IAttributeValue> | unknown[]} [additionalFields] - Nested attribute values keyed by marker; the API may also return an empty array when none are configured. Optional.
269
+ * @property {Record<string, IAttributeValue> | unknown[]} [additionalFields] - Nested attribute values keyed by marker; the API may also return an empty array when none are configured. Read it through `getAdditionalFields`, which collapses that array to an empty map. Optional.
270
270
  * @property {boolean} [isIcon] - Block/preview attribute flag — whether the field is treated as an icon. Optional.
271
271
  * @property {boolean} [isProductPreview] - Block/preview attribute flag — whether the field is shown in product preview. Optional.
272
272
  * @description A single attribute value attached to an entity (admin, user, page, product, …).
273
+ *
274
+ * `value` is `unknown` because its shape is decided by `type` at runtime, which
275
+ * a single interface cannot express. Do not read it by assumption — the type
276
+ * checker cannot catch a wrong guess (an array read on a single file yields
277
+ * `undefined`, silently, with no error anywhere). Reach it through the guards
278
+ * and helpers instead: `isFileAttribute` / `getAttributeFiles`,
279
+ * `isStringAttribute`, `isNumberAttribute`, `isListAttribute`,
280
+ * `isTimeIntervalAttribute`; or annotate with {@link ITypedAttributeValue} to
281
+ * have the compiler enforce the rule.
273
282
  */
274
283
  interface IAttributeValue {
275
284
  type: string;
@@ -286,6 +295,90 @@ interface IAttributeValue {
286
295
  interface IAttributeValues {
287
296
  [key: string]: IAttributeValue;
288
297
  }
298
+ /**
299
+ * @interface IAttributeFile
300
+ * @property {string} filename - Storage-relative path of the file. Example: "files/project/product/2954/image/48853cbf-4462-42c6-8b99-d14f721c21a2.jpeg".
301
+ * @property {string} downloadLink - Public URL of the original file. Example: "https://my-project.oneentry.cloud/cloud-static/files/project/product/2954/image/48853cbf.jpeg".
302
+ * @property {number} size - File size in bytes. Example: 742690.
303
+ * @property {string} contentType - MIME type of the file. Example: "image/jpeg".
304
+ * @property {Record<string, string[]>} [previewLink] - Generated previews keyed by preview template. Each entry is a `[placeholder, url]` pair: the first item is an inline base64 thumbnail (usable as a `blurDataURL`), the second the preview URL. Optional — previews are generated for images; a non-image `file` may arrive without them. Example: { "default": ["data:image/webp;base64,UklGRmwA…", "https://…48853cbf.preview.default.jpeg"] }.
305
+ * @property {string} [defaultPreview] - Key of `previewLink` to use by default. Optional — present alongside `previewLink`. Example: "default".
306
+ * @description One file of an `image`, `file` or `groupOfImages` attribute value. Reach it through {@link IFileAttributeValue} — or, without caring whether the attribute holds one file or many, through `getAttributeFiles`.
307
+ */
308
+ interface IAttributeFile {
309
+ filename: string;
310
+ downloadLink: string;
311
+ size: number;
312
+ contentType: string;
313
+ previewLink?: Record<string, string[]>;
314
+ defaultPreview?: string;
315
+ }
316
+ /**
317
+ * @interface IFileAttributeValue
318
+ * @property {'image' | 'file' | 'groupOfImages'} type - Discriminant identifying the attribute as file-bearing.
319
+ * @property {IAttributeFile | IAttributeFile[] | null} value - The file itself when an `image`/`file` attribute holds exactly one, an array when it holds several, always an array for `groupOfImages`, `null` when nothing is set.
320
+ * @description {@link IAttributeValue} narrowed to attributes that carry files, whose generic `value: unknown` resolves to {@link IAttributeFile}. The single/array split is what the SDK's normalization produces, so it has to be handled: narrow with `isFileAttribute`, or skip the branch entirely with `getAttributeFiles` / `getAttributeFile`.
321
+ */
322
+ interface IFileAttributeValue extends IAttributeValue {
323
+ type: 'image' | 'file' | 'groupOfImages';
324
+ value: IAttributeFile | IAttributeFile[] | null;
325
+ }
326
+ /**
327
+ * @interface IStringAttributeValue
328
+ * @property {'string' | 'text' | 'textEditor'} type - Discriminant identifying the attribute as text-bearing.
329
+ * @property {string | null} value - The text; `null` when no value is set.
330
+ * @description {@link IAttributeValue} narrowed to text attributes. Narrow to it with `isStringAttribute`.
331
+ */
332
+ interface IStringAttributeValue extends IAttributeValue {
333
+ type: 'string' | 'text' | 'textEditor';
334
+ value: string | null;
335
+ }
336
+ /**
337
+ * @interface INumberAttributeValue
338
+ * @property {'integer' | 'float' | 'real'} type - Discriminant identifying the attribute as numeric.
339
+ * @property {number | null} value - The number, cast by the SDK from the API's string form; `null` when no value is set.
340
+ * @description {@link IAttributeValue} narrowed to numeric attributes. Narrow to it with `isNumberAttribute`.
341
+ */
342
+ interface INumberAttributeValue extends IAttributeValue {
343
+ type: 'integer' | 'float' | 'real';
344
+ value: number | null;
345
+ }
346
+ /**
347
+ * @interface IListAttributeValue
348
+ * @property {'list'} type - Discriminant identifying the attribute as a list.
349
+ * @property {unknown[]} value - The selected options. The option shape is defined by the attribute set, so the entries stay `unknown` — only the array container is guaranteed.
350
+ * @description {@link IAttributeValue} narrowed to `list` attributes. Narrow to it with `isListAttribute`.
351
+ */
352
+ interface IListAttributeValue extends IAttributeValue {
353
+ type: 'list';
354
+ value: unknown[];
355
+ }
356
+ /**
357
+ * ITypedAttributeValue — {@link IAttributeValue} as a discriminated union over `type`.
358
+ *
359
+ * Opt-in, and deliberately closed: it lists only the types whose `value` shape
360
+ * the SDK guarantees, so `switch (attr.type)` narrows `value` for you. A loose
361
+ * member (`type: string`) would be assignable to every case and collapse the
362
+ * narrowing back to `unknown`, which is exactly what this type exists to avoid.
363
+ *
364
+ * `IAttributeValues` keeps the loose `IAttributeValue`, so nothing existing
365
+ * changes: narrow into this union with the guards (`isFileAttribute` and the
366
+ * rest), or annotate a value you control. Attribute types the SDK does not
367
+ * model (`entity`, `date`, custom ones) stay on `IAttributeValue`.
368
+ * @example
369
+ * ```ts
370
+ * function render(attr: ITypedAttributeValue) {
371
+ * switch (attr.type) {
372
+ * case 'image':
373
+ * case 'file':
374
+ * return attr.value; // IAttributeFile | IAttributeFile[] | null
375
+ * case 'integer':
376
+ * return attr.value; // number | null
377
+ * }
378
+ * }
379
+ * ```
380
+ */
381
+ type ITypedAttributeValue = IFileAttributeValue | IStringAttributeValue | INumberAttributeValue | IListAttributeValue | ITimeIntervalAttributeValue;
289
382
  /**
290
383
  * @interface IRating
291
384
  * @property {number} [value] - Average rating value. Example: 4.5.
@@ -351,4 +444,4 @@ type LangType = string | Array<string>;
351
444
  * LocalizeType
352
445
  */
353
446
  type LocalizeType = ILocalizeInfo;
354
- export type { IAttributeLocalizeInfo, IAttributes, IAttributeValue, IAttributeValues, IConfig, IError, IHttpHeaders, IHttpOptions, ILocalizeInfo, IRating, ITimeIntervalAttributeValue, ITimeIntervalEntitySchedule, ITimeIntervalGroup, ITimeIntervalPoint, ITimeIntervalRange, ITimeIntervalSchedule, ITimeIntervalWindow, LangType, LocalizeType, TimeIntervalPair, };
447
+ export type { IAttributeFile, IAttributeLocalizeInfo, IAttributes, IAttributeValue, IAttributeValues, IConfig, IError, IFileAttributeValue, IHttpHeaders, IHttpOptions, IListAttributeValue, ILocalizeInfo, INumberAttributeValue, IRating, IStringAttributeValue, ITimeIntervalAttributeValue, ITimeIntervalEntitySchedule, ITimeIntervalGroup, ITimeIntervalPoint, ITimeIntervalRange, ITimeIntervalSchedule, ITimeIntervalWindow, ITypedAttributeValue, LangType, LocalizeType, TimeIntervalPair, };
@@ -36,6 +36,35 @@ export declare const AttributeSchema: z.ZodObject<{
36
36
  }, z.core.$strip>>>;
37
37
  value: z.ZodOptional<z.ZodAny>;
38
38
  }, z.core.$strip>;
39
+ /**
40
+ * Attribute file schema
41
+ *
42
+ * One file of an `image`, `file` or `groupOfImages` attribute value, as the API
43
+ * returns it — mirrors `IAttributeFile`. Loose: the API may add fields, and a
44
+ * new one is not a reason to report a broken response.
45
+ */
46
+ export declare const AttributeFileSchema: z.ZodObject<{
47
+ filename: z.ZodString;
48
+ downloadLink: z.ZodString;
49
+ size: z.ZodNumber;
50
+ contentType: z.ZodString;
51
+ previewLink: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>;
52
+ defaultPreview: z.ZodOptional<z.ZodString>;
53
+ }, z.core.$loose>;
54
+ /**
55
+ * Attribute values schema
56
+ *
57
+ * The container the API returns under `attributeValues`. Both nestings are
58
+ * accepted, since the raw response wraps the marker map in a locale
59
+ * (`{ en_US: { marker: attr } }`) while some endpoints return it flat.
60
+ *
61
+ * Attribute values are open by design — what `value` holds is decided by the
62
+ * project's attribute set — so the schema validates the one part the API fixes
63
+ * and the SDK relies on: the shape of files. If a future API release renames or
64
+ * drops a file field, `getAttributeFiles` would silently return nothing;
65
+ * validation (`config.validation.enabled`) reports it instead.
66
+ */
67
+ export declare const AttributeValuesSchema: z.ZodRecord<z.ZodString, z.ZodUnknown>;
39
68
  /**
40
69
  * User entity schema
41
70
  */
@@ -33,6 +33,98 @@ export const AttributeSchema = z.object({
33
33
  localizeInfos: z.record(z.string(), LocalizeInfoSchema).optional(),
34
34
  value: z.any().optional(),
35
35
  });
36
+ /**
37
+ * Attribute file schema
38
+ *
39
+ * One file of an `image`, `file` or `groupOfImages` attribute value, as the API
40
+ * returns it — mirrors `IAttributeFile`. Loose: the API may add fields, and a
41
+ * new one is not a reason to report a broken response.
42
+ */
43
+ export const AttributeFileSchema = z.looseObject({
44
+ filename: z.string(),
45
+ downloadLink: z.string(),
46
+ size: z.number(),
47
+ contentType: z.string(),
48
+ previewLink: z.record(z.string(), z.array(z.string())).optional(),
49
+ defaultPreview: z.string().optional(),
50
+ });
51
+ /**
52
+ * Attribute types whose value carries files.
53
+ */
54
+ const FILE_ATTRIBUTE_TYPES = ['image', 'file', 'groupOfImages'];
55
+ /**
56
+ * Reports whether a value is the empty localization map the API sends for an
57
+ * attribute with no value set.
58
+ * @param {unknown} value - The value to test.
59
+ * @returns {boolean} True when the value carries nothing.
60
+ */
61
+ function isEmptyValue(value) {
62
+ if (value === null || value === undefined)
63
+ return true;
64
+ return (typeof value === 'object' &&
65
+ !Array.isArray(value) &&
66
+ Object.keys(value).length === 0);
67
+ }
68
+ /**
69
+ * Validates the files of one attribute value, recursing into nested fields.
70
+ *
71
+ * Only file-bearing attributes are inspected. Everything else is left alone:
72
+ * the shape of `value` is defined per attribute type by the project's own
73
+ * attribute set, so a schema cannot describe it — but the file shape is fixed
74
+ * by the API, and the SDK's own helpers (`getAttributeFiles`) depend on it.
75
+ * @param {unknown} node - An attribute value, or a container of them.
76
+ * @param {string[]} path - Path to the node, for issue reporting.
77
+ * @param {z.RefinementCtx} ctx - Zod refinement context issues are added to.
78
+ */
79
+ function validateAttributeFiles(node, path, ctx) {
80
+ if (!node || typeof node !== 'object')
81
+ return;
82
+ if (Array.isArray(node)) {
83
+ node.forEach((item, index) => validateAttributeFiles(item, [...path, String(index)], ctx));
84
+ return;
85
+ }
86
+ const attr = node;
87
+ const isAttribute = typeof attr.type === 'string' && 'value' in attr;
88
+ if (isAttribute && FILE_ATTRIBUTE_TYPES.includes(attr.type)) {
89
+ if (!isEmptyValue(attr.value)) {
90
+ // The API always sends files as an array; the SDK unwraps a single one
91
+ // afterwards, so both shapes are accepted here.
92
+ const files = Array.isArray(attr.value) ? attr.value : [attr.value];
93
+ files.forEach((file, index) => {
94
+ const result = AttributeFileSchema.safeParse(file);
95
+ if (result.success)
96
+ return;
97
+ result.error.issues.forEach((issue) => ctx.addIssue({
98
+ code: 'custom',
99
+ path: [...path, 'value', index, ...issue.path],
100
+ message: `Attribute of type "${attr.type}" carries an unexpected file shape: ${issue.message}`,
101
+ }));
102
+ });
103
+ }
104
+ }
105
+ // Nested fields carry attribute values of their own, files included.
106
+ if (isAttribute) {
107
+ validateAttributeFiles(attr.additionalFields, [...path, 'additionalFields'], ctx);
108
+ return;
109
+ }
110
+ Object.entries(attr).forEach(([key, value]) => validateAttributeFiles(value, [...path, key], ctx));
111
+ }
112
+ /**
113
+ * Attribute values schema
114
+ *
115
+ * The container the API returns under `attributeValues`. Both nestings are
116
+ * accepted, since the raw response wraps the marker map in a locale
117
+ * (`{ en_US: { marker: attr } }`) while some endpoints return it flat.
118
+ *
119
+ * Attribute values are open by design — what `value` holds is decided by the
120
+ * project's attribute set — so the schema validates the one part the API fixes
121
+ * and the SDK relies on: the shape of files. If a future API release renames or
122
+ * drops a file field, `getAttributeFiles` would silently return nothing;
123
+ * validation (`config.validation.enabled`) reports it instead.
124
+ */
125
+ export const AttributeValuesSchema = z
126
+ .record(z.string(), z.unknown())
127
+ .superRefine((values, ctx) => validateAttributeFiles(values, [], ctx));
36
128
  /**
37
129
  * User entity schema
38
130
  */
@@ -29,7 +29,7 @@ interface IBlocks {
29
29
  * @throws {IError} - If there is an error during the fetch operation, it will return an error object.
30
30
  * @description This method retrieves a block object by its marker.
31
31
  */
32
- getBlockByMarker(marker: string, langCode: string, offset?: number, limit?: number): Promise<IBlockEntity | IError>;
32
+ getBlockByMarker(marker: string, langCode?: string, offset?: number, limit?: number): Promise<IBlockEntity | IError>;
33
33
  /**
34
34
  * Performs a quick search for block objects with limited output.
35
35
  * @handleName searchBlock
@@ -12,7 +12,7 @@ export declare const BlockEntitySchema: z.ZodObject<{
12
12
  type: z.ZodString;
13
13
  templateIdentifier: z.ZodNullable<z.ZodString>;
14
14
  isVisible: z.ZodBoolean;
15
- attributeValues: z.ZodRecord<z.ZodString, z.ZodAny>;
15
+ attributeValues: z.ZodRecord<z.ZodString, z.ZodUnknown>;
16
16
  similarProducts: z.ZodOptional<z.ZodObject<{
17
17
  items: z.ZodArray<z.ZodObject<{
18
18
  id: z.ZodNumber;
@@ -30,7 +30,7 @@ export declare const BlockEntitySchema: z.ZodObject<{
30
30
  }, z.core.$strip>;
31
31
  sku: z.ZodNullable<z.ZodString>;
32
32
  isSync: z.ZodBoolean;
33
- attributeValues: z.ZodRecord<z.ZodString, z.ZodAny>;
33
+ attributeValues: z.ZodRecord<z.ZodString, z.ZodUnknown>;
34
34
  categories: z.ZodArray<z.ZodString>;
35
35
  isVisible: z.ZodBoolean;
36
36
  moduleFormConfigs: z.ZodOptional<z.ZodArray<z.ZodAny>>;
@@ -73,7 +73,7 @@ export declare const BlockEntitySchema: z.ZodObject<{
73
73
  }, z.core.$strip>;
74
74
  sku: z.ZodNullable<z.ZodString>;
75
75
  isSync: z.ZodBoolean;
76
- attributeValues: z.ZodRecord<z.ZodString, z.ZodAny>;
76
+ attributeValues: z.ZodRecord<z.ZodString, z.ZodUnknown>;
77
77
  categories: z.ZodArray<z.ZodString>;
78
78
  isVisible: z.ZodBoolean;
79
79
  moduleFormConfigs: z.ZodOptional<z.ZodArray<z.ZodAny>>;
@@ -109,7 +109,7 @@ export declare const BlocksResponseSchema: z.ZodObject<{
109
109
  type: z.ZodString;
110
110
  templateIdentifier: z.ZodNullable<z.ZodString>;
111
111
  isVisible: z.ZodBoolean;
112
- attributeValues: z.ZodRecord<z.ZodString, z.ZodAny>;
112
+ attributeValues: z.ZodRecord<z.ZodString, z.ZodUnknown>;
113
113
  similarProducts: z.ZodOptional<z.ZodObject<{
114
114
  items: z.ZodArray<z.ZodObject<{
115
115
  id: z.ZodNumber;
@@ -127,7 +127,7 @@ export declare const BlocksResponseSchema: z.ZodObject<{
127
127
  }, z.core.$strip>;
128
128
  sku: z.ZodNullable<z.ZodString>;
129
129
  isSync: z.ZodBoolean;
130
- attributeValues: z.ZodRecord<z.ZodString, z.ZodAny>;
130
+ attributeValues: z.ZodRecord<z.ZodString, z.ZodUnknown>;
131
131
  categories: z.ZodArray<z.ZodString>;
132
132
  isVisible: z.ZodBoolean;
133
133
  moduleFormConfigs: z.ZodOptional<z.ZodArray<z.ZodAny>>;
@@ -170,7 +170,7 @@ export declare const BlocksResponseSchema: z.ZodObject<{
170
170
  }, z.core.$strip>;
171
171
  sku: z.ZodNullable<z.ZodString>;
172
172
  isSync: z.ZodBoolean;
173
- attributeValues: z.ZodRecord<z.ZodString, z.ZodAny>;
173
+ attributeValues: z.ZodRecord<z.ZodString, z.ZodUnknown>;
174
174
  categories: z.ZodArray<z.ZodString>;
175
175
  isVisible: z.ZodBoolean;
176
176
  moduleFormConfigs: z.ZodOptional<z.ZodArray<z.ZodAny>>;
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import { AttributeValuesSchema } from '../base/validation.js';
2
3
  import { ProductEntitySchema, ProductsResponseSchema, } from '../products/productsSchemas.js';
3
4
  /**
4
5
  * Block entity schema
@@ -13,7 +14,7 @@ export const BlockEntitySchema = z.object({
13
14
  type: z.string(),
14
15
  templateIdentifier: z.string().nullable(),
15
16
  isVisible: z.boolean(),
16
- attributeValues: z.record(z.string(), z.any()),
17
+ attributeValues: AttributeValuesSchema,
17
18
  similarProducts: ProductsResponseSchema.optional(),
18
19
  countElementsPerRow: z.number().optional(),
19
20
  quantity: z.number().optional(),
@@ -29,7 +29,7 @@ interface IDiscountsApi {
29
29
  * @throws {IError} When isShell=false and an error occurs during the fetch
30
30
  * @description Fetches a discount by its marker.
31
31
  */
32
- getDiscountByMarker(marker: string, langCode: string): Promise<IDiscountsEntity | IError>;
32
+ getDiscountByMarker(marker: string, langCode?: string): Promise<IDiscountsEntity | IError>;
33
33
  /**
34
34
  * Validate discounts coupon
35
35
  * @handleName validateDiscountsCoupon
@@ -1,7 +1,7 @@
1
1
  import AsyncModules from '../base/asyncModules.js';
2
2
  import type StateModule from '../base/stateModule.js';
3
3
  import type { IError } from '../base/utils.js';
4
- import type { IBodyPostFormData, IFormsByMarkerDataEntity, IFormsData, IFormsDataSearchResponse, IFormsDataVectorSearch, IPostFormResponse, IUpdateFormsData } from './formsDataInterfaces.js';
4
+ import type { IBodyPostFormData, IFormsByMarkerDataEntity, IFormsData, IFormsDataFilter, IFormsDataSearchResponse, IFormsDataVectorSearch, IPostFormResponse, IUpdateFormsData } from './formsDataInterfaces.js';
5
5
  /**
6
6
  * Controllers for working with form data
7
7
  * @handle /api/content/form-data
@@ -56,6 +56,7 @@ export default class FormsDataApi extends AsyncModules implements IFormsData {
56
56
  * @param {string} [langCode] - Language code. Default: "en_US".
57
57
  * @returns {Promise<IPostFormResponse | IError>} Returns an object containing the created form data entry and message.
58
58
  * @throws {IError} When isShell=false and an error occurs during the fetch
59
+ * @description Field values are typed per field type by {@link FormDataType}. For attached files see {@link IImageValue} (`image`, `groupOfImages`) and {@link IFileValue} (`file`) — the shape the endpoint expects, which is **not** what `FileUploading.upload` returns: that object carries `contentType`, which form data rejects. Passing a `File`/`FileList` as the value is also supported — the SDK uploads it first, then strips `contentType` from the resulting value.
59
60
  * @see {@link https://js-sdk.oneentry.cloud/docs/forms-data/postFormsData postFormsData} documentation.
60
61
  */
61
62
  postFormsData(body: IBodyPostFormData, langCode?: string): Promise<IPostFormResponse | IError>;
@@ -64,25 +65,34 @@ export default class FormsDataApi extends AsyncModules implements IFormsData {
64
65
  * @handleName getFormsDataByMarker
65
66
  * @param {string} marker - Marker of the form. Example: "contact_form".
66
67
  * @param {number} formModuleConfigId - Form module configuration ID. Example: 4.
67
- * @param {object} [body] - Request body.
68
+ * @param {IFormsDataFilter} [body] - Filter for the records to return. Every field is optional; an omitted or empty one is not applied.
68
69
  * @example
69
70
  {
70
- "entityIdentifier": 14,
71
- "entityparentIdentifier": 167,
71
+ "entityIdentifier": "blog",
72
+ "parentId": 10,
72
73
  "userIdentifier": "",
73
- "status": ["new", "approved"],
74
+ "status": ["moderation", "approved"],
74
75
  "dateFrom": "2025-01-01",
75
76
  "dateTo": ""
76
77
  }
78
+ * @param {string} [body.entityIdentifier] - Text identifier (marker) of the entity the records belong to. Example: "blog".
79
+ * @param {number} [body.parentId] - Identifier of the parent record, to fetch replies to one submission. Example: 10.
80
+ * @param {string} [body.userIdentifier] - Text identifier of the sender. Example: "admin".
81
+ * @param {FormDataStatus[]} [body.status] - Moderation statuses to keep: "sent", "moderation", "approved", "banned", "deleted". Must be an array; anything outside the set is rejected with `400 each value in status must be a valid enum value`. Example: ["approved"].
82
+ * @param {string} [body.dateFrom] - Lower bound of the submission date, `YYYY-MM-DD`. An unparsable value fails the request with a `500`. Example: "2025-01-01".
83
+ * @param {string} [body.dateTo] - Upper bound of the submission date, `YYYY-MM-DD`. Example: "2025-12-31".
77
84
  * @param {number} [isExtended] - Flag for getting additional fields. Example: 1.
78
85
  * @param {string} [langCode] - Language code. Default: "en_US".
79
86
  * @param {number} [offset] - Parameter for pagination. Default: 0.
80
87
  * @param {number} [limit] - Parameter for pagination. Default: 30.
81
88
  * @returns {Promise<IFormsByMarkerDataEntity | IError>} Returns an object containing the form data or an error object if there was an issue.
82
89
  * @throws {IError} When isShell=false and an error occurs during the fetch
90
+ * @description Each record's `formData` comes back **already unwrapped from its locale**. The API sends `formData: { "en_US": [ … ] }`; the SDK normalizes the response and hands over the array for `langCode`, so read `record.formData` — `record.formData[langCode]` is `undefined` and yields an empty list with no error. This holds for both values of `isExtended`.
91
+ *
92
+ * The filter body is typed for a reason: the API silently ignores a field it does not know, so a misspelled `statuses` returns every record — unmoderated ones included — and the code looks like it works.
83
93
  * @see {@link https://js-sdk.oneentry.cloud/docs/forms-data/getFormsDataByMarker getFormsDataByMarker} documentation.
84
94
  */
85
- getFormsDataByMarker(marker: string, formModuleConfigId: number, body?: object, isExtended?: number, langCode?: string, offset?: number, limit?: number): Promise<IFormsByMarkerDataEntity | IError>;
95
+ getFormsDataByMarker(marker: string, formModuleConfigId: number, body?: IFormsDataFilter, isExtended?: number, langCode?: string, offset?: number, limit?: number): Promise<IFormsByMarkerDataEntity | IError>;
86
96
  /**
87
97
  * Update one object of form data by id. Requires user authentication.
88
98
  * @handleName updateFormsDataByid
@@ -58,6 +58,7 @@ export default class FormsDataApi extends AsyncModules {
58
58
  * @param {string} [langCode] - Language code. Default: "en_US".
59
59
  * @returns {Promise<IPostFormResponse | IError>} Returns an object containing the created form data entry and message.
60
60
  * @throws {IError} When isShell=false and an error occurs during the fetch
61
+ * @description Field values are typed per field type by {@link FormDataType}. For attached files see {@link IImageValue} (`image`, `groupOfImages`) and {@link IFileValue} (`file`) — the shape the endpoint expects, which is **not** what `FileUploading.upload` returns: that object carries `contentType`, which form data rejects. Passing a `File`/`FileList` as the value is also supported — the SDK uploads it first, then strips `contentType` from the resulting value.
61
62
  * @see {@link https://js-sdk.oneentry.cloud/docs/forms-data/postFormsData postFormsData} documentation.
62
63
  */
63
64
  async postFormsData(body, langCode = this.state.lang) {
@@ -126,22 +127,31 @@ export default class FormsDataApi extends AsyncModules {
126
127
  * @handleName getFormsDataByMarker
127
128
  * @param {string} marker - Marker of the form. Example: "contact_form".
128
129
  * @param {number} formModuleConfigId - Form module configuration ID. Example: 4.
129
- * @param {object} [body] - Request body.
130
+ * @param {IFormsDataFilter} [body] - Filter for the records to return. Every field is optional; an omitted or empty one is not applied.
130
131
  * @example
131
132
  {
132
- "entityIdentifier": 14,
133
- "entityparentIdentifier": 167,
133
+ "entityIdentifier": "blog",
134
+ "parentId": 10,
134
135
  "userIdentifier": "",
135
- "status": ["new", "approved"],
136
+ "status": ["moderation", "approved"],
136
137
  "dateFrom": "2025-01-01",
137
138
  "dateTo": ""
138
139
  }
140
+ * @param {string} [body.entityIdentifier] - Text identifier (marker) of the entity the records belong to. Example: "blog".
141
+ * @param {number} [body.parentId] - Identifier of the parent record, to fetch replies to one submission. Example: 10.
142
+ * @param {string} [body.userIdentifier] - Text identifier of the sender. Example: "admin".
143
+ * @param {FormDataStatus[]} [body.status] - Moderation statuses to keep: "sent", "moderation", "approved", "banned", "deleted". Must be an array; anything outside the set is rejected with `400 each value in status must be a valid enum value`. Example: ["approved"].
144
+ * @param {string} [body.dateFrom] - Lower bound of the submission date, `YYYY-MM-DD`. An unparsable value fails the request with a `500`. Example: "2025-01-01".
145
+ * @param {string} [body.dateTo] - Upper bound of the submission date, `YYYY-MM-DD`. Example: "2025-12-31".
139
146
  * @param {number} [isExtended] - Flag for getting additional fields. Example: 1.
140
147
  * @param {string} [langCode] - Language code. Default: "en_US".
141
148
  * @param {number} [offset] - Parameter for pagination. Default: 0.
142
149
  * @param {number} [limit] - Parameter for pagination. Default: 30.
143
150
  * @returns {Promise<IFormsByMarkerDataEntity | IError>} Returns an object containing the form data or an error object if there was an issue.
144
151
  * @throws {IError} When isShell=false and an error occurs during the fetch
152
+ * @description Each record's `formData` comes back **already unwrapped from its locale**. The API sends `formData: { "en_US": [ … ] }`; the SDK normalizes the response and hands over the array for `langCode`, so read `record.formData` — `record.formData[langCode]` is `undefined` and yields an empty list with no error. This holds for both values of `isExtended`.
153
+ *
154
+ * The filter body is typed for a reason: the API silently ignores a field it does not know, so a misspelled `statuses` returns every record — unmoderated ones included — and the code looks like it works.
145
155
  * @see {@link https://js-sdk.oneentry.cloud/docs/forms-data/getFormsDataByMarker getFormsDataByMarker} documentation.
146
156
  */
147
157
  async getFormsDataByMarker(marker, formModuleConfigId, body = {}, isExtended = 0, langCode = this.state.lang, offset = 0, limit = 30) {
@@ -52,24 +52,24 @@ interface IFormsData {
52
52
  * @handleName getFormsDataByMarker
53
53
  * @param {string} marker - The marker identifying the form data. Example: "contact_form_data".
54
54
  * @param {number} formModuleConfigId - The form module configuration ID. Example: 4.
55
- * @param {object} [body] - Request body. Default: {}.
55
+ * @param {IFormsDataFilter} [body] - Filter for the records to return. Default: {}. Every field is optional; an omitted or empty one is not applied. Valid `status` values: "sent", "moderation", "approved", "banned", "deleted".
56
56
  * @example
57
57
  {
58
- "entityIdentifier": "test",
58
+ "entityIdentifier": "blog",
59
59
  "userIdentifier": "",
60
- "status": "",
60
+ "status": ["approved"],
61
61
  "dateFrom": "2025-08-11",
62
62
  "dateTo": ""
63
63
  }
64
- * @param {number} [isNested] - Flag for getting hierarchical data. Example: 1.
64
+ * @param {number} [isExtended] - Flag for getting additional fields. Example: 1.
65
65
  * @param {string} [langCode] - The language code for localization. Default: "en_US".
66
66
  * @param {number} [offset] - Optional offset for pagination. Default: 0.
67
67
  * @param {number} [limit] - Optional limit for pagination. Default: 30.
68
68
  * @returns {IFormsByMarkerDataEntity} A promise that resolves to a form data entity or an error.
69
69
  * @throws {IError} - If there is an error during the fetch operation, it will return an error object.
70
- * @description This method retrieves form data by its marker.
70
+ * @description This method retrieves form data by its marker. Each record's `formData` is returned **already unwrapped from its locale**: the API sends `{ "en_US": [ … ] }`, the SDK hands over the array for `langCode` — read `record.formData`, not `record.formData[langCode]`.
71
71
  */
72
- getFormsDataByMarker(marker: string, formModuleConfigId: number, body?: object, isNested?: number, langCode?: string, offset?: number, limit?: number): Promise<IFormsByMarkerDataEntity | IError>;
72
+ getFormsDataByMarker(marker: string, formModuleConfigId: number, body?: IFormsDataFilter, isExtended?: number, langCode?: string, offset?: number, limit?: number): Promise<IFormsByMarkerDataEntity | IError>;
73
73
  /**
74
74
  * Semantic (vector) search for form data.
75
75
  * @handleName getFormsDataByVectorSearch
@@ -97,6 +97,39 @@ interface IFormsDataVectorSearch {
97
97
  maxHits?: number;
98
98
  debug?: boolean;
99
99
  }
100
+ /**
101
+ * FormDataStatus — moderation status of a form data record.
102
+ *
103
+ * The set the API accepts and returns: a submission arrives as `sent`, may be
104
+ * routed to `moderation`, and ends up `approved`, `banned` or `deleted`. Any
105
+ * other value is rejected with `400 each value in status must be a valid enum
106
+ * value` — an error that does not list the valid ones.
107
+ */
108
+ type FormDataStatus = 'sent' | 'moderation' | 'approved' | 'banned' | 'deleted';
109
+ /**
110
+ * Represents the filter body of `getFormsDataByMarker`.
111
+ * @interface IFormsDataFilter
112
+ * @property {string} [entityIdentifier] - Text identifier (marker) of the entity the records belong to; an empty string means "no filter". Example: "blog".
113
+ * @property {number} [parentId] - Identifier of the parent record — the way to fetch replies to one comment. Example: 10.
114
+ * @property {string} [userIdentifier] - Text identifier of the sender; an empty string means "no filter". Example: "admin".
115
+ * @property {FormDataStatus[]} [status] - Moderation statuses to keep. Must be an array — a bare string is rejected with `400`. An empty array means "no filter". Example: ["approved"].
116
+ * @property {string} [dateFrom] - Lower bound of the submission date, `YYYY-MM-DD`; an empty string means "no bound". A value the API cannot parse fails the request with a `500`. Example: "2025-01-01".
117
+ * @property {string} [dateTo] - Upper bound of the submission date, `YYYY-MM-DD`; an empty string means "no bound". Example: "2025-12-31".
118
+ * @description Filter for reading form submissions. Every field is optional and an omitted one is simply not applied.
119
+ *
120
+ * Typed rather than left as `object` on purpose: the API **ignores** a field it
121
+ * does not know, so `statuses: ['approved']` (note the "s") is not an error —
122
+ * it returns every record, unmoderated ones included. That is premoderation
123
+ * leaking onto a public page, from one typo the compiler used to accept.
124
+ */
125
+ interface IFormsDataFilter {
126
+ entityIdentifier?: string;
127
+ parentId?: number;
128
+ userIdentifier?: string;
129
+ status?: FormDataStatus[];
130
+ dateFrom?: string;
131
+ dateTo?: string;
132
+ }
100
133
  /**
101
134
  * Represents a form data record returned by the vector search endpoint.
102
135
  * @interface IFormDataSearchEntity
@@ -118,7 +151,7 @@ interface IFormsDataVectorSearch {
118
151
  * @property {string} [ip] - IP address the form was submitted from. Example: "127.0.0.1".
119
152
  * @property {string} [fingerprint] - Device fingerprint of the sender. Example: "UQ_123456_abcdef".
120
153
  * @property {boolean} [isUserAdmin] - Whether the record was submitted by an admin. Example: false.
121
- * @property {'sent' | 'banned' | 'deleted' | 'moderation' | 'approved'} [status] - Moderation status of the record. Example: "sent".
154
+ * @property {FormDataStatus} [status] - Moderation status of the record. Example: "sent".
122
155
  * @property {number} [formModuleId] - Identifier of the form module the record belongs to. Example: 2.
123
156
  * @property {number} [distance] - Cosine distance to the query. Returned only when `debug: true` is passed in the search body. Example: 0.12.
124
157
  * @description This interface defines a form data record as returned by the semantic (vector) search endpoint. Unlike IFormDataEntity it carries the moderation and sender fields of the raw record.
@@ -134,7 +167,7 @@ interface IFormDataSearchEntity {
134
167
  ip?: string;
135
168
  fingerprint?: string;
136
169
  isUserAdmin?: boolean;
137
- status?: 'sent' | 'banned' | 'deleted' | 'moderation' | 'approved';
170
+ status?: FormDataStatus;
138
171
  formModuleId?: number;
139
172
  distance?: number;
140
173
  }
@@ -154,7 +187,7 @@ interface IFormsDataSearchResponse {
154
187
  * @property {number} id - The unique identifier of the form page. Example: 12345.
155
188
  * @property {string} formIdentifier - The identifier of the page. Example: "contact_form".
156
189
  * @property {Date | string} time - The identifier of the form. Example: "2023-10-01T12:00:00Z".
157
- * @property {FormDataType[]} formData - Form data.
190
+ * @property {FormDataType[]} formData - Submitted fields, **already unwrapped from the locale**: the API returns `{ "en_US": [ … ] }` and the SDK hands over the array for the requested `langCode`.
158
191
  * @example
159
192
  [
160
193
  {
@@ -206,9 +239,9 @@ interface IFormsDataEntity {
206
239
  * @property {number} depth - Example: 1.
207
240
  * @property {string | null} ip - Ip. Example: '127.0.0.1'.
208
241
  * @property {string | null} fingerprint - Fingerprint. Example: 'fingerprint'.
209
- * @property {string | null} status - Status. Example: 'approved'.
242
+ * @property {string | null} status - Moderation status of the record: "sent", "moderation", "approved", "banned" or "deleted"; `null` when the form does not moderate. Example: 'approved'.
210
243
  * @property {string | null} userIdentifier - Text identifier (marker) of the user. Example: "admin".
211
- * @property {FormDataType[]} formData - Form data.
244
+ * @property {FormDataType[]} formData - Submitted fields, **already unwrapped from the locale**: the API returns `{ "en_US": [ … ] }` and the SDK hands over the array for the requested `langCode`. Read `formData` directly — `formData[langCode]` is `undefined`.
212
245
  * @example
213
246
  [
214
247
  {
@@ -547,7 +580,7 @@ interface ITextWithHeaderValue {
547
580
  * Represents an image or group of images form data entity.
548
581
  * @interface IBodyTypeImageGroupOfImages
549
582
  * @property {string} marker - marker name. Example: "avatar".
550
- * @property {'image' | 'groupOfImage'} type - Type value. Example: "image".
583
+ * @property {'image' | 'groupOfImages'} type - Type value — must match the attribute type in the set, note the plural in "groupOfImages". Example: "image".
551
584
  * @property {object} value - Image Object. Contains image information.
552
585
  * @example
553
586
  [
@@ -565,7 +598,7 @@ interface ITextWithHeaderValue {
565
598
  */
566
599
  interface IBodyTypeImageGroupOfImages {
567
600
  marker: string;
568
- type: 'image' | 'groupOfImage';
601
+ type: 'image' | 'groupOfImages';
569
602
  value: IImageValue | IImageValue[];
570
603
  }
571
604
  /**
@@ -583,7 +616,7 @@ interface IImageValueParams {
583
616
  * @property {number} size - File size in bytes. Example: 392585.
584
617
  * @property {string} previewLink - URL to a preview-sized version (may be empty).
585
618
  * @property {IImageValueParams} params - Image-specific parameters.
586
- * @description Value payload for `image`/`groupOfImage` form fields.
619
+ * @description Value payload for `image`/`groupOfImages` form fields. Note it is **not** the object `FileUploading.upload` returns: that one carries `contentType`, which the form-data endpoint rejects. Build the value from `filename`, `downloadLink`, `size` and `previewLink` (the SDK also strips `contentType` from an image value passed to `postFormsData`).
587
620
  */
588
621
  interface IImageValue {
589
622
  filename: string;
@@ -671,4 +704,4 @@ interface IListOptionValue {
671
704
  value: string;
672
705
  extended: IListOptionExtended;
673
706
  }
674
- export type { FormDataType, IBodyPostFormData, IBodyTypeFile, IBodyTypeImageGroupOfImages, IBodyTypeRadioButtonList, IBodyTypeStringNumberFloat, IBodyTypeText, IBodyTypeTextWithHeader, IBodyTypeTimeDate, IFileValue, IFormByMarkerDataEntity, IFormDataEntity, IFormDataSearchEntity, IFormsByMarkerDataEntity, IFormsData, IFormsDataEntity, IFormsDataSearchResponse, IFormsDataVectorSearch, IImageValue, IImageValueParams, IListOptionExtended, IListOptionValue, IPostFormResponse, IPostFormResponseData, ITextEditorParams, ITextValue, ITextWithHeaderValue, ITimeDateValue, IUpdateFormsData, };
707
+ export type { FormDataStatus, FormDataType, IBodyPostFormData, IBodyTypeFile, IBodyTypeImageGroupOfImages, IBodyTypeRadioButtonList, IBodyTypeStringNumberFloat, IBodyTypeText, IBodyTypeTextWithHeader, IBodyTypeTimeDate, IFileValue, IFormByMarkerDataEntity, IFormDataEntity, IFormDataSearchEntity, IFormsByMarkerDataEntity, IFormsData, IFormsDataEntity, IFormsDataFilter, IFormsDataSearchResponse, IFormsDataVectorSearch, IImageValue, IImageValueParams, IListOptionExtended, IListOptionValue, IPostFormResponse, IPostFormResponseData, ITextEditorParams, ITextValue, ITextWithHeaderValue, ITimeDateValue, IUpdateFormsData, };
package/esm/index.d.ts CHANGED
@@ -30,6 +30,7 @@ import TemplatePreviewsApi from './templates-preview/templatesPreviewApi.js';
30
30
  import UserActivityApi from './user-activity/userActivityApi.js';
31
31
  import UsersApi from './users/usersApi.js';
32
32
  import WsApi from './web-socket/wsApi.js';
33
+ export { getAdditionalFields, getAttributeFile, getAttributeFiles, isFileAttribute, isListAttribute, isNumberAttribute, isStringAttribute, } from './base/attributes.js';
33
34
  export { expandAttributeTimeIntervals, expandTimeIntervals, isTimeIntervalAttribute, } from './base/timeIntervals.js';
34
35
  export type * from './types.js';
35
36
  /**
package/esm/index.js CHANGED
@@ -30,6 +30,7 @@ import TemplatePreviewsApi from './templates-preview/templatesPreviewApi.js';
30
30
  import UserActivityApi from './user-activity/userActivityApi.js';
31
31
  import UsersApi from './users/usersApi.js';
32
32
  import WsApi from './web-socket/wsApi.js';
33
+ export { getAdditionalFields, getAttributeFile, getAttributeFiles, isFileAttribute, isListAttribute, isNumberAttribute, isStringAttribute, } from './base/attributes.js';
33
34
  export { expandAttributeTimeIntervals, expandTimeIntervals, isTimeIntervalAttribute, } from './base/timeIntervals.js';
34
35
  /**
35
36
  * Define API.