promptopskit 0.3.8 → 0.4.0
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 +97 -10
- package/SKILL.md +69 -6
- package/dist/chunk-F6UJ73Y6.js +113 -0
- package/dist/chunk-F6UJ73Y6.js.map +1 -0
- package/dist/chunk-HLNVKKMS.js +145 -0
- package/dist/chunk-HLNVKKMS.js.map +1 -0
- package/dist/{chunk-5OYWNRNC.js → chunk-JBLUPKY4.js} +191 -5
- package/dist/chunk-JBLUPKY4.js.map +1 -0
- package/dist/{chunk-UJU5XERR.js → chunk-L2V7LDKD.js} +25 -4
- package/dist/chunk-L2V7LDKD.js.map +1 -0
- package/dist/{chunk-E6NPZOJL.js → chunk-RY5DVTLV.js} +29 -4
- package/dist/chunk-RY5DVTLV.js.map +1 -0
- package/dist/chunk-UPTCSWK3.js +60 -0
- package/dist/chunk-UPTCSWK3.js.map +1 -0
- package/dist/{chunk-X73VG645.js → chunk-XNNDGDPI.js} +64 -3
- package/dist/chunk-XNNDGDPI.js.map +1 -0
- package/dist/cli/index.js +423 -7
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +655 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -4
- package/dist/index.d.ts +7 -4
- package/dist/index.js +172 -11
- package/dist/index.js.map +1 -1
- package/dist/providers/anthropic.cjs +306 -10
- package/dist/providers/anthropic.cjs.map +1 -1
- package/dist/providers/anthropic.d.cts +2 -2
- package/dist/providers/anthropic.d.ts +2 -2
- package/dist/providers/anthropic.js +3 -3
- package/dist/providers/gemini.cjs +274 -6
- package/dist/providers/gemini.cjs.map +1 -1
- package/dist/providers/gemini.d.cts +2 -2
- package/dist/providers/gemini.d.ts +2 -2
- package/dist/providers/gemini.js +3 -3
- package/dist/providers/openai-responses.cjs +1245 -0
- package/dist/providers/openai-responses.cjs.map +1 -0
- package/dist/providers/openai-responses.d.cts +11 -0
- package/dist/providers/openai-responses.d.ts +11 -0
- package/dist/providers/openai-responses.js +9 -0
- package/dist/providers/openai-responses.js.map +1 -0
- package/dist/providers/openai.cjs +270 -6
- package/dist/providers/openai.cjs.map +1 -1
- package/dist/providers/openai.d.cts +2 -2
- package/dist/providers/openai.d.ts +2 -2
- package/dist/providers/openai.js +3 -3
- package/dist/providers/openrouter.cjs +305 -9
- package/dist/providers/openrouter.cjs.map +1 -1
- package/dist/providers/openrouter.d.cts +2 -2
- package/dist/providers/openrouter.d.ts +2 -2
- package/dist/providers/openrouter.js +4 -4
- package/dist/schema-B9A1zLRk.d.cts +2403 -0
- package/dist/schema-B9A1zLRk.d.ts +2403 -0
- package/dist/testing.cjs +264 -6
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +59 -2
- package/dist/testing.d.ts +59 -2
- package/dist/testing.js +70 -2
- package/dist/testing.js.map +1 -1
- package/dist/{types-fBvo_d4k.d.ts → types-DjGAwJxC.d.ts} +13 -1
- package/dist/{types-C6RG0Si0.d.cts → types-DpbzzZO2.d.cts} +13 -1
- package/dist/usagetap/index.d.cts +2 -2
- package/dist/usagetap/index.d.ts +2 -2
- package/package.json +12 -2
- package/dist/chunk-5OYWNRNC.js.map +0 -1
- package/dist/chunk-BBVVA27J.js +0 -92
- package/dist/chunk-BBVVA27J.js.map +0 -1
- package/dist/chunk-E2NXJLS6.js +0 -26
- package/dist/chunk-E2NXJLS6.js.map +0 -1
- package/dist/chunk-E6NPZOJL.js.map +0 -1
- package/dist/chunk-UJU5XERR.js.map +0 -1
- package/dist/chunk-X73VG645.js.map +0 -1
- package/dist/schema-DwzYbZba.d.cts +0 -859
- package/dist/schema-DwzYbZba.d.ts +0 -859
package/dist/testing.cjs
CHANGED
|
@@ -30,11 +30,19 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/testing.ts
|
|
31
31
|
var testing_exports = {};
|
|
32
32
|
__export(testing_exports, {
|
|
33
|
+
createHardcodedPromptResponder: () => createHardcodedPromptResponder,
|
|
33
34
|
createMockAsset: () => createMockAsset,
|
|
34
35
|
createMockResolvedAsset: () => createMockResolvedAsset,
|
|
35
|
-
|
|
36
|
+
getHardcodedPromptResponse: () => getHardcodedPromptResponse,
|
|
37
|
+
getPromptTestCase: () => getPromptTestCase,
|
|
38
|
+
loadPromptTestSidecar: () => loadPromptTestSidecar,
|
|
39
|
+
parsePromptTestSidecar: () => parsePromptTestSidecar,
|
|
40
|
+
parseTestPrompt: () => parseTestPrompt,
|
|
41
|
+
renderPromptTestCase: () => renderPromptTestCase
|
|
36
42
|
});
|
|
37
43
|
module.exports = __toCommonJS(testing_exports);
|
|
44
|
+
var import_promises2 = require("fs/promises");
|
|
45
|
+
var import_gray_matter3 = __toESM(require("gray-matter"), 1);
|
|
38
46
|
|
|
39
47
|
// src/parser/parser.ts
|
|
40
48
|
var import_gray_matter = __toESM(require("gray-matter"), 1);
|
|
@@ -68,7 +76,66 @@ var SamplingSchema = import_zod.z.object({
|
|
|
68
76
|
});
|
|
69
77
|
var ResponseSchema = import_zod.z.object({
|
|
70
78
|
format: import_zod.z.enum(["text", "json", "markdown"]).optional(),
|
|
71
|
-
stream: import_zod.z.boolean().optional()
|
|
79
|
+
stream: import_zod.z.boolean().optional(),
|
|
80
|
+
schema: import_zod.z.record(import_zod.z.unknown()).optional(),
|
|
81
|
+
schema_name: import_zod.z.string().optional(),
|
|
82
|
+
schema_description: import_zod.z.string().optional(),
|
|
83
|
+
schema_strict: import_zod.z.boolean().optional()
|
|
84
|
+
});
|
|
85
|
+
var AnthropicProviderOptionsSchema = import_zod.z.object({
|
|
86
|
+
top_k: import_zod.z.number().int().min(0).optional(),
|
|
87
|
+
tool_choice: import_zod.z.record(import_zod.z.unknown()).optional(),
|
|
88
|
+
output_config: import_zod.z.record(import_zod.z.unknown()).optional()
|
|
89
|
+
});
|
|
90
|
+
var GeminiProviderOptionsSchema = import_zod.z.object({
|
|
91
|
+
candidate_count: import_zod.z.number().int().positive().optional(),
|
|
92
|
+
top_k: import_zod.z.number().int().min(0).optional(),
|
|
93
|
+
seed: import_zod.z.number().int().optional(),
|
|
94
|
+
response_schema: import_zod.z.record(import_zod.z.unknown()).optional(),
|
|
95
|
+
response_json_schema: import_zod.z.record(import_zod.z.unknown()).optional(),
|
|
96
|
+
response_modalities: import_zod.z.array(import_zod.z.string()).optional(),
|
|
97
|
+
thinking_budget_tokens: import_zod.z.number().int().positive().optional()
|
|
98
|
+
});
|
|
99
|
+
var OpenRouterProviderOptionsSchema = import_zod.z.object({
|
|
100
|
+
provider: import_zod.z.record(import_zod.z.unknown()).optional(),
|
|
101
|
+
transforms: import_zod.z.array(import_zod.z.string()).optional(),
|
|
102
|
+
plugins: import_zod.z.array(import_zod.z.record(import_zod.z.unknown())).optional(),
|
|
103
|
+
models: import_zod.z.array(import_zod.z.string()).optional()
|
|
104
|
+
});
|
|
105
|
+
var ProviderOptionsSchema = import_zod.z.object({
|
|
106
|
+
anthropic: AnthropicProviderOptionsSchema.optional(),
|
|
107
|
+
gemini: GeminiProviderOptionsSchema.optional(),
|
|
108
|
+
openrouter: OpenRouterProviderOptionsSchema.optional()
|
|
109
|
+
});
|
|
110
|
+
var RawProviderBodySchema = import_zod.z.object({
|
|
111
|
+
openai: import_zod.z.record(import_zod.z.unknown()).optional(),
|
|
112
|
+
"openai-responses": import_zod.z.record(import_zod.z.unknown()).optional(),
|
|
113
|
+
openai_responses: import_zod.z.record(import_zod.z.unknown()).optional(),
|
|
114
|
+
anthropic: import_zod.z.record(import_zod.z.unknown()).optional(),
|
|
115
|
+
gemini: import_zod.z.record(import_zod.z.unknown()).optional(),
|
|
116
|
+
google: import_zod.z.record(import_zod.z.unknown()).optional(),
|
|
117
|
+
openrouter: import_zod.z.record(import_zod.z.unknown()).optional()
|
|
118
|
+
});
|
|
119
|
+
var OpenAICacheSchema = import_zod.z.object({
|
|
120
|
+
prompt_cache_key: import_zod.z.string().min(1).optional(),
|
|
121
|
+
retention: import_zod.z.enum(["in_memory", "24h"]).optional()
|
|
122
|
+
});
|
|
123
|
+
var AnthropicCacheSchema = import_zod.z.object({
|
|
124
|
+
mode: import_zod.z.enum(["automatic", "explicit"]).optional(),
|
|
125
|
+
type: import_zod.z.literal("ephemeral").optional(),
|
|
126
|
+
ttl: import_zod.z.enum(["5m", "1h"]).optional(),
|
|
127
|
+
cache_system_instructions: import_zod.z.boolean().optional(),
|
|
128
|
+
cache_tools: import_zod.z.boolean().optional(),
|
|
129
|
+
cache_prompt_template: import_zod.z.boolean().optional()
|
|
130
|
+
});
|
|
131
|
+
var GeminiCacheSchema = import_zod.z.object({
|
|
132
|
+
cached_content: import_zod.z.string().min(1).optional()
|
|
133
|
+
});
|
|
134
|
+
var CacheSchema = import_zod.z.object({
|
|
135
|
+
openai: OpenAICacheSchema.optional(),
|
|
136
|
+
anthropic: AnthropicCacheSchema.optional(),
|
|
137
|
+
gemini: GeminiCacheSchema.optional(),
|
|
138
|
+
google: GeminiCacheSchema.optional()
|
|
72
139
|
});
|
|
73
140
|
var HistorySchema = import_zod.z.object({
|
|
74
141
|
max_items: import_zod.z.number().int().positive().optional()
|
|
@@ -119,7 +186,10 @@ var PromptAssetOverridesSchema = import_zod.z.object({
|
|
|
119
186
|
reasoning: ReasoningSchema.optional(),
|
|
120
187
|
sampling: SamplingSchema.optional(),
|
|
121
188
|
response: ResponseSchema.optional(),
|
|
122
|
-
|
|
189
|
+
cache: CacheSchema.optional(),
|
|
190
|
+
raw: RawProviderBodySchema.optional(),
|
|
191
|
+
tools: import_zod.z.array(ToolRefSchema).optional(),
|
|
192
|
+
provider_options: ProviderOptionsSchema.optional()
|
|
123
193
|
});
|
|
124
194
|
var SourceSchema = import_zod.z.object({
|
|
125
195
|
file_path: import_zod.z.string().optional(),
|
|
@@ -131,8 +201,9 @@ var SectionsSchema = import_zod.z.object({
|
|
|
131
201
|
notes: import_zod.z.string().optional()
|
|
132
202
|
});
|
|
133
203
|
var PromptDefaultsSchema = import_zod.z.object({
|
|
134
|
-
provider: import_zod.z.enum(["openai", "anthropic", "google", "gemini", "openrouter", "any"]).optional(),
|
|
204
|
+
provider: import_zod.z.enum(["openai", "openai-responses", "anthropic", "google", "gemini", "openrouter", "any"]).optional(),
|
|
135
205
|
model: import_zod.z.string().optional(),
|
|
206
|
+
cache: CacheSchema.optional(),
|
|
136
207
|
metadata: MetadataSchema.optional(),
|
|
137
208
|
sections: import_zod.z.object({
|
|
138
209
|
system_instructions: import_zod.z.string().optional()
|
|
@@ -142,13 +213,16 @@ var PromptAssetSchema = import_zod.z.object({
|
|
|
142
213
|
id: import_zod.z.string(),
|
|
143
214
|
schema_version: import_zod.z.number().int().positive().default(1),
|
|
144
215
|
description: import_zod.z.string().optional(),
|
|
145
|
-
provider: import_zod.z.enum(["openai", "anthropic", "google", "gemini", "openrouter", "any"]).optional(),
|
|
216
|
+
provider: import_zod.z.enum(["openai", "openai-responses", "anthropic", "google", "gemini", "openrouter", "any"]).optional(),
|
|
146
217
|
model: import_zod.z.string().optional(),
|
|
147
218
|
fallback_models: import_zod.z.array(import_zod.z.string()).optional(),
|
|
148
219
|
reasoning: ReasoningSchema.optional(),
|
|
149
220
|
sampling: SamplingSchema.optional(),
|
|
150
221
|
response: ResponseSchema.optional(),
|
|
222
|
+
cache: CacheSchema.optional(),
|
|
223
|
+
raw: RawProviderBodySchema.optional(),
|
|
151
224
|
tools: import_zod.z.array(ToolRefSchema).optional(),
|
|
225
|
+
provider_options: ProviderOptionsSchema.optional(),
|
|
152
226
|
mcp: MCPSchema.optional(),
|
|
153
227
|
context: ContextSchema.optional(),
|
|
154
228
|
includes: import_zod.z.array(import_zod.z.string()).optional(),
|
|
@@ -160,6 +234,123 @@ var PromptAssetSchema = import_zod.z.object({
|
|
|
160
234
|
source: SourceSchema.optional()
|
|
161
235
|
});
|
|
162
236
|
|
|
237
|
+
// src/parser/frontmatter-guard.ts
|
|
238
|
+
var FRONT_MATTER_DELIMITER = /^---[ \t]*$/;
|
|
239
|
+
var REGEX_FIELD = /^(?<indent>\s*)(?<field>allow_regex|deny_regex)\s*:\s*(?<value>.*)$/;
|
|
240
|
+
var PATTERN_FIELD = /^(?<indent>\s*)pattern\s*:\s*(?<value>.*)$/;
|
|
241
|
+
var INLINE_PATTERN_FIELD = /(?:^|[{,]\s*)pattern\s*:\s*(?<value>"(?:\\.|[^"\\])*")/;
|
|
242
|
+
function assertRegexFrontMatterQuoting(content, filePath) {
|
|
243
|
+
const frontMatter = extractFrontMatter(content);
|
|
244
|
+
if (!frontMatter) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
let pendingRegex;
|
|
248
|
+
for (const [index, line] of frontMatter.lines.entries()) {
|
|
249
|
+
const lineNumber = frontMatter.startLine + index;
|
|
250
|
+
const regexMatch = line.match(REGEX_FIELD);
|
|
251
|
+
if (regexMatch?.groups) {
|
|
252
|
+
const field = regexMatch.groups.field;
|
|
253
|
+
const indent2 = regexMatch.groups.indent.length;
|
|
254
|
+
const value = regexMatch.groups.value.trim();
|
|
255
|
+
pendingRegex = value === "" ? { field, indent: indent2 } : void 0;
|
|
256
|
+
assertNoUnescapedBackslashInDoubleQuotedRegex(value, field, filePath, lineNumber);
|
|
257
|
+
assertNoUnescapedBackslashInInlinePattern(value, field, filePath, lineNumber);
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
if (!pendingRegex) {
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
if (line.trim() === "" || line.trimStart().startsWith("#")) {
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
const indent = line.match(/^\s*/)?.[0].length ?? 0;
|
|
267
|
+
if (indent <= pendingRegex.indent) {
|
|
268
|
+
pendingRegex = void 0;
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
const patternMatch = line.match(PATTERN_FIELD);
|
|
272
|
+
if (!patternMatch?.groups) {
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
assertNoUnescapedBackslashInDoubleQuotedRegex(
|
|
276
|
+
patternMatch.groups.value.trim(),
|
|
277
|
+
`${pendingRegex.field}.pattern`,
|
|
278
|
+
filePath,
|
|
279
|
+
lineNumber
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
function assertNoUnescapedBackslashInInlinePattern(value, field, filePath, lineNumber) {
|
|
284
|
+
const match = value.match(INLINE_PATTERN_FIELD);
|
|
285
|
+
const pattern = match?.groups?.value;
|
|
286
|
+
if (!pattern) {
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
assertNoUnescapedBackslashInDoubleQuotedRegex(pattern, `${field}.pattern`, filePath, lineNumber);
|
|
290
|
+
}
|
|
291
|
+
function extractFrontMatter(content) {
|
|
292
|
+
const lines = content.split(/\r?\n/);
|
|
293
|
+
if (!FRONT_MATTER_DELIMITER.test(lines[0] ?? "")) {
|
|
294
|
+
return void 0;
|
|
295
|
+
}
|
|
296
|
+
for (let index = 1; index < lines.length; index += 1) {
|
|
297
|
+
if (FRONT_MATTER_DELIMITER.test(lines[index] ?? "")) {
|
|
298
|
+
return {
|
|
299
|
+
lines: lines.slice(1, index),
|
|
300
|
+
startLine: 2
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return void 0;
|
|
305
|
+
}
|
|
306
|
+
function assertNoUnescapedBackslashInDoubleQuotedRegex(value, field, filePath, lineNumber) {
|
|
307
|
+
if (!value.startsWith('"')) {
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
const quoted = readDoubleQuotedScalar(value);
|
|
311
|
+
if (!quoted) {
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
if (!hasUnescapedBackslash(quoted)) {
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
const location = filePath ? `${filePath}:${lineNumber}` : `line ${lineNumber}`;
|
|
318
|
+
throw new Error(
|
|
319
|
+
`POK013: Invalid context regex YAML at ${location}, field "${field}": double-quoted regex strings treat backslashes as YAML escapes. Use unquoted /pattern/i literal form, single quotes, or double each backslash.`
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
function readDoubleQuotedScalar(value) {
|
|
323
|
+
let result = "";
|
|
324
|
+
for (let index = 1; index < value.length; index += 1) {
|
|
325
|
+
const char = value[index];
|
|
326
|
+
if (char === '"') {
|
|
327
|
+
return result;
|
|
328
|
+
}
|
|
329
|
+
result += char;
|
|
330
|
+
if (char === "\\" && index + 1 < value.length) {
|
|
331
|
+
index += 1;
|
|
332
|
+
result += value[index];
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
return void 0;
|
|
336
|
+
}
|
|
337
|
+
function hasUnescapedBackslash(value) {
|
|
338
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
339
|
+
if (value[index] !== "\\") {
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
let count = 1;
|
|
343
|
+
while (value[index + count] === "\\") {
|
|
344
|
+
count += 1;
|
|
345
|
+
}
|
|
346
|
+
if (count % 2 === 1) {
|
|
347
|
+
return true;
|
|
348
|
+
}
|
|
349
|
+
index += count - 1;
|
|
350
|
+
}
|
|
351
|
+
return false;
|
|
352
|
+
}
|
|
353
|
+
|
|
163
354
|
// src/parser/sections.ts
|
|
164
355
|
var SECTION_MAP = {
|
|
165
356
|
"system instructions": "system_instructions",
|
|
@@ -200,6 +391,7 @@ function extractSections(body) {
|
|
|
200
391
|
|
|
201
392
|
// src/parser/parser.ts
|
|
202
393
|
function parsePrompt(content, filePath) {
|
|
394
|
+
assertRegexFrontMatterQuoting(content, filePath);
|
|
203
395
|
const { data: frontMatter, content: body } = (0, import_gray_matter.default)(content);
|
|
204
396
|
const sections = extractSections(body);
|
|
205
397
|
const raw = {
|
|
@@ -256,10 +448,76 @@ function parseTestPrompt(source) {
|
|
|
256
448
|
const { asset } = parsePrompt(source);
|
|
257
449
|
return asset;
|
|
258
450
|
}
|
|
451
|
+
function parsePromptTestSidecar(source) {
|
|
452
|
+
const parsed = (0, import_gray_matter3.default)(`---
|
|
453
|
+
${source.trim()}
|
|
454
|
+
---
|
|
455
|
+
`);
|
|
456
|
+
const data = parsed.data;
|
|
457
|
+
if (!Array.isArray(data.cases)) {
|
|
458
|
+
throw new Error('Prompt test sidecar must include a "cases" array.');
|
|
459
|
+
}
|
|
460
|
+
return {
|
|
461
|
+
cases: data.cases.map((testCase, index) => {
|
|
462
|
+
if (!testCase || typeof testCase !== "object") {
|
|
463
|
+
throw new Error(`Prompt test case at index ${index} must be an object.`);
|
|
464
|
+
}
|
|
465
|
+
if (typeof testCase.name !== "string" || testCase.name.length === 0) {
|
|
466
|
+
throw new Error(`Prompt test case at index ${index} must include a non-empty "name".`);
|
|
467
|
+
}
|
|
468
|
+
return testCase;
|
|
469
|
+
})
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
async function loadPromptTestSidecar(filePath) {
|
|
473
|
+
return parsePromptTestSidecar(await (0, import_promises2.readFile)(filePath, "utf-8"));
|
|
474
|
+
}
|
|
475
|
+
function getPromptTestCase(sidecar, name) {
|
|
476
|
+
const cases = Array.isArray(sidecar) ? sidecar : sidecar.cases;
|
|
477
|
+
const testCase = cases.find((candidate) => candidate.name === name);
|
|
478
|
+
if (!testCase) {
|
|
479
|
+
throw new Error(`Prompt test case "${name}" was not found.`);
|
|
480
|
+
}
|
|
481
|
+
return testCase;
|
|
482
|
+
}
|
|
483
|
+
function getHardcodedPromptResponse(sidecar, name) {
|
|
484
|
+
const testCase = getPromptTestCase(sidecar, name);
|
|
485
|
+
const response = testCase.response ?? testCase.expected_response;
|
|
486
|
+
if (response === void 0) {
|
|
487
|
+
throw new Error(`Prompt test case "${name}" does not define a "response".`);
|
|
488
|
+
}
|
|
489
|
+
return response;
|
|
490
|
+
}
|
|
491
|
+
function createHardcodedPromptResponder(sidecar) {
|
|
492
|
+
return (name) => getHardcodedPromptResponse(sidecar, name);
|
|
493
|
+
}
|
|
494
|
+
async function renderPromptTestCase(kit, options) {
|
|
495
|
+
const testCase = getPromptTestCase(options.sidecar, options.caseName);
|
|
496
|
+
const rendered = await kit.renderPrompt({
|
|
497
|
+
path: options.path,
|
|
498
|
+
source: options.source,
|
|
499
|
+
provider: options.provider,
|
|
500
|
+
environment: options.environment,
|
|
501
|
+
tier: options.tier,
|
|
502
|
+
variables: testCase.variables,
|
|
503
|
+
strict: options.strict
|
|
504
|
+
});
|
|
505
|
+
return {
|
|
506
|
+
testCase,
|
|
507
|
+
rendered,
|
|
508
|
+
response: testCase.response ?? testCase.expected_response
|
|
509
|
+
};
|
|
510
|
+
}
|
|
259
511
|
// Annotate the CommonJS export names for ESM import in node:
|
|
260
512
|
0 && (module.exports = {
|
|
513
|
+
createHardcodedPromptResponder,
|
|
261
514
|
createMockAsset,
|
|
262
515
|
createMockResolvedAsset,
|
|
263
|
-
|
|
516
|
+
getHardcodedPromptResponse,
|
|
517
|
+
getPromptTestCase,
|
|
518
|
+
loadPromptTestSidecar,
|
|
519
|
+
parsePromptTestSidecar,
|
|
520
|
+
parseTestPrompt,
|
|
521
|
+
renderPromptTestCase
|
|
264
522
|
});
|
|
265
523
|
//# sourceMappingURL=testing.cjs.map
|
package/dist/testing.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/testing.ts","../src/parser/parser.ts","../src/schema/schema.ts","../src/parser/sections.ts","../src/parser/loader.ts"],"sourcesContent":["import { parsePrompt } from './parser/index.js';\nimport type { PromptAsset, ResolvedPromptAsset } from './schema/index.js';\n\n/**\n * Create a mock PromptAsset for testing.\n */\nexport function createMockAsset(overrides: Partial<PromptAsset> = {}): PromptAsset {\n return {\n id: 'test.prompt',\n schema_version: 1,\n provider: 'openai',\n model: 'gpt-5.4',\n sections: {\n system_instructions: 'You are a test assistant.',\n prompt_template: 'Hello {{ name }}',\n },\n ...overrides,\n };\n}\n\n/**\n * Create a mock ResolvedPromptAsset for testing.\n */\nexport function createMockResolvedAsset(\n overrides: Partial<ResolvedPromptAsset> = {},\n): ResolvedPromptAsset {\n return {\n id: 'test.prompt',\n schema_version: 1,\n provider: 'openai',\n model: 'gpt-5.4',\n sections: {\n system_instructions: 'You are a test assistant.',\n prompt_template: 'Hello {{ name }}',\n },\n source: {\n file_path: 'test.md',\n },\n ...overrides,\n } as ResolvedPromptAsset;\n}\n\n/**\n * Parse an inline prompt string for testing.\n */\nexport function parseTestPrompt(source: string): PromptAsset {\n const { asset } = parsePrompt(source);\n return asset;\n}\n","import matter from 'gray-matter';\nimport { PromptAssetSchema } from '../schema/index.js';\nimport type { PromptAsset } from '../schema/index.js';\nimport { extractSections } from './sections.js';\n\nexport interface ParseResult {\n asset: PromptAsset;\n raw: {\n frontMatter: Record<string, unknown>;\n body: string;\n };\n}\n\n/**\n * Parse a prompt markdown string (YAML front matter + markdown body)\n * into a validated PromptAsset.\n */\nexport function parsePrompt(content: string, filePath?: string): ParseResult {\n const { data: frontMatter, content: body } = matter(content);\n\n const sections = extractSections(body);\n\n const raw = {\n ...frontMatter,\n sections,\n source: filePath ? { file_path: filePath } : undefined,\n };\n\n const asset = PromptAssetSchema.parse(raw);\n\n return {\n asset,\n raw: {\n frontMatter: frontMatter as Record<string, unknown>,\n body,\n },\n };\n}\n","import { z } from 'zod';\n\n// --- Tool definitions ---\n\nexport const InlineToolDefSchema = z.object({\n name: z.string(),\n description: z.string().optional(),\n input_schema: z.record(z.unknown()).optional(),\n});\n\nexport type InlineToolDef = z.infer<typeof InlineToolDefSchema>;\n\nexport const ToolRefSchema = z.union([z.string(), InlineToolDefSchema]);\n\n// --- MCP ---\n\nexport const MCPServerRefSchema = z.union([\n z.string(),\n z.object({\n name: z.string(),\n config: z.record(z.unknown()).optional(),\n }),\n]);\n\nexport type MCPServerRef = z.infer<typeof MCPServerRefSchema>;\n\n// --- Reasoning ---\n\nexport const ReasoningSchema = z.object({\n effort: z.enum(['low', 'medium', 'high']).optional(),\n budget_tokens: z.number().int().positive().optional(),\n});\n\n// --- Sampling ---\n\nexport const SamplingSchema = z.object({\n temperature: z.number().min(0).max(2).optional(),\n top_p: z.number().min(0).max(1).optional(),\n frequency_penalty: z.number().optional(),\n presence_penalty: z.number().optional(),\n stop: z.array(z.string()).optional(),\n max_output_tokens: z.number().int().positive().optional(),\n});\n\n// --- Response ---\n\nexport const ResponseSchema = z.object({\n format: z.enum(['text', 'json', 'markdown']).optional(),\n stream: z.boolean().optional(),\n});\n\n// --- Context ---\n\nexport const HistorySchema = z.object({\n max_items: z.number().int().positive().optional(),\n});\n\nexport const ContextRegexSchema = z.union([\n z.string(),\n z.object({\n pattern: z.string(),\n flags: z.string().optional(),\n return_message: z.string().optional(),\n }),\n]);\n\nexport const ContextBuiltInValidatorSchema = z.union([\n z.boolean(),\n z.object({\n return_message: z.string().optional(),\n }),\n]);\n\nexport const ContextInputDefinitionObjectSchema = z.object({\n name: z.string(),\n max_size: z.number().int().positive().optional(),\n trim: z.union([z.boolean(), z.enum(['start', 'end', 'both'])]).optional(),\n allow_regex: ContextRegexSchema.optional(),\n deny_regex: ContextRegexSchema.optional(),\n non_empty: ContextBuiltInValidatorSchema.optional(),\n reject_secrets: ContextBuiltInValidatorSchema.optional(),\n});\n\nexport const ContextInputDefinitionSchema = z.union([\n z.string(),\n ContextInputDefinitionObjectSchema,\n]);\n\nexport type ContextInputDefinition = z.infer<typeof ContextInputDefinitionSchema>;\nexport type ContextRegexDefinition = z.infer<typeof ContextRegexSchema>;\nexport type ContextBuiltInValidatorDefinition = z.infer<typeof ContextBuiltInValidatorSchema>;\n\nexport const ContextSchema = z.object({\n inputs: z.array(ContextInputDefinitionSchema).optional(),\n history: HistorySchema.optional(),\n});\n\n// --- Metadata ---\n\nexport const MetadataSchema = z.object({\n owner: z.string().optional(),\n tags: z.array(z.string()).optional(),\n review_required: z.boolean().optional(),\n stable: z.boolean().optional(),\n});\n\n// --- MCP block ---\n\nexport const MCPSchema = z.object({\n servers: z.array(MCPServerRefSchema).optional(),\n});\n\n// --- Overrides (subset allowed in environments/tiers) ---\n\nexport const PromptAssetOverridesSchema = z.object({\n model: z.string().optional(),\n fallback_models: z.array(z.string()).optional(),\n reasoning: ReasoningSchema.optional(),\n sampling: SamplingSchema.optional(),\n response: ResponseSchema.optional(),\n tools: z.array(ToolRefSchema).optional(),\n});\n\nexport type PromptAssetOverrides = z.infer<typeof PromptAssetOverridesSchema>;\n\n// --- Source tracking ---\n\nexport const SourceSchema = z.object({\n file_path: z.string().optional(),\n checksum: z.string().optional(),\n});\n\n// --- Sections (populated by parser) ---\n\nexport const SectionsSchema = z.object({\n system_instructions: z.string().optional(),\n prompt_template: z.string().optional(),\n notes: z.string().optional(),\n});\n\n// --- Defaults files (folder-level inheritance) ---\n\nexport const PromptDefaultsSchema = z.object({\n provider: z.enum(['openai', 'anthropic', 'google', 'gemini', 'openrouter', 'any']).optional(),\n model: z.string().optional(),\n metadata: MetadataSchema.optional(),\n sections: z.object({\n system_instructions: z.string().optional(),\n }).optional(),\n});\n\nexport type PromptDefaults = z.infer<typeof PromptDefaultsSchema>;\n\n// --- Top-level PromptAsset ---\n\nexport const PromptAssetSchema = z.object({\n id: z.string(),\n schema_version: z.number().int().positive().default(1),\n description: z.string().optional(),\n\n provider: z.enum(['openai', 'anthropic', 'google', 'gemini', 'openrouter', 'any']).optional(),\n model: z.string().optional(),\n fallback_models: z.array(z.string()).optional(),\n\n reasoning: ReasoningSchema.optional(),\n sampling: SamplingSchema.optional(),\n response: ResponseSchema.optional(),\n\n tools: z.array(ToolRefSchema).optional(),\n mcp: MCPSchema.optional(),\n\n context: ContextSchema.optional(),\n\n includes: z.array(z.string()).optional(),\n\n environments: z.record(PromptAssetOverridesSchema).optional(),\n tiers: z.record(PromptAssetOverridesSchema).optional(),\n\n metadata: MetadataSchema.optional(),\n\n // Populated by parser, not authored in YAML\n sections: SectionsSchema.optional(),\n source: SourceSchema.optional(),\n});\n\nexport type PromptAsset = z.infer<typeof PromptAssetSchema>;\n\n// --- Resolved asset (after includes, overrides applied) ---\n\nexport interface ResolvedPromptAsset extends PromptAsset {\n sections: {\n system_instructions?: string;\n prompt_template?: string;\n notes?: string;\n };\n source: {\n file_path?: string;\n checksum?: string;\n };\n}\n","export interface Sections {\n system_instructions?: string;\n prompt_template?: string;\n notes?: string;\n}\n\nconst SECTION_MAP: Record<string, keyof Sections> = {\n 'system instructions': 'system_instructions',\n 'prompt template': 'prompt_template',\n 'notes': 'notes',\n};\n\n/**\n * Extract named sections from the markdown body using H1 headings.\n * Case-insensitive. H2+ within a section is treated as content.\n * If no H1 headings are found, the entire body is treated as prompt_template.\n */\nexport function extractSections(body: string): Sections {\n const lines = body.split(/\\r?\\n/);\n const sections: Sections = {};\n\n let currentKey: keyof Sections | null = null;\n let currentLines: string[] = [];\n let foundAnyH1 = false;\n\n for (const line of lines) {\n const h1Match = line.match(/^#\\s+(.+)$/);\n if (h1Match) {\n // Flush previous section\n if (currentKey) {\n sections[currentKey] = currentLines.join('\\n').trim();\n }\n\n foundAnyH1 = true;\n const heading = h1Match[1].trim().toLowerCase();\n currentKey = SECTION_MAP[heading] ?? null;\n currentLines = [];\n } else {\n currentLines.push(line);\n }\n }\n\n // Flush last section\n if (currentKey) {\n sections[currentKey] = currentLines.join('\\n').trim();\n }\n\n // If no H1 headings found, treat entire body as prompt_template\n if (!foundAnyH1) {\n const trimmed = body.trim();\n if (trimmed) {\n sections.prompt_template = trimmed;\n }\n }\n\n return sections;\n}\n","import { readFile } from 'node:fs/promises';\nimport { dirname, join, resolve } from 'node:path';\nimport matter from 'gray-matter';\nimport { parsePrompt } from './parser.js';\nimport type { ParseResult } from './parser.js';\nimport { extractSections } from './sections.js';\nimport { PromptDefaultsSchema } from '../schema/index.js';\nimport type { PromptDefaults } from '../schema/index.js';\n\nconst DEFAULTS_FILE_NAME = 'defaults.md';\n\nexport interface LoadPromptOptions {\n /**\n * Optional boundary directory for defaults discovery.\n * If provided, defaults are loaded from this directory down to the prompt directory.\n */\n defaultsRoot?: string;\n}\n\n/**\n * Load and parse a prompt file from disk.\n */\nexport async function loadPromptFile(filePath: string, options: LoadPromptOptions = {}): Promise<ParseResult> {\n const content = await readFile(filePath, 'utf-8');\n const parsed = parsePrompt(content, filePath);\n // Default the boundary to the file's own directory so traversal never\n // walks above the prompt tree when no explicit root is provided.\n const root = options.defaultsRoot ?? dirname(filePath);\n const defaults = await loadDefaultsForPath(filePath, root);\n const asset = applyDefaults(parsed.asset, defaults);\n\n return {\n ...parsed,\n asset,\n };\n}\n\nasync function loadDefaultsForPath(filePath: string, defaultsRoot?: string): Promise<PromptDefaults> {\n const directories = getDirectoriesToCheck(filePath, defaultsRoot);\n let merged: PromptDefaults = {};\n\n for (const dir of directories) {\n const defaultsPath = join(dir, DEFAULTS_FILE_NAME);\n try {\n const defaultsContent = await readFile(defaultsPath, 'utf-8');\n const defaults = parseDefaults(defaultsContent);\n merged = mergeDefaults(merged, defaults);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw error;\n }\n }\n }\n\n return merged;\n}\n\nfunction getDirectoriesToCheck(filePath: string, defaultsRoot?: string): string[] {\n const dirs: string[] = [];\n let current = resolve(dirname(filePath));\n const boundary = defaultsRoot ? resolve(defaultsRoot) : undefined;\n\n while (true) {\n dirs.unshift(current);\n if ((boundary && current === boundary) || current === dirname(current)) {\n break;\n }\n current = dirname(current);\n }\n\n return dirs;\n}\n\nfunction parseDefaults(content: string): PromptDefaults {\n const { data: frontMatter, content: body } = matter(content);\n const sections = extractSections(body);\n\n return PromptDefaultsSchema.parse({\n ...frontMatter,\n sections: {\n system_instructions: sections.system_instructions,\n },\n });\n}\n\nfunction mergeDefaults(base: PromptDefaults, local: PromptDefaults): PromptDefaults {\n return {\n provider: local.provider ?? base.provider,\n model: local.model ?? base.model,\n metadata: {\n ...(base.metadata ?? {}),\n ...(local.metadata ?? {}),\n },\n sections: {\n ...(base.sections ?? {}),\n ...(local.sections ?? {}),\n },\n };\n}\n\nfunction applyDefaults(asset: ParseResult['asset'], defaults: PromptDefaults): ParseResult['asset'] {\n const hasDefaultMetadata = defaults.metadata && Object.keys(defaults.metadata).length > 0;\n const hasDefaultSystem = !!defaults.sections?.system_instructions;\n const hasDefaultScalars = defaults.provider !== undefined\n || defaults.model !== undefined;\n\n // Short-circuit: nothing to merge\n if (!hasDefaultMetadata && !hasDefaultSystem && !hasDefaultScalars) {\n return asset;\n }\n\n const mergedMetadata = {\n ...(defaults.metadata ?? {}),\n ...(asset.metadata ?? {}),\n };\n const metadata = Object.keys(mergedMetadata).length > 0 ? mergedMetadata : undefined;\n\n const systemInstructions = asset.sections?.system_instructions ?? defaults.sections?.system_instructions;\n\n const sections = asset.sections\n ? { ...asset.sections, system_instructions: systemInstructions }\n : systemInstructions\n ? { system_instructions: systemInstructions }\n : undefined;\n\n return {\n ...asset,\n provider: asset.provider ?? defaults.provider,\n model: asset.model ?? defaults.model,\n metadata,\n sections,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,yBAAmB;;;ACAnB,iBAAkB;AAIX,IAAM,sBAAsB,aAAE,OAAO;AAAA,EAC1C,MAAM,aAAE,OAAO;AAAA,EACf,aAAa,aAAE,OAAO,EAAE,SAAS;AAAA,EACjC,cAAc,aAAE,OAAO,aAAE,QAAQ,CAAC,EAAE,SAAS;AAC/C,CAAC;AAIM,IAAM,gBAAgB,aAAE,MAAM,CAAC,aAAE,OAAO,GAAG,mBAAmB,CAAC;AAI/D,IAAM,qBAAqB,aAAE,MAAM;AAAA,EACxC,aAAE,OAAO;AAAA,EACT,aAAE,OAAO;AAAA,IACP,MAAM,aAAE,OAAO;AAAA,IACf,QAAQ,aAAE,OAAO,aAAE,QAAQ,CAAC,EAAE,SAAS;AAAA,EACzC,CAAC;AACH,CAAC;AAMM,IAAM,kBAAkB,aAAE,OAAO;AAAA,EACtC,QAAQ,aAAE,KAAK,CAAC,OAAO,UAAU,MAAM,CAAC,EAAE,SAAS;AAAA,EACnD,eAAe,aAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS;AACtD,CAAC;AAIM,IAAM,iBAAiB,aAAE,OAAO;AAAA,EACrC,aAAa,aAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EAC/C,OAAO,aAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EACzC,mBAAmB,aAAE,OAAO,EAAE,SAAS;AAAA,EACvC,kBAAkB,aAAE,OAAO,EAAE,SAAS;AAAA,EACtC,MAAM,aAAE,MAAM,aAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EACnC,mBAAmB,aAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS;AAC1D,CAAC;AAIM,IAAM,iBAAiB,aAAE,OAAO;AAAA,EACrC,QAAQ,aAAE,KAAK,CAAC,QAAQ,QAAQ,UAAU,CAAC,EAAE,SAAS;AAAA,EACtD,QAAQ,aAAE,QAAQ,EAAE,SAAS;AAC/B,CAAC;AAIM,IAAM,gBAAgB,aAAE,OAAO;AAAA,EACpC,WAAW,aAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS;AAClD,CAAC;AAEM,IAAM,qBAAqB,aAAE,MAAM;AAAA,EACxC,aAAE,OAAO;AAAA,EACT,aAAE,OAAO;AAAA,IACP,SAAS,aAAE,OAAO;AAAA,IAClB,OAAO,aAAE,OAAO,EAAE,SAAS;AAAA,IAC3B,gBAAgB,aAAE,OAAO,EAAE,SAAS;AAAA,EACtC,CAAC;AACH,CAAC;AAEM,IAAM,gCAAgC,aAAE,MAAM;AAAA,EACnD,aAAE,QAAQ;AAAA,EACV,aAAE,OAAO;AAAA,IACP,gBAAgB,aAAE,OAAO,EAAE,SAAS;AAAA,EACtC,CAAC;AACH,CAAC;AAEM,IAAM,qCAAqC,aAAE,OAAO;AAAA,EACzD,MAAM,aAAE,OAAO;AAAA,EACf,UAAU,aAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS;AAAA,EAC/C,MAAM,aAAE,MAAM,CAAC,aAAE,QAAQ,GAAG,aAAE,KAAK,CAAC,SAAS,OAAO,MAAM,CAAC,CAAC,CAAC,EAAE,SAAS;AAAA,EACxE,aAAa,mBAAmB,SAAS;AAAA,EACzC,YAAY,mBAAmB,SAAS;AAAA,EACxC,WAAW,8BAA8B,SAAS;AAAA,EAClD,gBAAgB,8BAA8B,SAAS;AACzD,CAAC;AAEM,IAAM,+BAA+B,aAAE,MAAM;AAAA,EAClD,aAAE,OAAO;AAAA,EACT;AACF,CAAC;AAMM,IAAM,gBAAgB,aAAE,OAAO;AAAA,EACpC,QAAQ,aAAE,MAAM,4BAA4B,EAAE,SAAS;AAAA,EACvD,SAAS,cAAc,SAAS;AAClC,CAAC;AAIM,IAAM,iBAAiB,aAAE,OAAO;AAAA,EACrC,OAAO,aAAE,OAAO,EAAE,SAAS;AAAA,EAC3B,MAAM,aAAE,MAAM,aAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EACnC,iBAAiB,aAAE,QAAQ,EAAE,SAAS;AAAA,EACtC,QAAQ,aAAE,QAAQ,EAAE,SAAS;AAC/B,CAAC;AAIM,IAAM,YAAY,aAAE,OAAO;AAAA,EAChC,SAAS,aAAE,MAAM,kBAAkB,EAAE,SAAS;AAChD,CAAC;AAIM,IAAM,6BAA6B,aAAE,OAAO;AAAA,EACjD,OAAO,aAAE,OAAO,EAAE,SAAS;AAAA,EAC3B,iBAAiB,aAAE,MAAM,aAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EAC9C,WAAW,gBAAgB,SAAS;AAAA,EACpC,UAAU,eAAe,SAAS;AAAA,EAClC,UAAU,eAAe,SAAS;AAAA,EAClC,OAAO,aAAE,MAAM,aAAa,EAAE,SAAS;AACzC,CAAC;AAMM,IAAM,eAAe,aAAE,OAAO;AAAA,EACnC,WAAW,aAAE,OAAO,EAAE,SAAS;AAAA,EAC/B,UAAU,aAAE,OAAO,EAAE,SAAS;AAChC,CAAC;AAIM,IAAM,iBAAiB,aAAE,OAAO;AAAA,EACrC,qBAAqB,aAAE,OAAO,EAAE,SAAS;AAAA,EACzC,iBAAiB,aAAE,OAAO,EAAE,SAAS;AAAA,EACrC,OAAO,aAAE,OAAO,EAAE,SAAS;AAC7B,CAAC;AAIM,IAAM,uBAAuB,aAAE,OAAO;AAAA,EAC3C,UAAU,aAAE,KAAK,CAAC,UAAU,aAAa,UAAU,UAAU,cAAc,KAAK,CAAC,EAAE,SAAS;AAAA,EAC5F,OAAO,aAAE,OAAO,EAAE,SAAS;AAAA,EAC3B,UAAU,eAAe,SAAS;AAAA,EAClC,UAAU,aAAE,OAAO;AAAA,IACjB,qBAAqB,aAAE,OAAO,EAAE,SAAS;AAAA,EAC3C,CAAC,EAAE,SAAS;AACd,CAAC;AAMM,IAAM,oBAAoB,aAAE,OAAO;AAAA,EACxC,IAAI,aAAE,OAAO;AAAA,EACb,gBAAgB,aAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,CAAC;AAAA,EACrD,aAAa,aAAE,OAAO,EAAE,SAAS;AAAA,EAEjC,UAAU,aAAE,KAAK,CAAC,UAAU,aAAa,UAAU,UAAU,cAAc,KAAK,CAAC,EAAE,SAAS;AAAA,EAC5F,OAAO,aAAE,OAAO,EAAE,SAAS;AAAA,EAC3B,iBAAiB,aAAE,MAAM,aAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EAE9C,WAAW,gBAAgB,SAAS;AAAA,EACpC,UAAU,eAAe,SAAS;AAAA,EAClC,UAAU,eAAe,SAAS;AAAA,EAElC,OAAO,aAAE,MAAM,aAAa,EAAE,SAAS;AAAA,EACvC,KAAK,UAAU,SAAS;AAAA,EAExB,SAAS,cAAc,SAAS;AAAA,EAEhC,UAAU,aAAE,MAAM,aAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EAEvC,cAAc,aAAE,OAAO,0BAA0B,EAAE,SAAS;AAAA,EAC5D,OAAO,aAAE,OAAO,0BAA0B,EAAE,SAAS;AAAA,EAErD,UAAU,eAAe,SAAS;AAAA;AAAA,EAGlC,UAAU,eAAe,SAAS;AAAA,EAClC,QAAQ,aAAa,SAAS;AAChC,CAAC;;;ACjLD,IAAM,cAA8C;AAAA,EAClD,uBAAuB;AAAA,EACvB,mBAAmB;AAAA,EACnB,SAAS;AACX;AAOO,SAAS,gBAAgB,MAAwB;AACtD,QAAM,QAAQ,KAAK,MAAM,OAAO;AAChC,QAAM,WAAqB,CAAC;AAE5B,MAAI,aAAoC;AACxC,MAAI,eAAyB,CAAC;AAC9B,MAAI,aAAa;AAEjB,aAAW,QAAQ,OAAO;AACxB,UAAM,UAAU,KAAK,MAAM,YAAY;AACvC,QAAI,SAAS;AAEX,UAAI,YAAY;AACd,iBAAS,UAAU,IAAI,aAAa,KAAK,IAAI,EAAE,KAAK;AAAA,MACtD;AAEA,mBAAa;AACb,YAAM,UAAU,QAAQ,CAAC,EAAE,KAAK,EAAE,YAAY;AAC9C,mBAAa,YAAY,OAAO,KAAK;AACrC,qBAAe,CAAC;AAAA,IAClB,OAAO;AACL,mBAAa,KAAK,IAAI;AAAA,IACxB;AAAA,EACF;AAGA,MAAI,YAAY;AACd,aAAS,UAAU,IAAI,aAAa,KAAK,IAAI,EAAE,KAAK;AAAA,EACtD;AAGA,MAAI,CAAC,YAAY;AACf,UAAM,UAAU,KAAK,KAAK;AAC1B,QAAI,SAAS;AACX,eAAS,kBAAkB;AAAA,IAC7B;AAAA,EACF;AAEA,SAAO;AACT;;;AFvCO,SAAS,YAAY,SAAiB,UAAgC;AAC3E,QAAM,EAAE,MAAM,aAAa,SAAS,KAAK,QAAI,mBAAAA,SAAO,OAAO;AAE3D,QAAM,WAAW,gBAAgB,IAAI;AAErC,QAAM,MAAM;AAAA,IACV,GAAG;AAAA,IACH;AAAA,IACA,QAAQ,WAAW,EAAE,WAAW,SAAS,IAAI;AAAA,EAC/C;AAEA,QAAM,QAAQ,kBAAkB,MAAM,GAAG;AAEzC,SAAO;AAAA,IACL;AAAA,IACA,KAAK;AAAA,MACH;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;;;AGrCA,sBAAyB;AACzB,uBAAuC;AACvC,IAAAC,sBAAmB;;;AJIZ,SAAS,gBAAgB,YAAkC,CAAC,GAAgB;AACjF,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,gBAAgB;AAAA,IAChB,UAAU;AAAA,IACV,OAAO;AAAA,IACP,UAAU;AAAA,MACR,qBAAqB;AAAA,MACrB,iBAAiB;AAAA,IACnB;AAAA,IACA,GAAG;AAAA,EACL;AACF;AAKO,SAAS,wBACd,YAA0C,CAAC,GACtB;AACrB,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,gBAAgB;AAAA,IAChB,UAAU;AAAA,IACV,OAAO;AAAA,IACP,UAAU;AAAA,MACR,qBAAqB;AAAA,MACrB,iBAAiB;AAAA,IACnB;AAAA,IACA,QAAQ;AAAA,MACN,WAAW;AAAA,IACb;AAAA,IACA,GAAG;AAAA,EACL;AACF;AAKO,SAAS,gBAAgB,QAA6B;AAC3D,QAAM,EAAE,MAAM,IAAI,YAAY,MAAM;AACpC,SAAO;AACT;","names":["matter","import_gray_matter"]}
|
|
1
|
+
{"version":3,"sources":["../src/testing.ts","../src/parser/parser.ts","../src/schema/schema.ts","../src/parser/frontmatter-guard.ts","../src/parser/sections.ts","../src/parser/loader.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\n\nimport matter from 'gray-matter';\n\nimport { parsePrompt } from './parser/index.js';\nimport type { PromptAsset, ResolvedPromptAsset } from './schema/index.js';\n\nexport interface PromptTestCase<TResponse = unknown> {\n name: string;\n variables?: Record<string, string>;\n response?: TResponse;\n expected_response?: TResponse;\n}\n\nexport interface PromptTestSidecar<TResponse = unknown> {\n cases: Array<PromptTestCase<TResponse>>;\n}\n\nexport interface PromptTestRenderer {\n renderPrompt(options: {\n path?: string;\n source?: string;\n provider: string;\n environment?: string;\n tier?: string;\n variables?: Record<string, string>;\n strict?: boolean;\n }): Promise<unknown>;\n}\n\n/**\n * Create a mock PromptAsset for testing.\n */\nexport function createMockAsset(overrides: Partial<PromptAsset> = {}): PromptAsset {\n return {\n id: 'test.prompt',\n schema_version: 1,\n provider: 'openai',\n model: 'gpt-5.4',\n sections: {\n system_instructions: 'You are a test assistant.',\n prompt_template: 'Hello {{ name }}',\n },\n ...overrides,\n };\n}\n\n/**\n * Create a mock ResolvedPromptAsset for testing.\n */\nexport function createMockResolvedAsset(\n overrides: Partial<ResolvedPromptAsset> = {},\n): ResolvedPromptAsset {\n return {\n id: 'test.prompt',\n schema_version: 1,\n provider: 'openai',\n model: 'gpt-5.4',\n sections: {\n system_instructions: 'You are a test assistant.',\n prompt_template: 'Hello {{ name }}',\n },\n source: {\n file_path: 'test.md',\n },\n ...overrides,\n } as ResolvedPromptAsset;\n}\n\n/**\n * Parse an inline prompt string for testing.\n */\nexport function parseTestPrompt(source: string): PromptAsset {\n const { asset } = parsePrompt(source);\n return asset;\n}\n\n/**\n * Parse a .test.yaml sidecar file.\n */\nexport function parsePromptTestSidecar<TResponse = unknown>(source: string): PromptTestSidecar<TResponse> {\n const parsed = matter(`---\\n${source.trim()}\\n---\\n`);\n const data = parsed.data as Partial<PromptTestSidecar<TResponse>>;\n\n if (!Array.isArray(data.cases)) {\n throw new Error('Prompt test sidecar must include a \"cases\" array.');\n }\n\n return {\n cases: data.cases.map((testCase, index) => {\n if (!testCase || typeof testCase !== 'object') {\n throw new Error(`Prompt test case at index ${index} must be an object.`);\n }\n\n if (typeof testCase.name !== 'string' || testCase.name.length === 0) {\n throw new Error(`Prompt test case at index ${index} must include a non-empty \"name\".`);\n }\n\n return testCase;\n }),\n };\n}\n\n/**\n * Load a .test.yaml sidecar file from disk.\n */\nexport async function loadPromptTestSidecar<TResponse = unknown>(\n filePath: string | URL,\n): Promise<PromptTestSidecar<TResponse>> {\n return parsePromptTestSidecar<TResponse>(await readFile(filePath, 'utf-8'));\n}\n\n/**\n * Find a named test case in a sidecar.\n */\nexport function getPromptTestCase<TResponse = unknown>(\n sidecar: PromptTestSidecar<TResponse> | Array<PromptTestCase<TResponse>>,\n name: string,\n): PromptTestCase<TResponse> {\n const cases = Array.isArray(sidecar) ? sidecar : sidecar.cases;\n const testCase = cases.find((candidate) => candidate.name === name);\n\n if (!testCase) {\n throw new Error(`Prompt test case \"${name}\" was not found.`);\n }\n\n return testCase;\n}\n\n/**\n * Read the canned response for a named case.\n */\nexport function getHardcodedPromptResponse<TResponse = unknown>(\n sidecar: PromptTestSidecar<TResponse> | Array<PromptTestCase<TResponse>>,\n name: string,\n): TResponse {\n const testCase = getPromptTestCase(sidecar, name);\n const response = testCase.response ?? testCase.expected_response;\n\n if (response === undefined) {\n throw new Error(`Prompt test case \"${name}\" does not define a \"response\".`);\n }\n\n return response;\n}\n\n/**\n * Create a small responder for unit tests and local development flows.\n */\nexport function createHardcodedPromptResponder<TResponse = unknown>(\n sidecar: PromptTestSidecar<TResponse> | Array<PromptTestCase<TResponse>>,\n): (name: string) => TResponse {\n return (name) => getHardcodedPromptResponse(sidecar, name);\n}\n\n/**\n * Render a prompt using variables from a named sidecar case.\n */\nexport async function renderPromptTestCase<TResponse = unknown>(\n kit: PromptTestRenderer,\n options: {\n sidecar: PromptTestSidecar<TResponse> | Array<PromptTestCase<TResponse>>;\n caseName: string;\n path?: string;\n source?: string;\n provider: string;\n environment?: string;\n tier?: string;\n strict?: boolean;\n },\n): Promise<{\n testCase: PromptTestCase<TResponse>;\n rendered: unknown;\n response?: TResponse;\n}> {\n const testCase = getPromptTestCase(options.sidecar, options.caseName);\n const rendered = await kit.renderPrompt({\n path: options.path,\n source: options.source,\n provider: options.provider,\n environment: options.environment,\n tier: options.tier,\n variables: testCase.variables,\n strict: options.strict,\n });\n\n return {\n testCase,\n rendered,\n response: testCase.response ?? testCase.expected_response,\n };\n}\n","import matter from 'gray-matter';\nimport { PromptAssetSchema } from '../schema/index.js';\nimport type { PromptAsset } from '../schema/index.js';\nimport { assertRegexFrontMatterQuoting } from './frontmatter-guard.js';\nimport { extractSections } from './sections.js';\n\nexport interface ParseResult {\n asset: PromptAsset;\n raw: {\n frontMatter: Record<string, unknown>;\n body: string;\n };\n}\n\n/**\n * Parse a prompt markdown string (YAML front matter + markdown body)\n * into a validated PromptAsset.\n */\nexport function parsePrompt(content: string, filePath?: string): ParseResult {\n assertRegexFrontMatterQuoting(content, filePath);\n const { data: frontMatter, content: body } = matter(content);\n\n const sections = extractSections(body);\n\n const raw = {\n ...frontMatter,\n sections,\n source: filePath ? { file_path: filePath } : undefined,\n };\n\n const asset = PromptAssetSchema.parse(raw);\n\n return {\n asset,\n raw: {\n frontMatter: frontMatter as Record<string, unknown>,\n body,\n },\n };\n}\n","import { z } from 'zod';\n\n// --- Tool definitions ---\n\nexport const InlineToolDefSchema = z.object({\n name: z.string(),\n description: z.string().optional(),\n input_schema: z.record(z.unknown()).optional(),\n});\n\nexport type InlineToolDef = z.infer<typeof InlineToolDefSchema>;\n\nexport const ToolRefSchema = z.union([z.string(), InlineToolDefSchema]);\n\n// --- MCP ---\n\nexport const MCPServerRefSchema = z.union([\n z.string(),\n z.object({\n name: z.string(),\n config: z.record(z.unknown()).optional(),\n }),\n]);\n\nexport type MCPServerRef = z.infer<typeof MCPServerRefSchema>;\n\n// --- Reasoning ---\n\nexport const ReasoningSchema = z.object({\n effort: z.enum(['low', 'medium', 'high']).optional(),\n budget_tokens: z.number().int().positive().optional(),\n});\n\n// --- Sampling ---\n\nexport const SamplingSchema = z.object({\n temperature: z.number().min(0).max(2).optional(),\n top_p: z.number().min(0).max(1).optional(),\n frequency_penalty: z.number().optional(),\n presence_penalty: z.number().optional(),\n stop: z.array(z.string()).optional(),\n max_output_tokens: z.number().int().positive().optional(),\n});\n\n// --- Response ---\n\nexport const ResponseSchema = z.object({\n format: z.enum(['text', 'json', 'markdown']).optional(),\n stream: z.boolean().optional(),\n schema: z.record(z.unknown()).optional(),\n schema_name: z.string().optional(),\n schema_description: z.string().optional(),\n schema_strict: z.boolean().optional(),\n});\n\n\n// --- Provider-specific options ---\n\nexport const AnthropicProviderOptionsSchema = z.object({\n top_k: z.number().int().min(0).optional(),\n tool_choice: z.record(z.unknown()).optional(),\n output_config: z.record(z.unknown()).optional(),\n});\n\nexport const GeminiProviderOptionsSchema = z.object({\n candidate_count: z.number().int().positive().optional(),\n top_k: z.number().int().min(0).optional(),\n seed: z.number().int().optional(),\n response_schema: z.record(z.unknown()).optional(),\n response_json_schema: z.record(z.unknown()).optional(),\n response_modalities: z.array(z.string()).optional(),\n thinking_budget_tokens: z.number().int().positive().optional(),\n});\n\nexport const OpenRouterProviderOptionsSchema = z.object({\n provider: z.record(z.unknown()).optional(),\n transforms: z.array(z.string()).optional(),\n plugins: z.array(z.record(z.unknown())).optional(),\n models: z.array(z.string()).optional(),\n});\n\nexport const ProviderOptionsSchema = z.object({\n anthropic: AnthropicProviderOptionsSchema.optional(),\n gemini: GeminiProviderOptionsSchema.optional(),\n openrouter: OpenRouterProviderOptionsSchema.optional(),\n});\n\nexport type ProviderOptions = z.infer<typeof ProviderOptionsSchema>;\n\n// --- Raw provider body passthrough ---\n\nexport const RawProviderBodySchema = z.object({\n openai: z.record(z.unknown()).optional(),\n 'openai-responses': z.record(z.unknown()).optional(),\n openai_responses: z.record(z.unknown()).optional(),\n anthropic: z.record(z.unknown()).optional(),\n gemini: z.record(z.unknown()).optional(),\n google: z.record(z.unknown()).optional(),\n openrouter: z.record(z.unknown()).optional(),\n});\n\nexport type RawProviderBody = z.infer<typeof RawProviderBodySchema>;\n\n// --- Cache controls ---\n\nexport const OpenAICacheSchema = z.object({\n prompt_cache_key: z.string().min(1).optional(),\n retention: z.enum(['in_memory', '24h']).optional(),\n});\n\nexport const AnthropicCacheSchema = z.object({\n mode: z.enum(['automatic', 'explicit']).optional(),\n type: z.literal('ephemeral').optional(),\n ttl: z.enum(['5m', '1h']).optional(),\n cache_system_instructions: z.boolean().optional(),\n cache_tools: z.boolean().optional(),\n cache_prompt_template: z.boolean().optional(),\n});\n\nexport const GeminiCacheSchema = z.object({\n cached_content: z.string().min(1).optional(),\n});\n\nexport const CacheSchema = z.object({\n openai: OpenAICacheSchema.optional(),\n anthropic: AnthropicCacheSchema.optional(),\n gemini: GeminiCacheSchema.optional(),\n google: GeminiCacheSchema.optional(),\n});\n\n// --- Context ---\n\nexport const HistorySchema = z.object({\n max_items: z.number().int().positive().optional(),\n});\n\nexport const ContextRegexSchema = z.union([\n z.string(),\n z.object({\n pattern: z.string(),\n flags: z.string().optional(),\n return_message: z.string().optional(),\n }),\n]);\n\nexport const ContextBuiltInValidatorSchema = z.union([\n z.boolean(),\n z.object({\n return_message: z.string().optional(),\n }),\n]);\n\nexport const ContextInputDefinitionObjectSchema = z.object({\n name: z.string(),\n max_size: z.number().int().positive().optional(),\n trim: z.union([z.boolean(), z.enum(['start', 'end', 'both'])]).optional(),\n allow_regex: ContextRegexSchema.optional(),\n deny_regex: ContextRegexSchema.optional(),\n non_empty: ContextBuiltInValidatorSchema.optional(),\n reject_secrets: ContextBuiltInValidatorSchema.optional(),\n});\n\nexport const ContextInputDefinitionSchema = z.union([\n z.string(),\n ContextInputDefinitionObjectSchema,\n]);\n\nexport type ContextInputDefinition = z.infer<typeof ContextInputDefinitionSchema>;\nexport type ContextRegexDefinition = z.infer<typeof ContextRegexSchema>;\nexport type ContextBuiltInValidatorDefinition = z.infer<typeof ContextBuiltInValidatorSchema>;\n\nexport const ContextSchema = z.object({\n inputs: z.array(ContextInputDefinitionSchema).optional(),\n history: HistorySchema.optional(),\n});\n\n// --- Metadata ---\n\nexport const MetadataSchema = z.object({\n owner: z.string().optional(),\n tags: z.array(z.string()).optional(),\n review_required: z.boolean().optional(),\n stable: z.boolean().optional(),\n});\n\n// --- MCP block ---\n\nexport const MCPSchema = z.object({\n servers: z.array(MCPServerRefSchema).optional(),\n});\n\n// --- Overrides (subset allowed in environments/tiers) ---\n\nexport const PromptAssetOverridesSchema = z.object({\n model: z.string().optional(),\n fallback_models: z.array(z.string()).optional(),\n reasoning: ReasoningSchema.optional(),\n sampling: SamplingSchema.optional(),\n response: ResponseSchema.optional(),\n cache: CacheSchema.optional(),\n raw: RawProviderBodySchema.optional(),\n tools: z.array(ToolRefSchema).optional(),\n provider_options: ProviderOptionsSchema.optional(),\n});\n\nexport type PromptAssetOverrides = z.infer<typeof PromptAssetOverridesSchema>;\n\n// --- Source tracking ---\n\nexport const SourceSchema = z.object({\n file_path: z.string().optional(),\n checksum: z.string().optional(),\n});\n\n// --- Sections (populated by parser) ---\n\nexport const SectionsSchema = z.object({\n system_instructions: z.string().optional(),\n prompt_template: z.string().optional(),\n notes: z.string().optional(),\n});\n\n// --- Defaults files (folder-level inheritance) ---\n\nexport const PromptDefaultsSchema = z.object({\n provider: z.enum(['openai', 'openai-responses', 'anthropic', 'google', 'gemini', 'openrouter', 'any']).optional(),\n model: z.string().optional(),\n cache: CacheSchema.optional(),\n metadata: MetadataSchema.optional(),\n sections: z.object({\n system_instructions: z.string().optional(),\n }).optional(),\n});\n\nexport type PromptDefaults = z.infer<typeof PromptDefaultsSchema>;\n\n// --- Top-level PromptAsset ---\n\nexport const PromptAssetSchema = z.object({\n id: z.string(),\n schema_version: z.number().int().positive().default(1),\n description: z.string().optional(),\n\n provider: z.enum(['openai', 'openai-responses', 'anthropic', 'google', 'gemini', 'openrouter', 'any']).optional(),\n model: z.string().optional(),\n fallback_models: z.array(z.string()).optional(),\n\n reasoning: ReasoningSchema.optional(),\n sampling: SamplingSchema.optional(),\n response: ResponseSchema.optional(),\n cache: CacheSchema.optional(),\n raw: RawProviderBodySchema.optional(),\n\n tools: z.array(ToolRefSchema).optional(),\n provider_options: ProviderOptionsSchema.optional(),\n mcp: MCPSchema.optional(),\n\n context: ContextSchema.optional(),\n\n includes: z.array(z.string()).optional(),\n\n environments: z.record(PromptAssetOverridesSchema).optional(),\n tiers: z.record(PromptAssetOverridesSchema).optional(),\n\n metadata: MetadataSchema.optional(),\n\n // Populated by parser, not authored in YAML\n sections: SectionsSchema.optional(),\n source: SourceSchema.optional(),\n});\n\nexport type PromptAsset = z.infer<typeof PromptAssetSchema>;\n\n// --- Resolved asset (after includes, overrides applied) ---\n\nexport interface ResolvedPromptAsset extends PromptAsset {\n sections: {\n system_instructions?: string;\n prompt_template?: string;\n notes?: string;\n };\n source: {\n file_path?: string;\n checksum?: string;\n };\n}\n","const FRONT_MATTER_DELIMITER = /^---[ \\t]*$/;\nconst REGEX_FIELD = /^(?<indent>\\s*)(?<field>allow_regex|deny_regex)\\s*:\\s*(?<value>.*)$/;\nconst PATTERN_FIELD = /^(?<indent>\\s*)pattern\\s*:\\s*(?<value>.*)$/;\nconst INLINE_PATTERN_FIELD = /(?:^|[{,]\\s*)pattern\\s*:\\s*(?<value>\"(?:\\\\.|[^\"\\\\])*\")/;\n\ninterface PendingRegexField {\n field: string;\n indent: number;\n}\n\nexport function assertRegexFrontMatterQuoting(content: string, filePath?: string): void {\n const frontMatter = extractFrontMatter(content);\n if (!frontMatter) {\n return;\n }\n\n let pendingRegex: PendingRegexField | undefined;\n\n for (const [index, line] of frontMatter.lines.entries()) {\n const lineNumber = frontMatter.startLine + index;\n const regexMatch = line.match(REGEX_FIELD);\n\n if (regexMatch?.groups) {\n const field = regexMatch.groups.field;\n const indent = regexMatch.groups.indent.length;\n const value = regexMatch.groups.value.trim();\n pendingRegex = value === '' ? { field, indent } : undefined;\n\n assertNoUnescapedBackslashInDoubleQuotedRegex(value, field, filePath, lineNumber);\n assertNoUnescapedBackslashInInlinePattern(value, field, filePath, lineNumber);\n continue;\n }\n\n if (!pendingRegex) {\n continue;\n }\n\n if (line.trim() === '' || line.trimStart().startsWith('#')) {\n continue;\n }\n\n const indent = line.match(/^\\s*/)?.[0].length ?? 0;\n if (indent <= pendingRegex.indent) {\n pendingRegex = undefined;\n continue;\n }\n\n const patternMatch = line.match(PATTERN_FIELD);\n if (!patternMatch?.groups) {\n continue;\n }\n\n assertNoUnescapedBackslashInDoubleQuotedRegex(\n patternMatch.groups.value.trim(),\n `${pendingRegex.field}.pattern`,\n filePath,\n lineNumber,\n );\n }\n}\n\nfunction assertNoUnescapedBackslashInInlinePattern(\n value: string,\n field: string,\n filePath: string | undefined,\n lineNumber: number,\n): void {\n const match = value.match(INLINE_PATTERN_FIELD);\n const pattern = match?.groups?.value;\n if (!pattern) {\n return;\n }\n\n assertNoUnescapedBackslashInDoubleQuotedRegex(pattern, `${field}.pattern`, filePath, lineNumber);\n}\n\nfunction extractFrontMatter(content: string): { lines: string[]; startLine: number } | undefined {\n const lines = content.split(/\\r?\\n/);\n if (!FRONT_MATTER_DELIMITER.test(lines[0] ?? '')) {\n return undefined;\n }\n\n for (let index = 1; index < lines.length; index += 1) {\n if (FRONT_MATTER_DELIMITER.test(lines[index] ?? '')) {\n return {\n lines: lines.slice(1, index),\n startLine: 2,\n };\n }\n }\n\n return undefined;\n}\n\nfunction assertNoUnescapedBackslashInDoubleQuotedRegex(\n value: string,\n field: string,\n filePath: string | undefined,\n lineNumber: number,\n): void {\n if (!value.startsWith('\"')) {\n return;\n }\n\n const quoted = readDoubleQuotedScalar(value);\n if (!quoted) {\n return;\n }\n\n if (!hasUnescapedBackslash(quoted)) {\n return;\n }\n\n const location = filePath ? `${filePath}:${lineNumber}` : `line ${lineNumber}`;\n throw new Error(\n `POK013: Invalid context regex YAML at ${location}, field \"${field}\": `\n + 'double-quoted regex strings treat backslashes as YAML escapes. '\n + 'Use unquoted /pattern/i literal form, single quotes, or double each backslash.',\n );\n}\n\nfunction readDoubleQuotedScalar(value: string): string | undefined {\n let result = '';\n\n for (let index = 1; index < value.length; index += 1) {\n const char = value[index];\n\n if (char === '\"') {\n return result;\n }\n\n result += char;\n\n if (char === '\\\\' && index + 1 < value.length) {\n index += 1;\n result += value[index];\n }\n }\n\n return undefined;\n}\n\nfunction hasUnescapedBackslash(value: string): boolean {\n for (let index = 0; index < value.length; index += 1) {\n if (value[index] !== '\\\\') {\n continue;\n }\n\n let count = 1;\n while (value[index + count] === '\\\\') {\n count += 1;\n }\n\n if (count % 2 === 1) {\n return true;\n }\n\n index += count - 1;\n }\n\n return false;\n}\n","export interface Sections {\n system_instructions?: string;\n prompt_template?: string;\n notes?: string;\n}\n\nconst SECTION_MAP: Record<string, keyof Sections> = {\n 'system instructions': 'system_instructions',\n 'prompt template': 'prompt_template',\n 'notes': 'notes',\n};\n\n/**\n * Extract named sections from the markdown body using H1 headings.\n * Case-insensitive. H2+ within a section is treated as content.\n * If no H1 headings are found, the entire body is treated as prompt_template.\n */\nexport function extractSections(body: string): Sections {\n const lines = body.split(/\\r?\\n/);\n const sections: Sections = {};\n\n let currentKey: keyof Sections | null = null;\n let currentLines: string[] = [];\n let foundAnyH1 = false;\n\n for (const line of lines) {\n const h1Match = line.match(/^#\\s+(.+)$/);\n if (h1Match) {\n // Flush previous section\n if (currentKey) {\n sections[currentKey] = currentLines.join('\\n').trim();\n }\n\n foundAnyH1 = true;\n const heading = h1Match[1].trim().toLowerCase();\n currentKey = SECTION_MAP[heading] ?? null;\n currentLines = [];\n } else {\n currentLines.push(line);\n }\n }\n\n // Flush last section\n if (currentKey) {\n sections[currentKey] = currentLines.join('\\n').trim();\n }\n\n // If no H1 headings found, treat entire body as prompt_template\n if (!foundAnyH1) {\n const trimmed = body.trim();\n if (trimmed) {\n sections.prompt_template = trimmed;\n }\n }\n\n return sections;\n}\n","import { readFile } from 'node:fs/promises';\nimport { dirname, join, resolve } from 'node:path';\nimport matter from 'gray-matter';\nimport { parsePrompt } from './parser.js';\nimport type { ParseResult } from './parser.js';\nimport { extractSections } from './sections.js';\nimport { PromptDefaultsSchema } from '../schema/index.js';\nimport type { PromptDefaults } from '../schema/index.js';\n\nconst DEFAULTS_FILE_NAME = 'defaults.md';\n\nexport interface LoadPromptOptions {\n /**\n * Optional boundary directory for defaults discovery.\n * If provided, defaults are loaded from this directory down to the prompt directory.\n */\n defaultsRoot?: string;\n}\n\n/**\n * Load and parse a prompt file from disk.\n */\nexport async function loadPromptFile(filePath: string, options: LoadPromptOptions = {}): Promise<ParseResult> {\n const content = await readFile(filePath, 'utf-8');\n const parsed = parsePrompt(content, filePath);\n // Default the boundary to the file's own directory so traversal never\n // walks above the prompt tree when no explicit root is provided.\n const root = options.defaultsRoot ?? dirname(filePath);\n const defaults = await loadDefaultsForPath(filePath, root);\n const asset = applyDefaults(parsed.asset, defaults);\n\n return {\n ...parsed,\n asset,\n };\n}\n\nasync function loadDefaultsForPath(filePath: string, defaultsRoot?: string): Promise<PromptDefaults> {\n const directories = getDirectoriesToCheck(filePath, defaultsRoot);\n let merged: PromptDefaults = {};\n\n for (const dir of directories) {\n const defaultsPath = join(dir, DEFAULTS_FILE_NAME);\n try {\n const defaultsContent = await readFile(defaultsPath, 'utf-8');\n const defaults = parseDefaults(defaultsContent);\n merged = mergeDefaults(merged, defaults);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw error;\n }\n }\n }\n\n return merged;\n}\n\nfunction getDirectoriesToCheck(filePath: string, defaultsRoot?: string): string[] {\n const dirs: string[] = [];\n let current = resolve(dirname(filePath));\n const boundary = defaultsRoot ? resolve(defaultsRoot) : undefined;\n\n while (true) {\n dirs.unshift(current);\n if ((boundary && current === boundary) || current === dirname(current)) {\n break;\n }\n current = dirname(current);\n }\n\n return dirs;\n}\n\nfunction parseDefaults(content: string): PromptDefaults {\n const { data: frontMatter, content: body } = matter(content);\n const sections = extractSections(body);\n\n return PromptDefaultsSchema.parse({\n ...frontMatter,\n sections: {\n system_instructions: sections.system_instructions,\n },\n });\n}\n\nfunction mergeDefaults(base: PromptDefaults, local: PromptDefaults): PromptDefaults {\n return {\n provider: local.provider ?? base.provider,\n model: local.model ?? base.model,\n metadata: {\n ...(base.metadata ?? {}),\n ...(local.metadata ?? {}),\n },\n sections: {\n ...(base.sections ?? {}),\n ...(local.sections ?? {}),\n },\n };\n}\n\nfunction applyDefaults(asset: ParseResult['asset'], defaults: PromptDefaults): ParseResult['asset'] {\n const hasDefaultMetadata = defaults.metadata && Object.keys(defaults.metadata).length > 0;\n const hasDefaultSystem = !!defaults.sections?.system_instructions;\n const hasDefaultScalars = defaults.provider !== undefined\n || defaults.model !== undefined;\n\n // Short-circuit: nothing to merge\n if (!hasDefaultMetadata && !hasDefaultSystem && !hasDefaultScalars) {\n return asset;\n }\n\n const mergedMetadata = {\n ...(defaults.metadata ?? {}),\n ...(asset.metadata ?? {}),\n };\n const metadata = Object.keys(mergedMetadata).length > 0 ? mergedMetadata : undefined;\n\n const systemInstructions = asset.sections?.system_instructions ?? defaults.sections?.system_instructions;\n\n const sections = asset.sections\n ? { ...asset.sections, system_instructions: systemInstructions }\n : systemInstructions\n ? { system_instructions: systemInstructions }\n : undefined;\n\n return {\n ...asset,\n provider: asset.provider ?? defaults.provider,\n model: asset.model ?? defaults.model,\n metadata,\n sections,\n };\n}\n"],"mappings":"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","names":["import_promises","import_gray_matter","indent","matter","import_gray_matter","matter"]}
|
package/dist/testing.d.cts
CHANGED
|
@@ -1,6 +1,26 @@
|
|
|
1
|
-
import { P as PromptAsset, R as ResolvedPromptAsset } from './schema-
|
|
1
|
+
import { P as PromptAsset, R as ResolvedPromptAsset } from './schema-B9A1zLRk.cjs';
|
|
2
2
|
import 'zod';
|
|
3
3
|
|
|
4
|
+
interface PromptTestCase<TResponse = unknown> {
|
|
5
|
+
name: string;
|
|
6
|
+
variables?: Record<string, string>;
|
|
7
|
+
response?: TResponse;
|
|
8
|
+
expected_response?: TResponse;
|
|
9
|
+
}
|
|
10
|
+
interface PromptTestSidecar<TResponse = unknown> {
|
|
11
|
+
cases: Array<PromptTestCase<TResponse>>;
|
|
12
|
+
}
|
|
13
|
+
interface PromptTestRenderer {
|
|
14
|
+
renderPrompt(options: {
|
|
15
|
+
path?: string;
|
|
16
|
+
source?: string;
|
|
17
|
+
provider: string;
|
|
18
|
+
environment?: string;
|
|
19
|
+
tier?: string;
|
|
20
|
+
variables?: Record<string, string>;
|
|
21
|
+
strict?: boolean;
|
|
22
|
+
}): Promise<unknown>;
|
|
23
|
+
}
|
|
4
24
|
/**
|
|
5
25
|
* Create a mock PromptAsset for testing.
|
|
6
26
|
*/
|
|
@@ -13,5 +33,42 @@ declare function createMockResolvedAsset(overrides?: Partial<ResolvedPromptAsset
|
|
|
13
33
|
* Parse an inline prompt string for testing.
|
|
14
34
|
*/
|
|
15
35
|
declare function parseTestPrompt(source: string): PromptAsset;
|
|
36
|
+
/**
|
|
37
|
+
* Parse a .test.yaml sidecar file.
|
|
38
|
+
*/
|
|
39
|
+
declare function parsePromptTestSidecar<TResponse = unknown>(source: string): PromptTestSidecar<TResponse>;
|
|
40
|
+
/**
|
|
41
|
+
* Load a .test.yaml sidecar file from disk.
|
|
42
|
+
*/
|
|
43
|
+
declare function loadPromptTestSidecar<TResponse = unknown>(filePath: string | URL): Promise<PromptTestSidecar<TResponse>>;
|
|
44
|
+
/**
|
|
45
|
+
* Find a named test case in a sidecar.
|
|
46
|
+
*/
|
|
47
|
+
declare function getPromptTestCase<TResponse = unknown>(sidecar: PromptTestSidecar<TResponse> | Array<PromptTestCase<TResponse>>, name: string): PromptTestCase<TResponse>;
|
|
48
|
+
/**
|
|
49
|
+
* Read the canned response for a named case.
|
|
50
|
+
*/
|
|
51
|
+
declare function getHardcodedPromptResponse<TResponse = unknown>(sidecar: PromptTestSidecar<TResponse> | Array<PromptTestCase<TResponse>>, name: string): TResponse;
|
|
52
|
+
/**
|
|
53
|
+
* Create a small responder for unit tests and local development flows.
|
|
54
|
+
*/
|
|
55
|
+
declare function createHardcodedPromptResponder<TResponse = unknown>(sidecar: PromptTestSidecar<TResponse> | Array<PromptTestCase<TResponse>>): (name: string) => TResponse;
|
|
56
|
+
/**
|
|
57
|
+
* Render a prompt using variables from a named sidecar case.
|
|
58
|
+
*/
|
|
59
|
+
declare function renderPromptTestCase<TResponse = unknown>(kit: PromptTestRenderer, options: {
|
|
60
|
+
sidecar: PromptTestSidecar<TResponse> | Array<PromptTestCase<TResponse>>;
|
|
61
|
+
caseName: string;
|
|
62
|
+
path?: string;
|
|
63
|
+
source?: string;
|
|
64
|
+
provider: string;
|
|
65
|
+
environment?: string;
|
|
66
|
+
tier?: string;
|
|
67
|
+
strict?: boolean;
|
|
68
|
+
}): Promise<{
|
|
69
|
+
testCase: PromptTestCase<TResponse>;
|
|
70
|
+
rendered: unknown;
|
|
71
|
+
response?: TResponse;
|
|
72
|
+
}>;
|
|
16
73
|
|
|
17
|
-
export { createMockAsset, createMockResolvedAsset, parseTestPrompt };
|
|
74
|
+
export { type PromptTestCase, type PromptTestRenderer, type PromptTestSidecar, createHardcodedPromptResponder, createMockAsset, createMockResolvedAsset, getHardcodedPromptResponse, getPromptTestCase, loadPromptTestSidecar, parsePromptTestSidecar, parseTestPrompt, renderPromptTestCase };
|
package/dist/testing.d.ts
CHANGED
|
@@ -1,6 +1,26 @@
|
|
|
1
|
-
import { P as PromptAsset, R as ResolvedPromptAsset } from './schema-
|
|
1
|
+
import { P as PromptAsset, R as ResolvedPromptAsset } from './schema-B9A1zLRk.js';
|
|
2
2
|
import 'zod';
|
|
3
3
|
|
|
4
|
+
interface PromptTestCase<TResponse = unknown> {
|
|
5
|
+
name: string;
|
|
6
|
+
variables?: Record<string, string>;
|
|
7
|
+
response?: TResponse;
|
|
8
|
+
expected_response?: TResponse;
|
|
9
|
+
}
|
|
10
|
+
interface PromptTestSidecar<TResponse = unknown> {
|
|
11
|
+
cases: Array<PromptTestCase<TResponse>>;
|
|
12
|
+
}
|
|
13
|
+
interface PromptTestRenderer {
|
|
14
|
+
renderPrompt(options: {
|
|
15
|
+
path?: string;
|
|
16
|
+
source?: string;
|
|
17
|
+
provider: string;
|
|
18
|
+
environment?: string;
|
|
19
|
+
tier?: string;
|
|
20
|
+
variables?: Record<string, string>;
|
|
21
|
+
strict?: boolean;
|
|
22
|
+
}): Promise<unknown>;
|
|
23
|
+
}
|
|
4
24
|
/**
|
|
5
25
|
* Create a mock PromptAsset for testing.
|
|
6
26
|
*/
|
|
@@ -13,5 +33,42 @@ declare function createMockResolvedAsset(overrides?: Partial<ResolvedPromptAsset
|
|
|
13
33
|
* Parse an inline prompt string for testing.
|
|
14
34
|
*/
|
|
15
35
|
declare function parseTestPrompt(source: string): PromptAsset;
|
|
36
|
+
/**
|
|
37
|
+
* Parse a .test.yaml sidecar file.
|
|
38
|
+
*/
|
|
39
|
+
declare function parsePromptTestSidecar<TResponse = unknown>(source: string): PromptTestSidecar<TResponse>;
|
|
40
|
+
/**
|
|
41
|
+
* Load a .test.yaml sidecar file from disk.
|
|
42
|
+
*/
|
|
43
|
+
declare function loadPromptTestSidecar<TResponse = unknown>(filePath: string | URL): Promise<PromptTestSidecar<TResponse>>;
|
|
44
|
+
/**
|
|
45
|
+
* Find a named test case in a sidecar.
|
|
46
|
+
*/
|
|
47
|
+
declare function getPromptTestCase<TResponse = unknown>(sidecar: PromptTestSidecar<TResponse> | Array<PromptTestCase<TResponse>>, name: string): PromptTestCase<TResponse>;
|
|
48
|
+
/**
|
|
49
|
+
* Read the canned response for a named case.
|
|
50
|
+
*/
|
|
51
|
+
declare function getHardcodedPromptResponse<TResponse = unknown>(sidecar: PromptTestSidecar<TResponse> | Array<PromptTestCase<TResponse>>, name: string): TResponse;
|
|
52
|
+
/**
|
|
53
|
+
* Create a small responder for unit tests and local development flows.
|
|
54
|
+
*/
|
|
55
|
+
declare function createHardcodedPromptResponder<TResponse = unknown>(sidecar: PromptTestSidecar<TResponse> | Array<PromptTestCase<TResponse>>): (name: string) => TResponse;
|
|
56
|
+
/**
|
|
57
|
+
* Render a prompt using variables from a named sidecar case.
|
|
58
|
+
*/
|
|
59
|
+
declare function renderPromptTestCase<TResponse = unknown>(kit: PromptTestRenderer, options: {
|
|
60
|
+
sidecar: PromptTestSidecar<TResponse> | Array<PromptTestCase<TResponse>>;
|
|
61
|
+
caseName: string;
|
|
62
|
+
path?: string;
|
|
63
|
+
source?: string;
|
|
64
|
+
provider: string;
|
|
65
|
+
environment?: string;
|
|
66
|
+
tier?: string;
|
|
67
|
+
strict?: boolean;
|
|
68
|
+
}): Promise<{
|
|
69
|
+
testCase: PromptTestCase<TResponse>;
|
|
70
|
+
rendered: unknown;
|
|
71
|
+
response?: TResponse;
|
|
72
|
+
}>;
|
|
16
73
|
|
|
17
|
-
export { createMockAsset, createMockResolvedAsset, parseTestPrompt };
|
|
74
|
+
export { type PromptTestCase, type PromptTestRenderer, type PromptTestSidecar, createHardcodedPromptResponder, createMockAsset, createMockResolvedAsset, getHardcodedPromptResponse, getPromptTestCase, loadPromptTestSidecar, parsePromptTestSidecar, parseTestPrompt, renderPromptTestCase };
|