@vertesia/studio-utils 1.3.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/LICENSE +201 -0
- package/lib/index.d.ts +6 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +6 -0
- package/lib/index.js.map +1 -0
- package/lib/prompts/extract-vars.d.ts +19 -0
- package/lib/prompts/extract-vars.d.ts.map +1 -0
- package/lib/prompts/extract-vars.js +111 -0
- package/lib/prompts/extract-vars.js.map +1 -0
- package/lib/prompts/mock-data.d.ts +17 -0
- package/lib/prompts/mock-data.d.ts.map +1 -0
- package/lib/prompts/mock-data.js +52 -0
- package/lib/prompts/mock-data.js.map +1 -0
- package/lib/prompts/render.d.ts +52 -0
- package/lib/prompts/render.d.ts.map +1 -0
- package/lib/prompts/render.js +166 -0
- package/lib/prompts/render.js.map +1 -0
- package/lib/prompts/validate.d.ts +46 -0
- package/lib/prompts/validate.d.ts.map +1 -0
- package/lib/prompts/validate.js +167 -0
- package/lib/prompts/validate.js.map +1 -0
- package/lib/roles/classes.d.ts +59 -0
- package/lib/roles/classes.d.ts.map +1 -0
- package/lib/roles/classes.js +60 -0
- package/lib/roles/classes.js.map +1 -0
- package/lib/roles/content.d.ts +13 -0
- package/lib/roles/content.d.ts.map +1 -0
- package/lib/roles/content.js +39 -0
- package/lib/roles/content.js.map +1 -0
- package/lib/roles/index.d.ts +37 -0
- package/lib/roles/index.d.ts.map +1 -0
- package/lib/roles/index.js +87 -0
- package/lib/roles/index.js.map +1 -0
- package/lib/roles/system.d.ts +3 -0
- package/lib/roles/system.d.ts.map +1 -0
- package/lib/roles/system.js +187 -0
- package/lib/roles/system.js.map +1 -0
- package/lib/vertesia-studio-utils.js +2 -0
- package/lib/vertesia-studio-utils.js.map +1 -0
- package/package.json +50 -0
- package/src/index.ts +20 -0
- package/src/prompts/extract-vars.ts +110 -0
- package/src/prompts/mock-data.ts +63 -0
- package/src/prompts/render.test.ts +109 -0
- package/src/prompts/render.ts +192 -0
- package/src/prompts/validate.test.ts +274 -0
- package/src/prompts/validate.ts +216 -0
- package/src/roles/classes.ts +78 -0
- package/src/roles/content.ts +46 -0
- package/src/roles/index.test.ts +206 -0
- package/src/roles/index.ts +96 -0
- package/src/roles/system.ts +204 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { PromptSegmentDefType, TemplateType } from '@vertesia/common';
|
|
2
|
+
import { CompositeError, renderHandlebarsTemplate, renderJsTemplate } from '@vertesia/jst';
|
|
3
|
+
/**
|
|
4
|
+
* Render a prompt template with the given input data.
|
|
5
|
+
*
|
|
6
|
+
* Handlebars templates use {{variable}} interpolation against `data`.
|
|
7
|
+
* JST (JavaScript template) bodies evaluate against `data` with the schema's top-level
|
|
8
|
+
* property names exposed as globals, plus `_model` — the active model id, which the
|
|
9
|
+
* studio-server executor injects into the input as `{ ..._model: run.modelId }` when
|
|
10
|
+
* executing an interaction (see `apps/studio-server/src/executor/ExecutionRequest.ts`
|
|
11
|
+
* and `apps/studio-server/src/executor/rendering/template.ts`). Listing it here keeps
|
|
12
|
+
* the Playground preview and `validatePrompt` in sync with runtime resolution — a JST
|
|
13
|
+
* template referencing `_model` validates fine here and renders fine in production.
|
|
14
|
+
*
|
|
15
|
+
* For `TemplateType.text`, the content is returned verbatim — it is static text, not a
|
|
16
|
+
* template — matching the studio-server executor (see `apps/studio-server/src/executor/
|
|
17
|
+
* rendering/template.ts`). Routing it through the JST evaluator would compile the prose as
|
|
18
|
+
* JavaScript and throw on any plain sentence (e.g. "You are a helpful assistant.").
|
|
19
|
+
*/
|
|
20
|
+
export function renderTemplate(code, contentType, schema, data) {
|
|
21
|
+
if (contentType === TemplateType.handlebars) {
|
|
22
|
+
return renderHandlebarsTemplate(code, data);
|
|
23
|
+
}
|
|
24
|
+
if (contentType === TemplateType.text) {
|
|
25
|
+
return code;
|
|
26
|
+
}
|
|
27
|
+
const globals = [...(schema.properties ? Object.keys(schema.properties) : []), '_model'];
|
|
28
|
+
return renderJsTemplate(code, globals, data);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Execute a JST (JavaScript Template) with given data.
|
|
32
|
+
* Returns a discriminated union to surface errors without throwing.
|
|
33
|
+
*/
|
|
34
|
+
export function executeJST(jstContent, schema, data) {
|
|
35
|
+
try {
|
|
36
|
+
const result = renderTemplate(jstContent, TemplateType.jst, schema, data);
|
|
37
|
+
return { success: true, content: result };
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
return { success: false, error: error instanceof Error ? error.message : String(error) };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Execute a Handlebars template with given data.
|
|
45
|
+
* Returns a discriminated union to surface errors without throwing.
|
|
46
|
+
*/
|
|
47
|
+
export function executeHandlebars(handlebarsContent, schema, data) {
|
|
48
|
+
try {
|
|
49
|
+
const result = renderTemplate(handlebarsContent, TemplateType.handlebars, schema, data);
|
|
50
|
+
return { success: true, content: result };
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
return { success: false, error: error instanceof Error ? error.message : String(error) };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function renderChatSegment(segment, data) {
|
|
57
|
+
const chatKey = getChatKey(segment.configuration);
|
|
58
|
+
const chat = data[chatKey];
|
|
59
|
+
let content;
|
|
60
|
+
if (Array.isArray(chat)) {
|
|
61
|
+
content = JSON.stringify(chat, undefined, 2);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
content = `No "${chatKey}" property found on input data`;
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
title: 'Chat history',
|
|
68
|
+
content,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function renderTemplateSegment(segment, data) {
|
|
72
|
+
if (!segment.template) {
|
|
73
|
+
return { title: '(unknown segment)', content: '', error: new Error('Prompt segment is missing its template') };
|
|
74
|
+
}
|
|
75
|
+
const schema = segment.template.inputSchema || {};
|
|
76
|
+
const title = `@${segment.template.role}`;
|
|
77
|
+
try {
|
|
78
|
+
const content = renderTemplate(segment.template.content, segment.template.content_type, schema, data);
|
|
79
|
+
return { title, content, segmentId: segment.template.id };
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
// Isolate the failure to this segment so the remaining segments still render.
|
|
83
|
+
return {
|
|
84
|
+
title,
|
|
85
|
+
content: error instanceof Error ? error.message : String(error),
|
|
86
|
+
segmentId: segment.template.id,
|
|
87
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
export function renderSegments(segments, data) {
|
|
92
|
+
const out = [];
|
|
93
|
+
for (const segment of segments) {
|
|
94
|
+
if (segment.type === PromptSegmentDefType.chat) {
|
|
95
|
+
out.push(renderChatSegment(segment, data));
|
|
96
|
+
}
|
|
97
|
+
else if (segment.type === PromptSegmentDefType.template) {
|
|
98
|
+
out.push(renderTemplateSegment(segment, data));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return out;
|
|
102
|
+
}
|
|
103
|
+
export function renderSegmentsOrErrors(segments, textOrObject) {
|
|
104
|
+
try {
|
|
105
|
+
return renderSegments(segments, typeof textOrObject === 'string' ? JSON.parse(textOrObject.trim()) : textOrObject || {});
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
if (error instanceof CompositeError) {
|
|
109
|
+
return error.errors.map((err) => ({
|
|
110
|
+
error: err,
|
|
111
|
+
title: 'Rendering Error',
|
|
112
|
+
content: err.message,
|
|
113
|
+
}));
|
|
114
|
+
}
|
|
115
|
+
return [
|
|
116
|
+
{
|
|
117
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
118
|
+
title: 'Rendering Error',
|
|
119
|
+
content: error instanceof Error ? error.message : String(error),
|
|
120
|
+
},
|
|
121
|
+
];
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
export function renderPrompt(segments, payload) {
|
|
125
|
+
const out = [];
|
|
126
|
+
for (const segment of segments) {
|
|
127
|
+
if (segment.template) {
|
|
128
|
+
const schema = segment.template.inputSchema || {};
|
|
129
|
+
const content = renderTemplate(segment.template.content, segment.template.content_type, schema, payload);
|
|
130
|
+
out.push({ role: segment.template.role, content });
|
|
131
|
+
}
|
|
132
|
+
else if (segment.type === PromptSegmentDefType.chat) {
|
|
133
|
+
const chatKey = getChatKey(segment.configuration);
|
|
134
|
+
const messages = payload[chatKey];
|
|
135
|
+
if (!isPromptSegmentArray(messages)) {
|
|
136
|
+
throw new Error('Chat prompt segment must have a messages array');
|
|
137
|
+
}
|
|
138
|
+
for (const msg of messages) {
|
|
139
|
+
if (!msg.role) {
|
|
140
|
+
throw new Error('Chat prompt segment must have a role');
|
|
141
|
+
}
|
|
142
|
+
out.push({ role: msg.role, content: msg.content });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
throw new Error(`Unknown prompt segment type: ${segment.type}`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return out;
|
|
150
|
+
}
|
|
151
|
+
function getChatKey(configuration) {
|
|
152
|
+
if (typeof configuration === 'object' &&
|
|
153
|
+
configuration !== null &&
|
|
154
|
+
'chatKey' in configuration &&
|
|
155
|
+
typeof configuration.chatKey === 'string') {
|
|
156
|
+
return configuration.chatKey;
|
|
157
|
+
}
|
|
158
|
+
return 'chat';
|
|
159
|
+
}
|
|
160
|
+
function isPromptSegmentArray(value) {
|
|
161
|
+
return Array.isArray(value) && value.every(isPromptSegment);
|
|
162
|
+
}
|
|
163
|
+
function isPromptSegment(value) {
|
|
164
|
+
return typeof value === 'object' && value !== null && 'role' in value && 'content' in value;
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/prompts/render.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type JSONSchema, TemplateType } from '@vertesia/common';
|
|
2
|
+
export type PromptValidationIssueType = 'undeclared_template_variable' | 'unused_schema_variable' | 'handlebars_render_error' | 'jst_unsafe_construct' | 'jst_render_error';
|
|
3
|
+
export type PromptValidationIssueSeverity = 'error' | 'warning';
|
|
4
|
+
export interface PromptValidationIssue {
|
|
5
|
+
/** Discriminator for issue kind */
|
|
6
|
+
type: PromptValidationIssueType;
|
|
7
|
+
/** Errors should block prompt creation; warnings are informational. */
|
|
8
|
+
severity: PromptValidationIssueSeverity;
|
|
9
|
+
/** The variable name when the issue is variable-related. */
|
|
10
|
+
variable?: string;
|
|
11
|
+
/** Human-readable message; safe to surface directly to an LLM tool error. */
|
|
12
|
+
message: string;
|
|
13
|
+
}
|
|
14
|
+
export interface PromptValidationResult {
|
|
15
|
+
/** Flat list of issues found. `severity` discriminates errors (blocking) from warnings. */
|
|
16
|
+
issues: PromptValidationIssue[];
|
|
17
|
+
/** Count of `severity: 'error'` entries in `issues`. Zero ⇔ validation passed. */
|
|
18
|
+
error_count: number;
|
|
19
|
+
/** Count of `severity: 'warning'` entries in `issues`. Non-blocking informational findings. */
|
|
20
|
+
warning_count: number;
|
|
21
|
+
}
|
|
22
|
+
export interface PromptValidationInput {
|
|
23
|
+
/** The prompt's template source. */
|
|
24
|
+
content: string;
|
|
25
|
+
/** Template language. `handlebars` and `jst` are validated; `text` passes through. */
|
|
26
|
+
contentType: TemplateType;
|
|
27
|
+
/** JSON Schema declaring the variables the template expects. */
|
|
28
|
+
inputSchema?: JSONSchema;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Validate a single prompt template against its declared input schema.
|
|
32
|
+
*
|
|
33
|
+
* For `handlebars` and `jst` templates, the following checks are performed:
|
|
34
|
+
* 1. Every variable referenced in the template must be declared as a top-level property
|
|
35
|
+
* in `inputSchema.properties` (else → `undeclared_template_variable` error).
|
|
36
|
+
* 2. Every property declared in `inputSchema.properties` should be referenced by the template
|
|
37
|
+
* (else → `unused_schema_variable` warning — non-blocking).
|
|
38
|
+
* 3. The template must render successfully against schema-derived mock data
|
|
39
|
+
* (else → `handlebars_render_error` / `jst_render_error` error).
|
|
40
|
+
* 4. For JST only: unsafe constructs (`with`, `for`, `while`, `import`, class, `this`,
|
|
41
|
+
* dynamic property lookup, blacklisted props) → `jst_unsafe_construct` error.
|
|
42
|
+
*
|
|
43
|
+
* `text` content type passes through with no issues.
|
|
44
|
+
*/
|
|
45
|
+
export declare function validatePrompt(input: PromptValidationInput): PromptValidationResult;
|
|
46
|
+
//# sourceMappingURL=validate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/prompts/validate.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,UAAU,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAMjE,MAAM,MAAM,yBAAyB,GAC/B,8BAA8B,GAC9B,wBAAwB,GACxB,yBAAyB,GACzB,sBAAsB,GACtB,kBAAkB,CAAC;AAEzB,MAAM,MAAM,6BAA6B,GAAG,OAAO,GAAG,SAAS,CAAC;AAEhE,MAAM,WAAW,qBAAqB;IAClC,mCAAmC;IACnC,IAAI,EAAE,yBAAyB,CAAC;IAChC,uEAAuE;IACvE,QAAQ,EAAE,6BAA6B,CAAC;IACxC,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,sBAAsB;IACnC,2FAA2F;IAC3F,MAAM,EAAE,qBAAqB,EAAE,CAAC;IAChC,kFAAkF;IAClF,WAAW,EAAE,MAAM,CAAC;IACpB,+FAA+F;IAC/F,aAAa,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,qBAAqB;IAClC,oCAAoC;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,sFAAsF;IACtF,WAAW,EAAE,YAAY,CAAC;IAC1B,gEAAgE;IAChE,WAAW,CAAC,EAAE,UAAU,CAAC;CAC5B;AAkJD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,qBAAqB,GAAG,sBAAsB,CAWnF"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { TemplateType } from '@vertesia/common';
|
|
2
|
+
import { getFreeVariables, renderJsTemplate } from '@vertesia/jst';
|
|
3
|
+
import { extractHandlebarsVariables } from './extract-vars.js';
|
|
4
|
+
import { generateMockData } from './mock-data.js';
|
|
5
|
+
import { executeHandlebars } from './render.js';
|
|
6
|
+
// JST's renderJsTemplate auto-adds `_` (helpers object) and runtime injects `Set` and `Array`
|
|
7
|
+
// — treat them as globals so they don't appear as free vars in user templates.
|
|
8
|
+
// Globals always available to JST templates regardless of schema:
|
|
9
|
+
// - `_`, `Array`, `Set`: runtime-injected by `renderJsTemplate` (jst library)
|
|
10
|
+
// - `_model`: runtime-injected by the studio-server executor as `{ ..._model: run.modelId }`
|
|
11
|
+
// (see ExecutionRequest.ts:313 and executor/rendering/template.ts:13)
|
|
12
|
+
// Keeping these in sync with `renderTemplate` in ./render.ts so a JST template that runs in
|
|
13
|
+
// production also passes the validator.
|
|
14
|
+
const JST_AUTO_GLOBALS = ['_', 'Array', 'Set', '_model'];
|
|
15
|
+
function countSeverities(issues) {
|
|
16
|
+
let error_count = 0;
|
|
17
|
+
let warning_count = 0;
|
|
18
|
+
for (const issue of issues) {
|
|
19
|
+
if (issue.severity === 'error') {
|
|
20
|
+
error_count++;
|
|
21
|
+
}
|
|
22
|
+
else if (issue.severity === 'warning') {
|
|
23
|
+
warning_count++;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return { error_count, warning_count };
|
|
27
|
+
}
|
|
28
|
+
function validateHandlebarsPrompt(content, inputSchema) {
|
|
29
|
+
const issues = [];
|
|
30
|
+
const usedVars = extractHandlebarsVariables(content);
|
|
31
|
+
const declaredVars = new Set(inputSchema?.properties ? Object.keys(inputSchema.properties) : []);
|
|
32
|
+
for (const used of usedVars) {
|
|
33
|
+
if (!declaredVars.has(used)) {
|
|
34
|
+
issues.push({
|
|
35
|
+
type: 'undeclared_template_variable',
|
|
36
|
+
severity: 'error',
|
|
37
|
+
variable: used,
|
|
38
|
+
message: `Template references variable '{{${used}}}' but it is not declared in input_schema.properties. Add '${used}' to the schema with an appropriate type.`,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
for (const declared of declaredVars) {
|
|
43
|
+
if (!usedVars.has(declared)) {
|
|
44
|
+
issues.push({
|
|
45
|
+
type: 'unused_schema_variable',
|
|
46
|
+
severity: 'warning',
|
|
47
|
+
variable: declared,
|
|
48
|
+
message: `Schema declares property '${declared}' but the template never references it. Remove it from input_schema or use it via {{${declared}}}.`,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Render-time smoke test — always runs so syntax errors and failing helper calls are
|
|
53
|
+
// surfaced even when undeclared-variable errors are already in the list. Handlebars renders
|
|
54
|
+
// missing vars as empty strings (non-strict by default), so the render check does NOT echo
|
|
55
|
+
// the var errors — anything it reports is a distinct template problem worth showing.
|
|
56
|
+
const renderSchema = inputSchema ?? {};
|
|
57
|
+
const mockData = generateMockData(renderSchema);
|
|
58
|
+
const mockObject = typeof mockData === 'object' && mockData !== null && !Array.isArray(mockData) ? mockData : {};
|
|
59
|
+
const renderResult = executeHandlebars(content, renderSchema, mockObject);
|
|
60
|
+
if (!renderResult.success) {
|
|
61
|
+
issues.push({
|
|
62
|
+
type: 'handlebars_render_error',
|
|
63
|
+
severity: 'error',
|
|
64
|
+
message: `Handlebars rendering failed: ${renderResult.error}`,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return issues;
|
|
68
|
+
}
|
|
69
|
+
function validateJstPrompt(content, inputSchema) {
|
|
70
|
+
const issues = [];
|
|
71
|
+
const declaredVars = new Set(inputSchema?.properties ? Object.keys(inputSchema.properties) : []);
|
|
72
|
+
let referenced;
|
|
73
|
+
try {
|
|
74
|
+
const result = getFreeVariables(content, {
|
|
75
|
+
globals: JST_AUTO_GLOBALS,
|
|
76
|
+
acorn: { allowReturnOutsideFunction: true, locations: true },
|
|
77
|
+
});
|
|
78
|
+
referenced = result.vars;
|
|
79
|
+
for (const err of result.errors) {
|
|
80
|
+
issues.push({
|
|
81
|
+
type: 'jst_unsafe_construct',
|
|
82
|
+
severity: 'error',
|
|
83
|
+
message: `JST validation error at ${err.location}: ${err.message}`,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch (parseError) {
|
|
88
|
+
// Acorn parse failure — surface as render error since the template can't be compiled.
|
|
89
|
+
issues.push({
|
|
90
|
+
type: 'jst_render_error',
|
|
91
|
+
severity: 'error',
|
|
92
|
+
message: `JST parse failed: ${parseError instanceof Error ? parseError.message : String(parseError)}`,
|
|
93
|
+
});
|
|
94
|
+
return issues;
|
|
95
|
+
}
|
|
96
|
+
for (const used of referenced) {
|
|
97
|
+
if (!declaredVars.has(used)) {
|
|
98
|
+
issues.push({
|
|
99
|
+
type: 'undeclared_template_variable',
|
|
100
|
+
severity: 'error',
|
|
101
|
+
variable: used,
|
|
102
|
+
message: `Template references variable '${used}' but it is not declared in input_schema.properties. Add '${used}' to the schema with an appropriate type.`,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
for (const declared of declaredVars) {
|
|
107
|
+
if (!referenced.has(declared)) {
|
|
108
|
+
issues.push({
|
|
109
|
+
type: 'unused_schema_variable',
|
|
110
|
+
severity: 'warning',
|
|
111
|
+
variable: declared,
|
|
112
|
+
message: `Schema declares property '${declared}' but the template never references it. Remove it from input_schema or use it in the template.`,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Render-time smoke test — only if there are no blocking errors so far, otherwise
|
|
117
|
+
// the failure mode would just echo what we already reported.
|
|
118
|
+
const blockingSoFar = issues.some((i) => i.severity === 'error');
|
|
119
|
+
if (!blockingSoFar) {
|
|
120
|
+
const renderSchema = inputSchema ?? {};
|
|
121
|
+
const mockData = generateMockData(renderSchema);
|
|
122
|
+
const mockObject = typeof mockData === 'object' && mockData !== null && !Array.isArray(mockData)
|
|
123
|
+
? mockData
|
|
124
|
+
: {};
|
|
125
|
+
try {
|
|
126
|
+
renderJsTemplate(content, [...declaredVars], mockObject);
|
|
127
|
+
}
|
|
128
|
+
catch (renderError) {
|
|
129
|
+
issues.push({
|
|
130
|
+
type: 'jst_render_error',
|
|
131
|
+
severity: 'error',
|
|
132
|
+
message: `JST rendering failed: ${renderError instanceof Error ? renderError.message : String(renderError)}`,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return issues;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Validate a single prompt template against its declared input schema.
|
|
140
|
+
*
|
|
141
|
+
* For `handlebars` and `jst` templates, the following checks are performed:
|
|
142
|
+
* 1. Every variable referenced in the template must be declared as a top-level property
|
|
143
|
+
* in `inputSchema.properties` (else → `undeclared_template_variable` error).
|
|
144
|
+
* 2. Every property declared in `inputSchema.properties` should be referenced by the template
|
|
145
|
+
* (else → `unused_schema_variable` warning — non-blocking).
|
|
146
|
+
* 3. The template must render successfully against schema-derived mock data
|
|
147
|
+
* (else → `handlebars_render_error` / `jst_render_error` error).
|
|
148
|
+
* 4. For JST only: unsafe constructs (`with`, `for`, `while`, `import`, class, `this`,
|
|
149
|
+
* dynamic property lookup, blacklisted props) → `jst_unsafe_construct` error.
|
|
150
|
+
*
|
|
151
|
+
* `text` content type passes through with no issues.
|
|
152
|
+
*/
|
|
153
|
+
export function validatePrompt(input) {
|
|
154
|
+
let issues;
|
|
155
|
+
if (input.contentType === TemplateType.handlebars) {
|
|
156
|
+
issues = validateHandlebarsPrompt(input.content, input.inputSchema);
|
|
157
|
+
}
|
|
158
|
+
else if (input.contentType === TemplateType.jst) {
|
|
159
|
+
issues = validateJstPrompt(input.content, input.inputSchema);
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
issues = [];
|
|
163
|
+
}
|
|
164
|
+
const { error_count, warning_count } = countSeverities(issues);
|
|
165
|
+
return { issues, error_count, warning_count };
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/prompts/validate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { AbacScope, Permission, RoleDomain } from '@vertesia/common';
|
|
2
|
+
/**
|
|
3
|
+
* Class hierarchy and registry-bound interface for the role system. These
|
|
4
|
+
* are LOGIC — they have runtime behavior (constructors, instance methods,
|
|
5
|
+
* subclass dispatch via `instanceof`). They live in `@vertesia/studio-utils`
|
|
6
|
+
* (not common) per the package-layering contract: only types stay in common.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* A role: a named bundle of permissions that ACEs reference by name.
|
|
10
|
+
*
|
|
11
|
+
* Generic over the permission type so subclasses can tighten it:
|
|
12
|
+
* - **System roles** declare `extends Role<Permission>` — construction time
|
|
13
|
+
* type-checks against the central `Permission` enum.
|
|
14
|
+
* - **ABAC roles** (`AbacRole`) declare `extends Role<string>` — permissions
|
|
15
|
+
* are bare verbs (`'read'`, `'write'`, `'delete'`, future domain-specific
|
|
16
|
+
* verbs) consumed by the JWT generator to form `{scope}:{verb}` keys in
|
|
17
|
+
* `content_security`.
|
|
18
|
+
*
|
|
19
|
+
* The registry stores `Role` (defaulting to `Role<string>`) — the loose type
|
|
20
|
+
* is the lowest common denominator. Tight typing is only enforced at
|
|
21
|
+
* declaration sites of the role subclasses.
|
|
22
|
+
*/
|
|
23
|
+
export declare class Role<PermissionType extends string = string> {
|
|
24
|
+
name: string;
|
|
25
|
+
domain: RoleDomain;
|
|
26
|
+
permissions: Set<PermissionType>;
|
|
27
|
+
constructor(name: string, permissions: PermissionType[], domain: RoleDomain);
|
|
28
|
+
hasPermission(permission: PermissionType): boolean;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Base class for built-in system roles. Hardcodes `domain: 'system'` and
|
|
32
|
+
* specializes `Role<Permission>` so subclasses get compile-time type-checking
|
|
33
|
+
* against the central `Permission` enum at construction.
|
|
34
|
+
*/
|
|
35
|
+
export declare class SystemRole extends Role<Permission> {
|
|
36
|
+
constructor(name: string, permissions: Permission[]);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* A role usable in ContentSet ACEs. Adds `applicableScopes` — the kinds of
|
|
40
|
+
* objects the role can be applied to at the ABAC scope level. Inherits
|
|
41
|
+
* `Role<string>` because ABAC verbs aren't constrained to the central
|
|
42
|
+
* `Permission` enum.
|
|
43
|
+
*
|
|
44
|
+
* The IAM UI filters via `listAbacRolesForScope` which checks `instanceof AbacRole`.
|
|
45
|
+
*/
|
|
46
|
+
export declare class AbacRole extends Role<string> {
|
|
47
|
+
applicableScopes: readonly AbacScope[];
|
|
48
|
+
constructor(name: string, permissions: string[], domain: RoleDomain, applicableScopes: readonly AbacScope[]);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* A logical bucket of roles owned by a single domain. The registry iterates
|
|
52
|
+
* partitions in registration order — first match wins. The `system` partition
|
|
53
|
+
* is registered first so domain partitions cannot shadow built-in system roles.
|
|
54
|
+
*/
|
|
55
|
+
export interface RolePartition {
|
|
56
|
+
domain: RoleDomain;
|
|
57
|
+
roles: Map<string, Role>;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=classes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classes.d.ts","sourceRoot":"","sources":["../../src/roles/classes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE1E;;;;;GAKG;AAEH;;;;;;;;;;;;;;GAcG;AACH,qBAAa,IAAI,CAAC,cAAc,SAAS,MAAM,GAAG,MAAM;IAGzC,IAAI,EAAE,MAAM;IAEZ,MAAM,EAAE,UAAU;IAJ7B,WAAW,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC;gBAEtB,IAAI,EAAE,MAAM,EACnB,WAAW,EAAE,cAAc,EAAE,EACtB,MAAM,EAAE,UAAU;IAK7B,aAAa,CAAC,UAAU,EAAE,cAAc;CAG3C;AAED;;;;GAIG;AACH,qBAAa,UAAW,SAAQ,IAAI,CAAC,UAAU,CAAC;gBAChC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE;CAGtD;AAED;;;;;;;GAOG;AACH,qBAAa,QAAS,SAAQ,IAAI,CAAC,MAAM,CAAC;IAK3B,gBAAgB,EAAE,SAAS,SAAS,EAAE;gBAH7C,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EAAE,EACrB,MAAM,EAAE,UAAU,EACX,gBAAgB,EAAE,SAAS,SAAS,EAAE;CAIpD;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC1B,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;CAC5B"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Class hierarchy and registry-bound interface for the role system. These
|
|
3
|
+
* are LOGIC — they have runtime behavior (constructors, instance methods,
|
|
4
|
+
* subclass dispatch via `instanceof`). They live in `@vertesia/studio-utils`
|
|
5
|
+
* (not common) per the package-layering contract: only types stay in common.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* A role: a named bundle of permissions that ACEs reference by name.
|
|
9
|
+
*
|
|
10
|
+
* Generic over the permission type so subclasses can tighten it:
|
|
11
|
+
* - **System roles** declare `extends Role<Permission>` — construction time
|
|
12
|
+
* type-checks against the central `Permission` enum.
|
|
13
|
+
* - **ABAC roles** (`AbacRole`) declare `extends Role<string>` — permissions
|
|
14
|
+
* are bare verbs (`'read'`, `'write'`, `'delete'`, future domain-specific
|
|
15
|
+
* verbs) consumed by the JWT generator to form `{scope}:{verb}` keys in
|
|
16
|
+
* `content_security`.
|
|
17
|
+
*
|
|
18
|
+
* The registry stores `Role` (defaulting to `Role<string>`) — the loose type
|
|
19
|
+
* is the lowest common denominator. Tight typing is only enforced at
|
|
20
|
+
* declaration sites of the role subclasses.
|
|
21
|
+
*/
|
|
22
|
+
export class Role {
|
|
23
|
+
name;
|
|
24
|
+
domain;
|
|
25
|
+
permissions;
|
|
26
|
+
constructor(name, permissions, domain) {
|
|
27
|
+
this.name = name;
|
|
28
|
+
this.domain = domain;
|
|
29
|
+
this.permissions = new Set(permissions);
|
|
30
|
+
}
|
|
31
|
+
hasPermission(permission) {
|
|
32
|
+
return this.permissions.has(permission);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Base class for built-in system roles. Hardcodes `domain: 'system'` and
|
|
37
|
+
* specializes `Role<Permission>` so subclasses get compile-time type-checking
|
|
38
|
+
* against the central `Permission` enum at construction.
|
|
39
|
+
*/
|
|
40
|
+
export class SystemRole extends Role {
|
|
41
|
+
constructor(name, permissions) {
|
|
42
|
+
super(name, permissions, 'system');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* A role usable in ContentSet ACEs. Adds `applicableScopes` — the kinds of
|
|
47
|
+
* objects the role can be applied to at the ABAC scope level. Inherits
|
|
48
|
+
* `Role<string>` because ABAC verbs aren't constrained to the central
|
|
49
|
+
* `Permission` enum.
|
|
50
|
+
*
|
|
51
|
+
* The IAM UI filters via `listAbacRolesForScope` which checks `instanceof AbacRole`.
|
|
52
|
+
*/
|
|
53
|
+
export class AbacRole extends Role {
|
|
54
|
+
applicableScopes;
|
|
55
|
+
constructor(name, permissions, domain, applicableScopes) {
|
|
56
|
+
super(name, permissions, domain);
|
|
57
|
+
this.applicableScopes = applicableScopes;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=classes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classes.js","sourceRoot":"","sources":["../../src/roles/classes.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAEH;;;;;;;;;;;;;;GAcG;AACH,MAAM,OAAO,IAAI;IAGF;IAEA;IAJX,WAAW,CAAsB;IACjC,YACW,IAAY,EACnB,WAA6B,EACtB,MAAkB;QAFlB,SAAI,GAAJ,IAAI,CAAQ;QAEZ,WAAM,GAAN,MAAM,CAAY;QAEzB,IAAI,CAAC,WAAW,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC;IAC5C,CAAC;IAED,aAAa,CAAC,UAA0B;QACpC,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAC5C,CAAC;CACJ;AAED;;;;GAIG;AACH,MAAM,OAAO,UAAW,SAAQ,IAAgB;IAC5C,YAAY,IAAY,EAAE,WAAyB;QAC/C,KAAK,CAAC,IAAI,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC;IACvC,CAAC;CACJ;AAED;;;;;;;GAOG;AACH,MAAM,OAAO,QAAS,SAAQ,IAAY;IAK3B;IAJX,YACI,IAAY,EACZ,WAAqB,EACrB,MAAkB,EACX,gBAAsC;QAE7C,KAAK,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;QAF1B,qBAAgB,GAAhB,gBAAgB,CAAsB;IAGjD,CAAC;CACJ"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type RolePartition } from './classes.js';
|
|
2
|
+
/**
|
|
3
|
+
* Names of roles owned by the `content` domain. Apply to ContentSet ACEs
|
|
4
|
+
* scoped to either `document` or `collection` — the semantics of "read
|
|
5
|
+
* content" are the same for both kinds.
|
|
6
|
+
*/
|
|
7
|
+
export declare enum ContentRoleNames {
|
|
8
|
+
content_reader = "content_reader",
|
|
9
|
+
content_writer = "content_writer",
|
|
10
|
+
content_manager = "content_manager"
|
|
11
|
+
}
|
|
12
|
+
export declare const contentPartition: RolePartition;
|
|
13
|
+
//# sourceMappingURL=content.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../../src/roles/content.ts"],"names":[],"mappings":"AACA,OAAO,EAAuB,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AAMvE;;;;GAIG;AACH,oBAAY,gBAAgB;IACxB,cAAc,mBAAmB;IACjC,cAAc,mBAAmB;IACjC,eAAe,oBAAoB;CACtC;AA0BD,eAAO,MAAM,gBAAgB,EAAE,aAG9B,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { AbacRole } from './classes.js';
|
|
2
|
+
const ContentRoleDomain = 'content';
|
|
3
|
+
const APPLICABLE_SCOPES = ['document', 'collection'];
|
|
4
|
+
/**
|
|
5
|
+
* Names of roles owned by the `content` domain. Apply to ContentSet ACEs
|
|
6
|
+
* scoped to either `document` or `collection` — the semantics of "read
|
|
7
|
+
* content" are the same for both kinds.
|
|
8
|
+
*/
|
|
9
|
+
export var ContentRoleNames;
|
|
10
|
+
(function (ContentRoleNames) {
|
|
11
|
+
ContentRoleNames["content_reader"] = "content_reader";
|
|
12
|
+
ContentRoleNames["content_writer"] = "content_writer";
|
|
13
|
+
ContentRoleNames["content_manager"] = "content_manager";
|
|
14
|
+
})(ContentRoleNames || (ContentRoleNames = {}));
|
|
15
|
+
class ContentReaderRole extends AbacRole {
|
|
16
|
+
constructor() {
|
|
17
|
+
super(ContentRoleNames.content_reader, ['read'], ContentRoleDomain, APPLICABLE_SCOPES);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
class ContentWriterRole extends AbacRole {
|
|
21
|
+
constructor() {
|
|
22
|
+
super(ContentRoleNames.content_writer, ['read', 'write'], ContentRoleDomain, APPLICABLE_SCOPES);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
class ContentManagerRole extends AbacRole {
|
|
26
|
+
constructor() {
|
|
27
|
+
super(ContentRoleNames.content_manager, ['read', 'write', 'delete'], ContentRoleDomain, APPLICABLE_SCOPES);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const contentRoles = {
|
|
31
|
+
[ContentRoleNames.content_reader]: new ContentReaderRole(),
|
|
32
|
+
[ContentRoleNames.content_writer]: new ContentWriterRole(),
|
|
33
|
+
[ContentRoleNames.content_manager]: new ContentManagerRole(),
|
|
34
|
+
};
|
|
35
|
+
export const contentPartition = {
|
|
36
|
+
domain: ContentRoleDomain,
|
|
37
|
+
roles: new Map(Object.entries(contentRoles)),
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=content.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content.js","sourceRoot":"","sources":["../../src/roles/content.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAiC,MAAM,cAAc,CAAC;AAEvE,MAAM,iBAAiB,GAAe,SAAS,CAAC;AAEhD,MAAM,iBAAiB,GAAyB,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;AAE3E;;;;GAIG;AACH,MAAM,CAAN,IAAY,gBAIX;AAJD,WAAY,gBAAgB;IACxB,qDAAiC,CAAA;IACjC,qDAAiC,CAAA;IACjC,uDAAmC,CAAA;AACvC,CAAC,EAJW,gBAAgB,KAAhB,gBAAgB,QAI3B;AAED,MAAM,iBAAkB,SAAQ,QAAQ;IACpC;QACI,KAAK,CAAC,gBAAgB,CAAC,cAAc,EAAE,CAAC,MAAM,CAAC,EAAE,iBAAiB,EAAE,iBAAiB,CAAC,CAAC;IAC3F,CAAC;CACJ;AAED,MAAM,iBAAkB,SAAQ,QAAQ;IACpC;QACI,KAAK,CAAC,gBAAgB,CAAC,cAAc,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,EAAE,iBAAiB,CAAC,CAAC;IACpG,CAAC;CACJ;AAED,MAAM,kBAAmB,SAAQ,QAAQ;IACrC;QACI,KAAK,CAAC,gBAAgB,CAAC,eAAe,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,iBAAiB,EAAE,iBAAiB,CAAC,CAAC;IAC/G,CAAC;CACJ;AAED,MAAM,YAAY,GAAmC;IACjD,CAAC,gBAAgB,CAAC,cAAc,CAAC,EAAE,IAAI,iBAAiB,EAAE;IAC1D,CAAC,gBAAgB,CAAC,cAAc,CAAC,EAAE,IAAI,iBAAiB,EAAE;IAC1D,CAAC,gBAAgB,CAAC,eAAe,CAAC,EAAE,IAAI,kBAAkB,EAAE;CAC/D,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAkB;IAC3C,MAAM,EAAE,iBAAiB;IACzB,KAAK,EAAE,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;CAC/C,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { AbacScope, RoleDomain } from '@vertesia/common';
|
|
2
|
+
import { AbacRole, type Role, SystemRole } from './classes.js';
|
|
3
|
+
export { AbacRole, Role, type RolePartition, SystemRole } from './classes.js';
|
|
4
|
+
export { ContentRoleNames } from './content.js';
|
|
5
|
+
/** Look up a role by its name across all registered partitions. */
|
|
6
|
+
export declare function getRoleByName(name: string): Role;
|
|
7
|
+
/** List every role across all partitions, in partition registration order. */
|
|
8
|
+
export declare function listRoles(): Role[];
|
|
9
|
+
/** Roles owned by a specific domain (e.g. `'system'`, `'content'`). */
|
|
10
|
+
export declare function listRolesByDomain(domain: RoleDomain): Role[];
|
|
11
|
+
/**
|
|
12
|
+
* ABAC roles applicable to a given ContentSet scope (e.g. `'document'`,
|
|
13
|
+
* `'collection'`). System roles are excluded — they don't carry scope semantics.
|
|
14
|
+
*/
|
|
15
|
+
export declare function listAbacRolesForScope(scope: AbacScope): AbacRole[];
|
|
16
|
+
/** Shortcut for the system partition: returns only `SystemRole` instances. */
|
|
17
|
+
export declare function listSystemRoles(): SystemRole[];
|
|
18
|
+
/** Names of every registered role across all partitions — suited for mongoose schema enum constraints. */
|
|
19
|
+
export declare function getAllRoleNames(): string[];
|
|
20
|
+
/**
|
|
21
|
+
* Merge the permissions granted by a set of roles into a single array.
|
|
22
|
+
* Intended for the system-role gating path. For ABAC roles, the bare-verb
|
|
23
|
+
* permissions returned here aren't directly meaningful — use the JWT
|
|
24
|
+
* `content_security` pathway instead.
|
|
25
|
+
*/
|
|
26
|
+
export declare function getPermissionsForRoles(roleNames: Iterable<string>): string[];
|
|
27
|
+
/**
|
|
28
|
+
* A list of roles with a unified `hasPermission` check across them.
|
|
29
|
+
*/
|
|
30
|
+
export declare class RoleList {
|
|
31
|
+
readonly roles: Role[];
|
|
32
|
+
private constructor();
|
|
33
|
+
static fromRoleNames(roleNames: string[]): RoleList;
|
|
34
|
+
static fromRoleName(roleName: string): RoleList;
|
|
35
|
+
hasPermission(perm: string): boolean;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/roles/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,KAAK,IAAI,EAAsB,UAAU,EAAE,MAAM,cAAc,CAAC;AAInF,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,aAAa,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC9E,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAShD,mEAAmE;AACnE,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAMhD;AAED,8EAA8E;AAC9E,wBAAgB,SAAS,IAAI,IAAI,EAAE,CAQlC;AAED,uEAAuE;AACvE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,EAAE,CAG5D;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,SAAS,GAAG,QAAQ,EAAE,CAElE;AAED,8EAA8E;AAC9E,wBAAgB,eAAe,IAAI,UAAU,EAAE,CAE9C;AAED,0GAA0G;AAC1G,wBAAgB,eAAe,IAAI,MAAM,EAAE,CAE1C;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,MAAM,EAAE,CAS5E;AAED;;GAEG;AACH,qBAAa,QAAQ;aACmB,KAAK,EAAE,IAAI,EAAE;IAAjD,OAAO;IAEP,MAAM,CAAC,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,QAAQ;IAKnD,MAAM,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ;IAK/C,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;CAGvC"}
|