@fragola-ai/prompt 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -202,6 +202,16 @@ console.log(partialClone.value);
202
202
 
203
203
  For detailed documentation and examples, visit our [GitHub repository](https://github.com/shadokan87/prompt).
204
204
 
205
+ ## Changelog
206
+
207
+ ### Version 1.0.5
208
+
209
+ **Features:**
210
+ - Added `basePath` parameter to `load()` function - You can now specify a custom base path for loading prompt files, making it easier to use `__dirname` or other directory references instead of relying solely on `process.env.PWD`
211
+
212
+ **Bug Fixes:**
213
+ - Fixed `setVariables()` not properly resetting to original prompt template before re-interpolating variables - Previously, calling `setVariables()` multiple times would fail because it attempted to replace already-replaced placeholders
214
+
205
215
  ## License
206
216
 
207
217
  MIT
package/dist/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
- export { default as Prompt } from './Prompt.js';
2
- export { load, MissingVariablesError, NamespaceUndefinedError, LoadFileReadError, type Variables, type Load } from './Prompt.js';
1
+ import * as PromptModule from "./Prompt";
2
+ export * from "./Prompt";
3
+ export default PromptModule;
package/dist/index.js CHANGED
@@ -1,15 +1,187 @@
1
- import { default as default2 } from "./Prompt.js";
2
- import {
3
- load,
4
- MissingVariablesError,
5
- NamespaceUndefinedError,
6
- LoadFileReadError
7
- } from "./Prompt.js";
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
7
+ // Prompt.ts
8
+ var Prompt_exports = {};
9
+ __export(Prompt_exports, {
10
+ LoadFileReadError: () => LoadFileReadError,
11
+ MissingVariablesError: () => MissingVariablesError,
12
+ NamespaceUndefinedError: () => NamespaceUndefinedError,
13
+ default: () => Prompt,
14
+ load: () => load
15
+ });
16
+ import { readFileSync } from "fs";
17
+ import path from "path";
18
+ var MissingVariablesError = class extends Error {
19
+ constructor(variables, prompt) {
20
+ super(`Missing variables: ${variables.map((v) => `'${v}'`).join(", ")}. For prompt: ${prompt}`);
21
+ this.variables = variables;
22
+ this.name = "MissingVariablesError";
23
+ }
24
+ };
25
+ var NamespaceUndefinedError = class extends Error {
26
+ constructor(namespace) {
27
+ super(`Tried to acess undefined namespace when using load() function: '${namespace}' does not exist`);
28
+ this.namespace = namespace;
29
+ this.name = "NamespaceUndefinedError";
30
+ }
31
+ };
32
+ var LoadFileReadError = class extends Error {
33
+ constructor(filePath, originalError) {
34
+ super(`Failed to load prompt from ${filePath}: ${originalError.message}`);
35
+ this.filePath = filePath;
36
+ this.originalError = originalError;
37
+ this.name = "LoadFileReadError";
38
+ }
39
+ };
40
+ var load = (relativePath) => ({ load: true, relativePath });
41
+ var Prompt = class _Prompt {
42
+ static defaultVariableRegex = /\{\{(\s*[\w.]+\s*)\}\}/g;
43
+ /**
44
+ * A map of aliases to file paths for use with the {@link load} function.
45
+ * Allows defining shortcuts for commonly used prompt file paths.
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * Prompt.pathAlias = {
50
+ * browse: "prompts", // Maps "@browse" to "prompts/"
51
+ * refund: "prompts/refund" // Maps "@refund" to "prompts/refund/"
52
+ * }
53
+ *
54
+ * // Usage with load():
55
+ * load("@refund/shopRefund.md") // Resolves to "prompts/refund/shopRefund.md"
56
+ * ```
57
+ * @see {@link load}
58
+ */
59
+ static pathAlias = {};
60
+ #originalPrompt = "";
61
+ #value = "";
62
+ #variableRegex = _Prompt.defaultVariableRegex;
63
+ #variables = {};
64
+ #basePath = process.env["PWD"];
65
+ constructor(prompt, variables = {}) {
66
+ if (!process.env["PWD"])
67
+ console.warn("PWD env variable not set");
68
+ if (typeof prompt == "string") {
69
+ this.#originalPrompt = prompt;
70
+ this.#value = prompt;
71
+ } else {
72
+ this.#originalPrompt = this.#load(prompt.relativePath);
73
+ this.#value = this.#originalPrompt;
74
+ }
75
+ this.setVariables(variables);
76
+ }
77
+ #stringify(value) {
78
+ switch (typeof value) {
79
+ case "object":
80
+ return JSON.stringify(value);
81
+ default:
82
+ return value;
83
+ }
84
+ }
85
+ #isInsideCodeBlock(index, text) {
86
+ const codeBlockRegex = /```[\s\S]*?```/g;
87
+ let match;
88
+ while ((match = codeBlockRegex.exec(text)) !== null) {
89
+ const start = match.index;
90
+ const end = start + match[0].length;
91
+ if (index >= start && index < end) {
92
+ return true;
93
+ }
94
+ }
95
+ return false;
96
+ }
97
+ /**
98
+ * Updates the variables used for string interpolation and re-processes the prompt template.
99
+ *
100
+ * @param variables - Your new variables
101
+ * @throws {MissingVariablesError} When required variables are not provided in the variables object
102
+ */
103
+ setVariables(variables) {
104
+ this.#variables = variables;
105
+ const allMatch = Array.from(this.#originalPrompt.matchAll(this.#variableRegex)).filter((match) => !this.#isInsideCodeBlock(match.index, this.#originalPrompt)).map((match) => {
106
+ return {
107
+ placeholder: match[0],
108
+ index: match.index
109
+ };
110
+ });
111
+ const getVariableNameFromPlaceholder = (placeholder) => {
112
+ return placeholder.replaceAll(/[{}]/g, "").trim();
113
+ };
114
+ let missingVariables = [];
115
+ let applyReplace = [];
116
+ allMatch.forEach((match) => {
117
+ const variableName = getVariableNameFromPlaceholder(match.placeholder);
118
+ if (variableName in variables)
119
+ applyReplace.push(() => this.#value = this.#value.replace(match.placeholder, this.#stringify(this.#variables[variableName])));
120
+ else
121
+ missingVariables.push(variableName);
122
+ });
123
+ if (missingVariables.length)
124
+ throw new MissingVariablesError(missingVariables, this.#originalPrompt);
125
+ applyReplace.forEach((replace) => replace());
126
+ }
127
+ #load(relativePath) {
128
+ let _relativePath = (() => {
129
+ const split = relativePath.split("/");
130
+ if (split.length > 1) {
131
+ const namespace = split[0].trim();
132
+ if (namespace[0] == "@") {
133
+ const name = split[0].slice(1);
134
+ if (name in _Prompt.pathAlias) {
135
+ return path.join(_Prompt.pathAlias[name], split[1]);
136
+ } else
137
+ throw new NamespaceUndefinedError(namespace);
138
+ }
139
+ }
140
+ return relativePath;
141
+ })();
142
+ let fullPath = path.join(this.#basePath || "./", _relativePath);
143
+ if (!fullPath.split("/").at(-1)?.includes("."))
144
+ fullPath = `${fullPath}.md`;
145
+ try {
146
+ const content = readFileSync(fullPath, "utf-8");
147
+ return content;
148
+ } catch (error) {
149
+ if (error instanceof Error) {
150
+ throw new LoadFileReadError(fullPath, error);
151
+ }
152
+ throw error;
153
+ }
154
+ }
155
+ /**
156
+ * Gets the original prompt string used for this instance. Before any processing
157
+ * @returns {string} The original prompt string
158
+ */
159
+ get promptString() {
160
+ return this.#originalPrompt;
161
+ }
162
+ /**
163
+ * Gets the variables currently in use for this prompt.
164
+ * @returns {Map<string, any>} A map containing the prompt variables and their values.
165
+ */
166
+ get variables() {
167
+ return this.#variables;
168
+ }
169
+ /**
170
+ * Gets the interpolated value of the prompt.
171
+ * @returns {string} The resulting string after all variable interpolation has been applied.
172
+ */
173
+ get value() {
174
+ return this.#value;
175
+ }
176
+ };
177
+
178
+ // index.ts
179
+ var index_default = Prompt_exports;
8
180
  export {
9
181
  LoadFileReadError,
10
182
  MissingVariablesError,
11
183
  NamespaceUndefinedError,
12
- default2 as Prompt,
184
+ index_default as default,
13
185
  load
14
186
  };
15
187
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../index.ts"],
4
- "sourcesContent": ["export { default as Prompt } from './Prompt.js';\nexport { \n load, \n MissingVariablesError, \n NamespaceUndefinedError, \n LoadFileReadError,\n type Variables,\n type Load \n} from './Prompt.js';"],
5
- "mappings": "AAAA,SAAoB,WAAXA,gBAAyB;AAClC;AAAA,EACI;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGG;",
6
- "names": ["default"]
3
+ "sources": ["../Prompt.ts", "../index.ts"],
4
+ "sourcesContent": ["import { readFileSync } from \"fs\";\nimport path from \"path\";\n\n/**\n * Represents a record of variables used for string interpolation in prompts.\n * Each key in the record is a variable name to interpolate in the prompt template,\n * and its value can be any type (object, array, number, string, etc.) which will replace the placeholder.\n */\nexport type Variables = Record<string, any>;\n\nexport class MissingVariablesError extends Error {\n constructor(public variables: string[], prompt: string) {\n super(`Missing variables: ${variables.map(v => `'${v}'`).join(', ')}. For prompt: ${prompt}`);\n this.name = 'MissingVariablesError';\n }\n}\n\nexport class NamespaceUndefinedError extends Error {\n constructor(public namespace: string) {\n super(`Tried to acess undefined namespace when using load() function: '${namespace}' does not exist`)\n this.name = \"NamespaceUndefinedError\";\n }\n}\n\nexport class LoadFileReadError extends Error {\n constructor(public filePath: string, public originalError: Error) {\n super(`Failed to load prompt from ${filePath}: ${originalError.message}`);\n this.name = \"LoadFileReadError\";\n }\n}\n\nexport type Load = { load: true, relativePath: string };\n\n/**\n * Creates a Load object to specify a prompt file location.\n * This function is used when the prompt is located in a file system.\n * The process working directory (PWD) is used as the base path by default.\n * Path aliases can be used to reduce path overhead.\n * \n * @param {string} relativePath - The relative path to the prompt file. Can include path aliases.\n * @returns {Load} An object containing the load flag and the relative path to the prompt file\n * \n * @example\n * // Using relative path\n * load('./prompts/myPrompt.txt')\n * \n * @example\n * // Using path alias\n * load('prompts:myPrompt.txt')\n */\nexport const load = (relativePath: string): Load => ({ load: true, relativePath });\n\nexport default class Prompt {\n private static defaultVariableRegex = /\\{\\{(\\s*[\\w.]+\\s*)\\}\\}/g;\n /**\n * A map of aliases to file paths for use with the {@link load} function.\n * Allows defining shortcuts for commonly used prompt file paths.\n * \n * @example\n * ```typescript\n * Prompt.pathAlias = {\n * browse: \"prompts\", // Maps \"@browse\" to \"prompts/\"\n * refund: \"prompts/refund\" // Maps \"@refund\" to \"prompts/refund/\"\n * }\n * \n * // Usage with load():\n * load(\"@refund/shopRefund.md\") // Resolves to \"prompts/refund/shopRefund.md\"\n * ```\n * @see {@link load}\n */\n public static pathAlias: Record<string, string> = {};\n\n #originalPrompt: string = \"\";\n #value: string = \"\";\n #variableRegex: RegExp = Prompt.defaultVariableRegex;\n #variables: Variables = {};\n #basePath = process.env[\"PWD\"];\n\n constructor(prompt: string | Load, variables: Variables = {}) {\n if (!process.env[\"PWD\"])\n console.warn(\"PWD env variable not set\");\n if (typeof prompt == 'string') {\n this.#originalPrompt = prompt;\n this.#value = prompt;\n } else {\n this.#originalPrompt = this.#load(prompt.relativePath);\n this.#value = this.#originalPrompt;\n }\n this.setVariables(variables);\n }\n\n #stringify(value: any) {\n switch (typeof value) {\n case \"object\":\n return JSON.stringify(value);\n default:\n return value;\n }\n }\n\n #isInsideCodeBlock(index: number, text: string): boolean {\n const codeBlockRegex = /```[\\s\\S]*?```/g;\n let match;\n\n while ((match = codeBlockRegex.exec(text)) !== null) {\n const start = match.index;\n const end = start + match[0].length;\n\n if (index >= start && index < end) {\n return true;\n }\n }\n\n return false;\n }\n\n /**\n * Updates the variables used for string interpolation and re-processes the prompt template.\n * \n * @param variables - Your new variables\n * @throws {MissingVariablesError} When required variables are not provided in the variables object\n */\n setVariables(variables: Variables) {\n this.#variables = variables;\n const allMatch = Array.from(this.#originalPrompt.matchAll(this.#variableRegex))\n .filter(match => !this.#isInsideCodeBlock(match.index!, this.#originalPrompt))\n .map(match => {\n return {\n placeholder: match[0],\n index: match.index\n }\n });\n\n const getVariableNameFromPlaceholder = (placeholder: string) => {\n return placeholder.replaceAll(/[{}]/g, \"\").trim();\n }\n\n let missingVariables: string[] = [];\n let applyReplace: (() => {})[] = [];\n\n allMatch.forEach(match => {\n const variableName = getVariableNameFromPlaceholder(match.placeholder);\n if (variableName in variables)\n applyReplace.push(() => this.#value = this.#value.replace(match.placeholder, this.#stringify(this.#variables[variableName])));\n else\n missingVariables.push(variableName);\n });\n\n if (missingVariables.length)\n throw new MissingVariablesError(missingVariables, this.#originalPrompt);\n applyReplace.forEach(replace => replace());\n }\n\n #load(relativePath: string): string {\n let _relativePath = ((): string => {\n const split = relativePath.split(\"/\");\n if (split.length > 1) {\n const namespace = split[0].trim();\n if (namespace[0] == \"@\") {\n const name = split[0].slice(1);\n if (name in Prompt.pathAlias) {\n return path.join(Prompt.pathAlias[name], split[1]);\n } else\n throw new NamespaceUndefinedError(namespace);\n }\n }\n return relativePath;\n })();\n\n let fullPath = path.join(this.#basePath || \"./\", _relativePath);\n if (!fullPath.split(\"/\").at(-1)?.includes(\".\"))\n fullPath = `${fullPath}.md`;\n try {\n const content = readFileSync(fullPath, 'utf-8');\n return content;\n } catch (error) {\n if (error instanceof Error) {\n throw new LoadFileReadError(fullPath, error);\n }\n throw error;\n }\n }\n\n /**\n * Gets the original prompt string used for this instance. Before any processing\n * @returns {string} The original prompt string\n */\n get promptString() {\n return this.#originalPrompt;\n }\n\n /**\n * Gets the variables currently in use for this prompt.\n * @returns {Map<string, any>} A map containing the prompt variables and their values.\n */\n get variables() {\n return this.#variables;\n }\n\n /**\n * Gets the interpolated value of the prompt.\n * @returns {string} The resulting string after all variable interpolation has been applied.\n */\n get value() {\n return this.#value;\n }\n}", "import * as PromptModule from \"./Prompt\";\nexport * from \"./Prompt\";\nexport default PromptModule;\n"],
5
+ "mappings": "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",
6
+ "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fragola-ai/prompt",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "Prompt interpolation and loading library for Node.js/TypeScript.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
@@ -17,15 +17,15 @@
17
17
  ],
18
18
  "type": "module",
19
19
  "scripts": {
20
- "build": "node build.js",
21
- "prepublishOnly": "npm run build"
22
- },
23
- "publishConfig": {
24
- "access": "public"
20
+ "test": "vitest",
21
+ "test:ui": "vitest --ui",
22
+ "test:coverage": "vitest --coverage"
25
23
  },
26
24
  "devDependencies": {
27
- "@types/node": "^20.0.0",
28
- "esbuild": "^0.25.9"
25
+ "@types/bun": "latest",
26
+ "@types/node": "^20.10.0",
27
+ "@vitest/ui": "^4.0.16",
28
+ "vitest": "^1.1.0"
29
29
  },
30
30
  "peerDependencies": {
31
31
  "typescript": "^5.0.0"
package/dist/build.d.ts DELETED
@@ -1 +0,0 @@
1
- export {};