@promptbook/browser
Version:
Promptbook: Run AI apps in plain human language across multiple models and platforms
1,039 lines (973 loc) โข 35.1 kB
JavaScript
import spaceTrim$1, { spaceTrim } from 'spacetrim';
import { randomBytes } from 'crypto';
import { isRunningInBrowser } from 'openai/core';
// โ ๏ธ WARNING: This code has been generated so that any manual changes will be overwritten
/**
* The version of the Book language
*
* @generated
* @see https://github.com/webgptorg/book
*/
const BOOK_LANGUAGE_VERSION = '1.0.0';
/**
* The version of the Promptbook engine
*
* @generated
* @see https://github.com/webgptorg/promptbook
*/
const PROMPTBOOK_ENGINE_VERSION = '0.100.0-33';
/**
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
* Note: [๐] Ignore a discrepancy between file name and entity name
*/
/**
* Generates random token
*
* Note: This function is cryptographically secure (it uses crypto.randomBytes internally)
*
* @private internal helper function
* @returns secure random token
*/
function $randomToken(randomness) {
return randomBytes(randomness).toString('hex');
}
/**
* TODO: Maybe use nanoid instead https://github.com/ai/nanoid
*/
/**
* This error indicates errors during the execution of the pipeline
*
* @public exported from `@promptbook/core`
*/
class PipelineExecutionError extends Error {
constructor(message) {
// Added id parameter
super(message);
this.name = 'PipelineExecutionError';
// TODO: [๐] DRY - Maybe $randomId
this.id = `error-${$randomToken(8 /* <- TODO: To global config + Use Base58 to avoid similar char conflicts */)}`;
Object.setPrototypeOf(this, PipelineExecutionError.prototype);
}
}
/**
* TODO: [๐ง ][๐] Add id to all errors
*/
/**
* Wrapper around `window.prompt` synchronous function that interacts with the user via browser prompt
*
* Warning: It is used for testing and mocking
* **NOT intended to use in the production** due to its synchronous nature.
*
* @public exported from `@promptbook/browser`
*/
class SimplePromptInterfaceTools {
constructor(options = {}) {
this.options = options;
}
/**
* Trigger window.prompt dialog
*/
async promptDialog(options) {
const answer = window.prompt(spaceTrim((block) => `
${block(options.promptTitle)}
${block(options.promptMessage)}
`));
if (this.options.isVerbose) {
console.info(spaceTrim((block) => `
๐ ${block(options.promptTitle)}
๐ค ${block(answer || '๐ซ User cancelled prompt')}
`));
}
if (answer === null) {
throw new PipelineExecutionError('User cancelled prompt');
}
return answer;
}
}
/**
* Note: [๐ต] Code in this file should never be published outside of `@promptbook/browser`
*/
/**
* This error type indicates that you try to use a feature that is not available in the current environment
*
* @public exported from `@promptbook/core`
*/
class EnvironmentMismatchError extends Error {
constructor(message) {
super(message);
this.name = 'EnvironmentMismatchError';
Object.setPrototypeOf(this, EnvironmentMismatchError.prototype);
}
}
/**
* Detects if the code is running in a browser environment in main thread (Not in a web worker)
*
* Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
*
* @public exported from `@promptbook/utils`
*/
const $isRunningInBrowser = new Function(`
try {
return this === window;
} catch (e) {
return false;
}
`);
/**
* TODO: [๐บ]
*/
/**
* Detects if the code is running in a web worker
*
* Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
*
* @public exported from `@promptbook/utils`
*/
const $isRunningInWebWorker = new Function(`
try {
if (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) {
return true;
} else {
return false;
}
} catch (e) {
return false;
}
`);
/**
* TODO: [๐บ]
*/
/**
* Name for the Promptbook
*
* TODO: [๐ฝ] Unite branding and make single place for it
*
* @public exported from `@promptbook/core`
*/
const NAME = `Promptbook`;
/**
* Email of the responsible person
*
* @public exported from `@promptbook/core`
*/
const ADMIN_EMAIL = 'pavol@ptbk.io';
/**
* Name of the responsible person for the Promptbook on GitHub
*
* @public exported from `@promptbook/core`
*/
const ADMIN_GITHUB_NAME = 'hejny';
// <- TODO: [๐ง ] Better system for generator warnings - not always "code" and "by `@promptbook/cli`"
/**
* The maximum number of iterations for a loops
*
* @private within the repository - too low-level in comparison with other `MAX_...`
*/
const LOOP_LIMIT = 1000;
// <- TODO: [๐งโโ๏ธ]
/**
* Default settings for parsing and generating CSV files in Promptbook.
*
* @public exported from `@promptbook/core`
*/
Object.freeze({
delimiter: ',',
quoteChar: '"',
newline: '\n',
skipEmptyLines: true,
});
/**
* Note: [๐] Ignore a discrepancy between file name and entity name
* TODO: [๐ง ][๐งโโ๏ธ] Maybe join remoteServerUrl and path into single value
*/
/**
* This error type indicates that some part of the code is not implemented yet
*
* @public exported from `@promptbook/core`
*/
class NotYetImplementedError extends Error {
constructor(message) {
super(spaceTrim((block) => `
${block(message)}
Note: This feature is not implemented yet but it will be soon.
If you want speed up the implementation or just read more, look here:
https://github.com/webgptorg/promptbook
Or contact us on pavol@ptbk.io
`));
this.name = 'NotYetImplementedError';
Object.setPrototypeOf(this, NotYetImplementedError.prototype);
}
}
/**
* Make error report URL for the given error
*
* @private private within the repository
*/
function getErrorReportUrl(error) {
const report = {
title: `๐ Error report from ${NAME}`,
body: spaceTrim$1((block) => `
\`${error.name || 'Error'}\` has occurred in the [${NAME}], please look into it @${ADMIN_GITHUB_NAME}.
\`\`\`
${block(error.message || '(no error message)')}
\`\`\`
## More info:
- **Promptbook engine version:** ${PROMPTBOOK_ENGINE_VERSION}
- **Book language version:** ${BOOK_LANGUAGE_VERSION}
- **Time:** ${new Date().toISOString()}
<details>
<summary>Stack trace:</summary>
## Stack trace:
\`\`\`stacktrace
${block(error.stack || '(empty)')}
\`\`\`
</details>
`),
};
const reportUrl = new URL(`https://github.com/webgptorg/promptbook/issues/new`);
reportUrl.searchParams.set('labels', 'bug');
reportUrl.searchParams.set('assignees', ADMIN_GITHUB_NAME);
reportUrl.searchParams.set('title', report.title);
reportUrl.searchParams.set('body', report.body);
return reportUrl;
}
/**
* This error type indicates that the error should not happen and its last check before crashing with some other error
*
* @public exported from `@promptbook/core`
*/
class UnexpectedError extends Error {
constructor(message) {
super(spaceTrim((block) => `
${block(message)}
Note: This error should not happen.
It's probably a bug in the pipeline collection
Please report issue:
${block(getErrorReportUrl(new Error(message)).href)}
Or contact us on ${ADMIN_EMAIL}
`));
this.name = 'UnexpectedError';
Object.setPrototypeOf(this, UnexpectedError.prototype);
}
}
/**
* Safely retrieves the global scope object (window in browser, global in Node.js)
* regardless of the JavaScript environment in which the code is running
*
* Note: `$` is used to indicate that this function is not a pure function - it access global scope
*
* @private internal function of `$Register`
*/
function $getGlobalScope() {
return Function('return this')();
}
/**
* Normalizes a text string to SCREAMING_CASE (all uppercase with underscores).
*
* @param text The text string to be converted to SCREAMING_CASE format.
* @returns The normalized text in SCREAMING_CASE format.
* @example 'HELLO_WORLD'
* @example 'I_LOVE_PROMPTBOOK'
* @public exported from `@promptbook/utils`
*/
function normalizeTo_SCREAMING_CASE(text) {
let charType;
let lastCharType = 'OTHER';
let normalizedName = '';
for (const char of text) {
let normalizedChar;
if (/^[a-z]$/.test(char)) {
charType = 'LOWERCASE';
normalizedChar = char.toUpperCase();
}
else if (/^[A-Z]$/.test(char)) {
charType = 'UPPERCASE';
normalizedChar = char;
}
else if (/^[0-9]$/.test(char)) {
charType = 'NUMBER';
normalizedChar = char;
}
else {
charType = 'OTHER';
normalizedChar = '_';
}
if (charType !== lastCharType &&
!(lastCharType === 'UPPERCASE' && charType === 'LOWERCASE') &&
!(lastCharType === 'NUMBER') &&
!(charType === 'NUMBER')) {
normalizedName += '_';
}
normalizedName += normalizedChar;
lastCharType = charType;
}
normalizedName = normalizedName.replace(/_+/g, '_');
normalizedName = normalizedName.replace(/_?\/_?/g, '/');
normalizedName = normalizedName.replace(/^_/, '');
normalizedName = normalizedName.replace(/_$/, '');
return normalizedName;
}
/**
* TODO: Tests
* > expect(encodeRoutePath({ uriId: 'VtG7sR9rRJqwNEdM2', name: 'Moje tabule' })).toEqual('/VtG7sR9rRJqwNEdM2/Moje tabule');
* > expect(encodeRoutePath({ uriId: 'VtG7sR9rRJqwNEdM2', name: 'ฤลกฤลลพลพรฝรกรญรบลฏ' })).toEqual('/VtG7sR9rRJqwNEdM2/escrzyaieuu');
* > expect(encodeRoutePath({ uriId: 'VtG7sR9rRJqwNEdM2', name: ' ahoj ' })).toEqual('/VtG7sR9rRJqwNEdM2/ahoj');
* > expect(encodeRoutePath({ uriId: 'VtG7sR9rRJqwNEdM2', name: ' ahoj_ahojAhoj ahoj ' })).toEqual('/VtG7sR9rRJqwNEdM2/ahoj-ahoj-ahoj-ahoj');
* TODO: [๐บ] Use some intermediate util splitWords
*/
/**
* Normalizes a text string to snake_case format.
*
* @param text The text string to be converted to snake_case format.
* @returns The normalized text in snake_case format.
* @example 'hello_world'
* @example 'i_love_promptbook'
* @public exported from `@promptbook/utils`
*/
function normalizeTo_snake_case(text) {
return normalizeTo_SCREAMING_CASE(text).toLowerCase();
}
/**
* Global registry for storing and managing registered entities of a given type.
*
* Note: `$` is used to indicate that this function is not a pure function - it accesses and adds variables in global scope.
*
* @private internal utility, exported are only singleton instances of this class
*/
class $Register {
constructor(registerName) {
this.registerName = registerName;
const storageName = `_promptbook_${normalizeTo_snake_case(registerName)}`;
const globalScope = $getGlobalScope();
if (globalScope[storageName] === undefined) {
globalScope[storageName] = [];
}
else if (!Array.isArray(globalScope[storageName])) {
throw new UnexpectedError(`Expected (global) ${storageName} to be an array, but got ${typeof globalScope[storageName]}`);
}
this.storage = globalScope[storageName];
}
list() {
// <- TODO: ReadonlyDeep<ReadonlyArray<TRegistered>>
return this.storage;
}
register(registered) {
const { packageName, className } = registered;
const existingRegistrationIndex = this.storage.findIndex((item) => item.packageName === packageName && item.className === className);
const existingRegistration = this.storage[existingRegistrationIndex];
if (!existingRegistration) {
this.storage.push(registered);
}
else {
this.storage[existingRegistrationIndex] = registered;
}
return {
registerName: this.registerName,
packageName,
className,
get isDestroyed() {
return false;
},
destroy() {
throw new NotYetImplementedError(`Registration to ${this.registerName} is permanent in this version of Promptbook`);
},
};
}
}
/**
* Registry for all available scrapers in the system.
* Central point for registering and accessing different types of content scrapers.
*
* Note: `$` is used to indicate that this interacts with the global scope
* @singleton Only one instance of each register is created per build, but there can be more than one in different build modules
* @public exported from `@promptbook/core`
*/
const $scrapersRegister = new $Register('scraper_constructors');
/**
* TODO: [ยฎ] DRY Register logic
*/
/**
* Provides a collection of scrapers optimized for browser environments.
* Only includes scrapers that can safely run in a browser context.
*
* Note: Browser scrapers have limitations compared to Node.js scrapers.
*
* 1) `provideScrapersForNode` use as default
* 2) `provideScrapersForBrowser` use in limited browser environment
*
* @public exported from `@promptbook/browser`
*/
async function $provideScrapersForBrowser(tools, options) {
if (!$isRunningInBrowser() || $isRunningInWebWorker()) {
throw new EnvironmentMismatchError('Function `$provideScrapersForBrowser` works only in browser environment');
}
const { isAutoInstalled /* Note: [0] Intentionally not assigning a default value = IS_AUTO_INSTALLED */ } = options || {};
if (isAutoInstalled === true /* <- Note: [0] Ignoring undefined, just checking EXPLICIT requirement for install */) {
throw new EnvironmentMismatchError('Auto-installing is not supported in browser environment');
}
const scrapers = [];
for (const scraperFactory of $scrapersRegister.list()) {
const scraper = await scraperFactory(tools, options || {});
scrapers.push(scraper);
}
return scrapers;
}
/**
* Creates a PromptbookStorage backed by IndexedDB.
* Uses a single object store named 'promptbook'.
* @private for `getIndexedDbStorage`
*/
function makePromptbookStorageFromIndexedDb(options) {
const { databaseName, storeName } = options;
function getDatabase() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(databaseName, 1);
request.onupgradeneeded = () => {
request.result.createObjectStore(storeName);
};
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
return {
async getItem(key) {
const database = await getDatabase();
return new Promise((resolve, reject) => {
const transaction = database.transaction(storeName, 'readonly');
const objectStore = transaction.objectStore(storeName);
const request = objectStore.get(key);
request.onsuccess = () => { var _a; return resolve((_a = request.result) !== null && _a !== void 0 ? _a : null); };
request.onerror = () => reject(request.error);
});
},
async setItem(key, value) {
const database = await getDatabase();
return new Promise((resolve, reject) => {
const transaction = database.transaction(storeName, 'readwrite');
const objectStore = transaction.objectStore(storeName);
const request = objectStore.put(value, key);
request.onsuccess = () => resolve();
request.onerror = () => reject(request.error);
});
},
async removeItem(key) {
const database = await getDatabase();
return new Promise((resolve, reject) => {
const transaction = database.transaction(storeName, 'readwrite');
const objectStore = transaction.objectStore(storeName);
const request = objectStore.delete(key);
request.onsuccess = () => resolve();
request.onerror = () => reject(request.error);
});
},
};
}
/**
* Cache storage
*
* @private internal cache for `getIndexedDbStorage`
*/
const indexedDbStorageCache = new Map();
/**
* Gets wrapper around IndexedDB which can be used as PromptbookStorage
*
* @public exported from `@promptbook/browser`
*/
function getIndexedDbStorage(options) {
if (!isRunningInBrowser()) {
throw new EnvironmentMismatchError(`You can get IndexedDB storage only in browser environment`);
}
const { databaseName, storeName } = options;
const cacheKey = `${databaseName}/${storeName}`;
if (indexedDbStorageCache.has(cacheKey)) {
return indexedDbStorageCache.get(cacheKey);
}
const storage = makePromptbookStorageFromIndexedDb({ databaseName, storeName });
indexedDbStorageCache.set(cacheKey, storage);
return storage;
}
/**
* Note: [๐ต] Code in this file should never be published outside of `@promptbook/browser`
*/
/**
* Converts a JavaScript Object Notation (JSON) string into an object.
*
* Note: This is wrapper around `JSON.parse()` with better error and type handling
*
* @public exported from `@promptbook/utils`
*/
function jsonParse(value) {
if (value === undefined) {
throw new Error(`Can not parse JSON from undefined value.`);
}
else if (typeof value !== 'string') {
console.error('Can not parse JSON from non-string value.', { text: value });
throw new Error(spaceTrim$1(`
Can not parse JSON from non-string value.
The value type: ${typeof value}
See more in console.
`));
}
try {
return JSON.parse(value);
}
catch (error) {
if (!(error instanceof Error)) {
throw error;
}
throw new Error(spaceTrim$1((block) => `
${block(error.message)}
The expected JSON text:
${block(value)}
`));
}
}
/**
* Orders JSON object by keys
*
* @returns The same type of object as the input re-ordered
* @public exported from `@promptbook/utils`
*/
function orderJson(options) {
const { value, order } = options;
const orderedValue = {
...(order === undefined ? {} : Object.fromEntries(order.map((key) => [key, undefined]))),
...value,
};
return orderedValue;
}
/**
* Freezes the given object and all its nested objects recursively
*
* Note: `$` is used to indicate that this function is not a pure function - it mutates given object
* Note: This function mutates the object and returns the original (but mutated-deep-freezed) object
*
* @returns The same object as the input, but deeply frozen
* @public exported from `@promptbook/utils`
*/
function $deepFreeze(objectValue) {
if (Array.isArray(objectValue)) {
return Object.freeze(objectValue.map((item) => $deepFreeze(item)));
}
const propertyNames = Object.getOwnPropertyNames(objectValue);
for (const propertyName of propertyNames) {
const value = objectValue[propertyName];
if (value && typeof value === 'object') {
$deepFreeze(value);
}
}
Object.freeze(objectValue);
return objectValue;
}
/**
* TODO: [๐ง ] Is there a way how to meaningfully test this utility
*/
/**
* This error type indicates that somewhere in the code non-Error object was thrown and it was wrapped into the `WrappedError`
*
* @public exported from `@promptbook/core`
*/
class WrappedError extends Error {
constructor(whatWasThrown) {
const tag = `[๐คฎ]`;
console.error(tag, whatWasThrown);
super(spaceTrim(`
Non-Error object was thrown
Note: Look for ${tag} in the console for more details
Please report issue on ${ADMIN_EMAIL}
`));
this.name = 'WrappedError';
Object.setPrototypeOf(this, WrappedError.prototype);
}
}
/**
* Helper used in catch blocks to assert that the error is an instance of `Error`
*
* @param whatWasThrown Any object that was thrown
* @returns Nothing if the error is an instance of `Error`
* @throws `WrappedError` or `UnexpectedError` if the error is not standard
*
* @private within the repository
*/
function assertsError(whatWasThrown) {
// Case 1: Handle error which was rethrown as `WrappedError`
if (whatWasThrown instanceof WrappedError) {
const wrappedError = whatWasThrown;
throw wrappedError;
}
// Case 2: Handle unexpected errors
if (whatWasThrown instanceof UnexpectedError) {
const unexpectedError = whatWasThrown;
throw unexpectedError;
}
// Case 3: Handle standard errors - keep them up to consumer
if (whatWasThrown instanceof Error) {
return;
}
// Case 4: Handle non-standard errors - wrap them into `WrappedError` and throw
throw new WrappedError(whatWasThrown);
}
/**
* Checks if the value is [๐] serializable as JSON
* If not, throws an UnexpectedError with a rich error message and tracking
*
* - Almost all primitives are serializable BUT:
* - `undefined` is not serializable
* - `NaN` is not serializable
* - Objects and arrays are serializable if all their properties are serializable
* - Functions are not serializable
* - Circular references are not serializable
* - `Date` objects are not serializable
* - `Map` and `Set` objects are not serializable
* - `RegExp` objects are not serializable
* - `Error` objects are not serializable
* - `Symbol` objects are not serializable
* - And much more...
*
* @throws UnexpectedError if the value is not serializable as JSON
* @public exported from `@promptbook/utils`
*/
function checkSerializableAsJson(options) {
const { value, name, message } = options;
if (value === undefined) {
throw new UnexpectedError(`${name} is undefined`);
}
else if (value === null) {
return;
}
else if (typeof value === 'boolean') {
return;
}
else if (typeof value === 'number' && !isNaN(value)) {
return;
}
else if (typeof value === 'string') {
return;
}
else if (typeof value === 'symbol') {
throw new UnexpectedError(`${name} is symbol`);
}
else if (typeof value === 'function') {
throw new UnexpectedError(`${name} is function`);
}
else if (typeof value === 'object' && Array.isArray(value)) {
for (let i = 0; i < value.length; i++) {
checkSerializableAsJson({ name: `${name}[${i}]`, value: value[i], message });
}
}
else if (typeof value === 'object') {
if (value instanceof Date) {
throw new UnexpectedError(spaceTrim$1((block) => `
\`${name}\` is Date
Use \`string_date_iso8601\` instead
Additional message for \`${name}\`:
${block(message || '(nothing)')}
`));
}
else if (value instanceof Map) {
throw new UnexpectedError(`${name} is Map`);
}
else if (value instanceof Set) {
throw new UnexpectedError(`${name} is Set`);
}
else if (value instanceof RegExp) {
throw new UnexpectedError(`${name} is RegExp`);
}
else if (value instanceof Error) {
throw new UnexpectedError(spaceTrim$1((block) => `
\`${name}\` is unserialized Error
Use function \`serializeError\`
Additional message for \`${name}\`:
${block(message || '(nothing)')}
`));
}
else {
for (const [subName, subValue] of Object.entries(value)) {
if (subValue === undefined) {
// Note: undefined in object is serializable - it is just omitted
continue;
}
checkSerializableAsJson({ name: `${name}.${subName}`, value: subValue, message });
}
try {
JSON.stringify(value); // <- TODO: [0]
}
catch (error) {
assertsError(error);
throw new UnexpectedError(spaceTrim$1((block) => `
\`${name}\` is not serializable
${block(error.stack || error.message)}
Additional message for \`${name}\`:
${block(message || '(nothing)')}
`));
}
/*
TODO: [0] Is there some more elegant way to check circular references?
const seen = new Set();
const stack = [{ value }];
while (stack.length > 0) {
const { value } = stack.pop()!;
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) {
throw new UnexpectedError(`${name} has circular reference`);
}
seen.add(value);
if (Array.isArray(value)) {
stack.push(...value.map((value) => ({ value })));
} else {
stack.push(...Object.values(value).map((value) => ({ value })));
}
}
}
*/
return;
}
}
else {
throw new UnexpectedError(spaceTrim$1((block) => `
\`${name}\` is unknown type
Additional message for \`${name}\`:
${block(message || '(nothing)')}
`));
}
}
/**
* TODO: Can be return type more type-safe? like `asserts options.value is JsonValue`
* TODO: [๐ง ][main] !!3 In-memory cache of same values to prevent multiple checks
* Note: [๐ ] This is how `checkSerializableAsJson` + `isSerializableAsJson` together can just retun true/false or rich error message
*/
/**
* Creates a deep clone of the given object
*
* Note: This method only works for objects that are fully serializable to JSON and do not contain functions, Dates, or special types.
*
* @param objectValue The object to clone.
* @returns A deep, writable clone of the input object.
* @public exported from `@promptbook/utils`
*/
function deepClone(objectValue) {
return JSON.parse(JSON.stringify(objectValue));
/*
TODO: [๐ง ] Is there a better implementation?
> const propertyNames = Object.getOwnPropertyNames(objectValue);
> for (const propertyName of propertyNames) {
> const value = (objectValue as really_any)[propertyName];
> if (value && typeof value === 'object') {
> deepClone(value);
> }
> }
> return Object.assign({}, objectValue);
*/
}
/**
* TODO: [๐ง ] Is there a way how to meaningfully test this utility
*/
/**
* Utility to export a JSON object from a function
*
* 1) Checks if the value is serializable as JSON
* 2) Makes a deep clone of the object
* 2) Orders the object properties
* 2) Deeply freezes the cloned object
*
* Note: This function does not mutates the given object
*
* @returns The same type of object as the input but read-only and re-ordered
* @public exported from `@promptbook/utils`
*/
function exportJson(options) {
const { name, value, order, message } = options;
checkSerializableAsJson({ name, value, message });
const orderedValue =
// TODO: Fix error "Type instantiation is excessively deep and possibly infinite."
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
order === undefined
? deepClone(value)
: orderJson({
value: value,
// <- Note: checkSerializableAsJson asserts that the value is serializable as JSON
order: order,
});
$deepFreeze(orderedValue);
return orderedValue;
}
/**
* TODO: [๐ง ] Is there a way how to meaningfully test this utility
*/
/**
* Nonce which is used for replacing things in strings
*
* @private within the repository
*/
const REPLACING_NONCE = 'ptbkauk42kV2dzao34faw7FudQUHYPtW';
/**
* The names of the parameters that are reserved for special purposes
*
* @public exported from `@promptbook/core`
*/
exportJson({
name: 'RESERVED_PARAMETER_NAMES',
message: `The names of the parameters that are reserved for special purposes`,
value: [
'content',
'context',
'knowledge',
'examples',
'modelName',
'currentDate',
// <- TODO: list here all command names
// <- TODO: Add more like 'date', 'modelName',...
// <- TODO: Add [emoji] + instructions ACRY when adding new reserved parameter
],
});
/**
* Note: [๐] Ignore a discrepancy between file name and entity name
*/
// <- TODO: Auto convert to type `import { ... } from 'type-fest';`
/**
* Tests if the value is [๐] serializable as JSON
*
* - Almost all primitives are serializable BUT:
* - `undefined` is not serializable
* - `NaN` is not serializable
* - Objects and arrays are serializable if all their properties are serializable
* - Functions are not serializable
* - Circular references are not serializable
* - `Date` objects are not serializable
* - `Map` and `Set` objects are not serializable
* - `RegExp` objects are not serializable
* - `Error` objects are not serializable
* - `Symbol` objects are not serializable
* - And much more...
*
*
* @public exported from `@promptbook/utils`
*/
function isSerializableAsJson(value) {
try {
checkSerializableAsJson({ value });
return true;
}
catch (error) {
return false;
}
}
/**
* TODO: [๐ง ][main] !!3 In-memory cache of same values to prevent multiple checks
* TODO: [๐ง ][๐บ] Can be done this on type-level?
*/
/**
* Stringify the PipelineJson with proper formatting
*
* Note: [0] It can be used for more JSON types like whole collection of pipelines, single knowledge piece, etc.
* Note: In contrast to JSON.stringify, this function ensures that **embedding index** is on single line
*
* @public exported from `@promptbook/editable`
*/
function stringifyPipelineJson(pipeline) {
if (!isSerializableAsJson(pipeline)) {
throw new UnexpectedError(spaceTrim$1(`
Cannot stringify the pipeline, because it is not serializable as JSON
There can be multiple reasons:
1) The pipeline contains circular references
2) It is not a valid PipelineJson
`));
}
let pipelineJsonStringified = JSON.stringify(pipeline, null, 4);
for (let i = 0; i < LOOP_LIMIT; i++) {
pipelineJsonStringified = pipelineJsonStringified.replace(/(-?0\.\d+),[\n\s]+(-?0\.\d+)/gms, `$1${REPLACING_NONCE}$2`);
}
pipelineJsonStringified = pipelineJsonStringified.split(REPLACING_NONCE).join(', ');
pipelineJsonStringified += '\n';
return pipelineJsonStringified;
}
/**
* TODO: [๐] Not Working properly @see https://promptbook.studio/examples/mixed-knowledge.book
* TODO: [๐ง ][0] Maybe rename to `stringifyPipelineJson`, `stringifyIndexedJson`,...
* TODO: [๐ง ] Maybe more elegant solution than replacing via regex
* TODO: [๐] Make some standard order of json properties
*/
/**
* Creates a Promptbook storage interface from a web storage object.
* Facilitates using Web Storage (localStorage/sessionStorage) as a storage backend.
*
* @private for `getLocalStorage` and `getSessionStorage`
*/
function makePromptbookStorageFromWebStorage(webStorage) {
return {
getItem(key) {
const stringValue = webStorage.getItem(key);
if (stringValue === null) {
return null;
}
const value = jsonParse(stringValue);
// TODO: [๐]
return value;
},
setItem(key, value) {
if (!isSerializableAsJson(value)) {
throw new UnexpectedError(`The "${key}" you want to store in web storage is not serializable as JSON`);
}
const stringValue = stringifyPipelineJson(value);
webStorage.setItem(key, stringValue);
},
removeItem(key) {
webStorage.removeItem(key);
},
};
}
/**
* TODO: [๐ง ] Should this be named `makePromptbookStorageFromWebStorage` vs `createPromptbookStorageFromWebStorage`
* TODO: [๐] Maybe some checkers, not all valid JSONs are desired and valid values
*/
/**
* Cache storage
*
* @private internal cache for `getLocalStorage`
*/
let promptbookLocalStorage = null;
/**
* Gets wrapper around `localStorage` object which can be used as `PromptbookStorage`
*
* @public exported from `@promptbook/browser`
*/
function getLocalStorage() {
if (!isRunningInBrowser()) {
throw new EnvironmentMismatchError(`You can get localStorage works only in browser environment`);
}
if (promptbookLocalStorage) {
return promptbookLocalStorage;
}
promptbookLocalStorage = makePromptbookStorageFromWebStorage(localStorage);
return promptbookLocalStorage;
}
/**
* Note: [๐ต] Code in this file should never be published outside of `@promptbook/browser`
*/
/**
* Cache storage
*
* @private internal cache for `getSessionStorage`
*/
let promptbookSessionStorage = null;
/**
* Gets wrapper around `sessionStorage` object which can be used as `PromptbookStorage`
*
* @public exported from `@promptbook/browser`
*/
function getSessionStorage() {
if (!isRunningInBrowser()) {
throw new EnvironmentMismatchError(`You can get sessionStorage works only in browser environment`);
}
if (promptbookSessionStorage) {
return promptbookSessionStorage;
}
promptbookSessionStorage = makePromptbookStorageFromWebStorage(sessionStorage);
return promptbookSessionStorage;
}
/**
* Note: [๐ต] Code in this file should never be published outside of `@promptbook/browser`
*/
export { $provideScrapersForBrowser, BOOK_LANGUAGE_VERSION, PROMPTBOOK_ENGINE_VERSION, SimplePromptInterfaceTools, getIndexedDbStorage, getLocalStorage, getSessionStorage };
//# sourceMappingURL=index.es.js.map