@vertesia/studio-utils 1.5.0-dev.20260714.072725Z → 1.5.0-dev.20260717.131047Z

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.
@@ -0,0 +1,99 @@
1
+ import type { AppManifestData, Endpoints } from '@vertesia/common';
2
+ export interface ResolveAppEndpointOptions {
3
+ envName?: string;
4
+ vars?: Endpoints;
5
+ requestedOverride?: string;
6
+ version?: string;
7
+ }
8
+ /**
9
+ * Returns true if the given environment name is allowed as an endpoint override key.
10
+ * Any non-empty name is accepted except the reserved shared-deployment names.
11
+ */
12
+ export declare function isValidEndpointOverrideEnv(envName: string): boolean;
13
+ /**
14
+ * Substitutes `{{key}}` placeholders in a URL with the matching endpoint.
15
+ * Unknown placeholders are left untouched (so failures surface as fetch errors
16
+ * with the unresolved placeholder visible, rather than silently pointing nowhere).
17
+ * Trailing slashes on replacement values are stripped to avoid `//api/...` joins.
18
+ */
19
+ export declare function substituteEndpoints(url: string, endpoints?: Endpoints): string;
20
+ /**
21
+ * Resolves an app manifest endpoint to its package descriptor URL.
22
+ *
23
+ * Resolution has two modes:
24
+ *
25
+ * Gateway apps: if the base `endpoint` is an app-gateway URL, endpoint overrides
26
+ * are ignored. Gateway versions are resolved by rewriting the canonical gateway
27
+ * package URL itself:
28
+ * - `https://gw/tenants/t/apps/my-app`
29
+ * -> `https://gw/tenants/t/apps/my-app/api/package`
30
+ * - `https://gw/tenants/t/package/my-app`
31
+ * -> `https://gw/tenants/t/apps/my-app/api/package`
32
+ * - with `version: "1.2.3"`
33
+ * -> `https://gw/tenants/t/apps/my-app/versions/1.2.3/api/package`
34
+ *
35
+ * Regular apps: endpoint overrides are supported in this order:
36
+ * 1. Version override: if `version` matches `endpoint_overrides[version]`, use
37
+ * that URL. This lets regular, non-gateway apps expose version-pinned package
38
+ * endpoints without going through the app gateway.
39
+ * 2. Explicit override: if `requestedOverride` matches an allowed
40
+ * `endpoint_overrides` key, use that URL. Callers must still verify the user
41
+ * is allowed to request that override.
42
+ * 3. Environment override: if `envName` matches an allowed `endpoint_overrides`
43
+ * key, use that URL for deployment-env-specific endpoints.
44
+ * 4. Base endpoint: otherwise use `manifest.endpoint`.
45
+ * 5. Placeholder substitution: replace `{{gateway}}`, `{{studio}}`, etc. from
46
+ * `vars`.
47
+ *
48
+ * Regular package URLs are returned unchanged:
49
+ * - `https://plugin.example.com/api/package`
50
+ * -> `https://plugin.example.com/api/package`
51
+ *
52
+ * Non-standard package URLs are also returned unchanged, with a warning:
53
+ * - `https://plugin.example.com/exotic/pkg`
54
+ * -> `https://plugin.example.com/exotic/pkg`
55
+ */
56
+ export declare function resolveAppEndpoint(manifest: Pick<AppManifestData, 'endpoint' | 'endpoint_overrides'>, options?: ResolveAppEndpointOptions): string | undefined;
57
+ /**
58
+ * Resolves all URL placeholders in a manifest in place (both `endpoint` and
59
+ * `tool_collections[].url`). Intended for server-side serialization.
60
+ *
61
+ * Mutates the manifest rather than returning a copy so it works cleanly with
62
+ * Mongoose populated subdocs.
63
+ */
64
+ export declare function resolveManifestUrls(manifest: Partial<AppManifestData> | null | undefined, options?: ResolveAppEndpointOptions): void;
65
+ /**
66
+ * Parse a gateway app endpoint into its parts. Accepts BOTH the canonical apps mount
67
+ * (`.../tenants/<t>/apps/<app>`), the canonical package route
68
+ * (`.../tenants/<t>/apps/<app>/api/package`), versioned package routes, AND the
69
+ * legacy package mount (`.../tenants/<t>/package/<app>`). Returns undefined for
70
+ * non-gateway endpoints.
71
+ */
72
+ export declare function parseGatewayAppEndpoint(endpoint: string): {
73
+ origin: string;
74
+ tenant: string;
75
+ appId: string;
76
+ version?: string;
77
+ } | undefined;
78
+ /**
79
+ * Derives the runtime app API base from a package descriptor URL by removing the
80
+ * final path segment.
81
+ *
82
+ * Examples:
83
+ * - `https://plugin.example.com/api/package` -> `https://plugin.example.com/api`
84
+ * - `https://plugin.example.com/exotic/pkg` -> `https://plugin.example.com/exotic`
85
+ * - `https://gw/tenants/t/apps/my-app/api/package` -> `https://gw/tenants/t/apps/my-app/api`
86
+ */
87
+ export declare function resolveAppApiBase(packageEndpoint: string): string;
88
+ /**
89
+ * Resolves an app resource URL from a package descriptor endpoint and a path
90
+ * relative to the app `/api` base.
91
+ *
92
+ * Examples:
93
+ * - `resolveAppResource("https://plugin.example.com/api/package", "tools/main")`
94
+ * -> `https://plugin.example.com/api/tools/main`
95
+ * - `resolveAppResource("https://gw/tenants/t/apps/my-app/api/package", "/interactions/x")`
96
+ * -> `https://gw/tenants/t/apps/my-app/api/interactions/x`
97
+ */
98
+ export declare function resolveAppResource(packageEndpoint: string, pathRelativeToApiBase: string): string;
99
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/apps/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,SAAS,EAAwB,MAAM,kBAAkB,CAAC;AAYzF,MAAM,WAAW,yBAAyB;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAYD;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAGnE;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,SAAS,GAAG,MAAM,CAO9E;AAUD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,kBAAkB,CAC9B,QAAQ,EAAE,IAAI,CAAC,eAAe,EAAE,UAAU,GAAG,oBAAoB,CAAC,EAClE,OAAO,GAAE,yBAA8B,GACxC,MAAM,GAAG,SAAS,CAYpB;AAyCD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAC/B,QAAQ,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,IAAI,GAAG,SAAS,EACrD,OAAO,GAAE,yBAA8B,GACxC,IAAI,CAqBN;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACnC,QAAQ,EAAE,MAAM,GACjB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAUjF;AAoBD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,eAAe,EAAE,MAAM,GAAG,MAAM,CAMjE;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,eAAe,EAAE,MAAM,EAAE,qBAAqB,EAAE,MAAM,GAAG,MAAM,CAKjG"}
@@ -0,0 +1,212 @@
1
+ import { getStudioUtilsLogger } from '../logger.js';
2
+ /**
3
+ * Reserved deployment environment names that may never be used as endpoint
4
+ * override keys. Reserving them prevents a manifest from hijacking auto-resolution
5
+ * on a shared production studio-server (whose environment is one of these).
6
+ */
7
+ const RESERVED_ENDPOINT_OVERRIDE_ENVS = new Set(['production', 'preview', 'staging']);
8
+ const GATEWAY_APP_ENDPOINT_PATTERN = new RegExp('^(.*)/tenants/([^/]+)/(?:' + 'package/([^/]+)|' + 'apps/([^/]+)(?:/versions/([^/]+))?(?:/api/package)?' + ')/?$');
9
+ /**
10
+ * Returns true if the given environment name is allowed as an endpoint override key.
11
+ * Any non-empty name is accepted except the reserved shared-deployment names.
12
+ */
13
+ export function isValidEndpointOverrideEnv(envName) {
14
+ if (!envName)
15
+ return false;
16
+ return !RESERVED_ENDPOINT_OVERRIDE_ENVS.has(envName.toLowerCase());
17
+ }
18
+ /**
19
+ * Substitutes `{{key}}` placeholders in a URL with the matching endpoint.
20
+ * Unknown placeholders are left untouched (so failures surface as fetch errors
21
+ * with the unresolved placeholder visible, rather than silently pointing nowhere).
22
+ * Trailing slashes on replacement values are stripped to avoid `//api/...` joins.
23
+ */
24
+ export function substituteEndpoints(url, endpoints) {
25
+ if (!url || !endpoints)
26
+ return url;
27
+ return url.replace(/\{\{\s*(\w+)\s*\}\}/g, (match, key) => {
28
+ const value = endpoints[key];
29
+ if (typeof value !== 'string' || !value)
30
+ return match;
31
+ return trimTrailingSlashes(value);
32
+ });
33
+ }
34
+ function trimTrailingSlashes(value) {
35
+ let end = value.length;
36
+ while (end > 0 && value[end - 1] === '/') {
37
+ end--;
38
+ }
39
+ return end === value.length ? value : value.slice(0, end);
40
+ }
41
+ /**
42
+ * Resolves an app manifest endpoint to its package descriptor URL.
43
+ *
44
+ * Resolution has two modes:
45
+ *
46
+ * Gateway apps: if the base `endpoint` is an app-gateway URL, endpoint overrides
47
+ * are ignored. Gateway versions are resolved by rewriting the canonical gateway
48
+ * package URL itself:
49
+ * - `https://gw/tenants/t/apps/my-app`
50
+ * -> `https://gw/tenants/t/apps/my-app/api/package`
51
+ * - `https://gw/tenants/t/package/my-app`
52
+ * -> `https://gw/tenants/t/apps/my-app/api/package`
53
+ * - with `version: "1.2.3"`
54
+ * -> `https://gw/tenants/t/apps/my-app/versions/1.2.3/api/package`
55
+ *
56
+ * Regular apps: endpoint overrides are supported in this order:
57
+ * 1. Version override: if `version` matches `endpoint_overrides[version]`, use
58
+ * that URL. This lets regular, non-gateway apps expose version-pinned package
59
+ * endpoints without going through the app gateway.
60
+ * 2. Explicit override: if `requestedOverride` matches an allowed
61
+ * `endpoint_overrides` key, use that URL. Callers must still verify the user
62
+ * is allowed to request that override.
63
+ * 3. Environment override: if `envName` matches an allowed `endpoint_overrides`
64
+ * key, use that URL for deployment-env-specific endpoints.
65
+ * 4. Base endpoint: otherwise use `manifest.endpoint`.
66
+ * 5. Placeholder substitution: replace `{{gateway}}`, `{{studio}}`, etc. from
67
+ * `vars`.
68
+ *
69
+ * Regular package URLs are returned unchanged:
70
+ * - `https://plugin.example.com/api/package`
71
+ * -> `https://plugin.example.com/api/package`
72
+ *
73
+ * Non-standard package URLs are also returned unchanged, with a warning:
74
+ * - `https://plugin.example.com/exotic/pkg`
75
+ * -> `https://plugin.example.com/exotic/pkg`
76
+ */
77
+ export function resolveAppEndpoint(manifest, options = {}) {
78
+ if (!manifest.endpoint) {
79
+ return undefined;
80
+ }
81
+ const baseEndpoint = substituteEndpoints(manifest.endpoint, options.vars);
82
+ const gateway = parseGatewayAppEndpoint(baseEndpoint);
83
+ if (gateway) {
84
+ return resolveGatewayAppEndpoint(gateway, options.version);
85
+ }
86
+ return resolveRegularAppEndpoint(manifest, manifest.endpoint, options);
87
+ }
88
+ function resolveGatewayAppEndpoint(gateway, version) {
89
+ const effectiveVersion = version || gateway.version;
90
+ const versionSeg = effectiveVersion ? `/versions/${encodeURIComponent(effectiveVersion)}` : '';
91
+ return `${gateway.origin}/tenants/${gateway.tenant}/apps/${gateway.appId}${versionSeg}/api/package`;
92
+ }
93
+ function resolveRegularAppEndpoint(manifest, baseEndpoint, options) {
94
+ const { envName, vars, requestedOverride, version } = options;
95
+ let raw;
96
+ if (version && manifest.endpoint_overrides?.[version]) {
97
+ raw = manifest.endpoint_overrides[version];
98
+ }
99
+ else if (requestedOverride &&
100
+ manifest.endpoint_overrides?.[requestedOverride] &&
101
+ isValidEndpointOverrideEnv(requestedOverride)) {
102
+ raw = manifest.endpoint_overrides[requestedOverride];
103
+ }
104
+ else if (envName && manifest.endpoint_overrides?.[envName] && isValidEndpointOverrideEnv(envName)) {
105
+ raw = manifest.endpoint_overrides[envName];
106
+ }
107
+ else {
108
+ raw = baseEndpoint;
109
+ }
110
+ // Substitute the selected regular endpoint, not only the base endpoint:
111
+ // `raw` may be an override. Current server validation usually requires
112
+ // overrides to be absolute URLs, but keeping substitution here preserves
113
+ // compatibility if trusted/system manifests use placeholders in overrides.
114
+ const endpoint = substituteEndpoints(raw, vars);
115
+ warnIfNonStandardPackageEndpoint(endpoint);
116
+ return endpoint;
117
+ }
118
+ /**
119
+ * Resolves all URL placeholders in a manifest in place (both `endpoint` and
120
+ * `tool_collections[].url`). Intended for server-side serialization.
121
+ *
122
+ * Mutates the manifest rather than returning a copy so it works cleanly with
123
+ * Mongoose populated subdocs.
124
+ */
125
+ export function resolveManifestUrls(manifest, options = {}) {
126
+ if (!manifest)
127
+ return;
128
+ const { vars } = options;
129
+ if (manifest.endpoint) {
130
+ const resolved = resolveAppEndpoint(manifest, options);
131
+ if (resolved && resolved !== manifest.endpoint) {
132
+ manifest.endpoint = resolved;
133
+ }
134
+ }
135
+ const toolCollections = manifest.tool_collections;
136
+ if (toolCollections && Array.isArray(toolCollections)) {
137
+ for (let i = 0; i < toolCollections.length; i++) {
138
+ const item = toolCollections[i];
139
+ if (item && typeof item === 'object' && item.url) {
140
+ const sub = substituteEndpoints(item.url, vars);
141
+ if (sub !== item.url)
142
+ item.url = sub;
143
+ }
144
+ }
145
+ }
146
+ }
147
+ /**
148
+ * Parse a gateway app endpoint into its parts. Accepts BOTH the canonical apps mount
149
+ * (`.../tenants/<t>/apps/<app>`), the canonical package route
150
+ * (`.../tenants/<t>/apps/<app>/api/package`), versioned package routes, AND the
151
+ * legacy package mount (`.../tenants/<t>/package/<app>`). Returns undefined for
152
+ * non-gateway endpoints.
153
+ */
154
+ export function parseGatewayAppEndpoint(endpoint) {
155
+ const m = GATEWAY_APP_ENDPOINT_PATTERN.exec(endpoint);
156
+ if (!m)
157
+ return undefined;
158
+ const [, origin, tenant, legacyAppId, appsAppId, version] = m;
159
+ return {
160
+ origin,
161
+ tenant,
162
+ appId: legacyAppId || appsAppId,
163
+ ...(version ? { version } : {}),
164
+ };
165
+ }
166
+ function warnIfNonStandardPackageEndpoint(endpoint) {
167
+ if (!isStandardPackageEndpoint(endpoint)) {
168
+ getStudioUtilsLogger().warn({ endpoint }, 'Non-standard app package endpoint; returning it unchanged during app endpoint resolution');
169
+ }
170
+ }
171
+ function isStandardPackageEndpoint(endpoint) {
172
+ try {
173
+ const url = new URL(endpoint);
174
+ return trimTrailingSlashes(url.pathname).endsWith('/package');
175
+ }
176
+ catch {
177
+ return false;
178
+ }
179
+ }
180
+ /**
181
+ * Derives the runtime app API base from a package descriptor URL by removing the
182
+ * final path segment.
183
+ *
184
+ * Examples:
185
+ * - `https://plugin.example.com/api/package` -> `https://plugin.example.com/api`
186
+ * - `https://plugin.example.com/exotic/pkg` -> `https://plugin.example.com/exotic`
187
+ * - `https://gw/tenants/t/apps/my-app/api/package` -> `https://gw/tenants/t/apps/my-app/api`
188
+ */
189
+ export function resolveAppApiBase(packageEndpoint) {
190
+ const url = new URL(packageEndpoint);
191
+ const path = trimTrailingSlashes(url.pathname);
192
+ const lastSlash = path.lastIndexOf('/');
193
+ url.pathname = lastSlash <= 0 ? '/' : path.slice(0, lastSlash);
194
+ return url.toString();
195
+ }
196
+ /**
197
+ * Resolves an app resource URL from a package descriptor endpoint and a path
198
+ * relative to the app `/api` base.
199
+ *
200
+ * Examples:
201
+ * - `resolveAppResource("https://plugin.example.com/api/package", "tools/main")`
202
+ * -> `https://plugin.example.com/api/tools/main`
203
+ * - `resolveAppResource("https://gw/tenants/t/apps/my-app/api/package", "/interactions/x")`
204
+ * -> `https://gw/tenants/t/apps/my-app/api/interactions/x`
205
+ */
206
+ export function resolveAppResource(packageEndpoint, pathRelativeToApiBase) {
207
+ const base = resolveAppApiBase(packageEndpoint);
208
+ const normalizedBase = base.endsWith('/') ? base.slice(0, -1) : base;
209
+ const normalizedPath = pathRelativeToApiBase.startsWith('/') ? pathRelativeToApiBase : `/${pathRelativeToApiBase}`;
210
+ return `${normalizedBase}${normalizedPath}`;
211
+ }
212
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/apps/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(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 u(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)=>u(n,`${t}_${r}`))}return[]}case`object`:{let t={};if(e.properties)for(let[n,r]of Object.entries(e.properties))t[n]=u(r,n);return t}case`null`:return null;default:return`%${t}%`}}function d(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 f(e,t,n){try{return{success:!0,content:d(e,i.jst,t,n)}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}}function p(e,t,n){try{return{success:!0,content:d(e,i.handlebars,t,n)}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}}function m(e,t){let n=v(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 ee(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:d(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 h(e,t){let r=[];for(let i of e)i.type===n.chat?r.push(m(i,t)):i.type===n.template&&r.push(ee(i,t));return r}function g(e,t){try{return h(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 _(e,t){let r=[];for(let i of e)if(i.template){let e=i.template.inputSchema||{},n=d(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[v(i.configuration)];if(!y(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 v(e){return typeof e==`object`&&e&&`chatKey`in e&&typeof e.chatKey==`string`?e.chatKey:`chat`}function y(e){return Array.isArray(e)&&e.every(b)}function b(e){return typeof e==`object`&&!!e&&`role`in e&&`content`in e}const x=[`_`,`Array`,`Set`,`_model`];function S(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 te(e,t){let n=[],r=l(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=u(a),s=p(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 ne(e,t){let n=[],r=new Set(t?.properties?Object.keys(t.properties):[]),i;try{let t=o(e,{globals:x,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=u(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 re(e){let t;t=e.contentType===i.handlebars?te(e.content,e.inputSchema):e.contentType===i.jst?ne(e.content,e.inputSchema):[];let{error_count:n,warning_count:r}=S(t);return{issues:t,error_count:n,warning_count:r}}var C=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)}},w=class extends C{constructor(e,t){super(e,t,`system`)}},T=class extends C{applicableScopes;constructor(e,t,n,r){super(e,t,n),this.applicableScopes=r}};const E=`content`,D=[`document`,`collection`];var O;(function(e){e.content_reader=`content:reader`,e.content_writer=`content:writer`,e.content_manager=`content:manager`})(O||={});var k=class extends T{constructor(){super(O.content_reader,[`read`],E,D)}},A=class extends T{constructor(){super(O.content_writer,[`read`,`write`],E,D)}},j=class extends T{constructor(){super(O.content_manager,[`read`,`write`,`delete`],E,D)}};const M={[O.content_reader]:new k,[O.content_writer]:new A,[O.content_manager]:new j},N={domain:E,roles:new Map(Object.entries(M))};var P=class extends w{constructor(e,n){super(e,[t.account_member,...n])}},F=class extends P{constructor(){super(r.owner,Object.values(t))}},I=class extends P{constructor(){super(r.admin,Object.values(t))}},L=class extends P{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)}},R=class extends P{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)}},z=class extends P{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])}},ie=class extends P{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])}},B=class extends P{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])}},V=class extends P{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])}},H=class extends P{constructor(){super(r.executor,[t.int_execute,t.run_read,t.workflow_run])}},U=class extends P{constructor(){super(r.reader,[t.int_read,t.run_read,t.content_read])}};const W=[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 G=class extends P{constructor(e){super(e,W)}},K=class extends P{constructor(){super(r.billing,[t.manage_billing])}},q=class extends P{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])}},J=class extends R{constructor(){super(),this.name=r.content_superadmin,this.permissions.add(t.content_superadmin)}};const Y={[r.owner]:new F,[r.admin]:new I,[r.manager]:new L,[r.developer]:new R,[r.application]:new z,[r.automation]:new ie,[r.content_processor]:new B,[r.consumer]:new V,[r.executor]:new H,[r.reader]:new U,[r.auditor]:new G(r.auditor),[r.support]:new G(r.support),[r.billing]:new K,[r.app_member]:new q,[r.member]:new P(r.member,[]),[r.content_superadmin]:new J},X=[{domain:`system`,roles:new Map(Object.entries(Y))},N];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 ae(e){let t=X.find(t=>t.domain===e);return t?Array.from(t.roles.values()):[]}function oe(e){return Q().filter(t=>t instanceof T&&t.applicableScopes.includes(e))}function se(){return Q().filter(e=>e instanceof w)}function ce(){return Q().map(e=>e.name)}function le(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 $=class e{roles;constructor(e){this.roles=e}static fromRoleNames(t){return new e(t.map(e=>Z(e)))}static fromRoleName(t){return new e([Z(t)])}hasPermission(e){return this.roles.some(t=>t.hasPermission(e))}};export{T as AbacRole,O as ContentRoleNames,C as Role,$ as RoleList,w as SystemRole,p as executeHandlebars,f as executeJST,l as extractHandlebarsVariables,u as generateMockData,ce as getAllRoleNames,le as getPermissionsForRoles,Z as getRoleByName,oe as listAbacRolesForScope,Q as listRoles,ae as listRolesByDomain,se as listSystemRoles,_ as renderPrompt,h as renderSegments,g as renderSegmentsOrErrors,d as renderTemplate,re 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(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 u(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)=>u(n,`${t}_${r}`))}return[]}case`object`:{let t={};if(e.properties)for(let[n,r]of Object.entries(e.properties))t[n]=u(r,n);return t}case`null`:return null;default:return`%${t}%`}}function d(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 f(e,t,n){try{return{success:!0,content:d(e,i.jst,t,n)}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}}function p(e,t,n){try{return{success:!0,content:d(e,i.handlebars,t,n)}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}}function m(e,t){let n=v(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 ee(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:d(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 h(e,t){let r=[];for(let i of e)i.type===n.chat?r.push(m(i,t)):i.type===n.template&&r.push(ee(i,t));return r}function g(e,t){try{return h(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 _(e,t){let r=[];for(let i of e)if(i.template){let e=i.template.inputSchema||{},n=d(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[v(i.configuration)];if(!y(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 v(e){return typeof e==`object`&&e&&`chatKey`in e&&typeof e.chatKey==`string`?e.chatKey:`chat`}function y(e){return Array.isArray(e)&&e.every(b)}function b(e){return typeof e==`object`&&!!e&&`role`in e&&`content`in e}const x=[`_`,`Array`,`Set`,`_model`];function S(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 te(e,t){let n=[],r=l(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=u(a),s=p(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 ne(e,t){let n=[],r=new Set(t?.properties?Object.keys(t.properties):[]),i;try{let t=o(e,{globals:x,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=u(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 re(e){let t;t=e.contentType===i.handlebars?te(e.content,e.inputSchema):e.contentType===i.jst?ne(e.content,e.inputSchema):[];let{error_count:n,warning_count:r}=S(t);return{issues:t,error_count:n,warning_count:r}}var C=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)}},w=class extends C{constructor(e,t){super(e,t,`system`)}},T=class extends C{applicableScopes;constructor(e,t,n,r){super(e,t,n),this.applicableScopes=r}};const E=`content`,D=[`document`,`collection`];var O;(function(e){e.content_reader=`content:reader`,e.content_writer=`content:writer`,e.content_manager=`content:manager`})(O||={});var k=class extends T{constructor(){super(O.content_reader,[`read`],E,D)}},A=class extends T{constructor(){super(O.content_writer,[`read`,`write`],E,D)}},j=class extends T{constructor(){super(O.content_manager,[`read`,`write`,`delete`],E,D)}};const M={[O.content_reader]:new k,[O.content_writer]:new A,[O.content_manager]:new j},N={domain:E,roles:new Map(Object.entries(M))};var P=class extends w{constructor(e,n){super(e,[t.account_member,...n])}},F=class extends P{constructor(){super(r.owner,Object.values(t))}},I=class extends P{constructor(){super(r.admin,Object.values(t))}},L=class extends P{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)}},R=class extends P{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)}},z=class extends P{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])}},ie=class extends P{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])}},B=class extends P{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])}},V=class extends P{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])}},H=class extends P{constructor(){super(r.executor,[t.int_execute,t.run_read,t.workflow_run])}},U=class extends P{constructor(){super(r.reader,[t.int_read,t.run_read,t.content_read])}};const W=[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 G=class extends P{constructor(e){super(e,W)}},K=class extends P{constructor(){super(r.billing,[t.manage_billing])}},q=class extends P{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])}},J=class extends R{constructor(){super(),this.name=r.content_superadmin,this.permissions.add(t.content_superadmin)}};const Y={[r.owner]:new F,[r.admin]:new I,[r.manager]:new L,[r.developer]:new R,[r.application]:new z,[r.automation]:new ie,[r.content_processor]:new B,[r.consumer]:new V,[r.executor]:new H,[r.reader]:new U,[r.auditor]:new G(r.auditor),[r.support]:new G(r.support),[r.billing]:new K,[r.app_member]:new q,[r.member]:new P(r.member,[]),[r.content_superadmin]:new J},X=[{domain:`system`,roles:new Map(Object.entries(Y))},N];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 ae(e){let t=X.find(t=>t.domain===e);return t?Array.from(t.roles.values()):[]}function oe(e){return Q().filter(t=>t instanceof T&&t.applicableScopes.includes(e))}function se(){return Q().filter(e=>e instanceof w)}function ce(){return Q().map(e=>e.name)}function le(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 $=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{T as AbacRole,O as ContentRoleNames,C as Role,$ as RoleList,w as SystemRole,p as executeHandlebars,f as executeJST,l as extractHandlebarsVariables,u as generateMockData,ce as getAllRoleNames,le as getPermissionsForRoles,Z as getRoleByName,oe as listAbacRolesForScope,Q as listRoles,ae as listRolesByDomain,se as listSystemRoles,_ as renderPrompt,h as renderSegments,g as renderSegmentsOrErrors,d as renderTemplate,re as validatePrompt};
2
2
  //# sourceMappingURL=vertesia-studio-utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"vertesia-studio-utils.js","names":[],"sources":["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 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":["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 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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vertesia/studio-utils",
3
- "version": "1.5.0-dev.20260714.072725Z",
3
+ "version": "1.5.0-dev.20260717.131047Z",
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",
@@ -14,6 +14,10 @@
14
14
  "types": "./lib/conditions/index.d.ts",
15
15
  "default": "./lib/conditions/index.js"
16
16
  },
17
+ "./apps": {
18
+ "types": "./lib/apps/index.d.ts",
19
+ "default": "./lib/apps/index.js"
20
+ },
17
21
  "./logger": {
18
22
  "types": "./lib/logger.d.ts",
19
23
  "default": "./lib/logger.js"
@@ -43,17 +47,18 @@
43
47
  ],
44
48
  "devDependencies": {
45
49
  "@types/node": "^24.13.2",
46
- "rolldown": "1.1.3",
50
+ "rolldown": "1.1.4",
47
51
  "typescript": "^6.0.3",
48
52
  "vitest": "^4.1.9",
49
53
  "@vertesia/tsconfig": "0.1.0"
50
54
  },
51
55
  "dependencies": {
52
56
  "handlebars": "^4.7.9",
53
- "@vertesia/common": "1.5.0-dev.20260714.072725Z",
54
57
  "@llumiverse/common": "1.5.0-dev.20260713.114357Z",
55
- "@vertesia/jst": "1.5.0-dev.20260714.072725Z"
58
+ "@vertesia/common": "1.5.0-dev.20260717.131047Z",
59
+ "@vertesia/jst": "1.5.0-dev.20260717.131047Z"
56
60
  },
61
+ "gitHead": "8aaad228827b2927db8e6aa75b27bfac90fec36f",
57
62
  "scripts": {
58
63
  "lint": "biome lint src",
59
64
  "test": "vitest run",
@@ -0,0 +1,166 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { installStudioUtilsLogger, type StudioUtilsLogger } from '../logger.js';
3
+ import {
4
+ parseGatewayAppEndpoint,
5
+ type ResolveAppEndpointOptions,
6
+ resolveAppApiBase,
7
+ resolveAppEndpoint,
8
+ resolveAppResource,
9
+ } from './index.js';
10
+
11
+ const APPS_MOUNT = 'https://gw.example.com/tenants/1589ba_149d0b/apps/my-app';
12
+ const LEGACY_PACKAGE_MOUNT = 'https://gw.example.com/tenants/1589ba_149d0b/package/my-app';
13
+ const PACKAGE_MOUNT = 'https://gw.example.com/tenants/1589ba_149d0b/apps/my-app/api/package';
14
+ const VERSION_PACKAGE_MOUNT =
15
+ 'https://gw.example.com/tenants/1589ba_149d0b/apps/my-app/versions/20260101T000000Z/api/package';
16
+
17
+ function resolve(
18
+ endpoint: string,
19
+ options: ResolveAppEndpointOptions = {},
20
+ endpoint_overrides?: Record<string, string>,
21
+ ): string | undefined {
22
+ return resolveAppEndpoint({ endpoint, endpoint_overrides }, options);
23
+ }
24
+
25
+ describe('app endpoint resolution', () => {
26
+ it('parses gateway app endpoint shapes', () => {
27
+ expect(parseGatewayAppEndpoint(APPS_MOUNT)).toEqual({
28
+ origin: 'https://gw.example.com',
29
+ tenant: '1589ba_149d0b',
30
+ appId: 'my-app',
31
+ });
32
+ expect(parseGatewayAppEndpoint(LEGACY_PACKAGE_MOUNT)).toEqual({
33
+ origin: 'https://gw.example.com',
34
+ tenant: '1589ba_149d0b',
35
+ appId: 'my-app',
36
+ });
37
+ expect(parseGatewayAppEndpoint(VERSION_PACKAGE_MOUNT)).toEqual({
38
+ origin: 'https://gw.example.com',
39
+ tenant: '1589ba_149d0b',
40
+ appId: 'my-app',
41
+ version: '20260101T000000Z',
42
+ });
43
+ expect(parseGatewayAppEndpoint('https://third-party.example.com/api/package')).toBeUndefined();
44
+ });
45
+
46
+ it('normalizes gateway app endpoints to canonical package URLs', () => {
47
+ expect(resolve(APPS_MOUNT)).toBe(PACKAGE_MOUNT);
48
+ expect(resolve(LEGACY_PACKAGE_MOUNT)).toBe(PACKAGE_MOUNT);
49
+ expect(resolve(PACKAGE_MOUNT)).toBe(PACKAGE_MOUNT);
50
+ });
51
+
52
+ it('normalizes gateway app endpoints to version-pinned package URLs', () => {
53
+ expect(resolve(APPS_MOUNT, { version: '20260101T000000Z' })).toBe(VERSION_PACKAGE_MOUNT);
54
+ expect(resolve(LEGACY_PACKAGE_MOUNT, { version: '20260101T000000Z' })).toBe(VERSION_PACKAGE_MOUNT);
55
+ expect(resolve(PACKAGE_MOUNT, { version: '20260101T000000Z' })).toBe(VERSION_PACKAGE_MOUNT);
56
+ });
57
+
58
+ it('ignores endpoint overrides for gateway apps', () => {
59
+ const endpoint_overrides = {
60
+ '20260101T000000Z': 'https://plugin.example.com/releases/20260101T000000Z/package',
61
+ dev: 'https://plugin.example.com/dev/package',
62
+ };
63
+
64
+ expect(resolve(APPS_MOUNT, { version: '20260101T000000Z', envName: 'dev' }, endpoint_overrides)).toBe(
65
+ VERSION_PACKAGE_MOUNT,
66
+ );
67
+ expect(resolve(APPS_MOUNT, { envName: 'dev' }, endpoint_overrides)).toBe(PACKAGE_MOUNT);
68
+ });
69
+
70
+ it('uses endpoint overrides in version, requested override, environment, endpoint order', () => {
71
+ const endpoint_overrides = {
72
+ '1.2.3': 'https://plugin.example.com/releases/1.2.3/package',
73
+ dev: 'https://plugin.example.com/dev/package',
74
+ desktop: 'https://plugin.example.com/desktop/package',
75
+ };
76
+
77
+ expect(
78
+ resolve('https://plugin.example.com/api/package', { version: '1.2.3', envName: 'dev' }, endpoint_overrides),
79
+ ).toBe('https://plugin.example.com/releases/1.2.3/package');
80
+ expect(
81
+ resolve(
82
+ 'https://plugin.example.com/api/package',
83
+ { requestedOverride: 'desktop', envName: 'dev' },
84
+ endpoint_overrides,
85
+ ),
86
+ ).toBe('https://plugin.example.com/desktop/package');
87
+ expect(resolve('https://plugin.example.com/api/package', { envName: 'dev' }, endpoint_overrides)).toBe(
88
+ 'https://plugin.example.com/dev/package',
89
+ );
90
+ expect(resolve('https://plugin.example.com/api/package', {}, endpoint_overrides)).toBe(
91
+ 'https://plugin.example.com/api/package',
92
+ );
93
+ });
94
+
95
+ it('does not use endpoint overrides without a base endpoint', () => {
96
+ expect(
97
+ resolveAppEndpoint(
98
+ {
99
+ endpoint: undefined,
100
+ endpoint_overrides: {
101
+ dev: 'https://plugin.example.com/dev/package',
102
+ },
103
+ },
104
+ { envName: 'dev' },
105
+ ),
106
+ ).toBeUndefined();
107
+ });
108
+
109
+ it('substitutes endpoint variables before gateway normalization', () => {
110
+ expect(
111
+ resolve('{{gateway}}/tenants/1589ba_149d0b/package/my-app', {
112
+ vars: { gateway: 'https://gw.example.com/' },
113
+ }),
114
+ ).toBe(PACKAGE_MOUNT);
115
+ });
116
+
117
+ it('leaves regular package endpoints unchanged', () => {
118
+ expect(resolve('https://third-party.example.com/api/package')).toBe(
119
+ 'https://third-party.example.com/api/package',
120
+ );
121
+ });
122
+
123
+ it('leaves non-standard package endpoints unchanged and warns', () => {
124
+ const logger: StudioUtilsLogger = {
125
+ warn: vi.fn(),
126
+ };
127
+ const previousLogger = installStudioUtilsLogger(logger);
128
+
129
+ expect(resolve('https://third-party.example.com/exotic/pkg')).toBe(
130
+ 'https://third-party.example.com/exotic/pkg',
131
+ );
132
+ expect(logger.warn).toHaveBeenCalledWith(
133
+ { endpoint: 'https://third-party.example.com/exotic/pkg' },
134
+ 'Non-standard app package endpoint; returning it unchanged during app endpoint resolution',
135
+ );
136
+
137
+ installStudioUtilsLogger(previousLogger);
138
+ });
139
+ });
140
+
141
+ describe('app API base resolution', () => {
142
+ it('derives the API base by removing the final package URL segment', () => {
143
+ expect(resolveAppApiBase('https://third-party.example.com/api/package')).toBe(
144
+ 'https://third-party.example.com/api',
145
+ );
146
+ expect(resolveAppApiBase('https://third-party.example.com/exotic/pkg')).toBe(
147
+ 'https://third-party.example.com/exotic',
148
+ );
149
+ expect(resolveAppApiBase(PACKAGE_MOUNT)).toBe('https://gw.example.com/tenants/1589ba_149d0b/apps/my-app/api');
150
+ expect(resolveAppApiBase(VERSION_PACKAGE_MOUNT)).toBe(
151
+ 'https://gw.example.com/tenants/1589ba_149d0b/apps/my-app/versions/20260101T000000Z/api',
152
+ );
153
+ });
154
+
155
+ it('resolves API-relative resource paths from package endpoints', () => {
156
+ expect(resolveAppResource('https://third-party.example.com/api/package', 'tools/main')).toBe(
157
+ 'https://third-party.example.com/api/tools/main',
158
+ );
159
+ expect(resolveAppResource(PACKAGE_MOUNT, '/interactions/main/hello')).toBe(
160
+ 'https://gw.example.com/tenants/1589ba_149d0b/apps/my-app/api/interactions/main/hello',
161
+ );
162
+ expect(resolveAppResource(VERSION_PACKAGE_MOUNT, 'templates/invoice')).toBe(
163
+ 'https://gw.example.com/tenants/1589ba_149d0b/apps/my-app/versions/20260101T000000Z/api/templates/invoice',
164
+ );
165
+ });
166
+ });
@@ -0,0 +1,258 @@
1
+ import type { AppManifestData, Endpoints, ToolCollectionObject } from '@vertesia/common';
2
+ import { getStudioUtilsLogger } from '../logger.js';
3
+
4
+ declare const URL: {
5
+ new (
6
+ url: string,
7
+ ): {
8
+ pathname: string;
9
+ toString(): string;
10
+ };
11
+ };
12
+
13
+ export interface ResolveAppEndpointOptions {
14
+ envName?: string;
15
+ vars?: Endpoints;
16
+ requestedOverride?: string;
17
+ version?: string;
18
+ }
19
+
20
+ /**
21
+ * Reserved deployment environment names that may never be used as endpoint
22
+ * override keys. Reserving them prevents a manifest from hijacking auto-resolution
23
+ * on a shared production studio-server (whose environment is one of these).
24
+ */
25
+ const RESERVED_ENDPOINT_OVERRIDE_ENVS = new Set(['production', 'preview', 'staging']);
26
+ const GATEWAY_APP_ENDPOINT_PATTERN = new RegExp(
27
+ '^(.*)/tenants/([^/]+)/(?:' + 'package/([^/]+)|' + 'apps/([^/]+)(?:/versions/([^/]+))?(?:/api/package)?' + ')/?$',
28
+ );
29
+
30
+ /**
31
+ * Returns true if the given environment name is allowed as an endpoint override key.
32
+ * Any non-empty name is accepted except the reserved shared-deployment names.
33
+ */
34
+ export function isValidEndpointOverrideEnv(envName: string): boolean {
35
+ if (!envName) return false;
36
+ return !RESERVED_ENDPOINT_OVERRIDE_ENVS.has(envName.toLowerCase());
37
+ }
38
+
39
+ /**
40
+ * Substitutes `{{key}}` placeholders in a URL with the matching endpoint.
41
+ * Unknown placeholders are left untouched (so failures surface as fetch errors
42
+ * with the unresolved placeholder visible, rather than silently pointing nowhere).
43
+ * Trailing slashes on replacement values are stripped to avoid `//api/...` joins.
44
+ */
45
+ export function substituteEndpoints(url: string, endpoints?: Endpoints): string {
46
+ if (!url || !endpoints) return url;
47
+ return url.replace(/\{\{\s*(\w+)\s*\}\}/g, (match, key: string) => {
48
+ const value = (endpoints as Record<string, string | undefined>)[key];
49
+ if (typeof value !== 'string' || !value) return match;
50
+ return trimTrailingSlashes(value);
51
+ });
52
+ }
53
+
54
+ function trimTrailingSlashes(value: string): string {
55
+ let end = value.length;
56
+ while (end > 0 && value[end - 1] === '/') {
57
+ end--;
58
+ }
59
+ return end === value.length ? value : value.slice(0, end);
60
+ }
61
+
62
+ /**
63
+ * Resolves an app manifest endpoint to its package descriptor URL.
64
+ *
65
+ * Resolution has two modes:
66
+ *
67
+ * Gateway apps: if the base `endpoint` is an app-gateway URL, endpoint overrides
68
+ * are ignored. Gateway versions are resolved by rewriting the canonical gateway
69
+ * package URL itself:
70
+ * - `https://gw/tenants/t/apps/my-app`
71
+ * -> `https://gw/tenants/t/apps/my-app/api/package`
72
+ * - `https://gw/tenants/t/package/my-app`
73
+ * -> `https://gw/tenants/t/apps/my-app/api/package`
74
+ * - with `version: "1.2.3"`
75
+ * -> `https://gw/tenants/t/apps/my-app/versions/1.2.3/api/package`
76
+ *
77
+ * Regular apps: endpoint overrides are supported in this order:
78
+ * 1. Version override: if `version` matches `endpoint_overrides[version]`, use
79
+ * that URL. This lets regular, non-gateway apps expose version-pinned package
80
+ * endpoints without going through the app gateway.
81
+ * 2. Explicit override: if `requestedOverride` matches an allowed
82
+ * `endpoint_overrides` key, use that URL. Callers must still verify the user
83
+ * is allowed to request that override.
84
+ * 3. Environment override: if `envName` matches an allowed `endpoint_overrides`
85
+ * key, use that URL for deployment-env-specific endpoints.
86
+ * 4. Base endpoint: otherwise use `manifest.endpoint`.
87
+ * 5. Placeholder substitution: replace `{{gateway}}`, `{{studio}}`, etc. from
88
+ * `vars`.
89
+ *
90
+ * Regular package URLs are returned unchanged:
91
+ * - `https://plugin.example.com/api/package`
92
+ * -> `https://plugin.example.com/api/package`
93
+ *
94
+ * Non-standard package URLs are also returned unchanged, with a warning:
95
+ * - `https://plugin.example.com/exotic/pkg`
96
+ * -> `https://plugin.example.com/exotic/pkg`
97
+ */
98
+ export function resolveAppEndpoint(
99
+ manifest: Pick<AppManifestData, 'endpoint' | 'endpoint_overrides'>,
100
+ options: ResolveAppEndpointOptions = {},
101
+ ): string | undefined {
102
+ if (!manifest.endpoint) {
103
+ return undefined;
104
+ }
105
+
106
+ const baseEndpoint = substituteEndpoints(manifest.endpoint, options.vars);
107
+ const gateway = parseGatewayAppEndpoint(baseEndpoint);
108
+ if (gateway) {
109
+ return resolveGatewayAppEndpoint(gateway, options.version);
110
+ }
111
+
112
+ return resolveRegularAppEndpoint(manifest, manifest.endpoint, options);
113
+ }
114
+
115
+ function resolveGatewayAppEndpoint(
116
+ gateway: { origin: string; tenant: string; appId: string; version?: string },
117
+ version?: string,
118
+ ): string {
119
+ const effectiveVersion = version || gateway.version;
120
+ const versionSeg = effectiveVersion ? `/versions/${encodeURIComponent(effectiveVersion)}` : '';
121
+ return `${gateway.origin}/tenants/${gateway.tenant}/apps/${gateway.appId}${versionSeg}/api/package`;
122
+ }
123
+
124
+ function resolveRegularAppEndpoint(
125
+ manifest: Pick<AppManifestData, 'endpoint_overrides'>,
126
+ baseEndpoint: string,
127
+ options: ResolveAppEndpointOptions,
128
+ ): string | undefined {
129
+ const { envName, vars, requestedOverride, version } = options;
130
+ let raw: string;
131
+ if (version && manifest.endpoint_overrides?.[version]) {
132
+ raw = manifest.endpoint_overrides[version];
133
+ } else if (
134
+ requestedOverride &&
135
+ manifest.endpoint_overrides?.[requestedOverride] &&
136
+ isValidEndpointOverrideEnv(requestedOverride)
137
+ ) {
138
+ raw = manifest.endpoint_overrides[requestedOverride];
139
+ } else if (envName && manifest.endpoint_overrides?.[envName] && isValidEndpointOverrideEnv(envName)) {
140
+ raw = manifest.endpoint_overrides[envName];
141
+ } else {
142
+ raw = baseEndpoint;
143
+ }
144
+
145
+ // Substitute the selected regular endpoint, not only the base endpoint:
146
+ // `raw` may be an override. Current server validation usually requires
147
+ // overrides to be absolute URLs, but keeping substitution here preserves
148
+ // compatibility if trusted/system manifests use placeholders in overrides.
149
+ const endpoint = substituteEndpoints(raw, vars);
150
+ warnIfNonStandardPackageEndpoint(endpoint);
151
+ return endpoint;
152
+ }
153
+
154
+ /**
155
+ * Resolves all URL placeholders in a manifest in place (both `endpoint` and
156
+ * `tool_collections[].url`). Intended for server-side serialization.
157
+ *
158
+ * Mutates the manifest rather than returning a copy so it works cleanly with
159
+ * Mongoose populated subdocs.
160
+ */
161
+ export function resolveManifestUrls(
162
+ manifest: Partial<AppManifestData> | null | undefined,
163
+ options: ResolveAppEndpointOptions = {},
164
+ ): void {
165
+ if (!manifest) return;
166
+ const { vars } = options;
167
+
168
+ if (manifest.endpoint) {
169
+ const resolved = resolveAppEndpoint(manifest, options);
170
+ if (resolved && resolved !== manifest.endpoint) {
171
+ manifest.endpoint = resolved;
172
+ }
173
+ }
174
+
175
+ const toolCollections = manifest.tool_collections as ToolCollectionObject[] | undefined;
176
+ if (toolCollections && Array.isArray(toolCollections)) {
177
+ for (let i = 0; i < toolCollections.length; i++) {
178
+ const item = toolCollections[i];
179
+ if (item && typeof item === 'object' && item.url) {
180
+ const sub = substituteEndpoints(item.url, vars);
181
+ if (sub !== item.url) item.url = sub;
182
+ }
183
+ }
184
+ }
185
+ }
186
+
187
+ /**
188
+ * Parse a gateway app endpoint into its parts. Accepts BOTH the canonical apps mount
189
+ * (`.../tenants/<t>/apps/<app>`), the canonical package route
190
+ * (`.../tenants/<t>/apps/<app>/api/package`), versioned package routes, AND the
191
+ * legacy package mount (`.../tenants/<t>/package/<app>`). Returns undefined for
192
+ * non-gateway endpoints.
193
+ */
194
+ export function parseGatewayAppEndpoint(
195
+ endpoint: string,
196
+ ): { origin: string; tenant: string; appId: string; version?: string } | undefined {
197
+ const m = GATEWAY_APP_ENDPOINT_PATTERN.exec(endpoint);
198
+ if (!m) return undefined;
199
+ const [, origin, tenant, legacyAppId, appsAppId, version] = m;
200
+ return {
201
+ origin,
202
+ tenant,
203
+ appId: legacyAppId || appsAppId,
204
+ ...(version ? { version } : {}),
205
+ };
206
+ }
207
+
208
+ function warnIfNonStandardPackageEndpoint(endpoint: string): void {
209
+ if (!isStandardPackageEndpoint(endpoint)) {
210
+ getStudioUtilsLogger().warn(
211
+ { endpoint },
212
+ 'Non-standard app package endpoint; returning it unchanged during app endpoint resolution',
213
+ );
214
+ }
215
+ }
216
+
217
+ function isStandardPackageEndpoint(endpoint: string): boolean {
218
+ try {
219
+ const url = new URL(endpoint);
220
+ return trimTrailingSlashes(url.pathname).endsWith('/package');
221
+ } catch {
222
+ return false;
223
+ }
224
+ }
225
+
226
+ /**
227
+ * Derives the runtime app API base from a package descriptor URL by removing the
228
+ * final path segment.
229
+ *
230
+ * Examples:
231
+ * - `https://plugin.example.com/api/package` -> `https://plugin.example.com/api`
232
+ * - `https://plugin.example.com/exotic/pkg` -> `https://plugin.example.com/exotic`
233
+ * - `https://gw/tenants/t/apps/my-app/api/package` -> `https://gw/tenants/t/apps/my-app/api`
234
+ */
235
+ export function resolveAppApiBase(packageEndpoint: string): string {
236
+ const url = new URL(packageEndpoint);
237
+ const path = trimTrailingSlashes(url.pathname);
238
+ const lastSlash = path.lastIndexOf('/');
239
+ url.pathname = lastSlash <= 0 ? '/' : path.slice(0, lastSlash);
240
+ return url.toString();
241
+ }
242
+
243
+ /**
244
+ * Resolves an app resource URL from a package descriptor endpoint and a path
245
+ * relative to the app `/api` base.
246
+ *
247
+ * Examples:
248
+ * - `resolveAppResource("https://plugin.example.com/api/package", "tools/main")`
249
+ * -> `https://plugin.example.com/api/tools/main`
250
+ * - `resolveAppResource("https://gw/tenants/t/apps/my-app/api/package", "/interactions/x")`
251
+ * -> `https://gw/tenants/t/apps/my-app/api/interactions/x`
252
+ */
253
+ export function resolveAppResource(packageEndpoint: string, pathRelativeToApiBase: string): string {
254
+ const base = resolveAppApiBase(packageEndpoint);
255
+ const normalizedBase = base.endsWith('/') ? base.slice(0, -1) : base;
256
+ const normalizedPath = pathRelativeToApiBase.startsWith('/') ? pathRelativeToApiBase : `/${pathRelativeToApiBase}`;
257
+ return `${normalizedBase}${normalizedPath}`;
258
+ }
@@ -115,6 +115,18 @@ describe('validatePrompt — handlebars', () => {
115
115
  expect(findIssue(r.issues, 'handlebars_render_error')).toBeDefined();
116
116
  expect(r.error_count).toBeGreaterThanOrEqual(1);
117
117
  });
118
+
119
+ it('flags an unregistered helper call', () => {
120
+ const r = validatePrompt({
121
+ content: 'Tactics: {{join mitre_tactics ", "}}',
122
+ contentType: TemplateType.handlebars,
123
+ inputSchema: schema({ mitre_tactics: { type: 'array', items: { type: 'string' } } }),
124
+ });
125
+
126
+ const issue = findIssue(r.issues, 'handlebars_render_error');
127
+ expect(issue).toBeDefined();
128
+ expect(issue?.message).toContain('Missing helper: "join"');
129
+ });
118
130
  });
119
131
 
120
132
  describe('happy path', () => {