@promptbook/utils 0.92.0-13 → 0.92.0-15
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/esm/index.es.js +751 -742
- package/esm/index.es.js.map +1 -1
- package/package.json +1 -1
- package/umd/index.umd.js +751 -742
- package/umd/index.umd.js.map +1 -1
package/esm/index.es.js
CHANGED
|
@@ -16,7 +16,7 @@ const BOOK_LANGUAGE_VERSION = '1.0.0';
|
|
|
16
16
|
* @generated
|
|
17
17
|
* @see https://github.com/webgptorg/promptbook
|
|
18
18
|
*/
|
|
19
|
-
const PROMPTBOOK_ENGINE_VERSION = '0.92.0-
|
|
19
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.92.0-15';
|
|
20
20
|
/**
|
|
21
21
|
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
|
|
22
22
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -87,6 +87,48 @@ Object.freeze({
|
|
|
87
87
|
* TODO: [🧠][🧜♂️] Maybe join remoteServerUrl and path into single value
|
|
88
88
|
*/
|
|
89
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Orders JSON object by keys
|
|
92
|
+
*
|
|
93
|
+
* @returns The same type of object as the input re-ordered
|
|
94
|
+
* @public exported from `@promptbook/utils`
|
|
95
|
+
*/
|
|
96
|
+
function orderJson(options) {
|
|
97
|
+
const { value, order } = options;
|
|
98
|
+
const orderedValue = {
|
|
99
|
+
...(order === undefined ? {} : Object.fromEntries(order.map((key) => [key, undefined]))),
|
|
100
|
+
...value,
|
|
101
|
+
};
|
|
102
|
+
return orderedValue;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Freezes the given object and all its nested objects recursively
|
|
107
|
+
*
|
|
108
|
+
* Note: `$` is used to indicate that this function is not a pure function - it mutates given object
|
|
109
|
+
* Note: This function mutates the object and returns the original (but mutated-deep-freezed) object
|
|
110
|
+
*
|
|
111
|
+
* @returns The same object as the input, but deeply frozen
|
|
112
|
+
* @public exported from `@promptbook/utils`
|
|
113
|
+
*/
|
|
114
|
+
function $deepFreeze(objectValue) {
|
|
115
|
+
if (Array.isArray(objectValue)) {
|
|
116
|
+
return Object.freeze(objectValue.map((item) => $deepFreeze(item)));
|
|
117
|
+
}
|
|
118
|
+
const propertyNames = Object.getOwnPropertyNames(objectValue);
|
|
119
|
+
for (const propertyName of propertyNames) {
|
|
120
|
+
const value = objectValue[propertyName];
|
|
121
|
+
if (value && typeof value === 'object') {
|
|
122
|
+
$deepFreeze(value);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
Object.freeze(objectValue);
|
|
126
|
+
return objectValue;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* TODO: [🧠] Is there a way how to meaningfully test this utility
|
|
130
|
+
*/
|
|
131
|
+
|
|
90
132
|
/**
|
|
91
133
|
* Make error report URL for the given error
|
|
92
134
|
*
|
|
@@ -156,153 +198,438 @@ class UnexpectedError extends Error {
|
|
|
156
198
|
}
|
|
157
199
|
|
|
158
200
|
/**
|
|
159
|
-
*
|
|
201
|
+
* This error type indicates that somewhere in the code non-Error object was thrown and it was wrapped into the `WrappedError`
|
|
160
202
|
*
|
|
161
|
-
* @
|
|
162
|
-
* @param _isFirstLetterCapital @@@
|
|
163
|
-
* @returns @@@
|
|
164
|
-
* @example 'helloWorld'
|
|
165
|
-
* @example 'iLovePromptbook'
|
|
166
|
-
* @public exported from `@promptbook/utils`
|
|
203
|
+
* @public exported from `@promptbook/core`
|
|
167
204
|
*/
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
normalizedChar = char.toLowerCase();
|
|
181
|
-
}
|
|
182
|
-
else if (/^[0-9]$/.test(char)) {
|
|
183
|
-
charType = 'NUMBER';
|
|
184
|
-
normalizedChar = char;
|
|
185
|
-
}
|
|
186
|
-
else {
|
|
187
|
-
charType = 'OTHER';
|
|
188
|
-
normalizedChar = '';
|
|
189
|
-
}
|
|
190
|
-
if (!lastCharType) {
|
|
191
|
-
if (_isFirstLetterCapital) {
|
|
192
|
-
normalizedChar = normalizedChar.toUpperCase(); //TODO: DRY
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
else if (charType !== lastCharType &&
|
|
196
|
-
!(charType === 'LOWERCASE' && lastCharType === 'UPPERCASE') &&
|
|
197
|
-
!(lastCharType === 'NUMBER') &&
|
|
198
|
-
!(charType === 'NUMBER')) {
|
|
199
|
-
normalizedChar = normalizedChar.toUpperCase(); //TODO: [🌺] DRY
|
|
200
|
-
}
|
|
201
|
-
normalizedName += normalizedChar;
|
|
202
|
-
lastCharType = charType;
|
|
205
|
+
class WrappedError extends Error {
|
|
206
|
+
constructor(whatWasThrown) {
|
|
207
|
+
const tag = `[🤮]`;
|
|
208
|
+
console.error(tag, whatWasThrown);
|
|
209
|
+
super(spaceTrim$2(`
|
|
210
|
+
Non-Error object was thrown
|
|
211
|
+
|
|
212
|
+
Note: Look for ${tag} in the console for more details
|
|
213
|
+
Please report issue on ${ADMIN_EMAIL}
|
|
214
|
+
`));
|
|
215
|
+
this.name = 'WrappedError';
|
|
216
|
+
Object.setPrototypeOf(this, WrappedError.prototype);
|
|
203
217
|
}
|
|
204
|
-
return normalizedName;
|
|
205
218
|
}
|
|
206
|
-
/**
|
|
207
|
-
* TODO: [🌺] Use some intermediate util splitWords
|
|
208
|
-
*/
|
|
209
219
|
|
|
210
220
|
/**
|
|
211
|
-
*
|
|
221
|
+
* Helper used in catch blocks to assert that the error is an instance of `Error`
|
|
212
222
|
*
|
|
213
|
-
* @param
|
|
214
|
-
* @returns
|
|
215
|
-
* @
|
|
223
|
+
* @param whatWasThrown Any object that was thrown
|
|
224
|
+
* @returns Nothing if the error is an instance of `Error`
|
|
225
|
+
* @throws `WrappedError` or `UnexpectedError` if the error is not standard
|
|
226
|
+
*
|
|
227
|
+
* @private within the repository
|
|
216
228
|
*/
|
|
217
|
-
function
|
|
218
|
-
//
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
229
|
+
function assertsError(whatWasThrown) {
|
|
230
|
+
// Case 1: Handle error which was rethrown as `WrappedError`
|
|
231
|
+
if (whatWasThrown instanceof WrappedError) {
|
|
232
|
+
const wrappedError = whatWasThrown;
|
|
233
|
+
throw wrappedError;
|
|
234
|
+
}
|
|
235
|
+
// Case 2: Handle unexpected errors
|
|
236
|
+
if (whatWasThrown instanceof UnexpectedError) {
|
|
237
|
+
const unexpectedError = whatWasThrown;
|
|
238
|
+
throw unexpectedError;
|
|
239
|
+
}
|
|
240
|
+
// Case 3: Handle standard errors - keep them up to consumer
|
|
241
|
+
if (whatWasThrown instanceof Error) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
// Case 4: Handle non-standard errors - wrap them into `WrappedError` and throw
|
|
245
|
+
throw new WrappedError(whatWasThrown);
|
|
224
246
|
}
|
|
225
247
|
|
|
226
248
|
/**
|
|
227
|
-
*
|
|
249
|
+
* Checks if the value is [🚉] serializable as JSON
|
|
250
|
+
* If not, throws an UnexpectedError with a rich error message and tracking
|
|
228
251
|
*
|
|
229
|
-
*
|
|
252
|
+
* - Almost all primitives are serializable BUT:
|
|
253
|
+
* - `undefined` is not serializable
|
|
254
|
+
* - `NaN` is not serializable
|
|
255
|
+
* - Objects and arrays are serializable if all their properties are serializable
|
|
256
|
+
* - Functions are not serializable
|
|
257
|
+
* - Circular references are not serializable
|
|
258
|
+
* - `Date` objects are not serializable
|
|
259
|
+
* - `Map` and `Set` objects are not serializable
|
|
260
|
+
* - `RegExp` objects are not serializable
|
|
261
|
+
* - `Error` objects are not serializable
|
|
262
|
+
* - `Symbol` objects are not serializable
|
|
263
|
+
* - And much more...
|
|
264
|
+
*
|
|
265
|
+
* @throws UnexpectedError if the value is not serializable as JSON
|
|
230
266
|
* @public exported from `@promptbook/utils`
|
|
231
267
|
*/
|
|
232
|
-
function
|
|
233
|
-
|
|
234
|
-
|
|
268
|
+
function checkSerializableAsJson(options) {
|
|
269
|
+
const { value, name, message } = options;
|
|
270
|
+
if (value === undefined) {
|
|
271
|
+
throw new UnexpectedError(`${name} is undefined`);
|
|
235
272
|
}
|
|
236
|
-
if (
|
|
237
|
-
return
|
|
273
|
+
else if (value === null) {
|
|
274
|
+
return;
|
|
238
275
|
}
|
|
239
|
-
if (
|
|
240
|
-
|
|
241
|
-
return false;
|
|
276
|
+
else if (typeof value === 'boolean') {
|
|
277
|
+
return;
|
|
242
278
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
if (/^(\/)/i.test(filenameSlashes)) {
|
|
246
|
-
// console.log(filename, 'Absolute Unix path: /hello.txt');
|
|
247
|
-
return true;
|
|
279
|
+
else if (typeof value === 'number' && !isNaN(value)) {
|
|
280
|
+
return;
|
|
248
281
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
// console.log(filename, 'Absolute Windows path: /hello.txt');
|
|
252
|
-
return true;
|
|
282
|
+
else if (typeof value === 'string') {
|
|
283
|
+
return;
|
|
253
284
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
// console.log(filename, 'Relative path: ./hello.txt');
|
|
257
|
-
return true;
|
|
285
|
+
else if (typeof value === 'symbol') {
|
|
286
|
+
throw new UnexpectedError(`${name} is symbol`);
|
|
258
287
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
// console.log(filename, 'Allow paths like foo/hello');
|
|
262
|
-
return true;
|
|
288
|
+
else if (typeof value === 'function') {
|
|
289
|
+
throw new UnexpectedError(`${name} is function`);
|
|
263
290
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
291
|
+
else if (typeof value === 'object' && Array.isArray(value)) {
|
|
292
|
+
for (let i = 0; i < value.length; i++) {
|
|
293
|
+
checkSerializableAsJson({ name: `${name}[${i}]`, value: value[i], message });
|
|
294
|
+
}
|
|
268
295
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
*/
|
|
296
|
+
else if (typeof value === 'object') {
|
|
297
|
+
if (value instanceof Date) {
|
|
298
|
+
throw new UnexpectedError(spaceTrim$1((block) => `
|
|
299
|
+
\`${name}\` is Date
|
|
274
300
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
* - `isValidUrl` which tests any URL
|
|
281
|
-
* - `isValidPipelineUrl` *(this one)* which tests just promptbook URL
|
|
282
|
-
*
|
|
283
|
-
* @public exported from `@promptbook/utils`
|
|
284
|
-
*/
|
|
285
|
-
function isValidUrl(url) {
|
|
286
|
-
if (typeof url !== 'string') {
|
|
287
|
-
return false;
|
|
288
|
-
}
|
|
289
|
-
try {
|
|
290
|
-
if (url.startsWith('blob:')) {
|
|
291
|
-
url = url.replace(/^blob:/, '');
|
|
301
|
+
Use \`string_date_iso8601\` instead
|
|
302
|
+
|
|
303
|
+
Additional message for \`${name}\`:
|
|
304
|
+
${block(message || '(nothing)')}
|
|
305
|
+
`));
|
|
292
306
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
return false;
|
|
307
|
+
else if (value instanceof Map) {
|
|
308
|
+
throw new UnexpectedError(`${name} is Map`);
|
|
296
309
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
}
|
|
310
|
+
else if (value instanceof Set) {
|
|
311
|
+
throw new UnexpectedError(`${name} is Set`);
|
|
312
|
+
}
|
|
313
|
+
else if (value instanceof RegExp) {
|
|
314
|
+
throw new UnexpectedError(`${name} is RegExp`);
|
|
315
|
+
}
|
|
316
|
+
else if (value instanceof Error) {
|
|
317
|
+
throw new UnexpectedError(spaceTrim$1((block) => `
|
|
318
|
+
\`${name}\` is unserialized Error
|
|
303
319
|
|
|
304
|
-
|
|
305
|
-
|
|
320
|
+
Use function \`serializeError\`
|
|
321
|
+
|
|
322
|
+
Additional message for \`${name}\`:
|
|
323
|
+
${block(message || '(nothing)')}
|
|
324
|
+
|
|
325
|
+
`));
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
for (const [subName, subValue] of Object.entries(value)) {
|
|
329
|
+
if (subValue === undefined) {
|
|
330
|
+
// Note: undefined in object is serializable - it is just omited
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
checkSerializableAsJson({ name: `${name}.${subName}`, value: subValue, message });
|
|
334
|
+
}
|
|
335
|
+
try {
|
|
336
|
+
JSON.stringify(value); // <- TODO: [0]
|
|
337
|
+
}
|
|
338
|
+
catch (error) {
|
|
339
|
+
assertsError(error);
|
|
340
|
+
throw new UnexpectedError(spaceTrim$1((block) => `
|
|
341
|
+
\`${name}\` is not serializable
|
|
342
|
+
|
|
343
|
+
${block(error.stack || error.message)}
|
|
344
|
+
|
|
345
|
+
Additional message for \`${name}\`:
|
|
346
|
+
${block(message || '(nothing)')}
|
|
347
|
+
`));
|
|
348
|
+
}
|
|
349
|
+
/*
|
|
350
|
+
TODO: [0] Is there some more elegant way to check circular references?
|
|
351
|
+
const seen = new Set();
|
|
352
|
+
const stack = [{ value }];
|
|
353
|
+
while (stack.length > 0) {
|
|
354
|
+
const { value } = stack.pop()!;
|
|
355
|
+
if (typeof value === 'object' && value !== null) {
|
|
356
|
+
if (seen.has(value)) {
|
|
357
|
+
throw new UnexpectedError(`${name} has circular reference`);
|
|
358
|
+
}
|
|
359
|
+
seen.add(value);
|
|
360
|
+
if (Array.isArray(value)) {
|
|
361
|
+
stack.push(...value.map((value) => ({ value })));
|
|
362
|
+
} else {
|
|
363
|
+
stack.push(...Object.values(value).map((value) => ({ value })));
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
*/
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
throw new UnexpectedError(spaceTrim$1((block) => `
|
|
373
|
+
\`${name}\` is unknown type
|
|
374
|
+
|
|
375
|
+
Additional message for \`${name}\`:
|
|
376
|
+
${block(message || '(nothing)')}
|
|
377
|
+
`));
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* TODO: Can be return type more type-safe? like `asserts options.value is JsonValue`
|
|
382
|
+
* TODO: [🧠][main] !!3 In-memory cache of same values to prevent multiple checks
|
|
383
|
+
* Note: [🐠] This is how `checkSerializableAsJson` + `isSerializableAsJson` together can just retun true/false or rich error message
|
|
384
|
+
*/
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* @@@
|
|
388
|
+
*
|
|
389
|
+
* @public exported from `@promptbook/utils`
|
|
390
|
+
*/
|
|
391
|
+
function deepClone(objectValue) {
|
|
392
|
+
return JSON.parse(JSON.stringify(objectValue));
|
|
393
|
+
/*
|
|
394
|
+
TODO: [🧠] Is there a better implementation?
|
|
395
|
+
> const propertyNames = Object.getOwnPropertyNames(objectValue);
|
|
396
|
+
> for (const propertyName of propertyNames) {
|
|
397
|
+
> const value = (objectValue as really_any)[propertyName];
|
|
398
|
+
> if (value && typeof value === 'object') {
|
|
399
|
+
> deepClone(value);
|
|
400
|
+
> }
|
|
401
|
+
> }
|
|
402
|
+
> return Object.assign({}, objectValue);
|
|
403
|
+
*/
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* TODO: [🧠] Is there a way how to meaningfully test this utility
|
|
407
|
+
*/
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Utility to export a JSON object from a function
|
|
411
|
+
*
|
|
412
|
+
* 1) Checks if the value is serializable as JSON
|
|
413
|
+
* 2) Makes a deep clone of the object
|
|
414
|
+
* 2) Orders the object properties
|
|
415
|
+
* 2) Deeply freezes the cloned object
|
|
416
|
+
*
|
|
417
|
+
* Note: This function does not mutates the given object
|
|
418
|
+
*
|
|
419
|
+
* @returns The same type of object as the input but read-only and re-ordered
|
|
420
|
+
* @public exported from `@promptbook/utils`
|
|
421
|
+
*/
|
|
422
|
+
function exportJson(options) {
|
|
423
|
+
const { name, value, order, message } = options;
|
|
424
|
+
checkSerializableAsJson({ name, value, message });
|
|
425
|
+
const orderedValue =
|
|
426
|
+
// TODO: Fix error "Type instantiation is excessively deep and possibly infinite."
|
|
427
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
428
|
+
// @ts-ignore
|
|
429
|
+
order === undefined
|
|
430
|
+
? deepClone(value)
|
|
431
|
+
: orderJson({
|
|
432
|
+
value: value,
|
|
433
|
+
// <- Note: checkSerializableAsJson asserts that the value is serializable as JSON
|
|
434
|
+
order: order,
|
|
435
|
+
});
|
|
436
|
+
$deepFreeze(orderedValue);
|
|
437
|
+
return orderedValue;
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* TODO: [🧠] Is there a way how to meaningfully test this utility
|
|
441
|
+
*/
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Nonce which is used for replacing things in strings
|
|
445
|
+
*
|
|
446
|
+
* @private within the repository
|
|
447
|
+
*/
|
|
448
|
+
const REPLACING_NONCE = 'ptbkauk42kV2dzao34faw7FudQUHYPtW';
|
|
449
|
+
/**
|
|
450
|
+
* @@@
|
|
451
|
+
*
|
|
452
|
+
* @private within the repository
|
|
453
|
+
*/
|
|
454
|
+
const RESERVED_PARAMETER_MISSING_VALUE = 'MISSING-' + REPLACING_NONCE;
|
|
455
|
+
/**
|
|
456
|
+
* @@@
|
|
457
|
+
*
|
|
458
|
+
* @private within the repository
|
|
459
|
+
*/
|
|
460
|
+
const RESERVED_PARAMETER_RESTRICTED = 'RESTRICTED-' + REPLACING_NONCE;
|
|
461
|
+
/**
|
|
462
|
+
* The names of the parameters that are reserved for special purposes
|
|
463
|
+
*
|
|
464
|
+
* @public exported from `@promptbook/core`
|
|
465
|
+
*/
|
|
466
|
+
const RESERVED_PARAMETER_NAMES = exportJson({
|
|
467
|
+
name: 'RESERVED_PARAMETER_NAMES',
|
|
468
|
+
message: `The names of the parameters that are reserved for special purposes`,
|
|
469
|
+
value: [
|
|
470
|
+
'content',
|
|
471
|
+
'context',
|
|
472
|
+
'knowledge',
|
|
473
|
+
'examples',
|
|
474
|
+
'modelName',
|
|
475
|
+
'currentDate',
|
|
476
|
+
// <- TODO: list here all command names
|
|
477
|
+
// <- TODO: Add more like 'date', 'modelName',...
|
|
478
|
+
// <- TODO: Add [emoji] + instructions ACRY when adding new reserved parameter
|
|
479
|
+
],
|
|
480
|
+
});
|
|
481
|
+
/**
|
|
482
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
483
|
+
*/
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* @@@
|
|
487
|
+
*
|
|
488
|
+
* @param text @@@
|
|
489
|
+
* @param _isFirstLetterCapital @@@
|
|
490
|
+
* @returns @@@
|
|
491
|
+
* @example 'helloWorld'
|
|
492
|
+
* @example 'iLovePromptbook'
|
|
493
|
+
* @public exported from `@promptbook/utils`
|
|
494
|
+
*/
|
|
495
|
+
function normalizeTo_camelCase(text, _isFirstLetterCapital = false) {
|
|
496
|
+
let charType;
|
|
497
|
+
let lastCharType = null;
|
|
498
|
+
let normalizedName = '';
|
|
499
|
+
for (const char of text) {
|
|
500
|
+
let normalizedChar;
|
|
501
|
+
if (/^[a-z]$/.test(char)) {
|
|
502
|
+
charType = 'LOWERCASE';
|
|
503
|
+
normalizedChar = char;
|
|
504
|
+
}
|
|
505
|
+
else if (/^[A-Z]$/.test(char)) {
|
|
506
|
+
charType = 'UPPERCASE';
|
|
507
|
+
normalizedChar = char.toLowerCase();
|
|
508
|
+
}
|
|
509
|
+
else if (/^[0-9]$/.test(char)) {
|
|
510
|
+
charType = 'NUMBER';
|
|
511
|
+
normalizedChar = char;
|
|
512
|
+
}
|
|
513
|
+
else {
|
|
514
|
+
charType = 'OTHER';
|
|
515
|
+
normalizedChar = '';
|
|
516
|
+
}
|
|
517
|
+
if (!lastCharType) {
|
|
518
|
+
if (_isFirstLetterCapital) {
|
|
519
|
+
normalizedChar = normalizedChar.toUpperCase(); //TODO: DRY
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
else if (charType !== lastCharType &&
|
|
523
|
+
!(charType === 'LOWERCASE' && lastCharType === 'UPPERCASE') &&
|
|
524
|
+
!(lastCharType === 'NUMBER') &&
|
|
525
|
+
!(charType === 'NUMBER')) {
|
|
526
|
+
normalizedChar = normalizedChar.toUpperCase(); //TODO: [🌺] DRY
|
|
527
|
+
}
|
|
528
|
+
normalizedName += normalizedChar;
|
|
529
|
+
lastCharType = charType;
|
|
530
|
+
}
|
|
531
|
+
return normalizedName;
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* TODO: [🌺] Use some intermediate util splitWords
|
|
535
|
+
*/
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Removes emojis from a string and fix whitespaces
|
|
539
|
+
*
|
|
540
|
+
* @param text with emojis
|
|
541
|
+
* @returns text without emojis
|
|
542
|
+
* @public exported from `@promptbook/utils`
|
|
543
|
+
*/
|
|
544
|
+
function removeEmojis(text) {
|
|
545
|
+
// Replace emojis (and also ZWJ sequence) with hyphens
|
|
546
|
+
text = text.replace(/(\p{Extended_Pictographic})\p{Modifier_Symbol}/gu, '$1');
|
|
547
|
+
text = text.replace(/(\p{Extended_Pictographic})[\u{FE00}-\u{FE0F}]/gu, '$1');
|
|
548
|
+
text = text.replace(/(\p{Extended_Pictographic})(\u{200D}\p{Extended_Pictographic})*/gu, '$1');
|
|
549
|
+
text = text.replace(/\p{Extended_Pictographic}/gu, '');
|
|
550
|
+
return text;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Tests if given string is valid URL.
|
|
555
|
+
*
|
|
556
|
+
* Note: This does not check if the file exists only if the path is valid
|
|
557
|
+
* @public exported from `@promptbook/utils`
|
|
558
|
+
*/
|
|
559
|
+
function isValidFilePath(filename) {
|
|
560
|
+
if (typeof filename !== 'string') {
|
|
561
|
+
return false;
|
|
562
|
+
}
|
|
563
|
+
if (filename.split('\n').length > 1) {
|
|
564
|
+
return false;
|
|
565
|
+
}
|
|
566
|
+
if (filename.split(' ').length >
|
|
567
|
+
5 /* <- TODO: [🧠][🈷] Make some better non-arbitrary way how to distinct filenames from informational texts */) {
|
|
568
|
+
return false;
|
|
569
|
+
}
|
|
570
|
+
const filenameSlashes = filename.split('\\').join('/');
|
|
571
|
+
// Absolute Unix path: /hello.txt
|
|
572
|
+
if (/^(\/)/i.test(filenameSlashes)) {
|
|
573
|
+
// console.log(filename, 'Absolute Unix path: /hello.txt');
|
|
574
|
+
return true;
|
|
575
|
+
}
|
|
576
|
+
// Absolute Windows path: /hello.txt
|
|
577
|
+
if (/^([A-Z]{1,2}:\/?)\//i.test(filenameSlashes)) {
|
|
578
|
+
// console.log(filename, 'Absolute Windows path: /hello.txt');
|
|
579
|
+
return true;
|
|
580
|
+
}
|
|
581
|
+
// Relative path: ./hello.txt
|
|
582
|
+
if (/^(\.\.?\/)+/i.test(filenameSlashes)) {
|
|
583
|
+
// console.log(filename, 'Relative path: ./hello.txt');
|
|
584
|
+
return true;
|
|
585
|
+
}
|
|
586
|
+
// Allow paths like foo/hello
|
|
587
|
+
if (/^[^/]+\/[^/]+/i.test(filenameSlashes)) {
|
|
588
|
+
// console.log(filename, 'Allow paths like foo/hello');
|
|
589
|
+
return true;
|
|
590
|
+
}
|
|
591
|
+
// Allow paths like hello.book
|
|
592
|
+
if (/^[^/]+\.[^/]+$/i.test(filenameSlashes)) {
|
|
593
|
+
// console.log(filename, 'Allow paths like hello.book');
|
|
594
|
+
return true;
|
|
595
|
+
}
|
|
596
|
+
return false;
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* TODO: [🍏] Implement for MacOs
|
|
600
|
+
*/
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* Tests if given string is valid URL.
|
|
604
|
+
*
|
|
605
|
+
* Note: Dataurl are considered perfectly valid.
|
|
606
|
+
* Note: There are two simmilar functions:
|
|
607
|
+
* - `isValidUrl` which tests any URL
|
|
608
|
+
* - `isValidPipelineUrl` *(this one)* which tests just promptbook URL
|
|
609
|
+
*
|
|
610
|
+
* @public exported from `@promptbook/utils`
|
|
611
|
+
*/
|
|
612
|
+
function isValidUrl(url) {
|
|
613
|
+
if (typeof url !== 'string') {
|
|
614
|
+
return false;
|
|
615
|
+
}
|
|
616
|
+
try {
|
|
617
|
+
if (url.startsWith('blob:')) {
|
|
618
|
+
url = url.replace(/^blob:/, '');
|
|
619
|
+
}
|
|
620
|
+
const urlObject = new URL(url /* because fail is handled */);
|
|
621
|
+
if (!['http:', 'https:', 'data:'].includes(urlObject.protocol)) {
|
|
622
|
+
return false;
|
|
623
|
+
}
|
|
624
|
+
return true;
|
|
625
|
+
}
|
|
626
|
+
catch (error) {
|
|
627
|
+
return false;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
const defaultDiacriticsRemovalMap = [
|
|
632
|
+
{
|
|
306
633
|
base: 'A',
|
|
307
634
|
letters: '\u0041\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F',
|
|
308
635
|
},
|
|
@@ -647,6 +974,13 @@ function titleToName(value) {
|
|
|
647
974
|
function renderPromptbookMermaid(pipelineJson, options) {
|
|
648
975
|
const { linkTask = () => null } = options || {};
|
|
649
976
|
const parameterNameToTaskName = (parameterName) => {
|
|
977
|
+
if (parameterName === 'knowledge') {
|
|
978
|
+
return 'knowledge';
|
|
979
|
+
// <- TODO: !!!! Check that this works
|
|
980
|
+
}
|
|
981
|
+
else if (RESERVED_PARAMETER_NAMES.includes(parameterName)) {
|
|
982
|
+
return 'reserved';
|
|
983
|
+
}
|
|
650
984
|
const parameter = pipelineJson.parameters.find((parameter) => parameter.name === parameterName);
|
|
651
985
|
if (!parameter) {
|
|
652
986
|
throw new UnexpectedError(`Could not find {${parameterName}}`);
|
|
@@ -671,6 +1005,8 @@ function renderPromptbookMermaid(pipelineJson, options) {
|
|
|
671
1005
|
direction TB
|
|
672
1006
|
|
|
673
1007
|
input((Input)):::input
|
|
1008
|
+
other((Other)):::other
|
|
1009
|
+
knowledge((Knowledgebase)):::knowledge
|
|
674
1010
|
${block(pipelineJson.tasks
|
|
675
1011
|
.flatMap(({ title, dependentParameterNames, resultingParameterName }) => [
|
|
676
1012
|
`${parameterNameToTaskName(resultingParameterName)}("${title}")`,
|
|
@@ -799,753 +1135,426 @@ class ExpectError extends Error {
|
|
|
799
1135
|
}
|
|
800
1136
|
|
|
801
1137
|
/**
|
|
802
|
-
* This error indicates that the promptbook can not retrieve knowledge from external sources
|
|
803
|
-
*
|
|
804
|
-
* @public exported from `@promptbook/core`
|
|
805
|
-
*/
|
|
806
|
-
class KnowledgeScrapeError extends Error {
|
|
807
|
-
constructor(message) {
|
|
808
|
-
super(message);
|
|
809
|
-
this.name = 'KnowledgeScrapeError';
|
|
810
|
-
Object.setPrototypeOf(this, KnowledgeScrapeError.prototype);
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
/**
|
|
815
|
-
* This error type indicates that some limit was reached
|
|
816
|
-
*
|
|
817
|
-
* @public exported from `@promptbook/core`
|
|
818
|
-
*/
|
|
819
|
-
class LimitReachedError extends Error {
|
|
820
|
-
constructor(message) {
|
|
821
|
-
super(message);
|
|
822
|
-
this.name = 'LimitReachedError';
|
|
823
|
-
Object.setPrototypeOf(this, LimitReachedError.prototype);
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
/**
|
|
828
|
-
* This error type indicates that some tools are missing for pipeline execution or preparation
|
|
829
|
-
*
|
|
830
|
-
* @public exported from `@promptbook/core`
|
|
831
|
-
*/
|
|
832
|
-
class MissingToolsError extends Error {
|
|
833
|
-
constructor(message) {
|
|
834
|
-
super(spaceTrim$2((block) => `
|
|
835
|
-
${block(message)}
|
|
836
|
-
|
|
837
|
-
Note: You have probbably forgot to provide some tools for pipeline execution or preparation
|
|
838
|
-
|
|
839
|
-
`));
|
|
840
|
-
this.name = 'MissingToolsError';
|
|
841
|
-
Object.setPrototypeOf(this, MissingToolsError.prototype);
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
|
|
845
|
-
/**
|
|
846
|
-
* This error indicates that promptbook not found in the collection
|
|
847
|
-
*
|
|
848
|
-
* @public exported from `@promptbook/core`
|
|
849
|
-
*/
|
|
850
|
-
class NotFoundError extends Error {
|
|
851
|
-
constructor(message) {
|
|
852
|
-
super(message);
|
|
853
|
-
this.name = 'NotFoundError';
|
|
854
|
-
Object.setPrototypeOf(this, NotFoundError.prototype);
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
/**
|
|
859
|
-
* This error type indicates that some part of the code is not implemented yet
|
|
860
|
-
*
|
|
861
|
-
* @public exported from `@promptbook/core`
|
|
862
|
-
*/
|
|
863
|
-
class NotYetImplementedError extends Error {
|
|
864
|
-
constructor(message) {
|
|
865
|
-
super(spaceTrim$2((block) => `
|
|
866
|
-
${block(message)}
|
|
867
|
-
|
|
868
|
-
Note: This feature is not implemented yet but it will be soon.
|
|
869
|
-
|
|
870
|
-
If you want speed up the implementation or just read more, look here:
|
|
871
|
-
https://github.com/webgptorg/promptbook
|
|
872
|
-
|
|
873
|
-
Or contact us on pavol@ptbk.io
|
|
874
|
-
|
|
875
|
-
`));
|
|
876
|
-
this.name = 'NotYetImplementedError';
|
|
877
|
-
Object.setPrototypeOf(this, NotYetImplementedError.prototype);
|
|
878
|
-
}
|
|
879
|
-
}
|
|
880
|
-
|
|
881
|
-
/**
|
|
882
|
-
* This error indicates that the promptbook in a markdown format cannot be parsed into a valid promptbook object
|
|
883
|
-
*
|
|
884
|
-
* @public exported from `@promptbook/core`
|
|
885
|
-
*/
|
|
886
|
-
class ParseError extends Error {
|
|
887
|
-
constructor(message) {
|
|
888
|
-
super(message);
|
|
889
|
-
this.name = 'ParseError';
|
|
890
|
-
Object.setPrototypeOf(this, ParseError.prototype);
|
|
891
|
-
}
|
|
892
|
-
}
|
|
893
|
-
/**
|
|
894
|
-
* TODO: Maybe split `ParseError` and `ApplyError`
|
|
895
|
-
*/
|
|
896
|
-
|
|
897
|
-
/**
|
|
898
|
-
* Generates random token
|
|
899
|
-
*
|
|
900
|
-
* Note: This function is cryptographically secure (it uses crypto.randomBytes internally)
|
|
901
|
-
*
|
|
902
|
-
* @private internal helper function
|
|
903
|
-
* @returns secure random token
|
|
904
|
-
*/
|
|
905
|
-
function $randomToken(randomness) {
|
|
906
|
-
return randomBytes(randomness).toString('hex');
|
|
907
|
-
}
|
|
908
|
-
/**
|
|
909
|
-
* TODO: Maybe use nanoid instead https://github.com/ai/nanoid
|
|
910
|
-
*/
|
|
911
|
-
|
|
912
|
-
/**
|
|
913
|
-
* This error indicates errors during the execution of the pipeline
|
|
914
|
-
*
|
|
915
|
-
* @public exported from `@promptbook/core`
|
|
916
|
-
*/
|
|
917
|
-
class PipelineExecutionError extends Error {
|
|
918
|
-
constructor(message) {
|
|
919
|
-
// Added id parameter
|
|
920
|
-
super(message);
|
|
921
|
-
this.name = 'PipelineExecutionError';
|
|
922
|
-
// TODO: [🐙] DRY - Maybe $randomId
|
|
923
|
-
this.id = `error-${$randomToken(8 /* <- TODO: To global config + Use Base58 to avoid simmilar char conflicts */)}`;
|
|
924
|
-
Object.setPrototypeOf(this, PipelineExecutionError.prototype);
|
|
925
|
-
}
|
|
926
|
-
}
|
|
927
|
-
/**
|
|
928
|
-
* TODO: [🧠][🌂] Add id to all errors
|
|
929
|
-
*/
|
|
930
|
-
|
|
931
|
-
/**
|
|
932
|
-
* This error indicates that the promptbook object has valid syntax (=can be parsed) but contains logical errors (like circular dependencies)
|
|
933
|
-
*
|
|
934
|
-
* @public exported from `@promptbook/core`
|
|
935
|
-
*/
|
|
936
|
-
class PipelineLogicError extends Error {
|
|
937
|
-
constructor(message) {
|
|
938
|
-
super(message);
|
|
939
|
-
this.name = 'PipelineLogicError';
|
|
940
|
-
Object.setPrototypeOf(this, PipelineLogicError.prototype);
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
|
|
944
|
-
/**
|
|
945
|
-
* This error indicates errors in referencing promptbooks between each other
|
|
1138
|
+
* This error indicates that the promptbook can not retrieve knowledge from external sources
|
|
946
1139
|
*
|
|
947
1140
|
* @public exported from `@promptbook/core`
|
|
948
1141
|
*/
|
|
949
|
-
class
|
|
1142
|
+
class KnowledgeScrapeError extends Error {
|
|
950
1143
|
constructor(message) {
|
|
951
1144
|
super(message);
|
|
952
|
-
this.name = '
|
|
953
|
-
Object.setPrototypeOf(this,
|
|
1145
|
+
this.name = 'KnowledgeScrapeError';
|
|
1146
|
+
Object.setPrototypeOf(this, KnowledgeScrapeError.prototype);
|
|
954
1147
|
}
|
|
955
1148
|
}
|
|
956
1149
|
|
|
957
1150
|
/**
|
|
958
|
-
*
|
|
1151
|
+
* This error type indicates that some limit was reached
|
|
959
1152
|
*
|
|
960
1153
|
* @public exported from `@promptbook/core`
|
|
961
1154
|
*/
|
|
962
|
-
class
|
|
1155
|
+
class LimitReachedError extends Error {
|
|
963
1156
|
constructor(message) {
|
|
964
1157
|
super(message);
|
|
965
|
-
this.name = '
|
|
966
|
-
Object.setPrototypeOf(this,
|
|
1158
|
+
this.name = 'LimitReachedError';
|
|
1159
|
+
Object.setPrototypeOf(this, LimitReachedError.prototype);
|
|
967
1160
|
}
|
|
968
1161
|
}
|
|
969
1162
|
|
|
970
1163
|
/**
|
|
971
|
-
* This error type indicates that
|
|
1164
|
+
* This error type indicates that some tools are missing for pipeline execution or preparation
|
|
972
1165
|
*
|
|
973
1166
|
* @public exported from `@promptbook/core`
|
|
974
1167
|
*/
|
|
975
|
-
class
|
|
976
|
-
constructor(
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
super(spaceTrim$2(`
|
|
980
|
-
Non-Error object was thrown
|
|
1168
|
+
class MissingToolsError extends Error {
|
|
1169
|
+
constructor(message) {
|
|
1170
|
+
super(spaceTrim$2((block) => `
|
|
1171
|
+
${block(message)}
|
|
981
1172
|
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
this.name = '
|
|
986
|
-
Object.setPrototypeOf(this,
|
|
1173
|
+
Note: You have probbably forgot to provide some tools for pipeline execution or preparation
|
|
1174
|
+
|
|
1175
|
+
`));
|
|
1176
|
+
this.name = 'MissingToolsError';
|
|
1177
|
+
Object.setPrototypeOf(this, MissingToolsError.prototype);
|
|
987
1178
|
}
|
|
988
1179
|
}
|
|
989
1180
|
|
|
990
1181
|
/**
|
|
991
|
-
*
|
|
1182
|
+
* This error indicates that promptbook not found in the collection
|
|
992
1183
|
*
|
|
993
1184
|
* @public exported from `@promptbook/core`
|
|
994
1185
|
*/
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
ExpectError,
|
|
1001
|
-
KnowledgeScrapeError,
|
|
1002
|
-
LimitReachedError,
|
|
1003
|
-
MissingToolsError,
|
|
1004
|
-
NotFoundError,
|
|
1005
|
-
NotYetImplementedError,
|
|
1006
|
-
ParseError,
|
|
1007
|
-
PipelineExecutionError,
|
|
1008
|
-
PipelineLogicError,
|
|
1009
|
-
PipelineUrlError,
|
|
1010
|
-
AuthenticationError,
|
|
1011
|
-
PromptbookFetchError,
|
|
1012
|
-
UnexpectedError,
|
|
1013
|
-
WrappedError,
|
|
1014
|
-
// TODO: [🪑]> VersionMismatchError,
|
|
1015
|
-
};
|
|
1016
|
-
/**
|
|
1017
|
-
* Index of all javascript errors
|
|
1018
|
-
*
|
|
1019
|
-
* @private for internal usage
|
|
1020
|
-
*/
|
|
1021
|
-
const COMMON_JAVASCRIPT_ERRORS = {
|
|
1022
|
-
Error,
|
|
1023
|
-
EvalError,
|
|
1024
|
-
RangeError,
|
|
1025
|
-
ReferenceError,
|
|
1026
|
-
SyntaxError,
|
|
1027
|
-
TypeError,
|
|
1028
|
-
URIError,
|
|
1029
|
-
AggregateError,
|
|
1030
|
-
/*
|
|
1031
|
-
Note: Not widely supported
|
|
1032
|
-
> InternalError,
|
|
1033
|
-
> ModuleError,
|
|
1034
|
-
> HeapError,
|
|
1035
|
-
> WebAssemblyCompileError,
|
|
1036
|
-
> WebAssemblyRuntimeError,
|
|
1037
|
-
*/
|
|
1038
|
-
};
|
|
1039
|
-
/**
|
|
1040
|
-
* Index of all errors
|
|
1041
|
-
*
|
|
1042
|
-
* @private for internal usage
|
|
1043
|
-
*/
|
|
1044
|
-
const ALL_ERRORS = {
|
|
1045
|
-
...PROMPTBOOK_ERRORS,
|
|
1046
|
-
...COMMON_JAVASCRIPT_ERRORS,
|
|
1047
|
-
};
|
|
1048
|
-
/**
|
|
1049
|
-
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
1050
|
-
*/
|
|
1051
|
-
|
|
1052
|
-
/**
|
|
1053
|
-
* Deserializes the error object
|
|
1054
|
-
*
|
|
1055
|
-
* @public exported from `@promptbook/utils`
|
|
1056
|
-
*/
|
|
1057
|
-
function deserializeError(error) {
|
|
1058
|
-
const { name, stack, id } = error; // Added id
|
|
1059
|
-
let { message } = error;
|
|
1060
|
-
let ErrorClass = ALL_ERRORS[error.name];
|
|
1061
|
-
if (ErrorClass === undefined) {
|
|
1062
|
-
ErrorClass = Error;
|
|
1063
|
-
message = `${name}: ${message}`;
|
|
1064
|
-
}
|
|
1065
|
-
if (stack !== undefined && stack !== '') {
|
|
1066
|
-
message = spaceTrim$1((block) => `
|
|
1067
|
-
${block(message)}
|
|
1068
|
-
|
|
1069
|
-
Original stack trace:
|
|
1070
|
-
${block(stack || '')}
|
|
1071
|
-
`);
|
|
1186
|
+
class NotFoundError extends Error {
|
|
1187
|
+
constructor(message) {
|
|
1188
|
+
super(message);
|
|
1189
|
+
this.name = 'NotFoundError';
|
|
1190
|
+
Object.setPrototypeOf(this, NotFoundError.prototype);
|
|
1072
1191
|
}
|
|
1073
|
-
const deserializedError = new ErrorClass(message);
|
|
1074
|
-
deserializedError.id = id; // Assign id to the error object
|
|
1075
|
-
return deserializedError;
|
|
1076
1192
|
}
|
|
1077
1193
|
|
|
1078
1194
|
/**
|
|
1079
|
-
*
|
|
1195
|
+
* This error type indicates that some part of the code is not implemented yet
|
|
1080
1196
|
*
|
|
1081
|
-
* @public exported from `@promptbook/
|
|
1197
|
+
* @public exported from `@promptbook/core`
|
|
1082
1198
|
*/
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
console.error(spaceTrim$1((block) => `
|
|
1088
|
-
|
|
1089
|
-
Cannot serialize error with name "${name}"
|
|
1199
|
+
class NotYetImplementedError extends Error {
|
|
1200
|
+
constructor(message) {
|
|
1201
|
+
super(spaceTrim$2((block) => `
|
|
1202
|
+
${block(message)}
|
|
1090
1203
|
|
|
1091
|
-
|
|
1092
|
-
https://github.com/webgptorg/promptbook/blob/main/src/errors/0-index.ts
|
|
1204
|
+
Note: This feature is not implemented yet but it will be soon.
|
|
1093
1205
|
|
|
1206
|
+
If you want speed up the implementation or just read more, look here:
|
|
1207
|
+
https://github.com/webgptorg/promptbook
|
|
1094
1208
|
|
|
1095
|
-
|
|
1209
|
+
Or contact us on pavol@ptbk.io
|
|
1096
1210
|
|
|
1097
1211
|
`));
|
|
1212
|
+
this.name = 'NotYetImplementedError';
|
|
1213
|
+
Object.setPrototypeOf(this, NotYetImplementedError.prototype);
|
|
1098
1214
|
}
|
|
1099
|
-
return {
|
|
1100
|
-
name: name,
|
|
1101
|
-
message,
|
|
1102
|
-
stack,
|
|
1103
|
-
id, // Include id in the serialized object
|
|
1104
|
-
};
|
|
1105
1215
|
}
|
|
1106
1216
|
|
|
1107
1217
|
/**
|
|
1108
|
-
*
|
|
1218
|
+
* This error indicates that the promptbook in a markdown format cannot be parsed into a valid promptbook object
|
|
1109
1219
|
*
|
|
1110
|
-
* @
|
|
1111
|
-
* @param options - Options for the function
|
|
1112
|
-
* @param callbackfunction - Function to call for each item
|
|
1113
|
-
* @public exported from `@promptbook/utils`
|
|
1114
|
-
* @deprecated [🪂] Use queues instead
|
|
1220
|
+
* @public exported from `@promptbook/core`
|
|
1115
1221
|
*/
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
for (const item of array) {
|
|
1122
|
-
const currentIndex = index++;
|
|
1123
|
-
const task = callbackfunction(item, currentIndex, array);
|
|
1124
|
-
tasks.push(task);
|
|
1125
|
-
runningTasks.push(task);
|
|
1126
|
-
/* not await */ Promise.resolve(task).then(() => {
|
|
1127
|
-
runningTasks = runningTasks.filter((t) => t !== task);
|
|
1128
|
-
});
|
|
1129
|
-
if (maxParallelCount < runningTasks.length) {
|
|
1130
|
-
await Promise.race(runningTasks);
|
|
1131
|
-
}
|
|
1222
|
+
class ParseError extends Error {
|
|
1223
|
+
constructor(message) {
|
|
1224
|
+
super(message);
|
|
1225
|
+
this.name = 'ParseError';
|
|
1226
|
+
Object.setPrototypeOf(this, ParseError.prototype);
|
|
1132
1227
|
}
|
|
1133
|
-
await Promise.all(tasks);
|
|
1134
1228
|
}
|
|
1135
|
-
|
|
1136
1229
|
/**
|
|
1137
|
-
*
|
|
1138
|
-
*
|
|
1139
|
-
* @param whatWasThrown Any object that was thrown
|
|
1140
|
-
* @returns Nothing if the error is an instance of `Error`
|
|
1141
|
-
* @throws `WrappedError` or `UnexpectedError` if the error is not standard
|
|
1142
|
-
*
|
|
1143
|
-
* @private within the repository
|
|
1230
|
+
* TODO: Maybe split `ParseError` and `ApplyError`
|
|
1144
1231
|
*/
|
|
1145
|
-
function assertsError(whatWasThrown) {
|
|
1146
|
-
// Case 1: Handle error which was rethrown as `WrappedError`
|
|
1147
|
-
if (whatWasThrown instanceof WrappedError) {
|
|
1148
|
-
const wrappedError = whatWasThrown;
|
|
1149
|
-
throw wrappedError;
|
|
1150
|
-
}
|
|
1151
|
-
// Case 2: Handle unexpected errors
|
|
1152
|
-
if (whatWasThrown instanceof UnexpectedError) {
|
|
1153
|
-
const unexpectedError = whatWasThrown;
|
|
1154
|
-
throw unexpectedError;
|
|
1155
|
-
}
|
|
1156
|
-
// Case 3: Handle standard errors - keep them up to consumer
|
|
1157
|
-
if (whatWasThrown instanceof Error) {
|
|
1158
|
-
return;
|
|
1159
|
-
}
|
|
1160
|
-
// Case 4: Handle non-standard errors - wrap them into `WrappedError` and throw
|
|
1161
|
-
throw new WrappedError(whatWasThrown);
|
|
1162
|
-
}
|
|
1163
1232
|
|
|
1164
1233
|
/**
|
|
1165
|
-
*
|
|
1234
|
+
* Generates random token
|
|
1166
1235
|
*
|
|
1167
|
-
*
|
|
1168
|
-
* @returns True if the string is a valid CSV string, false otherwise
|
|
1236
|
+
* Note: This function is cryptographically secure (it uses crypto.randomBytes internally)
|
|
1169
1237
|
*
|
|
1170
|
-
* @
|
|
1238
|
+
* @private internal helper function
|
|
1239
|
+
* @returns secure random token
|
|
1171
1240
|
*/
|
|
1172
|
-
function
|
|
1173
|
-
|
|
1174
|
-
// A simple check for CSV format: at least one comma and no invalid characters
|
|
1175
|
-
if (value.includes(',') && /^[\w\s,"']+$/.test(value)) {
|
|
1176
|
-
return true;
|
|
1177
|
-
}
|
|
1178
|
-
return false;
|
|
1179
|
-
}
|
|
1180
|
-
catch (error) {
|
|
1181
|
-
assertsError(error);
|
|
1182
|
-
return false;
|
|
1183
|
-
}
|
|
1241
|
+
function $randomToken(randomness) {
|
|
1242
|
+
return randomBytes(randomness).toString('hex');
|
|
1184
1243
|
}
|
|
1244
|
+
/**
|
|
1245
|
+
* TODO: Maybe use nanoid instead https://github.com/ai/nanoid
|
|
1246
|
+
*/
|
|
1185
1247
|
|
|
1186
1248
|
/**
|
|
1187
|
-
*
|
|
1188
|
-
*
|
|
1189
|
-
* @param value The string to check
|
|
1190
|
-
* @returns True if the string is a valid JSON string, false otherwise
|
|
1249
|
+
* This error indicates errors during the execution of the pipeline
|
|
1191
1250
|
*
|
|
1192
|
-
* @public exported from `@promptbook/
|
|
1251
|
+
* @public exported from `@promptbook/core`
|
|
1193
1252
|
*/
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
return false;
|
|
1203
|
-
}
|
|
1204
|
-
return false;
|
|
1253
|
+
class PipelineExecutionError extends Error {
|
|
1254
|
+
constructor(message) {
|
|
1255
|
+
// Added id parameter
|
|
1256
|
+
super(message);
|
|
1257
|
+
this.name = 'PipelineExecutionError';
|
|
1258
|
+
// TODO: [🐙] DRY - Maybe $randomId
|
|
1259
|
+
this.id = `error-${$randomToken(8 /* <- TODO: To global config + Use Base58 to avoid simmilar char conflicts */)}`;
|
|
1260
|
+
Object.setPrototypeOf(this, PipelineExecutionError.prototype);
|
|
1205
1261
|
}
|
|
1206
1262
|
}
|
|
1263
|
+
/**
|
|
1264
|
+
* TODO: [🧠][🌂] Add id to all errors
|
|
1265
|
+
*/
|
|
1207
1266
|
|
|
1208
1267
|
/**
|
|
1209
|
-
*
|
|
1210
|
-
*
|
|
1211
|
-
* Note: This is wrapper around `JSON.parse()` with better error and type handling
|
|
1268
|
+
* This error indicates that the promptbook object has valid syntax (=can be parsed) but contains logical errors (like circular dependencies)
|
|
1212
1269
|
*
|
|
1213
|
-
* @public exported from `@promptbook/
|
|
1270
|
+
* @public exported from `@promptbook/core`
|
|
1214
1271
|
*/
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
console.error('Can not parse JSON from non-string value.', { text: value });
|
|
1221
|
-
throw new Error(spaceTrim$1(`
|
|
1222
|
-
Can not parse JSON from non-string value.
|
|
1223
|
-
|
|
1224
|
-
The value type: ${typeof value}
|
|
1225
|
-
See more in console.
|
|
1226
|
-
`));
|
|
1227
|
-
}
|
|
1228
|
-
try {
|
|
1229
|
-
return JSON.parse(value);
|
|
1230
|
-
}
|
|
1231
|
-
catch (error) {
|
|
1232
|
-
if (!(error instanceof Error)) {
|
|
1233
|
-
throw error;
|
|
1234
|
-
}
|
|
1235
|
-
throw new Error(spaceTrim$1((block) => `
|
|
1236
|
-
${block(error.message)}
|
|
1237
|
-
|
|
1238
|
-
The JSON text:
|
|
1239
|
-
${block(value)}
|
|
1240
|
-
`));
|
|
1272
|
+
class PipelineLogicError extends Error {
|
|
1273
|
+
constructor(message) {
|
|
1274
|
+
super(message);
|
|
1275
|
+
this.name = 'PipelineLogicError';
|
|
1276
|
+
Object.setPrototypeOf(this, PipelineLogicError.prototype);
|
|
1241
1277
|
}
|
|
1242
1278
|
}
|
|
1243
|
-
/**
|
|
1244
|
-
* TODO: !!!! Use in Promptbook.studio
|
|
1245
|
-
*/
|
|
1246
1279
|
|
|
1247
1280
|
/**
|
|
1248
|
-
*
|
|
1249
|
-
*
|
|
1250
|
-
* @param value
|
|
1251
|
-
* @returns True if the string is a valid XML string, false otherwise
|
|
1281
|
+
* This error indicates errors in referencing promptbooks between each other
|
|
1252
1282
|
*
|
|
1253
|
-
* @public exported from `@promptbook/
|
|
1283
|
+
* @public exported from `@promptbook/core`
|
|
1254
1284
|
*/
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
if (parserError.length > 0) {
|
|
1261
|
-
return false;
|
|
1262
|
-
}
|
|
1263
|
-
return true;
|
|
1264
|
-
}
|
|
1265
|
-
catch (error) {
|
|
1266
|
-
assertsError(error);
|
|
1267
|
-
return false;
|
|
1285
|
+
class PipelineUrlError extends Error {
|
|
1286
|
+
constructor(message) {
|
|
1287
|
+
super(message);
|
|
1288
|
+
this.name = 'PipelineUrlError';
|
|
1289
|
+
Object.setPrototypeOf(this, PipelineUrlError.prototype);
|
|
1268
1290
|
}
|
|
1269
1291
|
}
|
|
1270
1292
|
|
|
1271
1293
|
/**
|
|
1272
|
-
*
|
|
1294
|
+
* Error thrown when a fetch request fails
|
|
1273
1295
|
*
|
|
1274
|
-
* @
|
|
1275
|
-
* @public exported from `@promptbook/utils`
|
|
1296
|
+
* @public exported from `@promptbook/core`
|
|
1276
1297
|
*/
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
}
|
|
1283
|
-
return orderedValue;
|
|
1298
|
+
class PromptbookFetchError extends Error {
|
|
1299
|
+
constructor(message) {
|
|
1300
|
+
super(message);
|
|
1301
|
+
this.name = 'PromptbookFetchError';
|
|
1302
|
+
Object.setPrototypeOf(this, PromptbookFetchError.prototype);
|
|
1303
|
+
}
|
|
1284
1304
|
}
|
|
1285
1305
|
|
|
1286
1306
|
/**
|
|
1287
|
-
*
|
|
1307
|
+
* Index of all custom errors
|
|
1288
1308
|
*
|
|
1289
|
-
*
|
|
1290
|
-
|
|
1309
|
+
* @public exported from `@promptbook/core`
|
|
1310
|
+
*/
|
|
1311
|
+
const PROMPTBOOK_ERRORS = {
|
|
1312
|
+
AbstractFormatError,
|
|
1313
|
+
CsvFormatError,
|
|
1314
|
+
CollectionError,
|
|
1315
|
+
EnvironmentMismatchError,
|
|
1316
|
+
ExpectError,
|
|
1317
|
+
KnowledgeScrapeError,
|
|
1318
|
+
LimitReachedError,
|
|
1319
|
+
MissingToolsError,
|
|
1320
|
+
NotFoundError,
|
|
1321
|
+
NotYetImplementedError,
|
|
1322
|
+
ParseError,
|
|
1323
|
+
PipelineExecutionError,
|
|
1324
|
+
PipelineLogicError,
|
|
1325
|
+
PipelineUrlError,
|
|
1326
|
+
AuthenticationError,
|
|
1327
|
+
PromptbookFetchError,
|
|
1328
|
+
UnexpectedError,
|
|
1329
|
+
WrappedError,
|
|
1330
|
+
// TODO: [🪑]> VersionMismatchError,
|
|
1331
|
+
};
|
|
1332
|
+
/**
|
|
1333
|
+
* Index of all javascript errors
|
|
1291
1334
|
*
|
|
1292
|
-
* @
|
|
1293
|
-
* @public exported from `@promptbook/utils`
|
|
1335
|
+
* @private for internal usage
|
|
1294
1336
|
*/
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1337
|
+
const COMMON_JAVASCRIPT_ERRORS = {
|
|
1338
|
+
Error,
|
|
1339
|
+
EvalError,
|
|
1340
|
+
RangeError,
|
|
1341
|
+
ReferenceError,
|
|
1342
|
+
SyntaxError,
|
|
1343
|
+
TypeError,
|
|
1344
|
+
URIError,
|
|
1345
|
+
AggregateError,
|
|
1346
|
+
/*
|
|
1347
|
+
Note: Not widely supported
|
|
1348
|
+
> InternalError,
|
|
1349
|
+
> ModuleError,
|
|
1350
|
+
> HeapError,
|
|
1351
|
+
> WebAssemblyCompileError,
|
|
1352
|
+
> WebAssemblyRuntimeError,
|
|
1353
|
+
*/
|
|
1354
|
+
};
|
|
1309
1355
|
/**
|
|
1310
|
-
*
|
|
1356
|
+
* Index of all errors
|
|
1357
|
+
*
|
|
1358
|
+
* @private for internal usage
|
|
1359
|
+
*/
|
|
1360
|
+
const ALL_ERRORS = {
|
|
1361
|
+
...PROMPTBOOK_ERRORS,
|
|
1362
|
+
...COMMON_JAVASCRIPT_ERRORS,
|
|
1363
|
+
};
|
|
1364
|
+
/**
|
|
1365
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
1311
1366
|
*/
|
|
1312
1367
|
|
|
1313
1368
|
/**
|
|
1314
|
-
*
|
|
1315
|
-
* If not, throws an UnexpectedError with a rich error message and tracking
|
|
1316
|
-
*
|
|
1317
|
-
* - Almost all primitives are serializable BUT:
|
|
1318
|
-
* - `undefined` is not serializable
|
|
1319
|
-
* - `NaN` is not serializable
|
|
1320
|
-
* - Objects and arrays are serializable if all their properties are serializable
|
|
1321
|
-
* - Functions are not serializable
|
|
1322
|
-
* - Circular references are not serializable
|
|
1323
|
-
* - `Date` objects are not serializable
|
|
1324
|
-
* - `Map` and `Set` objects are not serializable
|
|
1325
|
-
* - `RegExp` objects are not serializable
|
|
1326
|
-
* - `Error` objects are not serializable
|
|
1327
|
-
* - `Symbol` objects are not serializable
|
|
1328
|
-
* - And much more...
|
|
1369
|
+
* Deserializes the error object
|
|
1329
1370
|
*
|
|
1330
|
-
* @throws UnexpectedError if the value is not serializable as JSON
|
|
1331
1371
|
* @public exported from `@promptbook/utils`
|
|
1332
1372
|
*/
|
|
1333
|
-
function
|
|
1334
|
-
const {
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
}
|
|
1341
|
-
else if (typeof value === 'boolean') {
|
|
1342
|
-
return;
|
|
1343
|
-
}
|
|
1344
|
-
else if (typeof value === 'number' && !isNaN(value)) {
|
|
1345
|
-
return;
|
|
1346
|
-
}
|
|
1347
|
-
else if (typeof value === 'string') {
|
|
1348
|
-
return;
|
|
1349
|
-
}
|
|
1350
|
-
else if (typeof value === 'symbol') {
|
|
1351
|
-
throw new UnexpectedError(`${name} is symbol`);
|
|
1352
|
-
}
|
|
1353
|
-
else if (typeof value === 'function') {
|
|
1354
|
-
throw new UnexpectedError(`${name} is function`);
|
|
1355
|
-
}
|
|
1356
|
-
else if (typeof value === 'object' && Array.isArray(value)) {
|
|
1357
|
-
for (let i = 0; i < value.length; i++) {
|
|
1358
|
-
checkSerializableAsJson({ name: `${name}[${i}]`, value: value[i], message });
|
|
1359
|
-
}
|
|
1373
|
+
function deserializeError(error) {
|
|
1374
|
+
const { name, stack, id } = error; // Added id
|
|
1375
|
+
let { message } = error;
|
|
1376
|
+
let ErrorClass = ALL_ERRORS[error.name];
|
|
1377
|
+
if (ErrorClass === undefined) {
|
|
1378
|
+
ErrorClass = Error;
|
|
1379
|
+
message = `${name}: ${message}`;
|
|
1360
1380
|
}
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
\`${name}\` is Date
|
|
1365
|
-
|
|
1366
|
-
Use \`string_date_iso8601\` instead
|
|
1381
|
+
if (stack !== undefined && stack !== '') {
|
|
1382
|
+
message = spaceTrim$1((block) => `
|
|
1383
|
+
${block(message)}
|
|
1367
1384
|
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
throw new UnexpectedError(`${name} is Set`);
|
|
1377
|
-
}
|
|
1378
|
-
else if (value instanceof RegExp) {
|
|
1379
|
-
throw new UnexpectedError(`${name} is RegExp`);
|
|
1380
|
-
}
|
|
1381
|
-
else if (value instanceof Error) {
|
|
1382
|
-
throw new UnexpectedError(spaceTrim$1((block) => `
|
|
1383
|
-
\`${name}\` is unserialized Error
|
|
1385
|
+
Original stack trace:
|
|
1386
|
+
${block(stack || '')}
|
|
1387
|
+
`);
|
|
1388
|
+
}
|
|
1389
|
+
const deserializedError = new ErrorClass(message);
|
|
1390
|
+
deserializedError.id = id; // Assign id to the error object
|
|
1391
|
+
return deserializedError;
|
|
1392
|
+
}
|
|
1384
1393
|
|
|
1385
|
-
|
|
1394
|
+
/**
|
|
1395
|
+
* Serializes an error into a [🚉] JSON-serializable object
|
|
1396
|
+
*
|
|
1397
|
+
* @public exported from `@promptbook/utils`
|
|
1398
|
+
*/
|
|
1399
|
+
function serializeError(error) {
|
|
1400
|
+
const { name, message, stack } = error;
|
|
1401
|
+
const { id } = error;
|
|
1402
|
+
if (!Object.keys(ALL_ERRORS).includes(name)) {
|
|
1403
|
+
console.error(spaceTrim$1((block) => `
|
|
1386
1404
|
|
|
1387
|
-
|
|
1388
|
-
${block(message || '(nothing)')}
|
|
1405
|
+
Cannot serialize error with name "${name}"
|
|
1389
1406
|
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
else {
|
|
1393
|
-
for (const [subName, subValue] of Object.entries(value)) {
|
|
1394
|
-
if (subValue === undefined) {
|
|
1395
|
-
// Note: undefined in object is serializable - it is just omited
|
|
1396
|
-
continue;
|
|
1397
|
-
}
|
|
1398
|
-
checkSerializableAsJson({ name: `${name}.${subName}`, value: subValue, message });
|
|
1399
|
-
}
|
|
1400
|
-
try {
|
|
1401
|
-
JSON.stringify(value); // <- TODO: [0]
|
|
1402
|
-
}
|
|
1403
|
-
catch (error) {
|
|
1404
|
-
assertsError(error);
|
|
1405
|
-
throw new UnexpectedError(spaceTrim$1((block) => `
|
|
1406
|
-
\`${name}\` is not serializable
|
|
1407
|
+
Authors of Promptbook probably forgot to add this error into the list of errors:
|
|
1408
|
+
https://github.com/webgptorg/promptbook/blob/main/src/errors/0-index.ts
|
|
1407
1409
|
|
|
1408
|
-
${block(error.stack || error.message)}
|
|
1409
1410
|
|
|
1410
|
-
|
|
1411
|
-
${block(message || '(nothing)')}
|
|
1412
|
-
`));
|
|
1413
|
-
}
|
|
1414
|
-
/*
|
|
1415
|
-
TODO: [0] Is there some more elegant way to check circular references?
|
|
1416
|
-
const seen = new Set();
|
|
1417
|
-
const stack = [{ value }];
|
|
1418
|
-
while (stack.length > 0) {
|
|
1419
|
-
const { value } = stack.pop()!;
|
|
1420
|
-
if (typeof value === 'object' && value !== null) {
|
|
1421
|
-
if (seen.has(value)) {
|
|
1422
|
-
throw new UnexpectedError(`${name} has circular reference`);
|
|
1423
|
-
}
|
|
1424
|
-
seen.add(value);
|
|
1425
|
-
if (Array.isArray(value)) {
|
|
1426
|
-
stack.push(...value.map((value) => ({ value })));
|
|
1427
|
-
} else {
|
|
1428
|
-
stack.push(...Object.values(value).map((value) => ({ value })));
|
|
1429
|
-
}
|
|
1430
|
-
}
|
|
1431
|
-
}
|
|
1432
|
-
*/
|
|
1433
|
-
return;
|
|
1434
|
-
}
|
|
1435
|
-
}
|
|
1436
|
-
else {
|
|
1437
|
-
throw new UnexpectedError(spaceTrim$1((block) => `
|
|
1438
|
-
\`${name}\` is unknown type
|
|
1411
|
+
${block(stack || message)}
|
|
1439
1412
|
|
|
1440
|
-
Additional message for \`${name}\`:
|
|
1441
|
-
${block(message || '(nothing)')}
|
|
1442
1413
|
`));
|
|
1443
1414
|
}
|
|
1415
|
+
return {
|
|
1416
|
+
name: name,
|
|
1417
|
+
message,
|
|
1418
|
+
stack,
|
|
1419
|
+
id, // Include id in the serialized object
|
|
1420
|
+
};
|
|
1444
1421
|
}
|
|
1445
|
-
/**
|
|
1446
|
-
* TODO: Can be return type more type-safe? like `asserts options.value is JsonValue`
|
|
1447
|
-
* TODO: [🧠][main] !!3 In-memory cache of same values to prevent multiple checks
|
|
1448
|
-
* Note: [🐠] This is how `checkSerializableAsJson` + `isSerializableAsJson` together can just retun true/false or rich error message
|
|
1449
|
-
*/
|
|
1450
1422
|
|
|
1451
1423
|
/**
|
|
1452
|
-
*
|
|
1424
|
+
* Async version of Array.forEach
|
|
1453
1425
|
*
|
|
1426
|
+
* @param array - Array to iterate over
|
|
1427
|
+
* @param options - Options for the function
|
|
1428
|
+
* @param callbackfunction - Function to call for each item
|
|
1454
1429
|
* @public exported from `@promptbook/utils`
|
|
1430
|
+
* @deprecated [🪂] Use queues instead
|
|
1455
1431
|
*/
|
|
1456
|
-
function
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1432
|
+
async function forEachAsync(array, options, callbackfunction) {
|
|
1433
|
+
const { maxParallelCount = Infinity } = options;
|
|
1434
|
+
let index = 0;
|
|
1435
|
+
let runningTasks = [];
|
|
1436
|
+
const tasks = [];
|
|
1437
|
+
for (const item of array) {
|
|
1438
|
+
const currentIndex = index++;
|
|
1439
|
+
const task = callbackfunction(item, currentIndex, array);
|
|
1440
|
+
tasks.push(task);
|
|
1441
|
+
runningTasks.push(task);
|
|
1442
|
+
/* not await */ Promise.resolve(task).then(() => {
|
|
1443
|
+
runningTasks = runningTasks.filter((t) => t !== task);
|
|
1444
|
+
});
|
|
1445
|
+
if (maxParallelCount < runningTasks.length) {
|
|
1446
|
+
await Promise.race(runningTasks);
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
await Promise.all(tasks);
|
|
1469
1450
|
}
|
|
1470
|
-
/**
|
|
1471
|
-
* TODO: [🧠] Is there a way how to meaningfully test this utility
|
|
1472
|
-
*/
|
|
1473
1451
|
|
|
1474
1452
|
/**
|
|
1475
|
-
*
|
|
1476
|
-
*
|
|
1477
|
-
* 1) Checks if the value is serializable as JSON
|
|
1478
|
-
* 2) Makes a deep clone of the object
|
|
1479
|
-
* 2) Orders the object properties
|
|
1480
|
-
* 2) Deeply freezes the cloned object
|
|
1453
|
+
* Function to check if a string is valid CSV
|
|
1481
1454
|
*
|
|
1482
|
-
*
|
|
1455
|
+
* @param value The string to check
|
|
1456
|
+
* @returns True if the string is a valid CSV string, false otherwise
|
|
1483
1457
|
*
|
|
1484
|
-
* @returns The same type of object as the input but read-only and re-ordered
|
|
1485
1458
|
* @public exported from `@promptbook/utils`
|
|
1486
1459
|
*/
|
|
1487
|
-
function
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
order: order,
|
|
1500
|
-
});
|
|
1501
|
-
$deepFreeze(orderedValue);
|
|
1502
|
-
return orderedValue;
|
|
1460
|
+
function isValidCsvString(value) {
|
|
1461
|
+
try {
|
|
1462
|
+
// A simple check for CSV format: at least one comma and no invalid characters
|
|
1463
|
+
if (value.includes(',') && /^[\w\s,"']+$/.test(value)) {
|
|
1464
|
+
return true;
|
|
1465
|
+
}
|
|
1466
|
+
return false;
|
|
1467
|
+
}
|
|
1468
|
+
catch (error) {
|
|
1469
|
+
assertsError(error);
|
|
1470
|
+
return false;
|
|
1471
|
+
}
|
|
1503
1472
|
}
|
|
1504
|
-
/**
|
|
1505
|
-
* TODO: [🧠] Is there a way how to meaningfully test this utility
|
|
1506
|
-
*/
|
|
1507
1473
|
|
|
1508
1474
|
/**
|
|
1509
|
-
*
|
|
1475
|
+
* Function isValidJsonString will tell you if the string is valid JSON or not
|
|
1510
1476
|
*
|
|
1511
|
-
* @
|
|
1512
|
-
|
|
1513
|
-
const REPLACING_NONCE = 'ptbkauk42kV2dzao34faw7FudQUHYPtW';
|
|
1514
|
-
/**
|
|
1515
|
-
* @@@
|
|
1477
|
+
* @param value The string to check
|
|
1478
|
+
* @returns True if the string is a valid JSON string, false otherwise
|
|
1516
1479
|
*
|
|
1517
|
-
* @
|
|
1480
|
+
* @public exported from `@promptbook/utils`
|
|
1518
1481
|
*/
|
|
1519
|
-
|
|
1482
|
+
function isValidJsonString(value /* <- [👨⚖️] */) {
|
|
1483
|
+
try {
|
|
1484
|
+
JSON.parse(value);
|
|
1485
|
+
return true;
|
|
1486
|
+
}
|
|
1487
|
+
catch (error) {
|
|
1488
|
+
assertsError(error);
|
|
1489
|
+
if (error.message.includes('Unexpected token')) {
|
|
1490
|
+
return false;
|
|
1491
|
+
}
|
|
1492
|
+
return false;
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1520
1496
|
/**
|
|
1521
|
-
*
|
|
1497
|
+
* Converts a JavaScript Object Notation (JSON) string into an object.
|
|
1522
1498
|
*
|
|
1523
|
-
*
|
|
1499
|
+
* Note: This is wrapper around `JSON.parse()` with better error and type handling
|
|
1500
|
+
*
|
|
1501
|
+
* @public exported from `@promptbook/utils`
|
|
1524
1502
|
*/
|
|
1525
|
-
|
|
1503
|
+
function jsonParse(value) {
|
|
1504
|
+
if (value === undefined) {
|
|
1505
|
+
throw new Error(`Can not parse JSON from undefined value.`);
|
|
1506
|
+
}
|
|
1507
|
+
else if (typeof value !== 'string') {
|
|
1508
|
+
console.error('Can not parse JSON from non-string value.', { text: value });
|
|
1509
|
+
throw new Error(spaceTrim$1(`
|
|
1510
|
+
Can not parse JSON from non-string value.
|
|
1511
|
+
|
|
1512
|
+
The value type: ${typeof value}
|
|
1513
|
+
See more in console.
|
|
1514
|
+
`));
|
|
1515
|
+
}
|
|
1516
|
+
try {
|
|
1517
|
+
return JSON.parse(value);
|
|
1518
|
+
}
|
|
1519
|
+
catch (error) {
|
|
1520
|
+
if (!(error instanceof Error)) {
|
|
1521
|
+
throw error;
|
|
1522
|
+
}
|
|
1523
|
+
throw new Error(spaceTrim$1((block) => `
|
|
1524
|
+
${block(error.message)}
|
|
1525
|
+
|
|
1526
|
+
The JSON text:
|
|
1527
|
+
${block(value)}
|
|
1528
|
+
`));
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1526
1531
|
/**
|
|
1527
|
-
*
|
|
1528
|
-
*
|
|
1529
|
-
* @public exported from `@promptbook/core`
|
|
1532
|
+
* TODO: !!!! Use in Promptbook.studio
|
|
1530
1533
|
*/
|
|
1531
|
-
|
|
1532
|
-
name: 'RESERVED_PARAMETER_NAMES',
|
|
1533
|
-
message: `The names of the parameters that are reserved for special purposes`,
|
|
1534
|
-
value: [
|
|
1535
|
-
'content',
|
|
1536
|
-
'context',
|
|
1537
|
-
'knowledge',
|
|
1538
|
-
'examples',
|
|
1539
|
-
'modelName',
|
|
1540
|
-
'currentDate',
|
|
1541
|
-
// <- TODO: list here all command names
|
|
1542
|
-
// <- TODO: Add more like 'date', 'modelName',...
|
|
1543
|
-
// <- TODO: Add [emoji] + instructions ACRY when adding new reserved parameter
|
|
1544
|
-
],
|
|
1545
|
-
});
|
|
1534
|
+
|
|
1546
1535
|
/**
|
|
1547
|
-
*
|
|
1536
|
+
* Function to check if a string is valid XML
|
|
1537
|
+
*
|
|
1538
|
+
* @param value
|
|
1539
|
+
* @returns True if the string is a valid XML string, false otherwise
|
|
1540
|
+
*
|
|
1541
|
+
* @public exported from `@promptbook/utils`
|
|
1548
1542
|
*/
|
|
1543
|
+
function isValidXmlString(value) {
|
|
1544
|
+
try {
|
|
1545
|
+
const parser = new DOMParser();
|
|
1546
|
+
const parsedDocument = parser.parseFromString(value, 'application/xml');
|
|
1547
|
+
const parserError = parsedDocument.getElementsByTagName('parsererror');
|
|
1548
|
+
if (parserError.length > 0) {
|
|
1549
|
+
return false;
|
|
1550
|
+
}
|
|
1551
|
+
return true;
|
|
1552
|
+
}
|
|
1553
|
+
catch (error) {
|
|
1554
|
+
assertsError(error);
|
|
1555
|
+
return false;
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1549
1558
|
|
|
1550
1559
|
/**
|
|
1551
1560
|
* Format either small or big number
|