oneentry 1.0.168 → 1.0.170
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/changelog.md +24 -0
- package/dist/attribute-sets/attributeSetsInterfaces.d.ts +114 -12
- package/dist/base/asyncModules.d.ts +28 -2
- package/dist/base/asyncModules.js +72 -9
- package/dist/base/utils.d.ts +1 -1
- package/esm/attribute-sets/attributeSetsInterfaces.d.ts +114 -12
- package/esm/base/asyncModules.d.ts +28 -2
- package/esm/base/asyncModules.js +72 -9
- package/esm/base/utils.d.ts +1 -1
- package/package.json +2 -2
package/changelog.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# SDK Change Log
|
|
2
2
|
|
|
3
|
+
## v.1.0.170
|
|
4
|
+
|
|
5
|
+
### What's Changed
|
|
6
|
+
|
|
7
|
+
- **`IAttributeValidators` named four validators of the eighteen the platform accepts.** The interface gave a shape to `requiredValidator`, `stringInspectionValidator`, `emailInspectionValidator` and `defaultValueValidator`, and closed with `[key: string]: unknown`; its JSDoc said only that the set is extensible. A developer reading the types learned that a required flag, a length constraint, an e-mail toggle and a default value exist, and did not learn that a **regular expression**, an **input mask**, a **whitespace trim** and **date bounds** exist — fourteen capabilities absent from the model of the platform, which on one pilot reached three client-facing documents as the sentence "a field has exactly four validators; there are no regular expressions and no phone check". All eighteen names are now declared with the shape the platform stores: `requiredValidator`, `trimValidator`, `stringInspectionValidator`, `urlInspectionValidator`, `emailInspectionValidator`, `checkForNumberValidator`, `roundingNumberValidator`, `booleanValidator`, `comparisonValidator`, `regExpValidator`, `fieldMaskValidator`, `checkForAGivenList`, `defaultValueValidator`, `sizeInPixelsValidator`, `checkingFilesValidator`, `minMaxDateValidator`, `valueValidator`, `checkForLettersValidator` (CMS-983).
|
|
8
|
+
|
|
9
|
+
- **A misspelt validator name is a compile error instead of a `400`.** The index signature `[key: string]: unknown` made the gap silent in the other direction too: `emailValidator` typechecked and the platform refused the write at run time with `schema contains unknown validator name(s)`. The signature is gone, so an unknown name is caught where it is written. Code that still needs a name the SDK does not know can cast at that one place.
|
|
10
|
+
|
|
11
|
+
- **Shapes follow what the platform executes, not what reads well.** `regExpValidator` takes `{ patternValue, invert, flags }` and not `{ regExp }`, `fieldMaskValidator` takes `{ maskValue }`, `checkForAGivenList` carries no `Validator` suffix, and `minMaxDateValidator` stores `minDateTimeValue` / `maxDateTimeValue`. The toggles — `requiredValidator`, `trimValidator`, `urlInspectionValidator`, `emailInspectionValidator` — are typed `object | boolean`, because both forms are in live data: an e-mail check is stored as the boolean `true` on seven fields of the stands checked and as an object on one. Every validator may also carry `customErrorText`, which is now in the type. `valueValidator` is kept with the same shape as `defaultValueValidator`: it is the panel's own name for that control, and the API stores it.
|
|
12
|
+
|
|
13
|
+
- **The attribute JSDoc in `base/utils.ts` named `regexValidator`,** which does not exist. It points at `IAttributeValidators` for the full list instead.
|
|
14
|
+
|
|
15
|
+
Types and documentation only; the wire format did not change.
|
|
16
|
+
|
|
17
|
+
## v.1.0.169
|
|
18
|
+
|
|
19
|
+
### What's Fixed
|
|
20
|
+
|
|
21
|
+
- **A response that is not JSON came back as a parser message, and on the error branch as nothing at all.** The platform has been seen answering `200 OK` with `content-type: text/html` and its own admin page as the body — every content endpoint of a project at once, for minutes, a single request enough to reproduce it (CMS-957). A successful status carrying someone else's body cannot be told apart from a real answer by status alone, and all the caller got was `SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON`: a sentence about the SDK's own parsing rather than about what arrived. Read as "this record does not exist", it made a static build of a demo shop bake `404` into 8 of 54 product pages while every one of those products was live. The same body behind a failing status was worse: `_parseJson` swallowed it and returned `{}`, which carries no `statusCode` and therefore does not read as an error at all, so a `502` came back looking like an empty success. A body that is not JSON is now described — `Expected JSON from the platform, received text/html (HTTP 200): <!DOCTYPE html>…` — with `statusCode: 0` on a successful status, the same "no usable answer" contract as a dropped connection, and the status the server really sent when it failed. Token refresh reads its body through the same helper instead of `response.json()`, so an HTML answer there fails the refresh rather than throwing a raw parse error out of the request that triggered it.
|
|
22
|
+
|
|
23
|
+
### Tests
|
|
24
|
+
|
|
25
|
+
- **`src/base/tests/nonjson.spec.ts` pins the five outcomes** with a stubbed `fetch`, no stand required: HTML behind a `200`, HTML behind a `502`, a normal JSON answer, an empty body, and an error the platform itself sent.
|
|
26
|
+
|
|
3
27
|
## v.1.0.168
|
|
4
28
|
|
|
5
29
|
### What's Fixed
|
|
@@ -189,28 +189,130 @@ interface IAttributeSchemaItem {
|
|
|
189
189
|
}
|
|
190
190
|
/**
|
|
191
191
|
* @interface IAttributeValidators
|
|
192
|
-
* @property {object} [requiredValidator] -
|
|
193
|
-
* @property {object} [
|
|
194
|
-
* @property {
|
|
195
|
-
* @property {object} [
|
|
196
|
-
* @
|
|
192
|
+
* @property {object | boolean} [requiredValidator] - Field must be filled in. Shape: `{ strict: boolean }`. Example: `{ strict: true }`.
|
|
193
|
+
* @property {object | boolean} [trimValidator] - Leading and trailing whitespace is stripped from the value. Stored as an empty object when the toggle is on. Example: `{}`.
|
|
194
|
+
* @property {object} [stringInspectionValidator] - Length constraints for string fields. Example: `{ stringLength: 0, stringMin: 2, stringMax: 60 }`.
|
|
195
|
+
* @property {object | boolean} [urlInspectionValidator] - Value must look like a URL. Stored either as `true` or as an object. Example: `true`.
|
|
196
|
+
* @property {object | boolean} [emailInspectionValidator] - Value must look like an e-mail address. Stored either as `true` or as an object. Example: `true`.
|
|
197
|
+
* @property {object} [checkForNumberValidator] - Value must be a number, optionally an integer within bounds. `0` disables a bound. Example: `{ integerOnly: true, minValue: 1, maxValue: 10 }`.
|
|
198
|
+
* @property {object} [roundingNumberValidator] - Rounds the number before storing it. Example: `{ integerOnly: false, numberOfDecimals: 2, direction: 'round' }`.
|
|
199
|
+
* @property {object} [booleanValidator] - Value must equal one of the two declared values. Example: `{ trueValue: 1, falseValue: 0 }`.
|
|
200
|
+
* @property {object} [comparisonValidator] - Compares the value with another attribute or with a constant. Example: `{ compareAttribute: 'notChoosed', compareValue: '10', type: 'number', operator: 'equality' }`.
|
|
201
|
+
* @property {object} [regExpValidator] - Value must match a regular expression; `invert` refuses matches instead. Example: `{ patternValue: '^[0-9]{5}$', invert: false, flags: ['i'] }`.
|
|
202
|
+
* @property {object} [fieldMaskValidator] - Value must match an input mask (`9` digit, `A` upper letter, `a` lower letter, `*` any, `$` one of `()+-`). Example: `{ maskValue: '+9 (999) 999-99-99' }`.
|
|
203
|
+
* @property {object} [checkForAGivenList] - Value must be in the list, or outside it when `notValue` is true. Example: `{ rangeValue: ['red', 'green'], notValue: false }`.
|
|
204
|
+
* @property {object} [defaultValueValidator] - Value substituted when the field is left empty. Example: `{ fieldDefaultValue: 'USD' }`.
|
|
205
|
+
* @property {object} [sizeInPixelsValidator] - Image bounds in pixels; executed by the admin panel. Example: `{ maxX: 1920, maxY: 1080 }`.
|
|
206
|
+
* @property {object} [checkingFilesValidator] - File size bounds and allowed extensions; executed by the admin panel. Example: `{ minValue: 0, maxValue: 5, minUnits: 'kb', maxUnits: 'mb', extensions: ['pdf'] }`.
|
|
207
|
+
* @property {object} [minMaxDateValidator] - Date bounds; executed by the admin panel. Example: `{ minDateTimeValue: '2026-01-01', maxDateTimeValue: '2026-12-31' }`.
|
|
208
|
+
* @property {object} [valueValidator] - The panel's "default value" control; it writes `defaultValueValidator`, and this name is kept because the API accepts and stores it. Example: `{ fieldDefaultValue: 'USD' }`.
|
|
209
|
+
* @property {object} [checkForLettersValidator] - Letters-only check by pattern; executed by the admin panel. Example: `{ patternValue: '^[A-Za-z ]+$' }`.
|
|
210
|
+
* @description Map of validators applied to an attribute or form field, keyed by validator name.
|
|
211
|
+
*
|
|
212
|
+
* These eighteen names are the full set the API accepts in
|
|
213
|
+
* `attributes_sets.schema.<attribute>.validators.<langCode>`; any other name is refused
|
|
214
|
+
* with `400`. The first thirteen are executed by the platform on write, the last five are
|
|
215
|
+
* stored as given and executed by the admin panel.
|
|
216
|
+
*
|
|
217
|
+
* Every validator may also carry `customErrorText` — the message shown instead of the
|
|
218
|
+
* default one when the value is refused.
|
|
197
219
|
*/
|
|
198
220
|
interface IAttributeValidators {
|
|
199
221
|
requiredValidator?: {
|
|
200
|
-
strict
|
|
201
|
-
|
|
222
|
+
strict?: boolean;
|
|
223
|
+
customErrorText?: string;
|
|
224
|
+
} | boolean;
|
|
225
|
+
trimValidator?: {
|
|
226
|
+
strict?: boolean;
|
|
227
|
+
customErrorText?: string;
|
|
228
|
+
} | boolean;
|
|
202
229
|
stringInspectionValidator?: {
|
|
203
|
-
stringMin
|
|
204
|
-
stringMax
|
|
205
|
-
stringLength
|
|
230
|
+
stringMin?: number;
|
|
231
|
+
stringMax?: number;
|
|
232
|
+
stringLength?: number;
|
|
233
|
+
customErrorText?: string;
|
|
234
|
+
};
|
|
235
|
+
urlInspectionValidator?: {
|
|
236
|
+
strict?: boolean;
|
|
237
|
+
customErrorText?: string;
|
|
238
|
+
} | boolean;
|
|
239
|
+
emailInspectionValidator?: {
|
|
240
|
+
strict?: boolean;
|
|
241
|
+
customErrorText?: string;
|
|
242
|
+
} | boolean;
|
|
243
|
+
checkForNumberValidator?: {
|
|
244
|
+
integerOnly?: boolean;
|
|
245
|
+
minValue?: number;
|
|
246
|
+
maxValue?: number;
|
|
247
|
+
customErrorText?: string;
|
|
248
|
+
};
|
|
249
|
+
roundingNumberValidator?: {
|
|
250
|
+
integerOnly?: boolean;
|
|
251
|
+
numberOfDecimals?: number;
|
|
252
|
+
direction?: 'round' | 'up' | 'down' | 'truncate';
|
|
253
|
+
customErrorText?: string;
|
|
254
|
+
};
|
|
255
|
+
booleanValidator?: {
|
|
256
|
+
trueValue?: unknown;
|
|
257
|
+
falseValue?: unknown;
|
|
258
|
+
customErrorText?: string;
|
|
259
|
+
};
|
|
260
|
+
comparisonValidator?: {
|
|
261
|
+
compareAttribute?: string;
|
|
262
|
+
compareValue?: string;
|
|
263
|
+
compareValue2?: string;
|
|
264
|
+
type?: string;
|
|
265
|
+
operator?: string;
|
|
266
|
+
customErrorText?: string;
|
|
267
|
+
};
|
|
268
|
+
regExpValidator?: {
|
|
269
|
+
patternValue?: string;
|
|
270
|
+
invert?: boolean;
|
|
271
|
+
flags?: string[];
|
|
272
|
+
customErrorText?: string;
|
|
273
|
+
};
|
|
274
|
+
fieldMaskValidator?: {
|
|
275
|
+
maskValue?: string;
|
|
276
|
+
customErrorText?: string;
|
|
277
|
+
};
|
|
278
|
+
checkForAGivenList?: {
|
|
279
|
+
rangeValue?: unknown[];
|
|
280
|
+
rangeValue2?: unknown[];
|
|
281
|
+
notValue?: boolean;
|
|
282
|
+
customErrorText?: string;
|
|
206
283
|
};
|
|
207
|
-
emailInspectionValidator?: boolean;
|
|
208
284
|
defaultValueValidator?: {
|
|
285
|
+
fieldDefaultValue?: unknown;
|
|
286
|
+
fieldDefaultValue2?: unknown;
|
|
287
|
+
customErrorText?: string;
|
|
288
|
+
};
|
|
289
|
+
sizeInPixelsValidator?: {
|
|
290
|
+
maxX?: number;
|
|
291
|
+
maxY?: number;
|
|
209
292
|
customErrorText?: string;
|
|
293
|
+
};
|
|
294
|
+
checkingFilesValidator?: {
|
|
295
|
+
minValue?: number;
|
|
296
|
+
maxValue?: number;
|
|
297
|
+
minUnits?: 'kb' | 'mb';
|
|
298
|
+
maxUnits?: 'kb' | 'mb';
|
|
299
|
+
extensions?: string[];
|
|
300
|
+
customErrorText?: string;
|
|
301
|
+
};
|
|
302
|
+
minMaxDateValidator?: {
|
|
303
|
+
minDateTimeValue?: string;
|
|
304
|
+
maxDateTimeValue?: string;
|
|
305
|
+
customErrorText?: string;
|
|
306
|
+
};
|
|
307
|
+
valueValidator?: {
|
|
210
308
|
fieldDefaultValue?: unknown;
|
|
211
309
|
fieldDefaultValue2?: unknown;
|
|
310
|
+
customErrorText?: string;
|
|
311
|
+
};
|
|
312
|
+
checkForLettersValidator?: {
|
|
313
|
+
patternValue?: string;
|
|
314
|
+
customErrorText?: string;
|
|
212
315
|
};
|
|
213
|
-
[key: string]: unknown;
|
|
214
316
|
}
|
|
215
317
|
/**
|
|
216
318
|
* Represents an attribute set entity.
|
|
@@ -103,13 +103,19 @@ export default abstract class AsyncModules extends SyncModules {
|
|
|
103
103
|
*/
|
|
104
104
|
private makeOptions;
|
|
105
105
|
/**
|
|
106
|
-
* Reads
|
|
106
|
+
* Reads an error response body as JSON, tolerating an empty body.
|
|
107
107
|
*
|
|
108
108
|
* `Response.json()` throws on an empty body (e.g. 204 or an empty error
|
|
109
109
|
* payload); this helper mirrors the OK-path guard (`text ? JSON.parse : {}`)
|
|
110
110
|
* so both the success and the error branches handle empty bodies the same way.
|
|
111
|
+
*
|
|
112
|
+
* A body that is present but is not JSON — a gateway's HTML page in front of a
|
|
113
|
+
* 502, the platform's own admin page (CMS-957) — used to collapse into `{}`,
|
|
114
|
+
* which carries no `statusCode` and therefore does not read as an error at all:
|
|
115
|
+
* a failed request came back looking like an empty success. It is described
|
|
116
|
+
* instead, keeping the status the server actually sent.
|
|
111
117
|
* @param {Response} response - The fetch Response to read.
|
|
112
|
-
* @returns {Promise<unknown>} Parsed JSON,
|
|
118
|
+
* @returns {Promise<unknown>} Parsed JSON, `{}` for an empty body, or a described error for a body that is not JSON.
|
|
113
119
|
*/
|
|
114
120
|
private _parseJson;
|
|
115
121
|
/**
|
|
@@ -120,6 +126,26 @@ export default abstract class AsyncModules extends SyncModules {
|
|
|
120
126
|
* @description Define an asynchronous method 'browserResponse' that takes a path and options as parameters
|
|
121
127
|
*/
|
|
122
128
|
private browserResponse;
|
|
129
|
+
/**
|
|
130
|
+
* Describes a response whose body is not the JSON the API is supposed to send.
|
|
131
|
+
*
|
|
132
|
+
* The platform has been seen answering `200 OK` with `content-type: text/html`
|
|
133
|
+
* and the admin page as the body — every content endpoint at once, for minutes
|
|
134
|
+
* (CMS-957). A successful status carrying someone else's body is indistinguishable
|
|
135
|
+
* from a real answer by status alone, and the only symptom used to be
|
|
136
|
+
* `SyntaxError: Unexpected token '<'` from the JSON parser: a message about the
|
|
137
|
+
* SDK's own parsing rather than about what the platform did. Callers read that as
|
|
138
|
+
* "this record does not exist" and a static build baked 404s over live products.
|
|
139
|
+
*
|
|
140
|
+
* The returned error names the received content type, the HTTP status and the
|
|
141
|
+
* first characters of the body, so a log line says what arrived instead of JSON.
|
|
142
|
+
* `statusCode` stays `0` — the request produced no usable answer, the same
|
|
143
|
+
* contract as a dropped connection, and a value no HTTP answer uses.
|
|
144
|
+
* @param {Response} response - The response whose body could not be parsed.
|
|
145
|
+
* @param {string} text - The raw body as read.
|
|
146
|
+
* @returns {IError} The error in the shape the SDK returns for platform errors.
|
|
147
|
+
*/
|
|
148
|
+
private _nonJsonError;
|
|
123
149
|
/**
|
|
124
150
|
* Turns whatever was thrown into the error shape the SDK already returns.
|
|
125
151
|
*
|
|
@@ -239,8 +239,14 @@ class AsyncModules extends syncModules_1.default {
|
|
|
239
239
|
});
|
|
240
240
|
// Check if the response status is OK (status code 200-299)
|
|
241
241
|
if (response.ok) {
|
|
242
|
-
//
|
|
243
|
-
|
|
242
|
+
// Parsed through the shared helper rather than `response.json()`: a `200` whose body is
|
|
243
|
+
// not JSON (CMS-957) threw a raw SyntaxError out of the refresh and took the request that
|
|
244
|
+
// triggered it with it. A body without tokens is a failed refresh, reported as such.
|
|
245
|
+
const result = await this._parseJson(response);
|
|
246
|
+
if (typeof (result === null || result === void 0 ? void 0 : result.refreshToken) !== 'string' ||
|
|
247
|
+
typeof (result === null || result === void 0 ? void 0 : result.accessToken) !== 'string') {
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
244
250
|
// Update the state's refreshToken and accessToken with the new tokens from the response
|
|
245
251
|
this.state.refreshToken = result.refreshToken;
|
|
246
252
|
this.state.accessToken = result.accessToken;
|
|
@@ -313,22 +319,37 @@ class AsyncModules extends syncModules_1.default {
|
|
|
313
319
|
return options;
|
|
314
320
|
}
|
|
315
321
|
/**
|
|
316
|
-
* Reads
|
|
322
|
+
* Reads an error response body as JSON, tolerating an empty body.
|
|
317
323
|
*
|
|
318
324
|
* `Response.json()` throws on an empty body (e.g. 204 or an empty error
|
|
319
325
|
* payload); this helper mirrors the OK-path guard (`text ? JSON.parse : {}`)
|
|
320
326
|
* so both the success and the error branches handle empty bodies the same way.
|
|
327
|
+
*
|
|
328
|
+
* A body that is present but is not JSON — a gateway's HTML page in front of a
|
|
329
|
+
* 502, the platform's own admin page (CMS-957) — used to collapse into `{}`,
|
|
330
|
+
* which carries no `statusCode` and therefore does not read as an error at all:
|
|
331
|
+
* a failed request came back looking like an empty success. It is described
|
|
332
|
+
* instead, keeping the status the server actually sent.
|
|
321
333
|
* @param {Response} response - The fetch Response to read.
|
|
322
|
-
* @returns {Promise<unknown>} Parsed JSON,
|
|
334
|
+
* @returns {Promise<unknown>} Parsed JSON, `{}` for an empty body, or a described error for a body that is not JSON.
|
|
323
335
|
*/
|
|
324
336
|
async _parseJson(response) {
|
|
337
|
+
let text;
|
|
325
338
|
try {
|
|
326
|
-
|
|
327
|
-
return text ? JSON.parse(text) : {};
|
|
339
|
+
text = await response.text();
|
|
328
340
|
}
|
|
329
341
|
catch {
|
|
330
342
|
return {};
|
|
331
343
|
}
|
|
344
|
+
if (!text) {
|
|
345
|
+
return {};
|
|
346
|
+
}
|
|
347
|
+
try {
|
|
348
|
+
return JSON.parse(text);
|
|
349
|
+
}
|
|
350
|
+
catch {
|
|
351
|
+
return this._nonJsonError(response, text);
|
|
352
|
+
}
|
|
332
353
|
}
|
|
333
354
|
/**
|
|
334
355
|
* Handles responses from the browser's fetch API.
|
|
@@ -386,10 +407,18 @@ class AsyncModules extends syncModules_1.default {
|
|
|
386
407
|
// Check if the response status is OK (status code 200-299)
|
|
387
408
|
if (response.ok) {
|
|
388
409
|
// Read the body as text; if it is empty return an empty object,
|
|
389
|
-
// otherwise parse it as JSON. A
|
|
390
|
-
//
|
|
410
|
+
// otherwise parse it as JSON. A body that is not JSON is turned into a
|
|
411
|
+
// described error by `_nonJsonError` and handled according to isShell.
|
|
391
412
|
const text = await response.text();
|
|
392
|
-
|
|
413
|
+
if (!text) {
|
|
414
|
+
return {};
|
|
415
|
+
}
|
|
416
|
+
try {
|
|
417
|
+
return JSON.parse(text);
|
|
418
|
+
}
|
|
419
|
+
catch {
|
|
420
|
+
throw this._nonJsonError(response, text);
|
|
421
|
+
}
|
|
393
422
|
}
|
|
394
423
|
// Handle non-OK responses (applies to the first response and to a failed retry).
|
|
395
424
|
const status = response.status;
|
|
@@ -417,6 +446,40 @@ class AsyncModules extends syncModules_1.default {
|
|
|
417
446
|
}
|
|
418
447
|
}
|
|
419
448
|
}
|
|
449
|
+
/**
|
|
450
|
+
* Describes a response whose body is not the JSON the API is supposed to send.
|
|
451
|
+
*
|
|
452
|
+
* The platform has been seen answering `200 OK` with `content-type: text/html`
|
|
453
|
+
* and the admin page as the body — every content endpoint at once, for minutes
|
|
454
|
+
* (CMS-957). A successful status carrying someone else's body is indistinguishable
|
|
455
|
+
* from a real answer by status alone, and the only symptom used to be
|
|
456
|
+
* `SyntaxError: Unexpected token '<'` from the JSON parser: a message about the
|
|
457
|
+
* SDK's own parsing rather than about what the platform did. Callers read that as
|
|
458
|
+
* "this record does not exist" and a static build baked 404s over live products.
|
|
459
|
+
*
|
|
460
|
+
* The returned error names the received content type, the HTTP status and the
|
|
461
|
+
* first characters of the body, so a log line says what arrived instead of JSON.
|
|
462
|
+
* `statusCode` stays `0` — the request produced no usable answer, the same
|
|
463
|
+
* contract as a dropped connection, and a value no HTTP answer uses.
|
|
464
|
+
* @param {Response} response - The response whose body could not be parsed.
|
|
465
|
+
* @param {string} text - The raw body as read.
|
|
466
|
+
* @returns {IError} The error in the shape the SDK returns for platform errors.
|
|
467
|
+
*/
|
|
468
|
+
_nonJsonError(response, text) {
|
|
469
|
+
var _a, _b, _c;
|
|
470
|
+
const contentType = (_c = (_b = (_a = response.headers) === null || _a === void 0 ? void 0 : _a.get) === null || _b === void 0 ? void 0 : _b.call(_a, 'content-type')) !== null && _c !== void 0 ? _c : 'unknown';
|
|
471
|
+
const excerpt = text.trim().slice(0, 120).replace(/\s+/g, ' ');
|
|
472
|
+
return {
|
|
473
|
+
// A successful status carrying a non-JSON body produced no usable answer, the same
|
|
474
|
+
// contract as a dropped connection. A failing one already says something true about
|
|
475
|
+
// the request, so that status is kept.
|
|
476
|
+
statusCode: response.ok ? 0 : response.status,
|
|
477
|
+
message: `Expected JSON from the platform, received ${contentType} ` +
|
|
478
|
+
`(HTTP ${response.status}): ${excerpt}`,
|
|
479
|
+
pageData: null,
|
|
480
|
+
timestamp: new Date().toISOString(),
|
|
481
|
+
};
|
|
482
|
+
}
|
|
420
483
|
/**
|
|
421
484
|
* Turns whatever was thrown into the error shape the SDK already returns.
|
|
422
485
|
*
|
package/dist/base/utils.d.ts
CHANGED
|
@@ -96,7 +96,7 @@ interface IConfig {
|
|
|
96
96
|
* @property {boolean} isVisible - Whether the attribute is exposed in the public API/UI. Example: true.
|
|
97
97
|
* @property {string} type - Attribute data type (e.g. "string", "integer", "list", "file", "image").
|
|
98
98
|
* @property {Record<string, unknown>} [settings] - Type-specific configuration (e.g. min/max for numbers, accepted MIME types for files). Optional.
|
|
99
|
-
* @property {Record<string, unknown>} validators - Validation rules applied to the attribute value (e.g. `requiredValidator`, `
|
|
99
|
+
* @property {Record<string, unknown>} validators - Validation rules applied to the attribute value, keyed by validator name (e.g. `requiredValidator`, `regExpValidator`); the full set of names is listed on `IAttributeValidators`. Example: { "requiredValidator": { "strict": true } }.
|
|
100
100
|
* @property {Record<string, IAttributes>} [additionalFields] - Nested sub-attributes attached to this attribute, keyed by marker. Optional.
|
|
101
101
|
* @property {boolean} [isIcon] - Block/preview attribute flag — whether the field is treated as an icon. Optional.
|
|
102
102
|
* @property {boolean} [isProductPreview] - Block/preview attribute flag — whether the field is shown in product preview. Optional.
|
|
@@ -189,28 +189,130 @@ interface IAttributeSchemaItem {
|
|
|
189
189
|
}
|
|
190
190
|
/**
|
|
191
191
|
* @interface IAttributeValidators
|
|
192
|
-
* @property {object} [requiredValidator] -
|
|
193
|
-
* @property {object} [
|
|
194
|
-
* @property {
|
|
195
|
-
* @property {object} [
|
|
196
|
-
* @
|
|
192
|
+
* @property {object | boolean} [requiredValidator] - Field must be filled in. Shape: `{ strict: boolean }`. Example: `{ strict: true }`.
|
|
193
|
+
* @property {object | boolean} [trimValidator] - Leading and trailing whitespace is stripped from the value. Stored as an empty object when the toggle is on. Example: `{}`.
|
|
194
|
+
* @property {object} [stringInspectionValidator] - Length constraints for string fields. Example: `{ stringLength: 0, stringMin: 2, stringMax: 60 }`.
|
|
195
|
+
* @property {object | boolean} [urlInspectionValidator] - Value must look like a URL. Stored either as `true` or as an object. Example: `true`.
|
|
196
|
+
* @property {object | boolean} [emailInspectionValidator] - Value must look like an e-mail address. Stored either as `true` or as an object. Example: `true`.
|
|
197
|
+
* @property {object} [checkForNumberValidator] - Value must be a number, optionally an integer within bounds. `0` disables a bound. Example: `{ integerOnly: true, minValue: 1, maxValue: 10 }`.
|
|
198
|
+
* @property {object} [roundingNumberValidator] - Rounds the number before storing it. Example: `{ integerOnly: false, numberOfDecimals: 2, direction: 'round' }`.
|
|
199
|
+
* @property {object} [booleanValidator] - Value must equal one of the two declared values. Example: `{ trueValue: 1, falseValue: 0 }`.
|
|
200
|
+
* @property {object} [comparisonValidator] - Compares the value with another attribute or with a constant. Example: `{ compareAttribute: 'notChoosed', compareValue: '10', type: 'number', operator: 'equality' }`.
|
|
201
|
+
* @property {object} [regExpValidator] - Value must match a regular expression; `invert` refuses matches instead. Example: `{ patternValue: '^[0-9]{5}$', invert: false, flags: ['i'] }`.
|
|
202
|
+
* @property {object} [fieldMaskValidator] - Value must match an input mask (`9` digit, `A` upper letter, `a` lower letter, `*` any, `$` one of `()+-`). Example: `{ maskValue: '+9 (999) 999-99-99' }`.
|
|
203
|
+
* @property {object} [checkForAGivenList] - Value must be in the list, or outside it when `notValue` is true. Example: `{ rangeValue: ['red', 'green'], notValue: false }`.
|
|
204
|
+
* @property {object} [defaultValueValidator] - Value substituted when the field is left empty. Example: `{ fieldDefaultValue: 'USD' }`.
|
|
205
|
+
* @property {object} [sizeInPixelsValidator] - Image bounds in pixels; executed by the admin panel. Example: `{ maxX: 1920, maxY: 1080 }`.
|
|
206
|
+
* @property {object} [checkingFilesValidator] - File size bounds and allowed extensions; executed by the admin panel. Example: `{ minValue: 0, maxValue: 5, minUnits: 'kb', maxUnits: 'mb', extensions: ['pdf'] }`.
|
|
207
|
+
* @property {object} [minMaxDateValidator] - Date bounds; executed by the admin panel. Example: `{ minDateTimeValue: '2026-01-01', maxDateTimeValue: '2026-12-31' }`.
|
|
208
|
+
* @property {object} [valueValidator] - The panel's "default value" control; it writes `defaultValueValidator`, and this name is kept because the API accepts and stores it. Example: `{ fieldDefaultValue: 'USD' }`.
|
|
209
|
+
* @property {object} [checkForLettersValidator] - Letters-only check by pattern; executed by the admin panel. Example: `{ patternValue: '^[A-Za-z ]+$' }`.
|
|
210
|
+
* @description Map of validators applied to an attribute or form field, keyed by validator name.
|
|
211
|
+
*
|
|
212
|
+
* These eighteen names are the full set the API accepts in
|
|
213
|
+
* `attributes_sets.schema.<attribute>.validators.<langCode>`; any other name is refused
|
|
214
|
+
* with `400`. The first thirteen are executed by the platform on write, the last five are
|
|
215
|
+
* stored as given and executed by the admin panel.
|
|
216
|
+
*
|
|
217
|
+
* Every validator may also carry `customErrorText` — the message shown instead of the
|
|
218
|
+
* default one when the value is refused.
|
|
197
219
|
*/
|
|
198
220
|
interface IAttributeValidators {
|
|
199
221
|
requiredValidator?: {
|
|
200
|
-
strict
|
|
201
|
-
|
|
222
|
+
strict?: boolean;
|
|
223
|
+
customErrorText?: string;
|
|
224
|
+
} | boolean;
|
|
225
|
+
trimValidator?: {
|
|
226
|
+
strict?: boolean;
|
|
227
|
+
customErrorText?: string;
|
|
228
|
+
} | boolean;
|
|
202
229
|
stringInspectionValidator?: {
|
|
203
|
-
stringMin
|
|
204
|
-
stringMax
|
|
205
|
-
stringLength
|
|
230
|
+
stringMin?: number;
|
|
231
|
+
stringMax?: number;
|
|
232
|
+
stringLength?: number;
|
|
233
|
+
customErrorText?: string;
|
|
234
|
+
};
|
|
235
|
+
urlInspectionValidator?: {
|
|
236
|
+
strict?: boolean;
|
|
237
|
+
customErrorText?: string;
|
|
238
|
+
} | boolean;
|
|
239
|
+
emailInspectionValidator?: {
|
|
240
|
+
strict?: boolean;
|
|
241
|
+
customErrorText?: string;
|
|
242
|
+
} | boolean;
|
|
243
|
+
checkForNumberValidator?: {
|
|
244
|
+
integerOnly?: boolean;
|
|
245
|
+
minValue?: number;
|
|
246
|
+
maxValue?: number;
|
|
247
|
+
customErrorText?: string;
|
|
248
|
+
};
|
|
249
|
+
roundingNumberValidator?: {
|
|
250
|
+
integerOnly?: boolean;
|
|
251
|
+
numberOfDecimals?: number;
|
|
252
|
+
direction?: 'round' | 'up' | 'down' | 'truncate';
|
|
253
|
+
customErrorText?: string;
|
|
254
|
+
};
|
|
255
|
+
booleanValidator?: {
|
|
256
|
+
trueValue?: unknown;
|
|
257
|
+
falseValue?: unknown;
|
|
258
|
+
customErrorText?: string;
|
|
259
|
+
};
|
|
260
|
+
comparisonValidator?: {
|
|
261
|
+
compareAttribute?: string;
|
|
262
|
+
compareValue?: string;
|
|
263
|
+
compareValue2?: string;
|
|
264
|
+
type?: string;
|
|
265
|
+
operator?: string;
|
|
266
|
+
customErrorText?: string;
|
|
267
|
+
};
|
|
268
|
+
regExpValidator?: {
|
|
269
|
+
patternValue?: string;
|
|
270
|
+
invert?: boolean;
|
|
271
|
+
flags?: string[];
|
|
272
|
+
customErrorText?: string;
|
|
273
|
+
};
|
|
274
|
+
fieldMaskValidator?: {
|
|
275
|
+
maskValue?: string;
|
|
276
|
+
customErrorText?: string;
|
|
277
|
+
};
|
|
278
|
+
checkForAGivenList?: {
|
|
279
|
+
rangeValue?: unknown[];
|
|
280
|
+
rangeValue2?: unknown[];
|
|
281
|
+
notValue?: boolean;
|
|
282
|
+
customErrorText?: string;
|
|
206
283
|
};
|
|
207
|
-
emailInspectionValidator?: boolean;
|
|
208
284
|
defaultValueValidator?: {
|
|
285
|
+
fieldDefaultValue?: unknown;
|
|
286
|
+
fieldDefaultValue2?: unknown;
|
|
287
|
+
customErrorText?: string;
|
|
288
|
+
};
|
|
289
|
+
sizeInPixelsValidator?: {
|
|
290
|
+
maxX?: number;
|
|
291
|
+
maxY?: number;
|
|
209
292
|
customErrorText?: string;
|
|
293
|
+
};
|
|
294
|
+
checkingFilesValidator?: {
|
|
295
|
+
minValue?: number;
|
|
296
|
+
maxValue?: number;
|
|
297
|
+
minUnits?: 'kb' | 'mb';
|
|
298
|
+
maxUnits?: 'kb' | 'mb';
|
|
299
|
+
extensions?: string[];
|
|
300
|
+
customErrorText?: string;
|
|
301
|
+
};
|
|
302
|
+
minMaxDateValidator?: {
|
|
303
|
+
minDateTimeValue?: string;
|
|
304
|
+
maxDateTimeValue?: string;
|
|
305
|
+
customErrorText?: string;
|
|
306
|
+
};
|
|
307
|
+
valueValidator?: {
|
|
210
308
|
fieldDefaultValue?: unknown;
|
|
211
309
|
fieldDefaultValue2?: unknown;
|
|
310
|
+
customErrorText?: string;
|
|
311
|
+
};
|
|
312
|
+
checkForLettersValidator?: {
|
|
313
|
+
patternValue?: string;
|
|
314
|
+
customErrorText?: string;
|
|
212
315
|
};
|
|
213
|
-
[key: string]: unknown;
|
|
214
316
|
}
|
|
215
317
|
/**
|
|
216
318
|
* Represents an attribute set entity.
|
|
@@ -103,13 +103,19 @@ export default abstract class AsyncModules extends SyncModules {
|
|
|
103
103
|
*/
|
|
104
104
|
private makeOptions;
|
|
105
105
|
/**
|
|
106
|
-
* Reads
|
|
106
|
+
* Reads an error response body as JSON, tolerating an empty body.
|
|
107
107
|
*
|
|
108
108
|
* `Response.json()` throws on an empty body (e.g. 204 or an empty error
|
|
109
109
|
* payload); this helper mirrors the OK-path guard (`text ? JSON.parse : {}`)
|
|
110
110
|
* so both the success and the error branches handle empty bodies the same way.
|
|
111
|
+
*
|
|
112
|
+
* A body that is present but is not JSON — a gateway's HTML page in front of a
|
|
113
|
+
* 502, the platform's own admin page (CMS-957) — used to collapse into `{}`,
|
|
114
|
+
* which carries no `statusCode` and therefore does not read as an error at all:
|
|
115
|
+
* a failed request came back looking like an empty success. It is described
|
|
116
|
+
* instead, keeping the status the server actually sent.
|
|
111
117
|
* @param {Response} response - The fetch Response to read.
|
|
112
|
-
* @returns {Promise<unknown>} Parsed JSON,
|
|
118
|
+
* @returns {Promise<unknown>} Parsed JSON, `{}` for an empty body, or a described error for a body that is not JSON.
|
|
113
119
|
*/
|
|
114
120
|
private _parseJson;
|
|
115
121
|
/**
|
|
@@ -120,6 +126,26 @@ export default abstract class AsyncModules extends SyncModules {
|
|
|
120
126
|
* @description Define an asynchronous method 'browserResponse' that takes a path and options as parameters
|
|
121
127
|
*/
|
|
122
128
|
private browserResponse;
|
|
129
|
+
/**
|
|
130
|
+
* Describes a response whose body is not the JSON the API is supposed to send.
|
|
131
|
+
*
|
|
132
|
+
* The platform has been seen answering `200 OK` with `content-type: text/html`
|
|
133
|
+
* and the admin page as the body — every content endpoint at once, for minutes
|
|
134
|
+
* (CMS-957). A successful status carrying someone else's body is indistinguishable
|
|
135
|
+
* from a real answer by status alone, and the only symptom used to be
|
|
136
|
+
* `SyntaxError: Unexpected token '<'` from the JSON parser: a message about the
|
|
137
|
+
* SDK's own parsing rather than about what the platform did. Callers read that as
|
|
138
|
+
* "this record does not exist" and a static build baked 404s over live products.
|
|
139
|
+
*
|
|
140
|
+
* The returned error names the received content type, the HTTP status and the
|
|
141
|
+
* first characters of the body, so a log line says what arrived instead of JSON.
|
|
142
|
+
* `statusCode` stays `0` — the request produced no usable answer, the same
|
|
143
|
+
* contract as a dropped connection, and a value no HTTP answer uses.
|
|
144
|
+
* @param {Response} response - The response whose body could not be parsed.
|
|
145
|
+
* @param {string} text - The raw body as read.
|
|
146
|
+
* @returns {IError} The error in the shape the SDK returns for platform errors.
|
|
147
|
+
*/
|
|
148
|
+
private _nonJsonError;
|
|
123
149
|
/**
|
|
124
150
|
* Turns whatever was thrown into the error shape the SDK already returns.
|
|
125
151
|
*
|
package/esm/base/asyncModules.js
CHANGED
|
@@ -201,8 +201,14 @@ export default class AsyncModules extends SyncModules {
|
|
|
201
201
|
});
|
|
202
202
|
// Check if the response status is OK (status code 200-299)
|
|
203
203
|
if (response.ok) {
|
|
204
|
-
//
|
|
205
|
-
|
|
204
|
+
// Parsed through the shared helper rather than `response.json()`: a `200` whose body is
|
|
205
|
+
// not JSON (CMS-957) threw a raw SyntaxError out of the refresh and took the request that
|
|
206
|
+
// triggered it with it. A body without tokens is a failed refresh, reported as such.
|
|
207
|
+
const result = await this._parseJson(response);
|
|
208
|
+
if (typeof (result === null || result === void 0 ? void 0 : result.refreshToken) !== 'string' ||
|
|
209
|
+
typeof (result === null || result === void 0 ? void 0 : result.accessToken) !== 'string') {
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
206
212
|
// Update the state's refreshToken and accessToken with the new tokens from the response
|
|
207
213
|
this.state.refreshToken = result.refreshToken;
|
|
208
214
|
this.state.accessToken = result.accessToken;
|
|
@@ -275,22 +281,37 @@ export default class AsyncModules extends SyncModules {
|
|
|
275
281
|
return options;
|
|
276
282
|
}
|
|
277
283
|
/**
|
|
278
|
-
* Reads
|
|
284
|
+
* Reads an error response body as JSON, tolerating an empty body.
|
|
279
285
|
*
|
|
280
286
|
* `Response.json()` throws on an empty body (e.g. 204 or an empty error
|
|
281
287
|
* payload); this helper mirrors the OK-path guard (`text ? JSON.parse : {}`)
|
|
282
288
|
* so both the success and the error branches handle empty bodies the same way.
|
|
289
|
+
*
|
|
290
|
+
* A body that is present but is not JSON — a gateway's HTML page in front of a
|
|
291
|
+
* 502, the platform's own admin page (CMS-957) — used to collapse into `{}`,
|
|
292
|
+
* which carries no `statusCode` and therefore does not read as an error at all:
|
|
293
|
+
* a failed request came back looking like an empty success. It is described
|
|
294
|
+
* instead, keeping the status the server actually sent.
|
|
283
295
|
* @param {Response} response - The fetch Response to read.
|
|
284
|
-
* @returns {Promise<unknown>} Parsed JSON,
|
|
296
|
+
* @returns {Promise<unknown>} Parsed JSON, `{}` for an empty body, or a described error for a body that is not JSON.
|
|
285
297
|
*/
|
|
286
298
|
async _parseJson(response) {
|
|
299
|
+
let text;
|
|
287
300
|
try {
|
|
288
|
-
|
|
289
|
-
return text ? JSON.parse(text) : {};
|
|
301
|
+
text = await response.text();
|
|
290
302
|
}
|
|
291
303
|
catch {
|
|
292
304
|
return {};
|
|
293
305
|
}
|
|
306
|
+
if (!text) {
|
|
307
|
+
return {};
|
|
308
|
+
}
|
|
309
|
+
try {
|
|
310
|
+
return JSON.parse(text);
|
|
311
|
+
}
|
|
312
|
+
catch {
|
|
313
|
+
return this._nonJsonError(response, text);
|
|
314
|
+
}
|
|
294
315
|
}
|
|
295
316
|
/**
|
|
296
317
|
* Handles responses from the browser's fetch API.
|
|
@@ -348,10 +369,18 @@ export default class AsyncModules extends SyncModules {
|
|
|
348
369
|
// Check if the response status is OK (status code 200-299)
|
|
349
370
|
if (response.ok) {
|
|
350
371
|
// Read the body as text; if it is empty return an empty object,
|
|
351
|
-
// otherwise parse it as JSON. A
|
|
352
|
-
//
|
|
372
|
+
// otherwise parse it as JSON. A body that is not JSON is turned into a
|
|
373
|
+
// described error by `_nonJsonError` and handled according to isShell.
|
|
353
374
|
const text = await response.text();
|
|
354
|
-
|
|
375
|
+
if (!text) {
|
|
376
|
+
return {};
|
|
377
|
+
}
|
|
378
|
+
try {
|
|
379
|
+
return JSON.parse(text);
|
|
380
|
+
}
|
|
381
|
+
catch {
|
|
382
|
+
throw this._nonJsonError(response, text);
|
|
383
|
+
}
|
|
355
384
|
}
|
|
356
385
|
// Handle non-OK responses (applies to the first response and to a failed retry).
|
|
357
386
|
const status = response.status;
|
|
@@ -379,6 +408,40 @@ export default class AsyncModules extends SyncModules {
|
|
|
379
408
|
}
|
|
380
409
|
}
|
|
381
410
|
}
|
|
411
|
+
/**
|
|
412
|
+
* Describes a response whose body is not the JSON the API is supposed to send.
|
|
413
|
+
*
|
|
414
|
+
* The platform has been seen answering `200 OK` with `content-type: text/html`
|
|
415
|
+
* and the admin page as the body — every content endpoint at once, for minutes
|
|
416
|
+
* (CMS-957). A successful status carrying someone else's body is indistinguishable
|
|
417
|
+
* from a real answer by status alone, and the only symptom used to be
|
|
418
|
+
* `SyntaxError: Unexpected token '<'` from the JSON parser: a message about the
|
|
419
|
+
* SDK's own parsing rather than about what the platform did. Callers read that as
|
|
420
|
+
* "this record does not exist" and a static build baked 404s over live products.
|
|
421
|
+
*
|
|
422
|
+
* The returned error names the received content type, the HTTP status and the
|
|
423
|
+
* first characters of the body, so a log line says what arrived instead of JSON.
|
|
424
|
+
* `statusCode` stays `0` — the request produced no usable answer, the same
|
|
425
|
+
* contract as a dropped connection, and a value no HTTP answer uses.
|
|
426
|
+
* @param {Response} response - The response whose body could not be parsed.
|
|
427
|
+
* @param {string} text - The raw body as read.
|
|
428
|
+
* @returns {IError} The error in the shape the SDK returns for platform errors.
|
|
429
|
+
*/
|
|
430
|
+
_nonJsonError(response, text) {
|
|
431
|
+
var _a, _b, _c;
|
|
432
|
+
const contentType = (_c = (_b = (_a = response.headers) === null || _a === void 0 ? void 0 : _a.get) === null || _b === void 0 ? void 0 : _b.call(_a, 'content-type')) !== null && _c !== void 0 ? _c : 'unknown';
|
|
433
|
+
const excerpt = text.trim().slice(0, 120).replace(/\s+/g, ' ');
|
|
434
|
+
return {
|
|
435
|
+
// A successful status carrying a non-JSON body produced no usable answer, the same
|
|
436
|
+
// contract as a dropped connection. A failing one already says something true about
|
|
437
|
+
// the request, so that status is kept.
|
|
438
|
+
statusCode: response.ok ? 0 : response.status,
|
|
439
|
+
message: `Expected JSON from the platform, received ${contentType} ` +
|
|
440
|
+
`(HTTP ${response.status}): ${excerpt}`,
|
|
441
|
+
pageData: null,
|
|
442
|
+
timestamp: new Date().toISOString(),
|
|
443
|
+
};
|
|
444
|
+
}
|
|
382
445
|
/**
|
|
383
446
|
* Turns whatever was thrown into the error shape the SDK already returns.
|
|
384
447
|
*
|
package/esm/base/utils.d.ts
CHANGED
|
@@ -96,7 +96,7 @@ interface IConfig {
|
|
|
96
96
|
* @property {boolean} isVisible - Whether the attribute is exposed in the public API/UI. Example: true.
|
|
97
97
|
* @property {string} type - Attribute data type (e.g. "string", "integer", "list", "file", "image").
|
|
98
98
|
* @property {Record<string, unknown>} [settings] - Type-specific configuration (e.g. min/max for numbers, accepted MIME types for files). Optional.
|
|
99
|
-
* @property {Record<string, unknown>} validators - Validation rules applied to the attribute value (e.g. `requiredValidator`, `
|
|
99
|
+
* @property {Record<string, unknown>} validators - Validation rules applied to the attribute value, keyed by validator name (e.g. `requiredValidator`, `regExpValidator`); the full set of names is listed on `IAttributeValidators`. Example: { "requiredValidator": { "strict": true } }.
|
|
100
100
|
* @property {Record<string, IAttributes>} [additionalFields] - Nested sub-attributes attached to this attribute, keyed by marker. Optional.
|
|
101
101
|
* @property {boolean} [isIcon] - Block/preview attribute flag — whether the field is treated as an icon. Optional.
|
|
102
102
|
* @property {boolean} [isProductPreview] - Block/preview attribute flag — whether the field is shown in product preview. Optional.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oneentry",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.170",
|
|
4
4
|
"description": "OneEntry NPM package",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "esm/index.js",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
50
|
"prepublishOnly": "node ../scripts/smoke-pack.js --files-only",
|
|
51
|
-
"postpublish": "node ../scripts/verify-published.js --named defineOneEntry --expect 1.0.
|
|
51
|
+
"postpublish": "node ../scripts/verify-published.js --named defineOneEntry --expect 1.0.170"
|
|
52
52
|
},
|
|
53
53
|
"sideEffects": false,
|
|
54
54
|
"files": [
|