@vertesia/studio-utils 1.5.0-dev.20260807.073259Z → 1.5.0-dev.20260826.064249Z

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.
@@ -1,2 +1,2 @@
1
- export { matchConditions, matchLike, resolvePath } from './match.js';
1
+ export { matchConditions, matchLike, resolveConditions, resolvePath } from './match.js';
2
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/conditions/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/conditions/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
@@ -1,2 +1,2 @@
1
- export { matchConditions, matchLike, resolvePath } from './match.js';
1
+ export { matchConditions, matchLike, resolveConditions, resolvePath } from './match.js';
2
2
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/conditions/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/conditions/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
@@ -46,9 +46,16 @@ export declare function matchLike(value: unknown, pattern: string): boolean;
46
46
  * Unknown operators emit a warning via the studio-utils logger and return false — surfacing
47
47
  * misconfiguration loudly without breaking the surrounding token mint or permission check.
48
48
  *
49
- * `$principal.X` substitutions in conditions are expected to have already been resolved at
50
- * JWT-mint time by `resolveConditions` in token-server; values reaching this function are
51
- * concrete.
49
+ * `$principal.X` substitutions in conditions are expected to have already been resolved by
50
+ * {@link resolveConditions}; values reaching this function are concrete.
52
51
  */
53
52
  export declare function matchConditions(conditions: PropertyConditions, properties: Record<string, unknown>): boolean;
53
+ /**
54
+ * Resolve `$principal.X` references in resource conditions to concrete values.
55
+ *
56
+ * Missing properties use the same non-matching defaults as token generation:
57
+ * numeric comparisons receive `0`, `$in`/`$nin` receive `[]`, `$exists`
58
+ * receives `false`, and other contexts receive an empty string.
59
+ */
60
+ export declare function resolveConditions(conditions: PropertyConditions, principalProps: Record<string, unknown>): PropertyConditions;
54
61
  //# sourceMappingURL=match.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"match.d.ts","sourceRoot":"","sources":["../../src/conditions/match.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAG3D;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAQ/E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAKlE;AAmBD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CA2D5G"}
1
+ {"version":3,"file":"match.d.ts","sourceRoot":"","sources":["../../src/conditions/match.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAG3D;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAQ/E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAKlE;AAiCD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CA2D5G;AAKD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC7B,UAAU,EAAE,kBAAkB,EAC9B,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACxC,kBAAkB,CAcpB"}
@@ -54,6 +54,18 @@ function compareOrdered(value, expected, op) {
54
54
  return left <= right;
55
55
  }
56
56
  }
57
+ function matchesEquality(value, expected) {
58
+ if (Array.isArray(value) && !Array.isArray(expected)) {
59
+ return value.includes(expected);
60
+ }
61
+ if (Array.isArray(value) && Array.isArray(expected)) {
62
+ return value.length === expected.length && value.every((entry, index) => entry === expected[index]);
63
+ }
64
+ return value === expected;
65
+ }
66
+ function matchesAny(value, expected) {
67
+ return Array.isArray(value) ? value.some((entry) => expected.includes(entry)) : expected.includes(value);
68
+ }
57
69
  /**
58
70
  * Evaluate a PropertyConditions object against a set of properties (JS-side, in-memory).
59
71
  *
@@ -78,16 +90,15 @@ function compareOrdered(value, expected, op) {
78
90
  * Unknown operators emit a warning via the studio-utils logger and return false — surfacing
79
91
  * misconfiguration loudly without breaking the surrounding token mint or permission check.
80
92
  *
81
- * `$principal.X` substitutions in conditions are expected to have already been resolved at
82
- * JWT-mint time by `resolveConditions` in token-server; values reaching this function are
83
- * concrete.
93
+ * `$principal.X` substitutions in conditions are expected to have already been resolved by
94
+ * {@link resolveConditions}; values reaching this function are concrete.
84
95
  */
85
96
  export function matchConditions(conditions, properties) {
86
97
  for (const [key, condition] of Object.entries(conditions)) {
87
98
  const value = resolvePath(properties, key);
88
99
  if (condition === null || condition === undefined || typeof condition !== 'object') {
89
100
  // Direct value match
90
- if (value !== condition)
101
+ if (!matchesEquality(value, condition))
91
102
  return false;
92
103
  }
93
104
  else {
@@ -96,11 +107,11 @@ export function matchConditions(conditions, properties) {
96
107
  for (const [op, expected] of Object.entries(ops)) {
97
108
  switch (op) {
98
109
  case '$eq':
99
- if (value !== expected)
110
+ if (!matchesEquality(value, expected))
100
111
  return false;
101
112
  break;
102
113
  case '$ne':
103
- if (value === expected)
114
+ if (matchesEquality(value, expected))
104
115
  return false;
105
116
  break;
106
117
  case '$gt':
@@ -120,11 +131,11 @@ export function matchConditions(conditions, properties) {
120
131
  return false;
121
132
  break;
122
133
  case '$in':
123
- if (!Array.isArray(expected) || !expected.includes(value))
134
+ if (!Array.isArray(expected) || !matchesAny(value, expected))
124
135
  return false;
125
136
  break;
126
137
  case '$nin':
127
- if (Array.isArray(expected) && expected.includes(value))
138
+ if (Array.isArray(expected) && matchesAny(value, expected))
128
139
  return false;
129
140
  break;
130
141
  case '$exists':
@@ -153,4 +164,57 @@ export function matchConditions(conditions, properties) {
153
164
  }
154
165
  return true;
155
166
  }
167
+ const PRINCIPAL_REF_PREFIX = '$principal.';
168
+ const NUMERIC_OPS = new Set(['$gt', '$gte', '$lt', '$lte']);
169
+ /**
170
+ * Resolve `$principal.X` references in resource conditions to concrete values.
171
+ *
172
+ * Missing properties use the same non-matching defaults as token generation:
173
+ * numeric comparisons receive `0`, `$in`/`$nin` receive `[]`, `$exists`
174
+ * receives `false`, and other contexts receive an empty string.
175
+ */
176
+ export function resolveConditions(conditions, principalProps) {
177
+ const resolved = {};
178
+ for (const [key, condition] of Object.entries(conditions)) {
179
+ if (typeof condition === 'string' && condition.startsWith(PRINCIPAL_REF_PREFIX)) {
180
+ resolved[key] = resolvePrincipalRef(condition, principalProps);
181
+ }
182
+ else if (condition !== null && typeof condition === 'object' && !Array.isArray(condition)) {
183
+ resolved[key] = resolveOperatorObject(condition, principalProps);
184
+ }
185
+ else {
186
+ resolved[key] = condition;
187
+ }
188
+ }
189
+ return resolved;
190
+ }
191
+ function resolvePrincipalRef(ref, principalProps) {
192
+ const value = resolvePath(principalProps, ref.slice(PRINCIPAL_REF_PREFIX.length));
193
+ return value === undefined ? '' : value;
194
+ }
195
+ function resolveOperatorObject(operators, principalProps) {
196
+ const resolved = {};
197
+ for (const [operator, value] of Object.entries(operators)) {
198
+ if (typeof value === 'string' && value.startsWith(PRINCIPAL_REF_PREFIX)) {
199
+ const principalValue = resolvePath(principalProps, value.slice(PRINCIPAL_REF_PREFIX.length));
200
+ resolved[operator] =
201
+ principalValue === undefined
202
+ ? operator === '$in' || operator === '$nin'
203
+ ? []
204
+ : defaultForOperator(operator)
205
+ : principalValue;
206
+ }
207
+ else {
208
+ resolved[operator] = value;
209
+ }
210
+ }
211
+ return resolved;
212
+ }
213
+ function defaultForOperator(operator) {
214
+ if (NUMERIC_OPS.has(operator))
215
+ return 0;
216
+ if (operator === '$exists')
217
+ return false;
218
+ return '';
219
+ }
156
220
  //# sourceMappingURL=match.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"match.js","sourceRoot":"","sources":["../../src/conditions/match.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"match.js","sourceRoot":"","sources":["../../src/conditions/match.ts"],"names":[],"mappings":"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"}
@@ -24,6 +24,14 @@ export declare function getAllRoleNames(): string[];
24
24
  * `content_security` pathway instead.
25
25
  */
26
26
  export declare function getPermissionsForRoles(roleNames: Iterable<string>): string[];
27
+ /**
28
+ * Return the platform permission keys a caller must hold to delegate a role.
29
+ *
30
+ * System roles already contain complete `domain:verb` Permission values. ABAC roles store bare
31
+ * verbs for content-security token generation, so map them back to their domain-qualified keys at
32
+ * an authorization boundary.
33
+ */
34
+ export declare function getDelegablePermissionsForRole(roleName: string): string[];
27
35
  /**
28
36
  * A list of roles with a unified `hasPermission` check across them.
29
37
  */
@@ -1 +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,eAA8C;IAErD,MAAM,CAAC,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,QAAQ,CAGlD;IAED,MAAM,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAG9C;IAED,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEnC;CACJ"}
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;;;;;;GAMG;AACH,wBAAgB,8BAA8B,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAMzE;AAED;;GAEG;AACH,qBAAa,QAAQ;aACmB,KAAK,EAAE,IAAI,EAAE;IAAjD,OAAO,eAA8C;IAErD,MAAM,CAAC,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,QAAQ,CAGlD;IAED,MAAM,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAG9C;IAED,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEnC;CACJ"}
@@ -64,6 +64,20 @@ export function getPermissionsForRoles(roleNames) {
64
64
  }
65
65
  return Array.from(permissions);
66
66
  }
67
+ /**
68
+ * Return the platform permission keys a caller must hold to delegate a role.
69
+ *
70
+ * System roles already contain complete `domain:verb` Permission values. ABAC roles store bare
71
+ * verbs for content-security token generation, so map them back to their domain-qualified keys at
72
+ * an authorization boundary.
73
+ */
74
+ export function getDelegablePermissionsForRole(roleName) {
75
+ const role = getRoleByName(roleName);
76
+ if (role instanceof AbacRole) {
77
+ return [...role.permissions].map((permission) => `${role.domain}:${permission}`);
78
+ }
79
+ return [...role.permissions];
80
+ }
67
81
  /**
68
82
  * A list of roles with a unified `hasPermission` check across them.
69
83
  */
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/roles/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/roles/index.ts"],"names":[],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import e from"handlebars";import{Permission as t,PromptSegmentDefType as n,SystemRoles as r,TemplateType as i}from"@vertesia/common";import{CompositeError as a,getFreeVariables as o,renderHandlebarsTemplate as s,renderJsTemplate as c}from"@vertesia/jst";function l(e,t){return!e||!t?e:e.replace(/\{\{\s*(\w+)\s*\}\}/g,(e,n)=>{let r=t[n];return typeof r!=`string`||!r?e:u(r)})}function u(e){let t=e.length;for(;t>0&&e[t-1]===`/`;)t--;return t===e.length?e:e.slice(0,t)}function d(t){let n;try{n=e.parse(t)}catch{return new Set}let r=new Set,i=[],a=e=>i.some(t=>t.has(e)),o=e=>{if(!e||typeof e!=`object`)return;let t=e;switch(t.type){case`Program`:{let e=t.body;if(e)for(let t of e)o(t);break}case`BlockStatement`:{let e=t.params;if(e)for(let t of e)o(t);o(t.hash);let n=t.program,r=t.inverse,a=new Set([...n?.blockParams??[],...r?.blockParams??[]]);i.push(a),o(t.program),o(t.inverse),i.pop();break}case`MustacheStatement`:{let e=t.params,n=t.hash;if((e?.length??0)>0||(n?.pairs?.length??0)>0||o(t.path),e)for(let t of e)o(t);o(n);break}case`SubExpression`:{let e=t.params;if(e)for(let t of e)o(t);o(t.hash);break}case`PathExpression`:{if(t.data)break;let e=t.parts;if(!e||e.length===0)break;let n=e[0];if(a(n))break;r.add(n);break}case`Hash`:{let e=t.pairs;if(e)for(let t of e)o(t.value);break}default:break}};return o(n),r}function f(e,t=`value`){if(`$ref`in e)return`%${t}%`;switch((Array.isArray(e.type)?e.type:[e.type])[0]){case`string`:return`%${t}%`;case`number`:case`integer`:return Math.floor(Math.random()*101);case`boolean`:return!0;case`array`:{let n=e.items;if(n){let e=Math.floor(Math.random()*2)+1;return Array.from({length:e},(e,r)=>f(n,`${t}_${r}`))}return[]}case`object`:{let t={};if(e.properties)for(let[n,r]of Object.entries(e.properties))t[n]=f(r,n);return t}case`null`:return null;default:return`%${t}%`}}function p(e,t,n,r){return t===i.handlebars?s(e,r):t===i.text?e:c(e,[...n.properties?Object.keys(n.properties):[],`_model`],r)}function m(e,t,n){try{return{success:!0,content:p(e,i.jst,t,n)}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}}function h(e,t,n){try{return{success:!0,content:p(e,i.handlebars,t,n)}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}}function g(e,t){let n=x(e.configuration),r=t[n],i;return i=Array.isArray(r)?JSON.stringify(r,void 0,2):`No "${n}" property found on input data`,{title:`Chat history`,content:i}}function _(e,t){if(!e.template)return{title:`(unknown segment)`,content:``,error:Error(`Prompt segment is missing its template`)};let n=e.template.inputSchema||{},r=`@${e.template.role}`;try{return{title:r,content:p(e.template.content,e.template.content_type,n,t),segmentId:e.template.id}}catch(t){return{title:r,content:t instanceof Error?t.message:String(t),segmentId:e.template.id,error:t instanceof Error?t:Error(String(t))}}}function v(e,t){let r=[];for(let i of e)i.type===n.chat?r.push(g(i,t)):i.type===n.template&&r.push(_(i,t));return r}function y(e,t){try{return v(e,typeof t==`string`?JSON.parse(t.trim()):t||{})}catch(e){return e instanceof a?e.errors.map(e=>({error:e,title:`Rendering Error`,content:e.message})):[{error:e instanceof Error?e:Error(String(e)),title:`Rendering Error`,content:e instanceof Error?e.message:String(e)}]}}function b(e,t){let r=[];for(let i of e)if(i.template){let e=i.template.inputSchema||{},n=p(i.template.content,i.template.content_type,e,t);r.push({role:i.template.role,content:n})}else if(i.type===n.chat){let e=t[x(i.configuration)];if(!ee(e))throw Error(`Chat prompt segment must have a messages array`);for(let t of e){if(!t.role)throw Error(`Chat prompt segment must have a role`);r.push({role:t.role,content:t.content})}}else throw Error(`Unknown prompt segment type: ${i.type}`);return r}function x(e){return typeof e==`object`&&e&&`chatKey`in e&&typeof e.chatKey==`string`?e.chatKey:`chat`}function ee(e){return Array.isArray(e)&&e.every(S)}function S(e){return typeof e==`object`&&!!e&&`role`in e&&`content`in e}const te=[`_`,`Array`,`Set`,`_model`];function ne(e){let t=0,n=0;for(let r of e)r.severity===`error`?t++:r.severity===`warning`&&n++;return{error_count:t,warning_count:n}}function re(e,t){let n=[],r=d(e),i=new Set(t?.properties?Object.keys(t.properties):[]);for(let e of r)i.has(e)||n.push({type:`undeclared_template_variable`,severity:`error`,variable:e,message:`Template references variable '{{${e}}}' but it is not declared in input_schema.properties. Add '${e}' to the schema with an appropriate type.`});for(let e of i)r.has(e)||n.push({type:`unused_schema_variable`,severity:`warning`,variable:e,message:`Schema declares property '${e}' but the template never references it. Remove it from input_schema or use it via {{${e}}}.`});let a=t??{},o=f(a),s=h(e,a,typeof o==`object`&&o&&!Array.isArray(o)?o:{});return s.success||n.push({type:`handlebars_render_error`,severity:`error`,message:`Handlebars rendering failed: ${s.error}`}),n}function ie(e,t){let n=[],r=new Set(t?.properties?Object.keys(t.properties):[]),i;try{let t=o(e,{globals:te,acorn:{allowReturnOutsideFunction:!0,locations:!0}});i=t.vars;for(let e of t.errors)n.push({type:`jst_unsafe_construct`,severity:`error`,message:`JST validation error at ${e.location}: ${e.message}`})}catch(e){return n.push({type:`jst_render_error`,severity:`error`,message:`JST parse failed: ${e instanceof Error?e.message:String(e)}`}),n}for(let e of i)r.has(e)||n.push({type:`undeclared_template_variable`,severity:`error`,variable:e,message:`Template references variable '${e}' but it is not declared in input_schema.properties. Add '${e}' to the schema with an appropriate type.`});for(let e of r)i.has(e)||n.push({type:`unused_schema_variable`,severity:`warning`,variable:e,message:`Schema declares property '${e}' but the template never references it. Remove it from input_schema or use it in the template.`});if(!n.some(e=>e.severity===`error`)){let i=f(t??{}),a=typeof i==`object`&&i&&!Array.isArray(i)?i:{};try{c(e,[...r],a)}catch(e){n.push({type:`jst_render_error`,severity:`error`,message:`JST rendering failed: ${e instanceof Error?e.message:String(e)}`})}}return n}function C(e){let t;t=e.contentType===i.handlebars?re(e.content,e.inputSchema):e.contentType===i.jst?ie(e.content,e.inputSchema):[];let{error_count:n,warning_count:r}=ne(t);return{issues:t,error_count:n,warning_count:r}}var w=class{name;domain;permissions;constructor(e,t,n){this.name=e,this.domain=n,this.permissions=new Set(t)}hasPermission(e){return this.permissions.has(e)}},T=class extends w{constructor(e,t){super(e,t,`system`)}},E=class extends w{applicableScopes;constructor(e,t,n,r){super(e,t,n),this.applicableScopes=r}};const D=`content`,O=[`document`,`collection`];var k;(function(e){e.content_reader=`content:reader`,e.content_writer=`content:writer`,e.content_manager=`content:manager`})(k||={});var A=class extends E{constructor(){super(k.content_reader,[`read`],D,O)}},j=class extends E{constructor(){super(k.content_writer,[`read`,`write`],D,O)}},M=class extends E{constructor(){super(k.content_manager,[`read`,`write`,`delete`],D,O)}};const N={[k.content_reader]:new A,[k.content_writer]:new j,[k.content_manager]:new M},P={domain:D,roles:new Map(Object.entries(N))};var F=class extends T{constructor(e,n){super(e,[t.account_member,...n])}},I=class extends F{constructor(){super(r.owner,Object.values(t))}},L=class extends F{constructor(){super(r.admin,Object.values(t))}},R=class extends F{constructor(){super(r.manager,Object.values(t)),this.permissions.delete(t.account_admin),this.permissions.delete(t.manage_billing),this.permissions.delete(t.audit_read),this.permissions.delete(t.agent_run_read),this.permissions.delete(t.content_read_all),this.permissions.delete(t.content_superadmin),this.permissions.delete(t.workflow_superadmin)}},z=class extends F{constructor(){super(r.developer,Object.values(t)),this.permissions.delete(t.account_admin),this.permissions.delete(t.project_admin),this.permissions.delete(t.project_settings_write),this.permissions.delete(t.env_admin),this.permissions.delete(t.manage_billing),this.permissions.delete(t.audit_read),this.permissions.delete(t.agent_run_read),this.permissions.delete(t.content_read_all),this.permissions.delete(t.content_superadmin),this.permissions.delete(t.workflow_superadmin)}},B=class extends F{constructor(){super(r.application,[t.int_read,t.int_execute,t.int_write,t.run_read,t.content_write,t.content_read,t.content_write,t.content_admin,t.project_admin,t.workflow_run,t.project_settings_write,t.account_write])}},V=class extends F{constructor(){super(r.automation,[t.content_read,t.content_write,t.content_admin,t.int_read,t.int_execute,t.run_read,t.workflow_run,t.project_integration_read])}},H=class extends F{constructor(){super(r.content_processor,[t.content_read,t.content_write,t.content_admin,t.content_superadmin,t.int_execute,t.workflow_read,t.workflow_run,t.run_read])}},U=class extends F{constructor(){super(r.consumer,[t.content_admin,t.content_read,t.content_write,t.content_delete,t.int_read,t.int_execute,t.run_read,t.workflow_run])}},W=class extends F{constructor(){super(r.executor,[t.int_execute,t.run_read,t.workflow_run])}},G=class extends F{constructor(){super(r.reader,[t.int_read,t.run_read,t.content_read])}};const K=[t.studio_access,t.account_read,t.project_integration_read,t.api_key_read,t.billing_read,t.audit_read,t.int_read,t.run_read,t.content_read,t.content_read_all,t.task_read,t.workflow_read,t.agent_run_read];var q=class extends F{constructor(e){super(e,K)}},J=class extends F{constructor(){super(r.billing,[t.manage_billing])}},Y=class extends F{constructor(){super(r.app_member,[t.int_read,t.int_execute,t.int_write,t.run_read,t.content_write,t.content_read,t.content_delete,t.workflow_run])}},ae=class extends z{constructor(){super(),this.name=r.content_superadmin,this.permissions.add(t.content_superadmin)}};const oe={[r.owner]:new I,[r.admin]:new L,[r.manager]:new R,[r.developer]:new z,[r.application]:new B,[r.automation]:new V,[r.content_processor]:new H,[r.consumer]:new U,[r.executor]:new W,[r.reader]:new G,[r.auditor]:new q(r.auditor),[r.support]:new q(r.support),[r.billing]:new J,[r.app_member]:new Y,[r.member]:new F(r.member,[]),[r.content_superadmin]:new ae},X=[{domain:`system`,roles:new Map(Object.entries(oe))},P];function Z(e){for(let t of X){let n=t.roles.get(e);if(n)return n}throw Error(`Role ${e} not found`)}function Q(){let e=[];for(let t of X)for(let n of t.roles.values())e.push(n);return e}function $(e){let t=X.find(t=>t.domain===e);return t?Array.from(t.roles.values()):[]}function se(e){return Q().filter(t=>t instanceof E&&t.applicableScopes.includes(e))}function ce(){return Q().filter(e=>e instanceof T)}function le(){return Q().map(e=>e.name)}function ue(e){let t=new Set;for(let n of e){let e=Z(n);for(let n of e.permissions)t.add(n)}return Array.from(t)}var de=class e{roles;constructor(e){this.roles=e}static fromRoleNames(t){let n=t.map(e=>Z(e));return new e(n)}static fromRoleName(t){let n=[Z(t)];return new e(n)}hasPermission(e){return this.roles.some(t=>t.hasPermission(e))}};export{E as AbacRole,k as ContentRoleNames,w as Role,de as RoleList,T as SystemRole,h as executeHandlebars,m as executeJST,d as extractHandlebarsVariables,f as generateMockData,le as getAllRoleNames,ue as getPermissionsForRoles,Z as getRoleByName,se as listAbacRolesForScope,Q as listRoles,$ as listRolesByDomain,ce as listSystemRoles,b as renderPrompt,v as renderSegments,y as renderSegmentsOrErrors,p as renderTemplate,l as substituteEndpoints,C as validatePrompt};
1
+ import e from"handlebars";import{Permission as t,PromptSegmentDefType as n,SystemRoles as r,TemplateType as i}from"@vertesia/common";import{CompositeError as a,getFreeVariables as o,renderHandlebarsTemplate as s,renderJsTemplate as c}from"@vertesia/jst";function l(e,t){return!e||!t?e:e.replace(/\{\{\s*(\w+)\s*\}\}/g,(e,n)=>{let r=t[n];return typeof r!=`string`||!r?e:u(r)})}function u(e){let t=e.length;for(;t>0&&e[t-1]===`/`;)t--;return t===e.length?e:e.slice(0,t)}function d(t){let n;try{n=e.parse(t)}catch{return new Set}let r=new Set,i=[],a=e=>i.some(t=>t.has(e)),o=e=>{if(!e||typeof e!=`object`)return;let t=e;switch(t.type){case`Program`:{let e=t.body;if(e)for(let t of e)o(t);break}case`BlockStatement`:{let e=t.params;if(e)for(let t of e)o(t);o(t.hash);let n=t.program,r=t.inverse,a=new Set([...n?.blockParams??[],...r?.blockParams??[]]);i.push(a),o(t.program),o(t.inverse),i.pop();break}case`MustacheStatement`:{let e=t.params,n=t.hash;if((e?.length??0)>0||(n?.pairs?.length??0)>0||o(t.path),e)for(let t of e)o(t);o(n);break}case`SubExpression`:{let e=t.params;if(e)for(let t of e)o(t);o(t.hash);break}case`PathExpression`:{if(t.data)break;let e=t.parts;if(!e||e.length===0)break;let n=e[0];if(a(n))break;r.add(n);break}case`Hash`:{let e=t.pairs;if(e)for(let t of e)o(t.value);break}}};return o(n),r}function f(e,t=`value`){if(`$ref`in e)return`%${t}%`;switch((Array.isArray(e.type)?e.type:[e.type])[0]){case`string`:return`%${t}%`;case`number`:case`integer`:return Math.floor(Math.random()*101);case`boolean`:return!0;case`array`:{let n=e.items;if(n){let e=Math.floor(Math.random()*2)+1;return Array.from({length:e},(e,r)=>f(n,`${t}_${r}`))}return[]}case`object`:{let t={};if(e.properties)for(let[n,r]of Object.entries(e.properties))t[n]=f(r,n);return t}case`null`:return null;default:return`%${t}%`}}function p(e,t,n,r){if(t===i.handlebars)return s(e,r);if(t===i.text)return e;let a=[...n.properties?Object.keys(n.properties):[],`_model`];return c(e,a,r)}function m(e,t,n){try{return{success:!0,content:p(e,i.jst,t,n)}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}}function h(e,t,n){try{return{success:!0,content:p(e,i.handlebars,t,n)}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}}function g(e,t){let n=x(e.configuration),r=t[n],i;return i=Array.isArray(r)?JSON.stringify(r,void 0,2):`No "${n}" property found on input data`,{title:`Chat history`,content:i}}function _(e,t){if(!e.template)return{title:`(unknown segment)`,content:``,error:Error(`Prompt segment is missing its template`)};let n=e.template.inputSchema||{},r=`@${e.template.role}`;try{return{title:r,content:p(e.template.content,e.template.content_type,n,t),segmentId:e.template.id}}catch(t){return{title:r,content:t instanceof Error?t.message:String(t),segmentId:e.template.id,error:t instanceof Error?t:Error(String(t))}}}function v(e,t){let r=[];for(let i of e)i.type===n.chat?r.push(g(i,t)):i.type===n.template&&r.push(_(i,t));return r}function y(e,t){try{return v(e,typeof t==`string`?JSON.parse(t.trim()):t||{})}catch(e){return e instanceof a?e.errors.map(e=>({error:e,title:`Rendering Error`,content:e.message})):[{error:e instanceof Error?e:Error(String(e)),title:`Rendering Error`,content:e instanceof Error?e.message:String(e)}]}}function b(e,t){let r=[];for(let i of e)if(i.template){let e=i.template.inputSchema||{},n=p(i.template.content,i.template.content_type,e,t);r.push({role:i.template.role,content:n})}else if(i.type===n.chat){let e=t[x(i.configuration)];if(!ee(e))throw Error(`Chat prompt segment must have a messages array`);for(let t of e){if(!t.role)throw Error(`Chat prompt segment must have a role`);r.push({role:t.role,content:t.content})}}else throw Error(`Unknown prompt segment type: ${i.type}`);return r}function x(e){return typeof e==`object`&&e&&`chatKey`in e&&typeof e.chatKey==`string`?e.chatKey:`chat`}function ee(e){return Array.isArray(e)&&e.every(S)}function S(e){return typeof e==`object`&&!!e&&`role`in e&&`content`in e}const te=[`_`,`Array`,`Set`,`_model`];function ne(e){let t=0,n=0;for(let r of e)r.severity===`error`?t++:r.severity===`warning`&&n++;return{error_count:t,warning_count:n}}function re(e,t){let n=[],r=d(e),i=new Set(t?.properties?Object.keys(t.properties):[]);for(let e of r)i.has(e)||n.push({type:`undeclared_template_variable`,severity:`error`,variable:e,message:`Template references variable '{{${e}}}' but it is not declared in input_schema.properties. Add '${e}' to the schema with an appropriate type.`});for(let e of i)r.has(e)||n.push({type:`unused_schema_variable`,severity:`warning`,variable:e,message:`Schema declares property '${e}' but the template never references it. Remove it from input_schema or use it via {{${e}}}.`});let a=t??{},o=f(a),s=h(e,a,typeof o==`object`&&o&&!Array.isArray(o)?o:{});return s.success||n.push({type:`handlebars_render_error`,severity:`error`,message:`Handlebars rendering failed: ${s.error}`}),n}function ie(e,t){let n=[],r=new Set(t?.properties?Object.keys(t.properties):[]),i;try{let t=o(e,{globals:te,acorn:{allowReturnOutsideFunction:!0,locations:!0}});i=t.vars;for(let e of t.errors)n.push({type:`jst_unsafe_construct`,severity:`error`,message:`JST validation error at ${e.location}: ${e.message}`})}catch(e){return n.push({type:`jst_render_error`,severity:`error`,message:`JST parse failed: ${e instanceof Error?e.message:String(e)}`}),n}for(let e of i)r.has(e)||n.push({type:`undeclared_template_variable`,severity:`error`,variable:e,message:`Template references variable '${e}' but it is not declared in input_schema.properties. Add '${e}' to the schema with an appropriate type.`});for(let e of r)i.has(e)||n.push({type:`unused_schema_variable`,severity:`warning`,variable:e,message:`Schema declares property '${e}' but the template never references it. Remove it from input_schema or use it in the template.`});if(!n.some(e=>e.severity===`error`)){let i=f(t??{}),a=typeof i==`object`&&i&&!Array.isArray(i)?i:{};try{c(e,[...r],a)}catch(e){n.push({type:`jst_render_error`,severity:`error`,message:`JST rendering failed: ${e instanceof Error?e.message:String(e)}`})}}return n}function C(e){let t;t=e.contentType===i.handlebars?re(e.content,e.inputSchema):e.contentType===i.jst?ie(e.content,e.inputSchema):[];let{error_count:n,warning_count:r}=ne(t);return{issues:t,error_count:n,warning_count:r}}var w=class{name;domain;permissions;constructor(e,t,n){this.name=e,this.domain=n,this.permissions=new Set(t)}hasPermission(e){return this.permissions.has(e)}},T=class extends w{constructor(e,t){super(e,t,`system`)}},E=class extends w{applicableScopes;constructor(e,t,n,r){super(e,t,n),this.applicableScopes=r}};const D=`content`,O=[`document`,`collection`];var k;(function(e){e.content_reader=`content:reader`,e.content_writer=`content:writer`,e.content_manager=`content:manager`})(k||={});var A=class extends E{constructor(){super(k.content_reader,[`read`],D,O)}},j=class extends E{constructor(){super(k.content_writer,[`read`,`write`],D,O)}},M=class extends E{constructor(){super(k.content_manager,[`read`,`write`,`delete`],D,O)}};const N={[k.content_reader]:new A,[k.content_writer]:new j,[k.content_manager]:new M},P={domain:D,roles:new Map(Object.entries(N))};var F=class extends T{constructor(e,n){super(e,[t.account_member,...n])}},I=class extends F{constructor(){super(r.owner,Object.values(t))}},L=class extends F{constructor(){super(r.admin,Object.values(t))}},R=class extends F{constructor(){super(r.manager,Object.values(t)),this.permissions.delete(t.account_admin),this.permissions.delete(t.manage_billing),this.permissions.delete(t.audit_read),this.permissions.delete(t.agent_run_read),this.permissions.delete(t.content_read_all),this.permissions.delete(t.content_superadmin),this.permissions.delete(t.workflow_superadmin)}},z=class extends F{constructor(){super(r.developer,Object.values(t)),this.permissions.delete(t.account_admin),this.permissions.delete(t.project_admin),this.permissions.delete(t.project_settings_write),this.permissions.delete(t.env_admin),this.permissions.delete(t.manage_billing),this.permissions.delete(t.audit_read),this.permissions.delete(t.agent_run_read),this.permissions.delete(t.content_read_all),this.permissions.delete(t.content_superadmin),this.permissions.delete(t.workflow_superadmin)}},B=class extends F{constructor(){super(r.application,[t.int_read,t.int_execute,t.int_write,t.run_read,t.content_write,t.content_read,t.content_write,t.content_admin,t.project_admin,t.workflow_run,t.project_settings_write,t.account_write])}},V=class extends F{constructor(){super(r.automation,[t.content_read,t.content_write,t.content_admin,t.int_read,t.int_execute,t.run_read,t.workflow_run,t.project_integration_read])}},H=class extends F{constructor(){super(r.content_processor,[t.content_read,t.content_write,t.content_admin,t.content_superadmin,t.int_execute,t.workflow_read,t.workflow_run,t.run_read])}},U=class extends F{constructor(){super(r.consumer,[t.content_admin,t.content_read,t.content_write,t.content_delete,t.int_read,t.int_execute,t.run_read,t.workflow_run])}},W=class extends F{constructor(){super(r.executor,[t.int_execute,t.run_read,t.workflow_run])}},G=class extends F{constructor(){super(r.reader,[t.int_read,t.run_read,t.content_read])}};const K=[t.studio_access,t.account_read,t.project_integration_read,t.api_key_read,t.billing_read,t.audit_read,t.int_read,t.run_read,t.content_read,t.content_read_all,t.task_read,t.workflow_read,t.agent_run_read];var q=class extends F{constructor(e){super(e,K)}},J=class extends F{constructor(){super(r.billing,[t.manage_billing])}},Y=class extends F{constructor(){super(r.app_member,[t.int_read,t.int_execute,t.int_write,t.run_read,t.content_write,t.content_read,t.content_delete,t.workflow_run])}},ae=class extends z{constructor(){super(),this.name=r.content_superadmin,this.permissions.add(t.content_superadmin)}};const oe={[r.owner]:new I,[r.admin]:new L,[r.manager]:new R,[r.developer]:new z,[r.application]:new B,[r.automation]:new V,[r.content_processor]:new H,[r.consumer]:new U,[r.executor]:new W,[r.reader]:new G,[r.auditor]:new q(r.auditor),[r.support]:new q(r.support),[r.billing]:new J,[r.app_member]:new Y,[r.member]:new F(r.member,[]),[r.content_superadmin]:new ae},X=[{domain:`system`,roles:new Map(Object.entries(oe))},P];function Z(e){for(let t of X){let n=t.roles.get(e);if(n)return n}throw Error(`Role ${e} not found`)}function Q(){let e=[];for(let t of X)for(let n of t.roles.values())e.push(n);return e}function $(e){let t=X.find(t=>t.domain===e);return t?Array.from(t.roles.values()):[]}function se(e){return Q().filter(t=>t instanceof E&&t.applicableScopes.includes(e))}function ce(){return Q().filter(e=>e instanceof T)}function le(){return Q().map(e=>e.name)}function ue(e){let t=new Set;for(let n of e){let e=Z(n);for(let n of e.permissions)t.add(n)}return Array.from(t)}function de(e){let t=Z(e);return t instanceof E?[...t.permissions].map(e=>`${t.domain}:${e}`):[...t.permissions]}var fe=class e{roles;constructor(e){this.roles=e}static fromRoleNames(t){let n=t.map(e=>Z(e));return new e(n)}static fromRoleName(t){let n=[Z(t)];return new e(n)}hasPermission(e){return this.roles.some(t=>t.hasPermission(e))}};export{E as AbacRole,k as ContentRoleNames,w as Role,fe as RoleList,T as SystemRole,h as executeHandlebars,m as executeJST,d as extractHandlebarsVariables,f as generateMockData,le as getAllRoleNames,de as getDelegablePermissionsForRole,ue as getPermissionsForRoles,Z as getRoleByName,se as listAbacRolesForScope,Q as listRoles,$ as listRolesByDomain,ce as listSystemRoles,b as renderPrompt,v as renderSegments,y as renderSegmentsOrErrors,p as renderTemplate,l as substituteEndpoints,C as validatePrompt};
2
2
  //# sourceMappingURL=vertesia-studio-utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"vertesia-studio-utils.js","names":[],"sources":["apps/index.js","prompts/extract-vars.js","prompts/mock-data.js","prompts/render.js","prompts/validate.js","roles/classes.js","roles/content.js","roles/system.js","roles/index.js"],"sourcesContent":["import { getStudioUtilsLogger } from '../logger.js';\n/**\n * Reserved deployment environment names that may never be used as endpoint\n * override keys. Reserving them prevents a manifest from hijacking auto-resolution\n * on a shared production studio-server (whose environment is one of these).\n */\nconst RESERVED_ENDPOINT_OVERRIDE_ENVS = new Set(['production', 'preview', 'staging']);\nconst GATEWAY_APP_ENDPOINT_PATTERN = new RegExp('^(.*)/tenants/([^/]+)/(?:' + 'package/([^/]+)|' + 'apps/([^/]+)(?:/versions/([^/]+))?(?:/api/package)?' + ')/?$');\n/**\n * Returns true if the given environment name is allowed as an endpoint override key.\n * Any non-empty name is accepted except the reserved shared-deployment names.\n */\nexport function isValidEndpointOverrideEnv(envName) {\n if (!envName)\n return false;\n return !RESERVED_ENDPOINT_OVERRIDE_ENVS.has(envName.toLowerCase());\n}\n/**\n * Substitutes `{{key}}` placeholders in a URL with the matching endpoint.\n * Unknown placeholders are left untouched (so failures surface as fetch errors\n * with the unresolved placeholder visible, rather than silently pointing nowhere).\n * Trailing slashes on replacement values are stripped to avoid `//api/...` joins.\n */\nexport function substituteEndpoints(url, endpoints) {\n if (!url || !endpoints)\n return url;\n return url.replace(/\\{\\{\\s*(\\w+)\\s*\\}\\}/g, (match, key) => {\n const value = endpoints[key];\n if (typeof value !== 'string' || !value)\n return match;\n return trimTrailingSlashes(value);\n });\n}\nfunction trimTrailingSlashes(value) {\n let end = value.length;\n while (end > 0 && value[end - 1] === '/') {\n end--;\n }\n return end === value.length ? value : value.slice(0, end);\n}\n/**\n * Resolves an app manifest endpoint to its package descriptor URL.\n *\n * Resolution has two modes:\n *\n * Gateway apps: if the base `endpoint` is an app-gateway URL, endpoint overrides\n * are ignored. Gateway versions are resolved by rewriting the canonical gateway\n * package URL itself:\n * - `https://gw/tenants/t/apps/my-app`\n * -> `https://gw/tenants/t/apps/my-app/api/package`\n * - `https://gw/tenants/t/package/my-app`\n * -> `https://gw/tenants/t/apps/my-app/api/package`\n * - with `version: \"1.2.3\"`\n * -> `https://gw/tenants/t/apps/my-app/versions/1.2.3/api/package`\n *\n * Regular apps: endpoint overrides are supported in this order:\n * 1. Version override: if `version` matches `endpoint_overrides[version]`, use\n * that URL. This lets regular, non-gateway apps expose version-pinned package\n * endpoints without going through the app gateway.\n * 2. Explicit override: if `requestedOverride` matches an allowed\n * `endpoint_overrides` key, use that URL. Callers must still verify the user\n * is allowed to request that override.\n * 3. Environment override: if `envName` matches an allowed `endpoint_overrides`\n * key, use that URL for deployment-env-specific endpoints.\n * 4. Base endpoint: otherwise use `manifest.endpoint`.\n * 5. Placeholder substitution: replace `{{gateway}}`, `{{studio}}`, etc. from\n * `vars`.\n *\n * Regular package URLs are returned unchanged:\n * - `https://plugin.example.com/api/package`\n * -> `https://plugin.example.com/api/package`\n *\n * Non-standard package URLs are also returned unchanged, with a warning:\n * - `https://plugin.example.com/exotic/pkg`\n * -> `https://plugin.example.com/exotic/pkg`\n */\nexport function resolveAppEndpoint(manifest, options = {}) {\n if (!manifest.endpoint) {\n return undefined;\n }\n const baseEndpoint = substituteEndpoints(manifest.endpoint, options.vars);\n const gateway = parseGatewayAppEndpoint(baseEndpoint);\n if (gateway) {\n return resolveGatewayAppEndpoint(gateway, options.version);\n }\n return resolveRegularAppEndpoint(manifest, manifest.endpoint, options);\n}\nfunction resolveGatewayAppEndpoint(gateway, version) {\n const effectiveVersion = version || gateway.version;\n const versionSeg = effectiveVersion ? `/versions/${encodeURIComponent(effectiveVersion)}` : '';\n return `${gateway.origin}/tenants/${gateway.tenant}/apps/${gateway.appId}${versionSeg}/api/package`;\n}\nfunction resolveRegularAppEndpoint(manifest, baseEndpoint, options) {\n const { envName, vars, requestedOverride, version } = options;\n let raw;\n if (version && manifest.endpoint_overrides?.[version]) {\n raw = manifest.endpoint_overrides[version];\n }\n else if (requestedOverride &&\n manifest.endpoint_overrides?.[requestedOverride] &&\n isValidEndpointOverrideEnv(requestedOverride)) {\n raw = manifest.endpoint_overrides[requestedOverride];\n }\n else if (envName && manifest.endpoint_overrides?.[envName] && isValidEndpointOverrideEnv(envName)) {\n raw = manifest.endpoint_overrides[envName];\n }\n else {\n raw = baseEndpoint;\n }\n // Substitute the selected regular endpoint, not only the base endpoint:\n // `raw` may be an override. Current server validation usually requires\n // overrides to be absolute URLs, but keeping substitution here preserves\n // compatibility if trusted/system manifests use placeholders in overrides.\n const endpoint = substituteEndpoints(raw, vars);\n warnIfNonStandardPackageEndpoint(endpoint);\n return endpoint;\n}\n/**\n * Resolves all URL placeholders in a manifest in place (both `endpoint` and\n * `tool_collections[].url`). Intended for server-side serialization.\n *\n * Mutates the manifest rather than returning a copy so it works cleanly with\n * Mongoose populated subdocs.\n */\nexport function resolveManifestUrls(manifest, options = {}) {\n if (!manifest)\n return;\n const { vars } = options;\n if (manifest.endpoint) {\n const resolved = resolveAppEndpoint(manifest, options);\n if (resolved && resolved !== manifest.endpoint) {\n manifest.endpoint = resolved;\n }\n }\n const toolCollections = manifest.tool_collections;\n if (toolCollections && Array.isArray(toolCollections)) {\n for (let i = 0; i < toolCollections.length; i++) {\n const item = toolCollections[i];\n if (item && typeof item === 'object' && item.url) {\n const sub = substituteEndpoints(item.url, vars);\n if (sub !== item.url)\n item.url = sub;\n }\n }\n }\n}\n/**\n * Parse a gateway app endpoint into its parts. Accepts BOTH the canonical apps mount\n * (`.../tenants/<t>/apps/<app>`), the canonical package route\n * (`.../tenants/<t>/apps/<app>/api/package`), versioned package routes, AND the\n * legacy package mount (`.../tenants/<t>/package/<app>`). Returns undefined for\n * non-gateway endpoints.\n */\nexport function parseGatewayAppEndpoint(endpoint) {\n const m = GATEWAY_APP_ENDPOINT_PATTERN.exec(endpoint);\n if (!m)\n return undefined;\n const [, origin, tenant, legacyAppId, appsAppId, version] = m;\n return {\n origin,\n tenant,\n appId: legacyAppId || appsAppId,\n ...(version ? { version } : {}),\n };\n}\nfunction warnIfNonStandardPackageEndpoint(endpoint) {\n if (!isStandardPackageEndpoint(endpoint)) {\n getStudioUtilsLogger().warn({ endpoint }, 'Non-standard app package endpoint; returning it unchanged during app endpoint resolution');\n }\n}\nfunction isStandardPackageEndpoint(endpoint) {\n try {\n const url = new URL(endpoint);\n return trimTrailingSlashes(url.pathname).endsWith('/package');\n }\n catch {\n return false;\n }\n}\n/**\n * Derives the runtime app API base from a package descriptor URL by removing the\n * final path segment.\n *\n * Examples:\n * - `https://plugin.example.com/api/package` -> `https://plugin.example.com/api`\n * - `https://plugin.example.com/exotic/pkg` -> `https://plugin.example.com/exotic`\n * - `https://gw/tenants/t/apps/my-app/api/package` -> `https://gw/tenants/t/apps/my-app/api`\n */\nexport function resolveAppApiBase(packageEndpoint) {\n const url = new URL(packageEndpoint);\n const path = trimTrailingSlashes(url.pathname);\n const lastSlash = path.lastIndexOf('/');\n url.pathname = lastSlash <= 0 ? '/' : path.slice(0, lastSlash);\n return url.toString();\n}\n/**\n * Resolves an app resource URL from a package descriptor endpoint and a path\n * relative to the app `/api` base.\n *\n * Examples:\n * - `resolveAppResource(\"https://plugin.example.com/api/package\", \"tools/main\")`\n * -> `https://plugin.example.com/api/tools/main`\n * - `resolveAppResource(\"https://gw/tenants/t/apps/my-app/api/package\", \"/interactions/x\")`\n * -> `https://gw/tenants/t/apps/my-app/api/interactions/x`\n */\nexport function resolveAppResource(packageEndpoint, pathRelativeToApiBase) {\n const base = resolveAppApiBase(packageEndpoint);\n const normalizedBase = base.endsWith('/') ? base.slice(0, -1) : base;\n const normalizedPath = pathRelativeToApiBase.startsWith('/') ? pathRelativeToApiBase : `/${pathRelativeToApiBase}`;\n return `${normalizedBase}${normalizedPath}`;\n}\n//# sourceMappingURL=index.js.map","import Handlebars from 'handlebars';\n/**\n * Extract the set of root-level variable names referenced by a Handlebars template.\n *\n * - `{{foo}}` → `foo`\n * - `{{obj.bar.baz}}` → `obj` (only the root identifier; nested access doesn't add `bar` or `baz`)\n * - `{{#if cond}}{{name}}{{/if}}` → `cond`, `name`\n * - `{{#each items as |item|}}{{item.name}}{{/each}}` → `items` (NOT `item` — bound by `as |item|`)\n * - `{{lookup obj key}}` → `obj`, `key` (helper name skipped)\n * - `{{@index}}`, `{{this}}` → ignored (built-in data vars / self)\n *\n * Helper names in mustaches and block heads (`{{customHelper x}}`, `{{#if x}}`) are NOT added to\n * the set — only the arguments and inner template references are. This matches the typical use\n * case for prompt validation: detect which schema properties the template actually reads.\n *\n * Returns an empty set on parse failure — callers should also run a syntactic render check\n * (e.g. `executeHandlebars`) to surface parse errors separately.\n */\nexport function extractHandlebarsVariables(template) {\n let ast;\n try {\n ast = Handlebars.parse(template);\n }\n catch {\n return new Set();\n }\n const variables = new Set();\n const localScopes = [];\n const isLocal = (name) => localScopes.some((scope) => scope.has(name));\n const visit = (node) => {\n if (!node || typeof node !== 'object')\n return;\n const n = node;\n switch (n.type) {\n case 'Program': {\n const body = n.body;\n if (body)\n for (const stmt of body)\n visit(stmt);\n break;\n }\n case 'BlockStatement': {\n // n.path is the helper name (`#if`, `#each`, `#customBlock`) — never a variable.\n // Visit params + hash to capture variables passed to the block helper.\n const params = n.params;\n if (params)\n for (const p of params)\n visit(p);\n visit(n.hash);\n // Track `as |x y|` block params — these are local bindings, not variables.\n const program = n.program;\n const inverse = n.inverse;\n const scope = new Set([...(program?.blockParams ?? []), ...(inverse?.blockParams ?? [])]);\n localScopes.push(scope);\n visit(n.program);\n visit(n.inverse);\n localScopes.pop();\n break;\n }\n case 'MustacheStatement': {\n // Bare mustache (no params, no hash) → it's a variable reference.\n // With params/hash, it's a helper call — skip the path (helper name), visit args.\n const params = n.params;\n const hash = n.hash;\n const isHelperCall = (params?.length ?? 0) > 0 || (hash?.pairs?.length ?? 0) > 0;\n if (!isHelperCall) {\n visit(n.path);\n }\n if (params)\n for (const p of params)\n visit(p);\n visit(hash);\n break;\n }\n case 'SubExpression': {\n // (helper arg1 arg2) — helper name in path is skipped, args carry variables.\n const params = n.params;\n if (params)\n for (const p of params)\n visit(p);\n visit(n.hash);\n break;\n }\n case 'PathExpression': {\n if (n.data)\n break; // `@-data` references (@index, @key, etc.)\n const parts = n.parts;\n if (!parts || parts.length === 0)\n break; // `this` / `.`\n const root = parts[0];\n if (isLocal(root))\n break;\n variables.add(root);\n break;\n }\n case 'Hash': {\n const pairs = n.pairs;\n if (pairs)\n for (const pair of pairs)\n visit(pair.value);\n break;\n }\n // ContentStatement, CommentStatement, *Literal, PartialStatement — no variables to extract.\n default:\n break;\n }\n };\n visit(ast);\n return variables;\n}\n//# sourceMappingURL=extract-vars.js.map","/**\n * Generate mock data that satisfies a JSON Schema.\n *\n * - string → `%property_name%` (visible placeholder, useful for previews and validation render tests)\n * - number/integer → random value 0–100\n * - boolean → true\n * - array → 1–2 items recursively generated from the items schema\n * - object → recursively populated from properties\n * - null → null\n * - $ref or unknown → `%property_name%`\n *\n * @param schema JSON schema to generate data for\n * @param propertyName Name used for string placeholders (default `value`)\n */\nexport function generateMockData(schema, propertyName = 'value') {\n if ('$ref' in schema) {\n return `%${propertyName}%`;\n }\n const types = Array.isArray(schema.type) ? schema.type : [schema.type];\n const primaryType = types[0];\n switch (primaryType) {\n case 'string':\n return `%${propertyName}%`;\n case 'number':\n case 'integer':\n return Math.floor(Math.random() * 101);\n case 'boolean':\n return true;\n case 'array': {\n const itemSchema = schema.items;\n if (itemSchema) {\n const itemCount = Math.floor(Math.random() * 2) + 1;\n return Array.from({ length: itemCount }, (_, index) => generateMockData(itemSchema, `${propertyName}_${index}`));\n }\n return [];\n }\n case 'object': {\n const result = {};\n if (schema.properties) {\n for (const [propName, propSchema] of Object.entries(schema.properties)) {\n result[propName] = generateMockData(propSchema, propName);\n }\n }\n return result;\n }\n case 'null':\n return null;\n default:\n return `%${propertyName}%`;\n }\n}\n//# sourceMappingURL=mock-data.js.map","import { PromptSegmentDefType, TemplateType } from '@vertesia/common';\nimport { CompositeError, renderHandlebarsTemplate, renderJsTemplate } from '@vertesia/jst';\n/**\n * Render a prompt template with the given input data.\n *\n * Handlebars templates use {{variable}} interpolation against `data`.\n * JST (JavaScript template) bodies evaluate against `data` with the schema's top-level\n * property names exposed as globals, plus `_model` — the active model id, which the\n * studio-server executor injects into the input as `{ ..._model: run.modelId }` when\n * executing an interaction (see `apps/studio-server/src/executor/ExecutionRequest.ts`\n * and `apps/studio-server/src/executor/rendering/template.ts`). Listing it here keeps\n * the Playground preview and `validatePrompt` in sync with runtime resolution — a JST\n * template referencing `_model` validates fine here and renders fine in production.\n *\n * For `TemplateType.text`, the content is returned verbatim — it is static text, not a\n * template — matching the studio-server executor (see `apps/studio-server/src/executor/\n * rendering/template.ts`). Routing it through the JST evaluator would compile the prose as\n * JavaScript and throw on any plain sentence (e.g. \"You are a helpful assistant.\").\n */\nexport function renderTemplate(code, contentType, schema, data) {\n if (contentType === TemplateType.handlebars) {\n return renderHandlebarsTemplate(code, data);\n }\n if (contentType === TemplateType.text) {\n return code;\n }\n const globals = [...(schema.properties ? Object.keys(schema.properties) : []), '_model'];\n return renderJsTemplate(code, globals, data);\n}\n/**\n * Execute a JST (JavaScript Template) with given data.\n * Returns a discriminated union to surface errors without throwing.\n */\nexport function executeJST(jstContent, schema, data) {\n try {\n const result = renderTemplate(jstContent, TemplateType.jst, schema, data);\n return { success: true, content: result };\n }\n catch (error) {\n return { success: false, error: error instanceof Error ? error.message : String(error) };\n }\n}\n/**\n * Execute a Handlebars template with given data.\n * Returns a discriminated union to surface errors without throwing.\n */\nexport function executeHandlebars(handlebarsContent, schema, data) {\n try {\n const result = renderTemplate(handlebarsContent, TemplateType.handlebars, schema, data);\n return { success: true, content: result };\n }\n catch (error) {\n return { success: false, error: error instanceof Error ? error.message : String(error) };\n }\n}\nfunction renderChatSegment(segment, data) {\n const chatKey = getChatKey(segment.configuration);\n const chat = data[chatKey];\n let content;\n if (Array.isArray(chat)) {\n content = JSON.stringify(chat, undefined, 2);\n }\n else {\n content = `No \"${chatKey}\" property found on input data`;\n }\n return {\n title: 'Chat history',\n content,\n };\n}\nfunction renderTemplateSegment(segment, data) {\n if (!segment.template) {\n return { title: '(unknown segment)', content: '', error: new Error('Prompt segment is missing its template') };\n }\n const schema = segment.template.inputSchema || {};\n const title = `@${segment.template.role}`;\n try {\n const content = renderTemplate(segment.template.content, segment.template.content_type, schema, data);\n return { title, content, segmentId: segment.template.id };\n }\n catch (error) {\n // Isolate the failure to this segment so the remaining segments still render.\n return {\n title,\n content: error instanceof Error ? error.message : String(error),\n segmentId: segment.template.id,\n error: error instanceof Error ? error : new Error(String(error)),\n };\n }\n}\nexport function renderSegments(segments, data) {\n const out = [];\n for (const segment of segments) {\n if (segment.type === PromptSegmentDefType.chat) {\n out.push(renderChatSegment(segment, data));\n }\n else if (segment.type === PromptSegmentDefType.template) {\n out.push(renderTemplateSegment(segment, data));\n }\n }\n return out;\n}\nexport function renderSegmentsOrErrors(segments, textOrObject) {\n try {\n return renderSegments(segments, typeof textOrObject === 'string' ? JSON.parse(textOrObject.trim()) : textOrObject || {});\n }\n catch (error) {\n if (error instanceof CompositeError) {\n return error.errors.map((err) => ({\n error: err,\n title: 'Rendering Error',\n content: err.message,\n }));\n }\n return [\n {\n error: error instanceof Error ? error : new Error(String(error)),\n title: 'Rendering Error',\n content: error instanceof Error ? error.message : String(error),\n },\n ];\n }\n}\nexport function renderPrompt(segments, payload) {\n const out = [];\n for (const segment of segments) {\n if (segment.template) {\n const schema = segment.template.inputSchema || {};\n const content = renderTemplate(segment.template.content, segment.template.content_type, schema, payload);\n out.push({ role: segment.template.role, content });\n }\n else if (segment.type === PromptSegmentDefType.chat) {\n const chatKey = getChatKey(segment.configuration);\n const messages = payload[chatKey];\n if (!isPromptSegmentArray(messages)) {\n throw new Error('Chat prompt segment must have a messages array');\n }\n for (const msg of messages) {\n if (!msg.role) {\n throw new Error('Chat prompt segment must have a role');\n }\n out.push({ role: msg.role, content: msg.content });\n }\n }\n else {\n throw new Error(`Unknown prompt segment type: ${segment.type}`);\n }\n }\n return out;\n}\nfunction getChatKey(configuration) {\n if (typeof configuration === 'object' &&\n configuration !== null &&\n 'chatKey' in configuration &&\n typeof configuration.chatKey === 'string') {\n return configuration.chatKey;\n }\n return 'chat';\n}\nfunction isPromptSegmentArray(value) {\n return Array.isArray(value) && value.every(isPromptSegment);\n}\nfunction isPromptSegment(value) {\n return typeof value === 'object' && value !== null && 'role' in value && 'content' in value;\n}\n//# sourceMappingURL=render.js.map","import { TemplateType } from '@vertesia/common';\nimport { getFreeVariables, renderJsTemplate } from '@vertesia/jst';\nimport { extractHandlebarsVariables } from './extract-vars.js';\nimport { generateMockData } from './mock-data.js';\nimport { executeHandlebars } from './render.js';\n// JST's renderJsTemplate auto-adds `_` (helpers object) and runtime injects `Set` and `Array`\n// — treat them as globals so they don't appear as free vars in user templates.\n// Globals always available to JST templates regardless of schema:\n// - `_`, `Array`, `Set`: runtime-injected by `renderJsTemplate` (jst library)\n// - `_model`: runtime-injected by the studio-server executor as `{ ..._model: run.modelId }`\n// (see ExecutionRequest.ts:313 and executor/rendering/template.ts:13)\n// Keeping these in sync with `renderTemplate` in ./render.ts so a JST template that runs in\n// production also passes the validator.\nconst JST_AUTO_GLOBALS = ['_', 'Array', 'Set', '_model'];\nfunction countSeverities(issues) {\n let error_count = 0;\n let warning_count = 0;\n for (const issue of issues) {\n if (issue.severity === 'error') {\n error_count++;\n }\n else if (issue.severity === 'warning') {\n warning_count++;\n }\n }\n return { error_count, warning_count };\n}\nfunction validateHandlebarsPrompt(content, inputSchema) {\n const issues = [];\n const usedVars = extractHandlebarsVariables(content);\n const declaredVars = new Set(inputSchema?.properties ? Object.keys(inputSchema.properties) : []);\n for (const used of usedVars) {\n if (!declaredVars.has(used)) {\n issues.push({\n type: 'undeclared_template_variable',\n severity: 'error',\n variable: used,\n message: `Template references variable '{{${used}}}' but it is not declared in input_schema.properties. Add '${used}' to the schema with an appropriate type.`,\n });\n }\n }\n for (const declared of declaredVars) {\n if (!usedVars.has(declared)) {\n issues.push({\n type: 'unused_schema_variable',\n severity: 'warning',\n variable: declared,\n message: `Schema declares property '${declared}' but the template never references it. Remove it from input_schema or use it via {{${declared}}}.`,\n });\n }\n }\n // Render-time smoke test — always runs so syntax errors and failing helper calls are\n // surfaced even when undeclared-variable errors are already in the list. Handlebars renders\n // missing vars as empty strings (non-strict by default), so the render check does NOT echo\n // the var errors — anything it reports is a distinct template problem worth showing.\n const renderSchema = inputSchema ?? {};\n const mockData = generateMockData(renderSchema);\n const mockObject = typeof mockData === 'object' && mockData !== null && !Array.isArray(mockData) ? mockData : {};\n const renderResult = executeHandlebars(content, renderSchema, mockObject);\n if (!renderResult.success) {\n issues.push({\n type: 'handlebars_render_error',\n severity: 'error',\n message: `Handlebars rendering failed: ${renderResult.error}`,\n });\n }\n return issues;\n}\nfunction validateJstPrompt(content, inputSchema) {\n const issues = [];\n const declaredVars = new Set(inputSchema?.properties ? Object.keys(inputSchema.properties) : []);\n let referenced;\n try {\n const result = getFreeVariables(content, {\n globals: JST_AUTO_GLOBALS,\n acorn: { allowReturnOutsideFunction: true, locations: true },\n });\n referenced = result.vars;\n for (const err of result.errors) {\n issues.push({\n type: 'jst_unsafe_construct',\n severity: 'error',\n message: `JST validation error at ${err.location}: ${err.message}`,\n });\n }\n }\n catch (parseError) {\n // Acorn parse failure — surface as render error since the template can't be compiled.\n issues.push({\n type: 'jst_render_error',\n severity: 'error',\n message: `JST parse failed: ${parseError instanceof Error ? parseError.message : String(parseError)}`,\n });\n return issues;\n }\n for (const used of referenced) {\n if (!declaredVars.has(used)) {\n issues.push({\n type: 'undeclared_template_variable',\n severity: 'error',\n variable: used,\n message: `Template references variable '${used}' but it is not declared in input_schema.properties. Add '${used}' to the schema with an appropriate type.`,\n });\n }\n }\n for (const declared of declaredVars) {\n if (!referenced.has(declared)) {\n issues.push({\n type: 'unused_schema_variable',\n severity: 'warning',\n variable: declared,\n message: `Schema declares property '${declared}' but the template never references it. Remove it from input_schema or use it in the template.`,\n });\n }\n }\n // Render-time smoke test — only if there are no blocking errors so far, otherwise\n // the failure mode would just echo what we already reported.\n const blockingSoFar = issues.some((i) => i.severity === 'error');\n if (!blockingSoFar) {\n const renderSchema = inputSchema ?? {};\n const mockData = generateMockData(renderSchema);\n const mockObject = typeof mockData === 'object' && mockData !== null && !Array.isArray(mockData)\n ? mockData\n : {};\n try {\n renderJsTemplate(content, [...declaredVars], mockObject);\n }\n catch (renderError) {\n issues.push({\n type: 'jst_render_error',\n severity: 'error',\n message: `JST rendering failed: ${renderError instanceof Error ? renderError.message : String(renderError)}`,\n });\n }\n }\n return issues;\n}\n/**\n * Validate a single prompt template against its declared input schema.\n *\n * For `handlebars` and `jst` templates, the following checks are performed:\n * 1. Every variable referenced in the template must be declared as a top-level property\n * in `inputSchema.properties` (else → `undeclared_template_variable` error).\n * 2. Every property declared in `inputSchema.properties` should be referenced by the template\n * (else → `unused_schema_variable` warning — non-blocking).\n * 3. The template must render successfully against schema-derived mock data\n * (else → `handlebars_render_error` / `jst_render_error` error).\n * 4. For JST only: unsafe constructs (`with`, `for`, `while`, `import`, class, `this`,\n * dynamic property lookup, blacklisted props) → `jst_unsafe_construct` error.\n *\n * `text` content type passes through with no issues.\n */\nexport function validatePrompt(input) {\n let issues;\n if (input.contentType === TemplateType.handlebars) {\n issues = validateHandlebarsPrompt(input.content, input.inputSchema);\n }\n else if (input.contentType === TemplateType.jst) {\n issues = validateJstPrompt(input.content, input.inputSchema);\n }\n else {\n issues = [];\n }\n const { error_count, warning_count } = countSeverities(issues);\n return { issues, error_count, warning_count };\n}\n//# sourceMappingURL=validate.js.map","/**\n * Class hierarchy and registry-bound interface for the role system. These\n * are LOGIC — they have runtime behavior (constructors, instance methods,\n * subclass dispatch via `instanceof`). They live in `@vertesia/studio-utils`\n * (not common) per the package-layering contract: only types stay in common.\n */\n/**\n * A role: a named bundle of permissions that ACEs reference by name.\n *\n * Generic over the permission type so subclasses can tighten it:\n * - **System roles** declare `extends Role<Permission>` — construction time\n * type-checks against the central `Permission` enum.\n * - **ABAC roles** (`AbacRole`) declare `extends Role<string>` — permissions\n * are bare verbs (`'read'`, `'write'`, `'delete'`, future domain-specific\n * verbs) consumed by the JWT generator to form `{scope}:{verb}` keys in\n * `content_security`.\n *\n * The registry stores `Role` (defaulting to `Role<string>`) — the loose type\n * is the lowest common denominator. Tight typing is only enforced at\n * declaration sites of the role subclasses.\n */\nexport class Role {\n name;\n domain;\n permissions;\n constructor(name, permissions, domain) {\n this.name = name;\n this.domain = domain;\n this.permissions = new Set(permissions);\n }\n hasPermission(permission) {\n return this.permissions.has(permission);\n }\n}\n/**\n * Base class for built-in system roles. Hardcodes `domain: 'system'` and\n * specializes `Role<Permission>` so subclasses get compile-time type-checking\n * against the central `Permission` enum at construction.\n */\nexport class SystemRole extends Role {\n constructor(name, permissions) {\n super(name, permissions, 'system');\n }\n}\n/**\n * A role usable in ResourceSet ACEs. Adds `applicableScopes` — the kinds of\n * objects the role can be applied to at the ABAC scope level. Inherits\n * `Role<string>` because ABAC verbs aren't constrained to the central\n * `Permission` enum.\n *\n * The IAM UI filters via `listAbacRolesForScope` which checks `instanceof AbacRole`.\n */\nexport class AbacRole extends Role {\n applicableScopes;\n constructor(name, permissions, domain, applicableScopes) {\n super(name, permissions, domain);\n this.applicableScopes = applicableScopes;\n }\n}\n//# sourceMappingURL=classes.js.map","import { AbacRole } from './classes.js';\nconst ContentRoleDomain = 'content';\nconst APPLICABLE_SCOPES = ['document', 'collection'];\n/**\n * Names of roles owned by the `content` domain. Apply to ResourceSet ACEs\n * scoped to either `document` or `collection` — the semantics of \"read\n * content\" are the same for both kinds.\n */\nexport var ContentRoleNames;\n(function (ContentRoleNames) {\n ContentRoleNames[\"content_reader\"] = \"content:reader\";\n ContentRoleNames[\"content_writer\"] = \"content:writer\";\n ContentRoleNames[\"content_manager\"] = \"content:manager\";\n})(ContentRoleNames || (ContentRoleNames = {}));\nclass ContentReaderRole extends AbacRole {\n constructor() {\n super(ContentRoleNames.content_reader, ['read'], ContentRoleDomain, APPLICABLE_SCOPES);\n }\n}\nclass ContentWriterRole extends AbacRole {\n constructor() {\n super(ContentRoleNames.content_writer, ['read', 'write'], ContentRoleDomain, APPLICABLE_SCOPES);\n }\n}\nclass ContentManagerRole extends AbacRole {\n constructor() {\n super(ContentRoleNames.content_manager, ['read', 'write', 'delete'], ContentRoleDomain, APPLICABLE_SCOPES);\n }\n}\nconst contentRoles = {\n [ContentRoleNames.content_reader]: new ContentReaderRole(),\n [ContentRoleNames.content_writer]: new ContentWriterRole(),\n [ContentRoleNames.content_manager]: new ContentManagerRole(),\n};\nexport const contentPartition = {\n domain: ContentRoleDomain,\n roles: new Map(Object.entries(contentRoles)),\n};\n//# sourceMappingURL=content.js.map","import { Permission, SystemRoles } from '@vertesia/common';\nimport { SystemRole } from './classes.js';\nclass OrgMemberRole extends SystemRole {\n constructor(name, permissions) {\n super(name, [Permission.account_member, ...permissions]);\n }\n}\nclass OwnerRole extends OrgMemberRole {\n constructor() {\n super(SystemRoles.owner, Object.values(Permission));\n }\n}\nclass AdminRole extends OrgMemberRole {\n constructor() {\n super(SystemRoles.admin, Object.values(Permission));\n }\n}\nclass ManagerRole extends OrgMemberRole {\n constructor() {\n super(SystemRoles.manager, Object.values(Permission));\n this.permissions.delete(Permission.account_admin);\n this.permissions.delete(Permission.manage_billing);\n this.permissions.delete(Permission.audit_read);\n this.permissions.delete(Permission.agent_run_read);\n this.permissions.delete(Permission.content_read_all);\n this.permissions.delete(Permission.content_superadmin);\n this.permissions.delete(Permission.workflow_superadmin);\n }\n}\nclass DeveloperRole extends OrgMemberRole {\n constructor() {\n super(SystemRoles.developer, Object.values(Permission));\n this.permissions.delete(Permission.account_admin);\n this.permissions.delete(Permission.project_admin);\n this.permissions.delete(Permission.project_settings_write);\n this.permissions.delete(Permission.env_admin);\n this.permissions.delete(Permission.manage_billing);\n this.permissions.delete(Permission.audit_read);\n this.permissions.delete(Permission.agent_run_read);\n this.permissions.delete(Permission.content_read_all);\n this.permissions.delete(Permission.content_superadmin);\n this.permissions.delete(Permission.workflow_superadmin);\n }\n}\nclass ApplicationRole extends OrgMemberRole {\n constructor() {\n super(SystemRoles.application, [\n Permission.int_read,\n Permission.int_execute,\n Permission.int_write,\n Permission.run_read,\n Permission.content_write,\n Permission.content_read,\n Permission.content_write,\n Permission.content_admin,\n Permission.project_admin,\n Permission.workflow_run,\n Permission.project_settings_write,\n Permission.account_write,\n ]);\n }\n}\nclass AutomationRole extends OrgMemberRole {\n constructor() {\n super(SystemRoles.automation, [\n Permission.content_read,\n Permission.content_write,\n Permission.content_admin,\n Permission.int_read,\n Permission.int_execute,\n Permission.run_read,\n Permission.workflow_run,\n Permission.project_integration_read,\n ]);\n }\n}\nclass ContentProcessorRole extends OrgMemberRole {\n constructor() {\n super(SystemRoles.content_processor, [\n Permission.content_read,\n Permission.content_write,\n Permission.content_admin,\n Permission.content_superadmin,\n Permission.int_execute,\n Permission.workflow_read,\n Permission.workflow_run,\n Permission.run_read,\n ]);\n }\n}\nclass ConsumerRole extends OrgMemberRole {\n constructor() {\n super(SystemRoles.consumer, [\n Permission.content_admin,\n Permission.content_read,\n Permission.content_write,\n Permission.content_delete,\n Permission.int_read,\n Permission.int_execute,\n Permission.run_read,\n Permission.workflow_run,\n ]);\n }\n}\nclass ExecutorRole extends OrgMemberRole {\n constructor() {\n super(SystemRoles.executor, [Permission.int_execute, Permission.run_read, Permission.workflow_run]);\n }\n}\nclass ReaderRole extends OrgMemberRole {\n constructor() {\n super(SystemRoles.reader, [Permission.int_read, Permission.run_read, Permission.content_read]);\n }\n}\nconst READ_ONLY_AUDIT_PERMISSIONS = [\n Permission.studio_access,\n Permission.account_read,\n Permission.project_integration_read,\n Permission.api_key_read,\n Permission.billing_read,\n Permission.audit_read,\n Permission.int_read,\n Permission.run_read,\n Permission.content_read,\n Permission.content_read_all,\n Permission.task_read,\n Permission.workflow_read,\n Permission.agent_run_read,\n];\nclass ReadOnlyAuditRole extends OrgMemberRole {\n constructor(name) {\n super(name, READ_ONLY_AUDIT_PERMISSIONS);\n }\n}\nclass BillingRole extends OrgMemberRole {\n constructor() {\n super(SystemRoles.billing, [Permission.manage_billing]);\n }\n}\nclass AppMemberRole extends OrgMemberRole {\n constructor() {\n super(SystemRoles.app_member, [\n Permission.int_read,\n Permission.int_execute,\n Permission.int_write,\n Permission.run_read,\n Permission.content_write,\n Permission.content_read,\n Permission.content_delete,\n Permission.workflow_run,\n ]);\n }\n}\nclass ContentSuperAdmin extends DeveloperRole {\n constructor() {\n super();\n this.name = SystemRoles.content_superadmin;\n this.permissions.add(Permission.content_superadmin);\n }\n}\n// The enum is still named `SystemRoles` (historical); the partition's domain\n// is `system` — the foundational built-in roles, distinct from feature domains\n// like `content` or `tasks`. Renaming the enum to `SystemRoles` is a separate\n// concern to revisit later.\nconst systemRoles = {\n [SystemRoles.owner]: new OwnerRole(),\n [SystemRoles.admin]: new AdminRole(),\n [SystemRoles.manager]: new ManagerRole(),\n [SystemRoles.developer]: new DeveloperRole(),\n [SystemRoles.application]: new ApplicationRole(),\n [SystemRoles.automation]: new AutomationRole(),\n [SystemRoles.content_processor]: new ContentProcessorRole(),\n [SystemRoles.consumer]: new ConsumerRole(),\n [SystemRoles.executor]: new ExecutorRole(),\n [SystemRoles.reader]: new ReaderRole(),\n [SystemRoles.auditor]: new ReadOnlyAuditRole(SystemRoles.auditor),\n [SystemRoles.support]: new ReadOnlyAuditRole(SystemRoles.support),\n [SystemRoles.billing]: new BillingRole(),\n [SystemRoles.app_member]: new AppMemberRole(),\n [SystemRoles.member]: new OrgMemberRole(SystemRoles.member, []),\n [SystemRoles.content_superadmin]: new ContentSuperAdmin(),\n};\nexport const systemPartition = {\n domain: 'system',\n roles: new Map(Object.entries(systemRoles)),\n};\n//# sourceMappingURL=system.js.map","import { AbacRole, SystemRole } from './classes.js';\nimport { contentPartition } from './content.js';\nimport { systemPartition } from './system.js';\nexport { AbacRole, Role, SystemRole } from './classes.js';\nexport { ContentRoleNames } from './content.js';\n/**\n * The ordered partition registry. Partitions are queried in this order — first\n * match wins. The `system` partition is registered first so domain-specific\n * partitions (added later) cannot shadow built-in system roles.\n */\nconst partitions = [systemPartition, contentPartition];\n/** Look up a role by its name across all registered partitions. */\nexport function getRoleByName(name) {\n for (const partition of partitions) {\n const role = partition.roles.get(name);\n if (role)\n return role;\n }\n throw new Error(`Role ${name} not found`);\n}\n/** List every role across all partitions, in partition registration order. */\nexport function listRoles() {\n const result = [];\n for (const partition of partitions) {\n for (const role of partition.roles.values()) {\n result.push(role);\n }\n }\n return result;\n}\n/** Roles owned by a specific domain (e.g. `'system'`, `'content'`). */\nexport function listRolesByDomain(domain) {\n const partition = partitions.find((p) => p.domain === domain);\n return partition ? Array.from(partition.roles.values()) : [];\n}\n/**\n * ABAC roles applicable to a given ResourceSet scope (e.g. `'document'`,\n * `'collection'`). System roles are excluded — they don't carry scope semantics.\n */\nexport function listAbacRolesForScope(scope) {\n return listRoles().filter((r) => r instanceof AbacRole && r.applicableScopes.includes(scope));\n}\n/** Shortcut for the system partition: returns only `SystemRole` instances. */\nexport function listSystemRoles() {\n return listRoles().filter((r) => r instanceof SystemRole);\n}\n/** Names of every registered role across all partitions — suited for mongoose schema enum constraints. */\nexport function getAllRoleNames() {\n return listRoles().map((r) => r.name);\n}\n/**\n * Merge the permissions granted by a set of roles into a single array.\n * Intended for the system-role gating path. For ABAC roles, the bare-verb\n * permissions returned here aren't directly meaningful — use the JWT\n * `content_security` pathway instead.\n */\nexport function getPermissionsForRoles(roleNames) {\n const permissions = new Set();\n for (const roleName of roleNames) {\n const role = getRoleByName(roleName);\n for (const permission of role.permissions) {\n permissions.add(permission);\n }\n }\n return Array.from(permissions);\n}\n/**\n * A list of roles with a unified `hasPermission` check across them.\n */\nexport class RoleList {\n roles;\n constructor(roles) {\n this.roles = roles;\n }\n static fromRoleNames(roleNames) {\n const roles = roleNames.map((r) => getRoleByName(r));\n return new RoleList(roles);\n }\n static fromRoleName(roleName) {\n const roles = [getRoleByName(roleName)];\n return new RoleList(roles);\n }\n hasPermission(perm) {\n return this.roles.some((role) => role.hasPermission(perm));\n }\n}\n//# sourceMappingURL=index.js.map"],"mappings":"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"}
1
+ {"version":3,"file":"vertesia-studio-utils.js","names":[],"sources":["apps/index.js","prompts/extract-vars.js","prompts/mock-data.js","prompts/render.js","prompts/validate.js","roles/classes.js","roles/content.js","roles/system.js","roles/index.js"],"sourcesContent":["import { getStudioUtilsLogger } from '../logger.js';\n/**\n * Reserved deployment environment names that may never be used as endpoint\n * override keys. Reserving them prevents a manifest from hijacking auto-resolution\n * on a shared production studio-server (whose environment is one of these).\n */\nconst RESERVED_ENDPOINT_OVERRIDE_ENVS = new Set(['production', 'preview', 'staging']);\nconst GATEWAY_APP_ENDPOINT_PATTERN = new RegExp('^(.*)/tenants/([^/]+)/(?:' + 'package/([^/]+)|' + 'apps/([^/]+)(?:/versions/([^/]+))?(?:/api/package)?' + ')/?$');\n/**\n * Returns true if the given environment name is allowed as an endpoint override key.\n * Any non-empty name is accepted except the reserved shared-deployment names.\n */\nexport function isValidEndpointOverrideEnv(envName) {\n if (!envName)\n return false;\n return !RESERVED_ENDPOINT_OVERRIDE_ENVS.has(envName.toLowerCase());\n}\n/**\n * Substitutes `{{key}}` placeholders in a URL with the matching endpoint.\n * Unknown placeholders are left untouched (so failures surface as fetch errors\n * with the unresolved placeholder visible, rather than silently pointing nowhere).\n * Trailing slashes on replacement values are stripped to avoid `//api/...` joins.\n */\nexport function substituteEndpoints(url, endpoints) {\n if (!url || !endpoints)\n return url;\n return url.replace(/\\{\\{\\s*(\\w+)\\s*\\}\\}/g, (match, key) => {\n const value = endpoints[key];\n if (typeof value !== 'string' || !value)\n return match;\n return trimTrailingSlashes(value);\n });\n}\nfunction trimTrailingSlashes(value) {\n let end = value.length;\n while (end > 0 && value[end - 1] === '/') {\n end--;\n }\n return end === value.length ? value : value.slice(0, end);\n}\n/**\n * Resolves an app manifest endpoint to its package descriptor URL.\n *\n * Resolution has two modes:\n *\n * Gateway apps: if the base `endpoint` is an app-gateway URL, endpoint overrides\n * are ignored. Gateway versions are resolved by rewriting the canonical gateway\n * package URL itself:\n * - `https://gw/tenants/t/apps/my-app`\n * -> `https://gw/tenants/t/apps/my-app/api/package`\n * - `https://gw/tenants/t/package/my-app`\n * -> `https://gw/tenants/t/apps/my-app/api/package`\n * - with `version: \"1.2.3\"`\n * -> `https://gw/tenants/t/apps/my-app/versions/1.2.3/api/package`\n *\n * Regular apps: endpoint overrides are supported in this order:\n * 1. Version override: if `version` matches `endpoint_overrides[version]`, use\n * that URL. This lets regular, non-gateway apps expose version-pinned package\n * endpoints without going through the app gateway.\n * 2. Explicit override: if `requestedOverride` matches an allowed\n * `endpoint_overrides` key, use that URL. Callers must still verify the user\n * is allowed to request that override.\n * 3. Environment override: if `envName` matches an allowed `endpoint_overrides`\n * key, use that URL for deployment-env-specific endpoints.\n * 4. Base endpoint: otherwise use `manifest.endpoint`.\n * 5. Placeholder substitution: replace `{{gateway}}`, `{{studio}}`, etc. from\n * `vars`.\n *\n * Regular package URLs are returned unchanged:\n * - `https://plugin.example.com/api/package`\n * -> `https://plugin.example.com/api/package`\n *\n * Non-standard package URLs are also returned unchanged, with a warning:\n * - `https://plugin.example.com/exotic/pkg`\n * -> `https://plugin.example.com/exotic/pkg`\n */\nexport function resolveAppEndpoint(manifest, options = {}) {\n if (!manifest.endpoint) {\n return undefined;\n }\n const baseEndpoint = substituteEndpoints(manifest.endpoint, options.vars);\n const gateway = parseGatewayAppEndpoint(baseEndpoint);\n if (gateway) {\n return resolveGatewayAppEndpoint(gateway, options.version);\n }\n return resolveRegularAppEndpoint(manifest, manifest.endpoint, options);\n}\nfunction resolveGatewayAppEndpoint(gateway, version) {\n const effectiveVersion = version || gateway.version;\n const versionSeg = effectiveVersion ? `/versions/${encodeURIComponent(effectiveVersion)}` : '';\n return `${gateway.origin}/tenants/${gateway.tenant}/apps/${gateway.appId}${versionSeg}/api/package`;\n}\nfunction resolveRegularAppEndpoint(manifest, baseEndpoint, options) {\n const { envName, vars, requestedOverride, version } = options;\n let raw;\n if (version && manifest.endpoint_overrides?.[version]) {\n raw = manifest.endpoint_overrides[version];\n }\n else if (requestedOverride &&\n manifest.endpoint_overrides?.[requestedOverride] &&\n isValidEndpointOverrideEnv(requestedOverride)) {\n raw = manifest.endpoint_overrides[requestedOverride];\n }\n else if (envName && manifest.endpoint_overrides?.[envName] && isValidEndpointOverrideEnv(envName)) {\n raw = manifest.endpoint_overrides[envName];\n }\n else {\n raw = baseEndpoint;\n }\n // Substitute the selected regular endpoint, not only the base endpoint:\n // `raw` may be an override. Current server validation usually requires\n // overrides to be absolute URLs, but keeping substitution here preserves\n // compatibility if trusted/system manifests use placeholders in overrides.\n const endpoint = substituteEndpoints(raw, vars);\n warnIfNonStandardPackageEndpoint(endpoint);\n return endpoint;\n}\n/**\n * Resolves all URL placeholders in a manifest in place (both `endpoint` and\n * `tool_collections[].url`). Intended for server-side serialization.\n *\n * Mutates the manifest rather than returning a copy so it works cleanly with\n * Mongoose populated subdocs.\n */\nexport function resolveManifestUrls(manifest, options = {}) {\n if (!manifest)\n return;\n const { vars } = options;\n if (manifest.endpoint) {\n const resolved = resolveAppEndpoint(manifest, options);\n if (resolved && resolved !== manifest.endpoint) {\n manifest.endpoint = resolved;\n }\n }\n const toolCollections = manifest.tool_collections;\n if (toolCollections && Array.isArray(toolCollections)) {\n for (let i = 0; i < toolCollections.length; i++) {\n const item = toolCollections[i];\n if (item && typeof item === 'object' && item.url) {\n const sub = substituteEndpoints(item.url, vars);\n if (sub !== item.url)\n item.url = sub;\n }\n }\n }\n}\n/**\n * Parse a gateway app endpoint into its parts. Accepts BOTH the canonical apps mount\n * (`.../tenants/<t>/apps/<app>`), the canonical package route\n * (`.../tenants/<t>/apps/<app>/api/package`), versioned package routes, AND the\n * legacy package mount (`.../tenants/<t>/package/<app>`). Returns undefined for\n * non-gateway endpoints.\n */\nexport function parseGatewayAppEndpoint(endpoint) {\n const m = GATEWAY_APP_ENDPOINT_PATTERN.exec(endpoint);\n if (!m)\n return undefined;\n const [, origin, tenant, legacyAppId, appsAppId, version] = m;\n return {\n origin,\n tenant,\n appId: legacyAppId || appsAppId,\n ...(version ? { version } : {}),\n };\n}\nfunction warnIfNonStandardPackageEndpoint(endpoint) {\n if (!isStandardPackageEndpoint(endpoint)) {\n getStudioUtilsLogger().warn({ endpoint }, 'Non-standard app package endpoint; returning it unchanged during app endpoint resolution');\n }\n}\nfunction isStandardPackageEndpoint(endpoint) {\n try {\n const url = new URL(endpoint);\n return trimTrailingSlashes(url.pathname).endsWith('/package');\n }\n catch {\n return false;\n }\n}\n/**\n * Derives the runtime app API base from a package descriptor URL by removing the\n * final path segment.\n *\n * Examples:\n * - `https://plugin.example.com/api/package` -> `https://plugin.example.com/api`\n * - `https://plugin.example.com/exotic/pkg` -> `https://plugin.example.com/exotic`\n * - `https://gw/tenants/t/apps/my-app/api/package` -> `https://gw/tenants/t/apps/my-app/api`\n */\nexport function resolveAppApiBase(packageEndpoint) {\n const url = new URL(packageEndpoint);\n const path = trimTrailingSlashes(url.pathname);\n const lastSlash = path.lastIndexOf('/');\n url.pathname = lastSlash <= 0 ? '/' : path.slice(0, lastSlash);\n return url.toString();\n}\n/**\n * Resolves an app resource URL from a package descriptor endpoint and a path\n * relative to the app `/api` base.\n *\n * Examples:\n * - `resolveAppResource(\"https://plugin.example.com/api/package\", \"tools/main\")`\n * -> `https://plugin.example.com/api/tools/main`\n * - `resolveAppResource(\"https://gw/tenants/t/apps/my-app/api/package\", \"/interactions/x\")`\n * -> `https://gw/tenants/t/apps/my-app/api/interactions/x`\n */\nexport function resolveAppResource(packageEndpoint, pathRelativeToApiBase) {\n const base = resolveAppApiBase(packageEndpoint);\n const normalizedBase = base.endsWith('/') ? base.slice(0, -1) : base;\n const normalizedPath = pathRelativeToApiBase.startsWith('/') ? pathRelativeToApiBase : `/${pathRelativeToApiBase}`;\n return `${normalizedBase}${normalizedPath}`;\n}\n//# sourceMappingURL=index.js.map","import Handlebars from 'handlebars';\n/**\n * Extract the set of root-level variable names referenced by a Handlebars template.\n *\n * - `{{foo}}` → `foo`\n * - `{{obj.bar.baz}}` → `obj` (only the root identifier; nested access doesn't add `bar` or `baz`)\n * - `{{#if cond}}{{name}}{{/if}}` → `cond`, `name`\n * - `{{#each items as |item|}}{{item.name}}{{/each}}` → `items` (NOT `item` — bound by `as |item|`)\n * - `{{lookup obj key}}` → `obj`, `key` (helper name skipped)\n * - `{{@index}}`, `{{this}}` → ignored (built-in data vars / self)\n *\n * Helper names in mustaches and block heads (`{{customHelper x}}`, `{{#if x}}`) are NOT added to\n * the set — only the arguments and inner template references are. This matches the typical use\n * case for prompt validation: detect which schema properties the template actually reads.\n *\n * Returns an empty set on parse failure — callers should also run a syntactic render check\n * (e.g. `executeHandlebars`) to surface parse errors separately.\n */\nexport function extractHandlebarsVariables(template) {\n let ast;\n try {\n ast = Handlebars.parse(template);\n }\n catch {\n return new Set();\n }\n const variables = new Set();\n const localScopes = [];\n const isLocal = (name) => localScopes.some((scope) => scope.has(name));\n const visit = (node) => {\n if (!node || typeof node !== 'object')\n return;\n const n = node;\n switch (n.type) {\n case 'Program': {\n const body = n.body;\n if (body)\n for (const stmt of body)\n visit(stmt);\n break;\n }\n case 'BlockStatement': {\n // n.path is the helper name (`#if`, `#each`, `#customBlock`) — never a variable.\n // Visit params + hash to capture variables passed to the block helper.\n const params = n.params;\n if (params)\n for (const p of params)\n visit(p);\n visit(n.hash);\n // Track `as |x y|` block params — these are local bindings, not variables.\n const program = n.program;\n const inverse = n.inverse;\n const scope = new Set([...(program?.blockParams ?? []), ...(inverse?.blockParams ?? [])]);\n localScopes.push(scope);\n visit(n.program);\n visit(n.inverse);\n localScopes.pop();\n break;\n }\n case 'MustacheStatement': {\n // Bare mustache (no params, no hash) → it's a variable reference.\n // With params/hash, it's a helper call — skip the path (helper name), visit args.\n const params = n.params;\n const hash = n.hash;\n const isHelperCall = (params?.length ?? 0) > 0 || (hash?.pairs?.length ?? 0) > 0;\n if (!isHelperCall) {\n visit(n.path);\n }\n if (params)\n for (const p of params)\n visit(p);\n visit(hash);\n break;\n }\n case 'SubExpression': {\n // (helper arg1 arg2) — helper name in path is skipped, args carry variables.\n const params = n.params;\n if (params)\n for (const p of params)\n visit(p);\n visit(n.hash);\n break;\n }\n case 'PathExpression': {\n if (n.data)\n break; // `@-data` references (@index, @key, etc.)\n const parts = n.parts;\n if (!parts || parts.length === 0)\n break; // `this` / `.`\n const root = parts[0];\n if (isLocal(root))\n break;\n variables.add(root);\n break;\n }\n case 'Hash': {\n const pairs = n.pairs;\n if (pairs)\n for (const pair of pairs)\n visit(pair.value);\n break;\n }\n // ContentStatement, CommentStatement, *Literal, PartialStatement — no variables to extract.\n default:\n break;\n }\n };\n visit(ast);\n return variables;\n}\n//# sourceMappingURL=extract-vars.js.map","/**\n * Generate mock data that satisfies a JSON Schema.\n *\n * - string → `%property_name%` (visible placeholder, useful for previews and validation render tests)\n * - number/integer → random value 0–100\n * - boolean → true\n * - array → 1–2 items recursively generated from the items schema\n * - object → recursively populated from properties\n * - null → null\n * - $ref or unknown → `%property_name%`\n *\n * @param schema JSON schema to generate data for\n * @param propertyName Name used for string placeholders (default `value`)\n */\nexport function generateMockData(schema, propertyName = 'value') {\n if ('$ref' in schema) {\n return `%${propertyName}%`;\n }\n const types = Array.isArray(schema.type) ? schema.type : [schema.type];\n const primaryType = types[0];\n switch (primaryType) {\n case 'string':\n return `%${propertyName}%`;\n case 'number':\n case 'integer':\n return Math.floor(Math.random() * 101);\n case 'boolean':\n return true;\n case 'array': {\n const itemSchema = schema.items;\n if (itemSchema) {\n const itemCount = Math.floor(Math.random() * 2) + 1;\n return Array.from({ length: itemCount }, (_, index) => generateMockData(itemSchema, `${propertyName}_${index}`));\n }\n return [];\n }\n case 'object': {\n const result = {};\n if (schema.properties) {\n for (const [propName, propSchema] of Object.entries(schema.properties)) {\n result[propName] = generateMockData(propSchema, propName);\n }\n }\n return result;\n }\n case 'null':\n return null;\n default:\n return `%${propertyName}%`;\n }\n}\n//# sourceMappingURL=mock-data.js.map","import { PromptSegmentDefType, TemplateType } from '@vertesia/common';\nimport { CompositeError, renderHandlebarsTemplate, renderJsTemplate } from '@vertesia/jst';\n/**\n * Render a prompt template with the given input data.\n *\n * Handlebars templates use {{variable}} interpolation against `data`.\n * JST (JavaScript template) bodies evaluate against `data` with the schema's top-level\n * property names exposed as globals, plus `_model` — the active model id, which the\n * studio-server executor injects into the input as `{ ..._model: run.modelId }` when\n * executing an interaction (see `apps/studio-server/src/executor/ExecutionRequest.ts`\n * and `apps/studio-server/src/executor/rendering/template.ts`). Listing it here keeps\n * the Playground preview and `validatePrompt` in sync with runtime resolution — a JST\n * template referencing `_model` validates fine here and renders fine in production.\n *\n * For `TemplateType.text`, the content is returned verbatim — it is static text, not a\n * template — matching the studio-server executor (see `apps/studio-server/src/executor/\n * rendering/template.ts`). Routing it through the JST evaluator would compile the prose as\n * JavaScript and throw on any plain sentence (e.g. \"You are a helpful assistant.\").\n */\nexport function renderTemplate(code, contentType, schema, data) {\n if (contentType === TemplateType.handlebars) {\n return renderHandlebarsTemplate(code, data);\n }\n if (contentType === TemplateType.text) {\n return code;\n }\n const globals = [...(schema.properties ? Object.keys(schema.properties) : []), '_model'];\n return renderJsTemplate(code, globals, data);\n}\n/**\n * Execute a JST (JavaScript Template) with given data.\n * Returns a discriminated union to surface errors without throwing.\n */\nexport function executeJST(jstContent, schema, data) {\n try {\n const result = renderTemplate(jstContent, TemplateType.jst, schema, data);\n return { success: true, content: result };\n }\n catch (error) {\n return { success: false, error: error instanceof Error ? error.message : String(error) };\n }\n}\n/**\n * Execute a Handlebars template with given data.\n * Returns a discriminated union to surface errors without throwing.\n */\nexport function executeHandlebars(handlebarsContent, schema, data) {\n try {\n const result = renderTemplate(handlebarsContent, TemplateType.handlebars, schema, data);\n return { success: true, content: result };\n }\n catch (error) {\n return { success: false, error: error instanceof Error ? error.message : String(error) };\n }\n}\nfunction renderChatSegment(segment, data) {\n const chatKey = getChatKey(segment.configuration);\n const chat = data[chatKey];\n let content;\n if (Array.isArray(chat)) {\n content = JSON.stringify(chat, undefined, 2);\n }\n else {\n content = `No \"${chatKey}\" property found on input data`;\n }\n return {\n title: 'Chat history',\n content,\n };\n}\nfunction renderTemplateSegment(segment, data) {\n if (!segment.template) {\n return { title: '(unknown segment)', content: '', error: new Error('Prompt segment is missing its template') };\n }\n const schema = segment.template.inputSchema || {};\n const title = `@${segment.template.role}`;\n try {\n const content = renderTemplate(segment.template.content, segment.template.content_type, schema, data);\n return { title, content, segmentId: segment.template.id };\n }\n catch (error) {\n // Isolate the failure to this segment so the remaining segments still render.\n return {\n title,\n content: error instanceof Error ? error.message : String(error),\n segmentId: segment.template.id,\n error: error instanceof Error ? error : new Error(String(error)),\n };\n }\n}\nexport function renderSegments(segments, data) {\n const out = [];\n for (const segment of segments) {\n if (segment.type === PromptSegmentDefType.chat) {\n out.push(renderChatSegment(segment, data));\n }\n else if (segment.type === PromptSegmentDefType.template) {\n out.push(renderTemplateSegment(segment, data));\n }\n }\n return out;\n}\nexport function renderSegmentsOrErrors(segments, textOrObject) {\n try {\n return renderSegments(segments, typeof textOrObject === 'string' ? JSON.parse(textOrObject.trim()) : textOrObject || {});\n }\n catch (error) {\n if (error instanceof CompositeError) {\n return error.errors.map((err) => ({\n error: err,\n title: 'Rendering Error',\n content: err.message,\n }));\n }\n return [\n {\n error: error instanceof Error ? error : new Error(String(error)),\n title: 'Rendering Error',\n content: error instanceof Error ? error.message : String(error),\n },\n ];\n }\n}\nexport function renderPrompt(segments, payload) {\n const out = [];\n for (const segment of segments) {\n if (segment.template) {\n const schema = segment.template.inputSchema || {};\n const content = renderTemplate(segment.template.content, segment.template.content_type, schema, payload);\n out.push({ role: segment.template.role, content });\n }\n else if (segment.type === PromptSegmentDefType.chat) {\n const chatKey = getChatKey(segment.configuration);\n const messages = payload[chatKey];\n if (!isPromptSegmentArray(messages)) {\n throw new Error('Chat prompt segment must have a messages array');\n }\n for (const msg of messages) {\n if (!msg.role) {\n throw new Error('Chat prompt segment must have a role');\n }\n out.push({ role: msg.role, content: msg.content });\n }\n }\n else {\n throw new Error(`Unknown prompt segment type: ${segment.type}`);\n }\n }\n return out;\n}\nfunction getChatKey(configuration) {\n if (typeof configuration === 'object' &&\n configuration !== null &&\n 'chatKey' in configuration &&\n typeof configuration.chatKey === 'string') {\n return configuration.chatKey;\n }\n return 'chat';\n}\nfunction isPromptSegmentArray(value) {\n return Array.isArray(value) && value.every(isPromptSegment);\n}\nfunction isPromptSegment(value) {\n return typeof value === 'object' && value !== null && 'role' in value && 'content' in value;\n}\n//# sourceMappingURL=render.js.map","import { TemplateType } from '@vertesia/common';\nimport { getFreeVariables, renderJsTemplate } from '@vertesia/jst';\nimport { extractHandlebarsVariables } from './extract-vars.js';\nimport { generateMockData } from './mock-data.js';\nimport { executeHandlebars } from './render.js';\n// JST's renderJsTemplate auto-adds `_` (helpers object) and runtime injects `Set` and `Array`\n// — treat them as globals so they don't appear as free vars in user templates.\n// Globals always available to JST templates regardless of schema:\n// - `_`, `Array`, `Set`: runtime-injected by `renderJsTemplate` (jst library)\n// - `_model`: runtime-injected by the studio-server executor as `{ ..._model: run.modelId }`\n// (see ExecutionRequest.ts:313 and executor/rendering/template.ts:13)\n// Keeping these in sync with `renderTemplate` in ./render.ts so a JST template that runs in\n// production also passes the validator.\nconst JST_AUTO_GLOBALS = ['_', 'Array', 'Set', '_model'];\nfunction countSeverities(issues) {\n let error_count = 0;\n let warning_count = 0;\n for (const issue of issues) {\n if (issue.severity === 'error') {\n error_count++;\n }\n else if (issue.severity === 'warning') {\n warning_count++;\n }\n }\n return { error_count, warning_count };\n}\nfunction validateHandlebarsPrompt(content, inputSchema) {\n const issues = [];\n const usedVars = extractHandlebarsVariables(content);\n const declaredVars = new Set(inputSchema?.properties ? Object.keys(inputSchema.properties) : []);\n for (const used of usedVars) {\n if (!declaredVars.has(used)) {\n issues.push({\n type: 'undeclared_template_variable',\n severity: 'error',\n variable: used,\n message: `Template references variable '{{${used}}}' but it is not declared in input_schema.properties. Add '${used}' to the schema with an appropriate type.`,\n });\n }\n }\n for (const declared of declaredVars) {\n if (!usedVars.has(declared)) {\n issues.push({\n type: 'unused_schema_variable',\n severity: 'warning',\n variable: declared,\n message: `Schema declares property '${declared}' but the template never references it. Remove it from input_schema or use it via {{${declared}}}.`,\n });\n }\n }\n // Render-time smoke test — always runs so syntax errors and failing helper calls are\n // surfaced even when undeclared-variable errors are already in the list. Handlebars renders\n // missing vars as empty strings (non-strict by default), so the render check does NOT echo\n // the var errors — anything it reports is a distinct template problem worth showing.\n const renderSchema = inputSchema ?? {};\n const mockData = generateMockData(renderSchema);\n const mockObject = typeof mockData === 'object' && mockData !== null && !Array.isArray(mockData) ? mockData : {};\n const renderResult = executeHandlebars(content, renderSchema, mockObject);\n if (!renderResult.success) {\n issues.push({\n type: 'handlebars_render_error',\n severity: 'error',\n message: `Handlebars rendering failed: ${renderResult.error}`,\n });\n }\n return issues;\n}\nfunction validateJstPrompt(content, inputSchema) {\n const issues = [];\n const declaredVars = new Set(inputSchema?.properties ? Object.keys(inputSchema.properties) : []);\n let referenced;\n try {\n const result = getFreeVariables(content, {\n globals: JST_AUTO_GLOBALS,\n acorn: { allowReturnOutsideFunction: true, locations: true },\n });\n referenced = result.vars;\n for (const err of result.errors) {\n issues.push({\n type: 'jst_unsafe_construct',\n severity: 'error',\n message: `JST validation error at ${err.location}: ${err.message}`,\n });\n }\n }\n catch (parseError) {\n // Acorn parse failure — surface as render error since the template can't be compiled.\n issues.push({\n type: 'jst_render_error',\n severity: 'error',\n message: `JST parse failed: ${parseError instanceof Error ? parseError.message : String(parseError)}`,\n });\n return issues;\n }\n for (const used of referenced) {\n if (!declaredVars.has(used)) {\n issues.push({\n type: 'undeclared_template_variable',\n severity: 'error',\n variable: used,\n message: `Template references variable '${used}' but it is not declared in input_schema.properties. Add '${used}' to the schema with an appropriate type.`,\n });\n }\n }\n for (const declared of declaredVars) {\n if (!referenced.has(declared)) {\n issues.push({\n type: 'unused_schema_variable',\n severity: 'warning',\n variable: declared,\n message: `Schema declares property '${declared}' but the template never references it. Remove it from input_schema or use it in the template.`,\n });\n }\n }\n // Render-time smoke test — only if there are no blocking errors so far, otherwise\n // the failure mode would just echo what we already reported.\n const blockingSoFar = issues.some((i) => i.severity === 'error');\n if (!blockingSoFar) {\n const renderSchema = inputSchema ?? {};\n const mockData = generateMockData(renderSchema);\n const mockObject = typeof mockData === 'object' && mockData !== null && !Array.isArray(mockData)\n ? mockData\n : {};\n try {\n renderJsTemplate(content, [...declaredVars], mockObject);\n }\n catch (renderError) {\n issues.push({\n type: 'jst_render_error',\n severity: 'error',\n message: `JST rendering failed: ${renderError instanceof Error ? renderError.message : String(renderError)}`,\n });\n }\n }\n return issues;\n}\n/**\n * Validate a single prompt template against its declared input schema.\n *\n * For `handlebars` and `jst` templates, the following checks are performed:\n * 1. Every variable referenced in the template must be declared as a top-level property\n * in `inputSchema.properties` (else → `undeclared_template_variable` error).\n * 2. Every property declared in `inputSchema.properties` should be referenced by the template\n * (else → `unused_schema_variable` warning — non-blocking).\n * 3. The template must render successfully against schema-derived mock data\n * (else → `handlebars_render_error` / `jst_render_error` error).\n * 4. For JST only: unsafe constructs (`with`, `for`, `while`, `import`, class, `this`,\n * dynamic property lookup, blacklisted props) → `jst_unsafe_construct` error.\n *\n * `text` content type passes through with no issues.\n */\nexport function validatePrompt(input) {\n let issues;\n if (input.contentType === TemplateType.handlebars) {\n issues = validateHandlebarsPrompt(input.content, input.inputSchema);\n }\n else if (input.contentType === TemplateType.jst) {\n issues = validateJstPrompt(input.content, input.inputSchema);\n }\n else {\n issues = [];\n }\n const { error_count, warning_count } = countSeverities(issues);\n return { issues, error_count, warning_count };\n}\n//# sourceMappingURL=validate.js.map","/**\n * Class hierarchy and registry-bound interface for the role system. These\n * are LOGIC — they have runtime behavior (constructors, instance methods,\n * subclass dispatch via `instanceof`). They live in `@vertesia/studio-utils`\n * (not common) per the package-layering contract: only types stay in common.\n */\n/**\n * A role: a named bundle of permissions that ACEs reference by name.\n *\n * Generic over the permission type so subclasses can tighten it:\n * - **System roles** declare `extends Role<Permission>` — construction time\n * type-checks against the central `Permission` enum.\n * - **ABAC roles** (`AbacRole`) declare `extends Role<string>` — permissions\n * are bare verbs (`'read'`, `'write'`, `'delete'`, future domain-specific\n * verbs) consumed by the JWT generator to form `{scope}:{verb}` keys in\n * `content_security`.\n *\n * The registry stores `Role` (defaulting to `Role<string>`) — the loose type\n * is the lowest common denominator. Tight typing is only enforced at\n * declaration sites of the role subclasses.\n */\nexport class Role {\n name;\n domain;\n permissions;\n constructor(name, permissions, domain) {\n this.name = name;\n this.domain = domain;\n this.permissions = new Set(permissions);\n }\n hasPermission(permission) {\n return this.permissions.has(permission);\n }\n}\n/**\n * Base class for built-in system roles. Hardcodes `domain: 'system'` and\n * specializes `Role<Permission>` so subclasses get compile-time type-checking\n * against the central `Permission` enum at construction.\n */\nexport class SystemRole extends Role {\n constructor(name, permissions) {\n super(name, permissions, 'system');\n }\n}\n/**\n * A role usable in ResourceSet ACEs. Adds `applicableScopes` — the kinds of\n * objects the role can be applied to at the ABAC scope level. Inherits\n * `Role<string>` because ABAC verbs aren't constrained to the central\n * `Permission` enum.\n *\n * The IAM UI filters via `listAbacRolesForScope` which checks `instanceof AbacRole`.\n */\nexport class AbacRole extends Role {\n applicableScopes;\n constructor(name, permissions, domain, applicableScopes) {\n super(name, permissions, domain);\n this.applicableScopes = applicableScopes;\n }\n}\n//# sourceMappingURL=classes.js.map","import { AbacRole } from './classes.js';\nconst ContentRoleDomain = 'content';\nconst APPLICABLE_SCOPES = ['document', 'collection'];\n/**\n * Names of roles owned by the `content` domain. Apply to ResourceSet ACEs\n * scoped to either `document` or `collection` — the semantics of \"read\n * content\" are the same for both kinds.\n */\nexport var ContentRoleNames;\n(function (ContentRoleNames) {\n ContentRoleNames[\"content_reader\"] = \"content:reader\";\n ContentRoleNames[\"content_writer\"] = \"content:writer\";\n ContentRoleNames[\"content_manager\"] = \"content:manager\";\n})(ContentRoleNames || (ContentRoleNames = {}));\nclass ContentReaderRole extends AbacRole {\n constructor() {\n super(ContentRoleNames.content_reader, ['read'], ContentRoleDomain, APPLICABLE_SCOPES);\n }\n}\nclass ContentWriterRole extends AbacRole {\n constructor() {\n super(ContentRoleNames.content_writer, ['read', 'write'], ContentRoleDomain, APPLICABLE_SCOPES);\n }\n}\nclass ContentManagerRole extends AbacRole {\n constructor() {\n super(ContentRoleNames.content_manager, ['read', 'write', 'delete'], ContentRoleDomain, APPLICABLE_SCOPES);\n }\n}\nconst contentRoles = {\n [ContentRoleNames.content_reader]: new ContentReaderRole(),\n [ContentRoleNames.content_writer]: new ContentWriterRole(),\n [ContentRoleNames.content_manager]: new ContentManagerRole(),\n};\nexport const contentPartition = {\n domain: ContentRoleDomain,\n roles: new Map(Object.entries(contentRoles)),\n};\n//# sourceMappingURL=content.js.map","import { Permission, SystemRoles } from '@vertesia/common';\nimport { SystemRole } from './classes.js';\nclass OrgMemberRole extends SystemRole {\n constructor(name, permissions) {\n super(name, [Permission.account_member, ...permissions]);\n }\n}\nclass OwnerRole extends OrgMemberRole {\n constructor() {\n super(SystemRoles.owner, Object.values(Permission));\n }\n}\nclass AdminRole extends OrgMemberRole {\n constructor() {\n super(SystemRoles.admin, Object.values(Permission));\n }\n}\nclass ManagerRole extends OrgMemberRole {\n constructor() {\n super(SystemRoles.manager, Object.values(Permission));\n this.permissions.delete(Permission.account_admin);\n this.permissions.delete(Permission.manage_billing);\n this.permissions.delete(Permission.audit_read);\n this.permissions.delete(Permission.agent_run_read);\n this.permissions.delete(Permission.content_read_all);\n this.permissions.delete(Permission.content_superadmin);\n this.permissions.delete(Permission.workflow_superadmin);\n }\n}\nclass DeveloperRole extends OrgMemberRole {\n constructor() {\n super(SystemRoles.developer, Object.values(Permission));\n this.permissions.delete(Permission.account_admin);\n this.permissions.delete(Permission.project_admin);\n this.permissions.delete(Permission.project_settings_write);\n this.permissions.delete(Permission.env_admin);\n this.permissions.delete(Permission.manage_billing);\n this.permissions.delete(Permission.audit_read);\n this.permissions.delete(Permission.agent_run_read);\n this.permissions.delete(Permission.content_read_all);\n this.permissions.delete(Permission.content_superadmin);\n this.permissions.delete(Permission.workflow_superadmin);\n }\n}\nclass ApplicationRole extends OrgMemberRole {\n constructor() {\n super(SystemRoles.application, [\n Permission.int_read,\n Permission.int_execute,\n Permission.int_write,\n Permission.run_read,\n Permission.content_write,\n Permission.content_read,\n Permission.content_write,\n Permission.content_admin,\n Permission.project_admin,\n Permission.workflow_run,\n Permission.project_settings_write,\n Permission.account_write,\n ]);\n }\n}\nclass AutomationRole extends OrgMemberRole {\n constructor() {\n super(SystemRoles.automation, [\n Permission.content_read,\n Permission.content_write,\n Permission.content_admin,\n Permission.int_read,\n Permission.int_execute,\n Permission.run_read,\n Permission.workflow_run,\n Permission.project_integration_read,\n ]);\n }\n}\nclass ContentProcessorRole extends OrgMemberRole {\n constructor() {\n super(SystemRoles.content_processor, [\n Permission.content_read,\n Permission.content_write,\n Permission.content_admin,\n Permission.content_superadmin,\n Permission.int_execute,\n Permission.workflow_read,\n Permission.workflow_run,\n Permission.run_read,\n ]);\n }\n}\nclass ConsumerRole extends OrgMemberRole {\n constructor() {\n super(SystemRoles.consumer, [\n Permission.content_admin,\n Permission.content_read,\n Permission.content_write,\n Permission.content_delete,\n Permission.int_read,\n Permission.int_execute,\n Permission.run_read,\n Permission.workflow_run,\n ]);\n }\n}\nclass ExecutorRole extends OrgMemberRole {\n constructor() {\n super(SystemRoles.executor, [Permission.int_execute, Permission.run_read, Permission.workflow_run]);\n }\n}\nclass ReaderRole extends OrgMemberRole {\n constructor() {\n super(SystemRoles.reader, [Permission.int_read, Permission.run_read, Permission.content_read]);\n }\n}\nconst READ_ONLY_AUDIT_PERMISSIONS = [\n Permission.studio_access,\n Permission.account_read,\n Permission.project_integration_read,\n Permission.api_key_read,\n Permission.billing_read,\n Permission.audit_read,\n Permission.int_read,\n Permission.run_read,\n Permission.content_read,\n Permission.content_read_all,\n Permission.task_read,\n Permission.workflow_read,\n Permission.agent_run_read,\n];\nclass ReadOnlyAuditRole extends OrgMemberRole {\n constructor(name) {\n super(name, READ_ONLY_AUDIT_PERMISSIONS);\n }\n}\nclass BillingRole extends OrgMemberRole {\n constructor() {\n super(SystemRoles.billing, [Permission.manage_billing]);\n }\n}\nclass AppMemberRole extends OrgMemberRole {\n constructor() {\n super(SystemRoles.app_member, [\n Permission.int_read,\n Permission.int_execute,\n Permission.int_write,\n Permission.run_read,\n Permission.content_write,\n Permission.content_read,\n Permission.content_delete,\n Permission.workflow_run,\n ]);\n }\n}\nclass ContentSuperAdmin extends DeveloperRole {\n constructor() {\n super();\n this.name = SystemRoles.content_superadmin;\n this.permissions.add(Permission.content_superadmin);\n }\n}\n// The enum is still named `SystemRoles` (historical); the partition's domain\n// is `system` — the foundational built-in roles, distinct from feature domains\n// like `content` or `tasks`. Renaming the enum to `SystemRoles` is a separate\n// concern to revisit later.\nconst systemRoles = {\n [SystemRoles.owner]: new OwnerRole(),\n [SystemRoles.admin]: new AdminRole(),\n [SystemRoles.manager]: new ManagerRole(),\n [SystemRoles.developer]: new DeveloperRole(),\n [SystemRoles.application]: new ApplicationRole(),\n [SystemRoles.automation]: new AutomationRole(),\n [SystemRoles.content_processor]: new ContentProcessorRole(),\n [SystemRoles.consumer]: new ConsumerRole(),\n [SystemRoles.executor]: new ExecutorRole(),\n [SystemRoles.reader]: new ReaderRole(),\n [SystemRoles.auditor]: new ReadOnlyAuditRole(SystemRoles.auditor),\n [SystemRoles.support]: new ReadOnlyAuditRole(SystemRoles.support),\n [SystemRoles.billing]: new BillingRole(),\n [SystemRoles.app_member]: new AppMemberRole(),\n [SystemRoles.member]: new OrgMemberRole(SystemRoles.member, []),\n [SystemRoles.content_superadmin]: new ContentSuperAdmin(),\n};\nexport const systemPartition = {\n domain: 'system',\n roles: new Map(Object.entries(systemRoles)),\n};\n//# sourceMappingURL=system.js.map","import { AbacRole, SystemRole } from './classes.js';\nimport { contentPartition } from './content.js';\nimport { systemPartition } from './system.js';\nexport { AbacRole, Role, SystemRole } from './classes.js';\nexport { ContentRoleNames } from './content.js';\n/**\n * The ordered partition registry. Partitions are queried in this order — first\n * match wins. The `system` partition is registered first so domain-specific\n * partitions (added later) cannot shadow built-in system roles.\n */\nconst partitions = [systemPartition, contentPartition];\n/** Look up a role by its name across all registered partitions. */\nexport function getRoleByName(name) {\n for (const partition of partitions) {\n const role = partition.roles.get(name);\n if (role)\n return role;\n }\n throw new Error(`Role ${name} not found`);\n}\n/** List every role across all partitions, in partition registration order. */\nexport function listRoles() {\n const result = [];\n for (const partition of partitions) {\n for (const role of partition.roles.values()) {\n result.push(role);\n }\n }\n return result;\n}\n/** Roles owned by a specific domain (e.g. `'system'`, `'content'`). */\nexport function listRolesByDomain(domain) {\n const partition = partitions.find((p) => p.domain === domain);\n return partition ? Array.from(partition.roles.values()) : [];\n}\n/**\n * ABAC roles applicable to a given ResourceSet scope (e.g. `'document'`,\n * `'collection'`). System roles are excluded — they don't carry scope semantics.\n */\nexport function listAbacRolesForScope(scope) {\n return listRoles().filter((r) => r instanceof AbacRole && r.applicableScopes.includes(scope));\n}\n/** Shortcut for the system partition: returns only `SystemRole` instances. */\nexport function listSystemRoles() {\n return listRoles().filter((r) => r instanceof SystemRole);\n}\n/** Names of every registered role across all partitions — suited for mongoose schema enum constraints. */\nexport function getAllRoleNames() {\n return listRoles().map((r) => r.name);\n}\n/**\n * Merge the permissions granted by a set of roles into a single array.\n * Intended for the system-role gating path. For ABAC roles, the bare-verb\n * permissions returned here aren't directly meaningful — use the JWT\n * `content_security` pathway instead.\n */\nexport function getPermissionsForRoles(roleNames) {\n const permissions = new Set();\n for (const roleName of roleNames) {\n const role = getRoleByName(roleName);\n for (const permission of role.permissions) {\n permissions.add(permission);\n }\n }\n return Array.from(permissions);\n}\n/**\n * Return the platform permission keys a caller must hold to delegate a role.\n *\n * System roles already contain complete `domain:verb` Permission values. ABAC roles store bare\n * verbs for content-security token generation, so map them back to their domain-qualified keys at\n * an authorization boundary.\n */\nexport function getDelegablePermissionsForRole(roleName) {\n const role = getRoleByName(roleName);\n if (role instanceof AbacRole) {\n return [...role.permissions].map((permission) => `${role.domain}:${permission}`);\n }\n return [...role.permissions];\n}\n/**\n * A list of roles with a unified `hasPermission` check across them.\n */\nexport class RoleList {\n roles;\n constructor(roles) {\n this.roles = roles;\n }\n static fromRoleNames(roleNames) {\n const roles = roleNames.map((r) => getRoleByName(r));\n return new RoleList(roles);\n }\n static fromRoleName(roleName) {\n const roles = [getRoleByName(roleName)];\n return new RoleList(roles);\n }\n hasPermission(perm) {\n return this.roles.some((role) => role.hasPermission(perm));\n }\n}\n//# sourceMappingURL=index.js.map"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vertesia/studio-utils",
3
- "version": "1.5.0-dev.20260807.073259Z",
3
+ "version": "1.5.0-dev.20260826.064249Z",
4
4
  "type": "module",
5
5
  "description": "Shared utilities for Vertesia studio: prompt template rendering, schema-based mock data generation, prompt and interaction validation helpers.",
6
6
  "main": "./lib/index.js",
@@ -47,18 +47,18 @@
47
47
  ],
48
48
  "devDependencies": {
49
49
  "@types/node": "^24.13.3",
50
- "rolldown": "1.2.0",
50
+ "rolldown": "1.2.4",
51
51
  "typescript": "7.0.2",
52
52
  "vitest": "^4.1.10",
53
53
  "@vertesia/tsconfig": "0.1.0"
54
54
  },
55
55
  "dependencies": {
56
56
  "handlebars": "^4.7.9",
57
- "@llumiverse/common": "1.5.0-dev.20260807.072147Z",
58
- "@vertesia/common": "1.5.0-dev.20260807.073259Z",
59
- "@vertesia/jst": "1.5.0-dev.20260807.073259Z"
57
+ "@llumiverse/common": "1.5.0-dev.20260826.063042Z",
58
+ "@vertesia/jst": "1.5.0-dev.20260826.064249Z",
59
+ "@vertesia/common": "1.5.0-dev.20260826.064249Z"
60
60
  },
61
- "gitHead": "e6ebc1482ad6c4df965d74c79f82eb25fc6e2ce3",
61
+ "gitHead": "c0d6426380b6f66657db83017c90de208ee4956a",
62
62
  "scripts": {
63
63
  "lint": "biome lint src",
64
64
  "test": "vitest run",
@@ -1 +1 @@
1
- export { matchConditions, matchLike, resolvePath } from './match.js';
1
+ export { matchConditions, matchLike, resolveConditions, resolvePath } from './match.js';
@@ -1,6 +1,6 @@
1
1
  import { describe, expect, it, vi } from 'vitest';
2
2
  import { installStudioUtilsLogger, type StudioUtilsLogger } from '../logger.js';
3
- import { matchConditions, matchLike, resolvePath } from './index.js';
3
+ import { matchConditions, matchLike, resolveConditions, resolvePath } from './index.js';
4
4
 
5
5
  describe('resolvePath', () => {
6
6
  it('resolves top-level fields and one-level properties fields', () => {
@@ -58,6 +58,23 @@ describe('matchConditions', () => {
58
58
  expect(matchConditions({ code: { $gt: 'a' } }, { code: 'b' })).toBe(true);
59
59
  });
60
60
 
61
+ it('matches set operators against scalar and array-valued object fields', () => {
62
+ expect(
63
+ matchConditions({ compartments: { $in: ['finance', 'legal'] } }, { compartments: ['hr', 'finance'] }),
64
+ ).toBe(true);
65
+ expect(matchConditions({ compartments: { $in: ['finance'] } }, { compartments: ['hr'] })).toBe(false);
66
+ expect(matchConditions({ compartments: { $nin: ['finance'] } }, { compartments: ['hr'] })).toBe(true);
67
+ expect(matchConditions({ compartments: { $nin: ['finance'] } }, { compartments: ['hr', 'finance'] })).toBe(
68
+ false,
69
+ );
70
+ });
71
+
72
+ it('uses Mongo-compatible equality for scalar conditions on array-valued fields', () => {
73
+ expect(matchConditions({ tags: 'finance' }, { tags: ['audit', 'finance'] })).toBe(true);
74
+ expect(matchConditions({ tags: { $eq: 'finance' } }, { tags: ['audit', 'finance'] })).toBe(true);
75
+ expect(matchConditions({ tags: { $ne: 'finance' } }, { tags: ['audit', 'finance'] })).toBe(false);
76
+ });
77
+
61
78
  it('treats unknown operators as non-matches', () => {
62
79
  const logger: StudioUtilsLogger = {
63
80
  warn: vi.fn(),
@@ -73,3 +90,45 @@ describe('matchConditions', () => {
73
90
  installStudioUtilsLogger(previousLogger);
74
91
  });
75
92
  });
93
+
94
+ describe('resolveConditions', () => {
95
+ it('resolves scalar and array principal references', () => {
96
+ expect(
97
+ resolveConditions(
98
+ {
99
+ sensitivity: { $lte: '$principal.clearance' },
100
+ compartments: { $in: '$principal.compartments' },
101
+ 'properties.department': '$principal.properties.department',
102
+ },
103
+ {
104
+ clearance: 3,
105
+ compartments: ['finance'],
106
+ properties: { department: 'finance' },
107
+ },
108
+ ),
109
+ ).toEqual({
110
+ sensitivity: { $lte: 3 },
111
+ compartments: { $in: ['finance'] },
112
+ 'properties.department': 'finance',
113
+ });
114
+ });
115
+
116
+ it('uses deterministic defaults for missing principal fields', () => {
117
+ expect(
118
+ resolveConditions(
119
+ {
120
+ sensitivity: { $lte: '$principal.clearance' },
121
+ compartments: { $in: '$principal.compartments' },
122
+ active: { $exists: '$principal.active' },
123
+ department: '$principal.properties.department',
124
+ },
125
+ {},
126
+ ),
127
+ ).toEqual({
128
+ sensitivity: { $lte: 0 },
129
+ compartments: { $in: [] },
130
+ active: { $exists: false },
131
+ department: '',
132
+ });
133
+ });
134
+ });
@@ -56,6 +56,20 @@ function compareOrdered(value: unknown, expected: unknown, op: '$gt' | '$gte' |
56
56
  }
57
57
  }
58
58
 
59
+ function matchesEquality(value: unknown, expected: unknown): boolean {
60
+ if (Array.isArray(value) && !Array.isArray(expected)) {
61
+ return value.includes(expected);
62
+ }
63
+ if (Array.isArray(value) && Array.isArray(expected)) {
64
+ return value.length === expected.length && value.every((entry, index) => entry === expected[index]);
65
+ }
66
+ return value === expected;
67
+ }
68
+
69
+ function matchesAny(value: unknown, expected: unknown[]): boolean {
70
+ return Array.isArray(value) ? value.some((entry) => expected.includes(entry)) : expected.includes(value);
71
+ }
72
+
59
73
  /**
60
74
  * Evaluate a PropertyConditions object against a set of properties (JS-side, in-memory).
61
75
  *
@@ -80,9 +94,8 @@ function compareOrdered(value: unknown, expected: unknown, op: '$gt' | '$gte' |
80
94
  * Unknown operators emit a warning via the studio-utils logger and return false — surfacing
81
95
  * misconfiguration loudly without breaking the surrounding token mint or permission check.
82
96
  *
83
- * `$principal.X` substitutions in conditions are expected to have already been resolved at
84
- * JWT-mint time by `resolveConditions` in token-server; values reaching this function are
85
- * concrete.
97
+ * `$principal.X` substitutions in conditions are expected to have already been resolved by
98
+ * {@link resolveConditions}; values reaching this function are concrete.
86
99
  */
87
100
  export function matchConditions(conditions: PropertyConditions, properties: Record<string, unknown>): boolean {
88
101
  for (const [key, condition] of Object.entries(conditions)) {
@@ -90,17 +103,17 @@ export function matchConditions(conditions: PropertyConditions, properties: Reco
90
103
 
91
104
  if (condition === null || condition === undefined || typeof condition !== 'object') {
92
105
  // Direct value match
93
- if (value !== condition) return false;
106
+ if (!matchesEquality(value, condition)) return false;
94
107
  } else {
95
108
  // Operator object
96
109
  const ops = condition as Record<string, unknown>;
97
110
  for (const [op, expected] of Object.entries(ops)) {
98
111
  switch (op) {
99
112
  case '$eq':
100
- if (value !== expected) return false;
113
+ if (!matchesEquality(value, expected)) return false;
101
114
  break;
102
115
  case '$ne':
103
- if (value === expected) return false;
116
+ if (matchesEquality(value, expected)) return false;
104
117
  break;
105
118
  case '$gt':
106
119
  if (!compareOrdered(value, expected, '$gt')) return false;
@@ -115,10 +128,10 @@ export function matchConditions(conditions: PropertyConditions, properties: Reco
115
128
  if (!compareOrdered(value, expected, '$lte')) return false;
116
129
  break;
117
130
  case '$in':
118
- if (!Array.isArray(expected) || !expected.includes(value)) return false;
131
+ if (!Array.isArray(expected) || !matchesAny(value, expected)) return false;
119
132
  break;
120
133
  case '$nin':
121
- if (Array.isArray(expected) && expected.includes(value)) return false;
134
+ if (Array.isArray(expected) && matchesAny(value, expected)) return false;
122
135
  break;
123
136
  case '$exists':
124
137
  if (typeof expected !== 'boolean' || (value !== undefined) !== expected) return false;
@@ -144,3 +157,66 @@ export function matchConditions(conditions: PropertyConditions, properties: Reco
144
157
  }
145
158
  return true;
146
159
  }
160
+
161
+ const PRINCIPAL_REF_PREFIX = '$principal.';
162
+ const NUMERIC_OPS = new Set(['$gt', '$gte', '$lt', '$lte']);
163
+
164
+ /**
165
+ * Resolve `$principal.X` references in resource conditions to concrete values.
166
+ *
167
+ * Missing properties use the same non-matching defaults as token generation:
168
+ * numeric comparisons receive `0`, `$in`/`$nin` receive `[]`, `$exists`
169
+ * receives `false`, and other contexts receive an empty string.
170
+ */
171
+ export function resolveConditions(
172
+ conditions: PropertyConditions,
173
+ principalProps: Record<string, unknown>,
174
+ ): PropertyConditions {
175
+ const resolved: PropertyConditions = {};
176
+
177
+ for (const [key, condition] of Object.entries(conditions)) {
178
+ if (typeof condition === 'string' && condition.startsWith(PRINCIPAL_REF_PREFIX)) {
179
+ resolved[key] = resolvePrincipalRef(condition, principalProps);
180
+ } else if (condition !== null && typeof condition === 'object' && !Array.isArray(condition)) {
181
+ resolved[key] = resolveOperatorObject(condition as Record<string, unknown>, principalProps);
182
+ } else {
183
+ resolved[key] = condition;
184
+ }
185
+ }
186
+
187
+ return resolved;
188
+ }
189
+
190
+ function resolvePrincipalRef(ref: string, principalProps: Record<string, unknown>): unknown {
191
+ const value = resolvePath(principalProps, ref.slice(PRINCIPAL_REF_PREFIX.length));
192
+ return value === undefined ? '' : value;
193
+ }
194
+
195
+ function resolveOperatorObject(
196
+ operators: Record<string, unknown>,
197
+ principalProps: Record<string, unknown>,
198
+ ): Record<string, unknown> {
199
+ const resolved: Record<string, unknown> = {};
200
+
201
+ for (const [operator, value] of Object.entries(operators)) {
202
+ if (typeof value === 'string' && value.startsWith(PRINCIPAL_REF_PREFIX)) {
203
+ const principalValue = resolvePath(principalProps, value.slice(PRINCIPAL_REF_PREFIX.length));
204
+ resolved[operator] =
205
+ principalValue === undefined
206
+ ? operator === '$in' || operator === '$nin'
207
+ ? []
208
+ : defaultForOperator(operator)
209
+ : principalValue;
210
+ } else {
211
+ resolved[operator] = value;
212
+ }
213
+ }
214
+
215
+ return resolved;
216
+ }
217
+
218
+ function defaultForOperator(operator: string): string | number | boolean {
219
+ if (NUMERIC_OPS.has(operator)) return 0;
220
+ if (operator === '$exists') return false;
221
+ return '';
222
+ }
@@ -4,6 +4,7 @@ import { ContentRoleNames } from './content.js';
4
4
  import {
5
5
  AbacRole,
6
6
  getAllRoleNames,
7
+ getDelegablePermissionsForRole,
7
8
  getPermissionsForRoles,
8
9
  getRoleByName,
9
10
  listAbacRolesForScope,
@@ -147,6 +148,20 @@ describe('getPermissionsForRoles', () => {
147
148
  });
148
149
  });
149
150
 
151
+ describe('getDelegablePermissionsForRole', () => {
152
+ it('returns system permission keys unchanged', () => {
153
+ expect(getDelegablePermissionsForRole(SystemRoles.reader)).toContain(Permission.content_read);
154
+ });
155
+
156
+ it('qualifies ABAC verbs with their role domain', () => {
157
+ expect(getDelegablePermissionsForRole(ContentRoleNames.content_manager).sort()).toEqual([
158
+ Permission.content_delete,
159
+ Permission.content_read,
160
+ Permission.content_write,
161
+ ]);
162
+ });
163
+ });
164
+
150
165
  describe('Role instances', () => {
151
166
  it('SystemRole hasPermission for granted Permission', () => {
152
167
  const owner = getRoleByName(SystemRoles.owner);
@@ -74,6 +74,21 @@ export function getPermissionsForRoles(roleNames: Iterable<string>): string[] {
74
74
  return Array.from(permissions);
75
75
  }
76
76
 
77
+ /**
78
+ * Return the platform permission keys a caller must hold to delegate a role.
79
+ *
80
+ * System roles already contain complete `domain:verb` Permission values. ABAC roles store bare
81
+ * verbs for content-security token generation, so map them back to their domain-qualified keys at
82
+ * an authorization boundary.
83
+ */
84
+ export function getDelegablePermissionsForRole(roleName: string): string[] {
85
+ const role = getRoleByName(roleName);
86
+ if (role instanceof AbacRole) {
87
+ return [...role.permissions].map((permission) => `${role.domain}:${permission}`);
88
+ }
89
+ return [...role.permissions];
90
+ }
91
+
77
92
  /**
78
93
  * A list of roles with a unified `hasPermission` check across them.
79
94
  */