@trayai/helix-sdk 0.96.0 → 0.97.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +9 -0
- package/dist/index.d.ts +9 -2
- package/dist/index.js +4 -3
- package/dist/internal/index.d.ts +2 -2
- package/dist/{sdk-config-_xIe9XvP.d.ts → sdk-config-vY_-Reh3.d.ts} +31 -5
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,18 @@ general availability, minor versions may include breaking changes.
|
|
|
9
9
|
The SDK and [`@trayai/helix-cli`](https://www.npmjs.com/package/@trayai/helix-cli) are
|
|
10
10
|
released together and share a version — install matching versions.
|
|
11
11
|
|
|
12
|
+
## 0.97.0
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- **Config-file override for multi-target projects.** `loadConfig`,
|
|
17
|
+
`createConfigService`, and `resolveConfigFile` accept an optional `{ configFile }`
|
|
18
|
+
naming a config file other than `helix.config.ts`, falling back to the
|
|
19
|
+
`HELIX_CONFIG` environment variable when the option is omitted (explicit option →
|
|
20
|
+
env var → default). New exports: `resolveConfigOverride`, `HELIX_CONFIG_ENV`, and
|
|
21
|
+
the `ConfigFileOptions` type. Pairs with the CLI's new `--config <path>` flag for
|
|
22
|
+
one-config-file-per-target workflows.
|
|
23
|
+
|
|
12
24
|
## 0.96.0
|
|
13
25
|
|
|
14
26
|
### Added
|
package/README.md
CHANGED
|
@@ -244,6 +244,15 @@ await service.stop();
|
|
|
244
244
|
|
|
245
245
|
Use the CLI (`helix project set`, `helix workspace set`, `helix auth add`) or the Helix MCP tools to write config fields — you rarely edit these by hand.
|
|
246
246
|
|
|
247
|
+
### Multiple config files
|
|
248
|
+
|
|
249
|
+
A config file describes exactly one target. Projects with several targets keep one config file per target (`helix.config.ts` for the one you develop against, `helix.<target>.config.ts` for the rest) and select one with the CLI's `--config` flag. `loadConfig`, `createConfigService`, and `resolveConfigFile` accept the same override as an option, and fall back to the `HELIX_CONFIG` environment variable when the option is omitted:
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
// explicit option → HELIX_CONFIG env var → ./helix.config.ts
|
|
253
|
+
const config = await loadConfig(process.cwd(), { configFile: 'helix.production.config.ts' });
|
|
254
|
+
```
|
|
255
|
+
|
|
247
256
|
## Logging
|
|
248
257
|
|
|
249
258
|
Use `ctx.log.info/warn/error/debug(message, properties?)` for structured logging from your handlers. `helix dev` prints human-readable logs by default.
|
package/dist/index.d.ts
CHANGED
|
@@ -2,8 +2,8 @@ import * as h3 from 'h3';
|
|
|
2
2
|
import { ZodType } from 'zod';
|
|
3
3
|
import { F as FunctionHandler, D as DefineFunctionOptions, a as DefineScheduleConfig, S as ScheduleDefinition, P as PlatformContext } from './types-DCCVrmZ3.js';
|
|
4
4
|
export { b as DbCallParameters, c as DbClient, d as FileSystemClient, H as HttpClient, K as KeyValueStore, L as Logger, e as ScheduleContext, f as ScheduleHandler, g as ScheduleTrigger, h as SqlBuilder, T as TrayContext, U as UnvalidatedInput } from './types-DCCVrmZ3.js';
|
|
5
|
-
import { T as TrayConfig, L as LoadedTrayConfig, W as WatchErrorPhase } from './sdk-config-
|
|
6
|
-
export { P as ProvisionedTrayConfig, S as SdkRuntimeConfig, U as UnprovisionedTrayConfig, a as WORKSPACE_ID_REQUIRED_CODE, b as WorkspaceIdRequiredError, g as getSdkConfig, l as loadConfig, c as loadedTrayConfigSchema, p as projectIdSchema, r as requireWorkspaceId, d as resolveConfigFile, w as workspaceIdSchema } from './sdk-config-
|
|
5
|
+
import { T as TrayConfig, L as LoadedTrayConfig, W as WatchErrorPhase } from './sdk-config-vY_-Reh3.js';
|
|
6
|
+
export { C as ConfigFileOptions, H as HELIX_CONFIG_ENV, P as ProvisionedTrayConfig, S as SdkRuntimeConfig, U as UnprovisionedTrayConfig, a as WORKSPACE_ID_REQUIRED_CODE, b as WorkspaceIdRequiredError, g as getSdkConfig, l as loadConfig, c as loadedTrayConfigSchema, p as projectIdSchema, r as requireWorkspaceId, d as resolveConfigFile, e as resolveConfigOverride, w as workspaceIdSchema } from './sdk-config-vY_-Reh3.js';
|
|
7
7
|
export { a as ENVIRONMENT_VALUES, E as Environment, e as environmentSchema } from './env-schema-DkgTG5b0.js';
|
|
8
8
|
import 'ky';
|
|
9
9
|
|
|
@@ -85,6 +85,13 @@ declare function defineConfig(config: TrayConfig): TrayConfig;
|
|
|
85
85
|
interface CreateConfigServiceOptions {
|
|
86
86
|
/** Directory containing `helix.config.ts`. */
|
|
87
87
|
cwd: string;
|
|
88
|
+
/**
|
|
89
|
+
* Explicit config file path (the CLI's `--config` flag), overriding the
|
|
90
|
+
* `helix.config.ts` default. Falls back to the `HELIX_CONFIG` env var; the
|
|
91
|
+
* effective override is pinned once at service creation, so later env-var
|
|
92
|
+
* changes never re-target a live service.
|
|
93
|
+
*/
|
|
94
|
+
configFile?: string;
|
|
88
95
|
/** If true, attach a c12 watcher and emit subscribers on each change. */
|
|
89
96
|
watch?: boolean;
|
|
90
97
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
import{A as Ce,B as ve,b as T,g as U,h as I,i as N,j as C,k as q,l as M,m as $,n as Z,o as ge,p as K,q as z,r as G,s as J,t as Q,x as X,y as he,z as ye}from"./chunk-6OZ47QHO.js";import{a as B,b as me}from"./chunk-WUCX7RZO.js";import{b as H,e as A,i as W,l as g,m as V}from"./chunk-Q6GJ4HQZ.js";import{randomUUID as be}from"crypto";import{SpanStatusCode as ne,SpanKind as Re}from"@opentelemetry/api";import{defineEventHandler as Pe,getMethod as Le,getRequestURL as Fe,getHeaders as se,getHeader as oe}from"h3";import{readBody as we,getQuery as Ee,getRouterParams as ke}from"h3";async function Y(e,t){let r=await Se(e),n=Ee(e),o=ke(e)??{},s={...n,...r,...o};return t?xe(s,t):s}async function Se(e){try{let t=await we(e);return t&&typeof t=="object"?t:(t!=null&&t!==""&&console.warn("[helix-sdk] Request body present but not a JSON object \u2014 ignored"),{})}catch(t){if(t instanceof Error&&"statusCode"in t&&t.statusCode===400){let n=new Error("Malformed request body");throw n.statusCode=400,n.data={message:"Request body is not valid JSON"},n}let r=e.method?.toUpperCase();if(r==="GET"||r==="HEAD"||r==="DELETE")return{};throw t}}function xe(e,t){let r=t.safeParse(e);if(!r.success){let n=new Error("Input validation failed");throw n.statusCode=400,n.data=r.error.flatten(),n}return r.data}var v=class extends Error{statusCode;constructor(t,r){super(r),this.statusCode=t,this.name="HttpError"}};function ee(e){return t=>{e.node.res.statusCode=t}}function te(){return(e,t)=>{throw new v(e,t)}}function re(e,t){let{statusCode:r,message:n}=Te(t.detail);return e.node.res.statusCode=r,{error:n,statusCode:r}}function Te(e){switch(e.kind){case"unknown_alias":return{statusCode:400,message:"Unknown authentication alias"};case"input_validation_failed":return{statusCode:400,message:"Input validation failed"};case"output_validation_failed":return{statusCode:500,message:"Internal server error"};case"config_load_failed":return{statusCode:500,message:"Configuration error"};case"broker_error":return{statusCode:502,message:"Broker error"};case"unsupported_body_type":return{statusCode:400,message:"Unsupported request body type"};case"unsupported_scheme":return{statusCode:400,message:"Unsupported URL scheme"};case"missing_execution_credentials":return{statusCode:401,message:"Missing execution credentials"};case"missing_tray_token":return{statusCode:500,message:"Missing Tray token"};case"db_call_failed":return{statusCode:502,message:e.message};case"db_query_failed":return{statusCode:502,message:`Database query failed: ${e.message}`};case"db_invalid_parameters":return{statusCode:400,message:e.message};case"kv_key_invalid":return{statusCode:400,message:e.message};case"kv_value_invalid":return{statusCode:400,message:e.message};case"kv_value_too_large":return{statusCode:400,message:e.message};case"kv_ttl_invalid":return{statusCode:400,message:e.message};case"kv_not_available":return{statusCode:501,message:"Key-value store is not available in this environment"};case"kv_call_failed":return{statusCode:502,message:"Key-value store request failed"};case"kv_requires_provisioning":return{statusCode:500,message:"Internal server error"};case"files_path_invalid":return{statusCode:400,message:e.message};case"files_value_invalid":return{statusCode:400,message:e.message};case"files_value_too_large":return{statusCode:400,message:e.message};case"files_not_available":return{statusCode:501,message:"File storage is not available in this environment"};case"files_call_failed":return{statusCode:502,message:"File storage request failed"};case"files_requires_provisioning":return{statusCode:500,message:"Internal server error"};default:return{statusCode:500,message:`Unhandled error kind: ${e.kind}`}}}var Ie=["authorization","x-tray-token","set-cookie","x-kong-request-id","traceparent","x-real-ip","cookie"];function _e(e){return e.toLowerCase().includes("tray_lid")}function _(e){let t={};for(let[r,n]of Object.entries(e))if(!Ie.includes(r.toLowerCase())){if(r.toLowerCase()==="cookie"&&n){let o=n.split(";").map(s=>s.trim()).filter(s=>{let a=s.split("=")[0].trim();return!_e(a)}).join("; ");o&&(t[r]=o);continue}t[r]=n}return t}function je(e,t){let{inputSchema:r,outputSchema:n}=Ue(t),o=e;return Pe(async s=>{let a=T(),{executionId:i,userId:c,deploymentId:f}=Ne(a,s),p=Date.now(),y=new Date().toISOString(),m=Le(s),l=Fe(s).pathname,u=z(),h=K(),E=`${m} ${l}`;q();let O={hasDb:!1,hasExternal:!1,hasAuth:!1,hasError:!1};return De(()=>I.run(O,()=>U.run(i,async()=>{let k=await Oe(s,r);if(!k.ok)return{response:k.response,errored:!1,errorMessage:null};let pe=Me(s,k.input,u,h,m,l,i,f,c);return He(o,pe,n,s,h,i,f,{method:m,path:l,rawInput:k.input})})),{executionId:i,deploymentId:f,routeOrJob:E,startedAt:y,startMs:p,userId:c,flags:O})})}async function De(e,t){let r=!1,n=null;try{let o=await e();return r=o.errored,n=o.errorMessage,o.response}finally{Z({...t,errored:r,errorMessage:n}),await M()}}async function Oe(e,t){try{return{ok:!0,input:await Y(e,t)}}catch(r){let n=qe(e,r);if(n)return{ok:!1,response:n};throw r}}async function He(e,t,r,n,o,s,a,i){return $.startActiveSpan("function",{root:!0,kind:Re.SERVER,attributes:{method:i.method,route:i.path,executionId:s,...a?{deploymentId:a}:{}}},async c=>{t.traceId=c.spanContext().traceId;let f=_(se(n));C(c,"input",i.rawInput),C(c,"request",{method:i.method,path:i.path,headers:f,body:i.rawInput});let p=!1,y=null,m,l;try{let u=await B.run(t,()=>e(t));if(m=u,u==null)n.node.res.statusCode=204,l="";else if(u instanceof Response)l=u;else if(r){let h=r.safeParse(u);if(!h.success){let E=Object.keys(h.error.flatten().fieldErrors);throw o.error("Output validation failed",{fields:E,executionId:s}),g.outputValidationFailed(E)}l=h.data}else l=u}catch(u){p=!0,y=u instanceof Error?u.message:String(u),N("hasError"),l=Ae(u,n,o,s)}finally{We(c,n,{errored:p,errorMessage:y,handlerResult:m,response:l})}return{response:l,errored:p,errorMessage:y}})}function Ae(e,t,r,n){if(e instanceof v)return t.node.res.statusCode=e.statusCode,{error:e.message,statusCode:e.statusCode};if(V(e))return re(t,e);r.error("Unhandled error in handler",{error:e instanceof Error?e.message:String(e),executionId:n}),t.node.res.statusCode=500;let o=process.env.NODE_ENV==="development";return{error:o&&e instanceof Error?e.message:"Internal server error",statusCode:500,...o&&e instanceof Error&&e.stack?{stack:e.stack}:{}}}function We(e,t,r){e.setAttribute("statusCode",t.node.res.statusCode),!r.errored&&r.handlerResult!==void 0&&C(e,"output",r.handlerResult),C(e,"response",r.response);let n=I.getStore();e.setAttribute("hasAuth",n?.hasAuth??!1),e.setAttribute("hasExternal",n?.hasExternal??!1),e.setAttribute("hasDb",n?.hasDb??!1),e.setAttribute("status",r.errored?"error":"success");let o=r.errored||(n?.hasError??!1);e.setStatus({code:o?ne.ERROR:ne.OK,...r.errorMessage?{message:r.errorMessage}:{}}),e.end()}function Ue(e){if(!e)return{inputSchema:void 0,outputSchema:void 0};if(e instanceof Object&&"_def"in e)return{inputSchema:e,outputSchema:void 0};if(typeof e=="object"&&("input"in e||"output"in e)){let t=e;return{inputSchema:t.input,outputSchema:t.output}}return{inputSchema:e,outputSchema:void 0}}function Ne(e,t){let r=e.mode==="lambda",n=(r?e.executionId:oe(t,"x-tray-execution-id"))??be(),o=(r?e.userId:oe(t,"x-tray-user-id"))??null,s=r?e.deploymentId??null:null;return{executionId:n,userId:o,deploymentId:s}}function qe(e,t){if(!(t instanceof Error)||!("statusCode"in t)||!("data"in t))return null;let r=t.statusCode,n=t.data;e.node.res.statusCode=r;let o=n&&typeof n=="object"&&"fieldErrors"in n?n.fieldErrors:void 0;return{error:"Input validation failed",statusCode:r,...o?{fields:Object.keys(o)}:{}}}function Me(e,t,r,n,o,s,a,i,c){return{input:t,method:o,path:s,headers:_(se(e)),http:G(),db:f=>J(f),kv:()=>Q(),files:()=>X(),log:n,config:r,status:ee(e),error:te(),state:{},request:e,executionId:a,deploymentId:i,userId:c,identity:c!==null?{user:{id:c}}:null,traceId:""}}function $e(e){return e}import{resolve as b}from"path";import{existsSync as ae}from"fs";import{loadConfig as Ke}from"c12";import{z as d}from"zod";import{z as Ze}from"zod";var Be=["production"],S=Ze.string().refine(e=>H(e),e=>({message:`Invalid enum value. Expected ${A().map(t=>`'${t}'`).join(" | ")}, received '${e}'`}));var R=d.string().uuid("projectId must be a valid UUID"),P=d.string().min(1,"workspaceId must be a non-empty string"),ze=d.object({name:d.string().min(1,"name is required"),projectId:R.optional(),workspaceId:P.optional(),env:S.default("production"),authentications:d.record(d.string()).optional().default({}),config:d.record(d.unknown()).optional().default({})}),ie=d.object({name:d.string().min(1,"name is required"),workspaceId:P.optional(),env:S,authentications:d.record(d.string()),config:d.record(d.unknown())}),Ve=d.discriminatedUnion("kind",[ie.extend({kind:d.literal("provisioned"),projectId:R}),ie.extend({kind:d.literal("unprovisioned")})]),de=["helix","tray"],ce=["ts","mts","cts","js","mjs","cjs"];function x(e){for(let t of de)for(let r of ce){let n=b(e,`${t}.config.${r}`);if(ae(n))return n}}function Ge(e){for(let t of de)if(ce.some(r=>ae(b(e,`${t}.config.${r}`))))return t;return"helix"}function L(e){return{name:Ge(e),cwd:e,rcFile:!1,globalRc:!1,packageJson:!1,dotenv:!1,extend:!1}}function F(e,t){if(e!==null&&typeof e=="object"&&"extends"in e)throw g.configLoadFailed(`helix.config.ts at ${t}: 'extends' is not supported \u2014 remove the field`);let r=ze.safeParse(e);if(!r.success){let f=r.error.issues.map(p=>p.message).join("; ");throw g.configLoadFailed(`Invalid helix.config.ts at ${t}: ${f}`)}let{projectId:n,workspaceId:o,...s}=r.data,a=o!==void 0?{workspaceId:o}:{},i=W(),c=i!==void 0?{...s,env:i}:s;return n!==void 0?{kind:"provisioned",projectId:n,...c,...a}:{kind:"unprovisioned",...c,...a}}async function w(e){let t=e??process.cwd(),r;try{r=await Ke(L(t))}catch(o){throw g.configLoadFailed(`Failed to load helix.config.ts: ${o instanceof Error?o.message:String(o)}`)}let n=r._configFile??x(t);if(!n)throw g.configLoadFailed(`helix.config.ts not found at ${b(t,"helix.config.ts")}`);return F(r.config,n)}var ue="WORKSPACE_ID_REQUIRED";function Je(e){if(e.workspaceId===void 0)throw Object.assign(new Error("No workspaceId found in helix.config.ts. Discover & choose one \u2014 self-serve fix: run `helix workspace select`; agent-driven fix: call the `tray_list_workspaces` MCP tool. Already know the id? Run `helix workspace set <id>` or call the `tray_set_workspace_id` MCP tool. deploy cannot proceed without a workspace."),{code:ue});return e.workspaceId}import{existsSync as Qe,watch as Xe,writeSync as Ye}from"fs";import{join as et,resolve as tt}from"path";import{watchConfig as rt}from"c12";function nt(e,t){let r=e instanceof Error?e.message:String(e);Ye(2,`[helix-watch] ${t.phase} error: ${r}
|
|
2
|
-
`)}function
|
|
3
|
-
|
|
1
|
+
import{A as ke,B as Se,b as R,g as M,h as F,i as Z,j as E,k as K,l as B,m as V,n as z,o as Ce,p as J,q as Q,r as Y,s as ee,t as te,x as re,y as Ee,z as we}from"./chunk-6OZ47QHO.js";import{a as G,b as ve}from"./chunk-WUCX7RZO.js";import{b as U,e as $,i as q,l as g,m as X}from"./chunk-Q6GJ4HQZ.js";import{randomUUID as Le}from"crypto";import{SpanStatusCode as ae,SpanKind as Oe}from"@opentelemetry/api";import{defineEventHandler as je,getMethod as De,getRequestURL as He,getHeaders as ce,getHeader as de}from"h3";import{readBody as xe,getQuery as Ie,getRouterParams as _e}from"h3";async function ne(e,t){let r=await be(e),n=Ie(e),o=_e(e)??{},i={...n,...r,...o};return t?Te(i,t):i}async function be(e){try{let t=await xe(e);return t&&typeof t=="object"?t:(t!=null&&t!==""&&console.warn("[helix-sdk] Request body present but not a JSON object \u2014 ignored"),{})}catch(t){if(t instanceof Error&&"statusCode"in t&&t.statusCode===400){let n=new Error("Malformed request body");throw n.statusCode=400,n.data={message:"Request body is not valid JSON"},n}let r=e.method?.toUpperCase();if(r==="GET"||r==="HEAD"||r==="DELETE")return{};throw t}}function Te(e,t){let r=t.safeParse(e);if(!r.success){let n=new Error("Input validation failed");throw n.statusCode=400,n.data=r.error.flatten(),n}return r.data}var w=class extends Error{statusCode;constructor(t,r){super(r),this.statusCode=t,this.name="HttpError"}};function oe(e){return t=>{e.node.res.statusCode=t}}function ie(){return(e,t)=>{throw new w(e,t)}}function se(e,t){let{statusCode:r,message:n}=Re(t.detail);return e.node.res.statusCode=r,{error:n,statusCode:r}}function Re(e){switch(e.kind){case"unknown_alias":return{statusCode:400,message:"Unknown authentication alias"};case"input_validation_failed":return{statusCode:400,message:"Input validation failed"};case"output_validation_failed":return{statusCode:500,message:"Internal server error"};case"config_load_failed":return{statusCode:500,message:"Configuration error"};case"broker_error":return{statusCode:502,message:"Broker error"};case"unsupported_body_type":return{statusCode:400,message:"Unsupported request body type"};case"unsupported_scheme":return{statusCode:400,message:"Unsupported URL scheme"};case"missing_execution_credentials":return{statusCode:401,message:"Missing execution credentials"};case"missing_tray_token":return{statusCode:500,message:"Missing Tray token"};case"db_call_failed":return{statusCode:502,message:e.message};case"db_query_failed":return{statusCode:502,message:`Database query failed: ${e.message}`};case"db_invalid_parameters":return{statusCode:400,message:e.message};case"kv_key_invalid":return{statusCode:400,message:e.message};case"kv_value_invalid":return{statusCode:400,message:e.message};case"kv_value_too_large":return{statusCode:400,message:e.message};case"kv_ttl_invalid":return{statusCode:400,message:e.message};case"kv_not_available":return{statusCode:501,message:"Key-value store is not available in this environment"};case"kv_call_failed":return{statusCode:502,message:"Key-value store request failed"};case"kv_requires_provisioning":return{statusCode:500,message:"Internal server error"};case"files_path_invalid":return{statusCode:400,message:e.message};case"files_value_invalid":return{statusCode:400,message:e.message};case"files_value_too_large":return{statusCode:400,message:e.message};case"files_not_available":return{statusCode:501,message:"File storage is not available in this environment"};case"files_call_failed":return{statusCode:502,message:"File storage request failed"};case"files_requires_provisioning":return{statusCode:500,message:"Internal server error"};default:return{statusCode:500,message:`Unhandled error kind: ${e.kind}`}}}var Fe=["authorization","x-tray-token","set-cookie","x-kong-request-id","traceparent","x-real-ip","cookie"];function Pe(e){return e.toLowerCase().includes("tray_lid")}function P(e){let t={};for(let[r,n]of Object.entries(e))if(!Fe.includes(r.toLowerCase())){if(r.toLowerCase()==="cookie"&&n){let o=n.split(";").map(i=>i.trim()).filter(i=>{let d=i.split("=")[0].trim();return!Pe(d)}).join("; ");o&&(t[r]=o);continue}t[r]=n}return t}function Ae(e,t){let{inputSchema:r,outputSchema:n}=Me(t),o=e;return je(async i=>{let d=R(),{executionId:s,userId:a,deploymentId:f}=Ze(d,i),p=Date.now(),v=new Date().toISOString(),h=De(i),l=He(i).pathname,u=Q(),y=J(),S=`${h} ${l}`;K();let N={hasDb:!1,hasExternal:!1,hasAuth:!1,hasError:!1};return We(()=>F.run(N,()=>M.run(s,async()=>{let x=await Ne(i,r);if(!x.ok)return{response:x.response,errored:!1,errorMessage:null};let ye=Be(i,x.input,u,y,h,l,s,f,a);return Ue(o,ye,n,i,y,s,f,{method:h,path:l,rawInput:x.input})})),{executionId:s,deploymentId:f,routeOrJob:S,startedAt:v,startMs:p,userId:a,flags:N})})}async function We(e,t){let r=!1,n=null;try{let o=await e();return r=o.errored,n=o.errorMessage,o.response}finally{z({...t,errored:r,errorMessage:n}),await B()}}async function Ne(e,t){try{return{ok:!0,input:await ne(e,t)}}catch(r){let n=Ke(e,r);if(n)return{ok:!1,response:n};throw r}}async function Ue(e,t,r,n,o,i,d,s){return V.startActiveSpan("function",{root:!0,kind:Oe.SERVER,attributes:{method:s.method,route:s.path,executionId:i,...d?{deploymentId:d}:{}}},async a=>{t.traceId=a.spanContext().traceId;let f=P(ce(n));E(a,"input",s.rawInput),E(a,"request",{method:s.method,path:s.path,headers:f,body:s.rawInput});let p=!1,v=null,h,l;try{let u=await G.run(t,()=>e(t));if(h=u,u==null)n.node.res.statusCode=204,l="";else if(u instanceof Response)l=u;else if(r){let y=r.safeParse(u);if(!y.success){let S=Object.keys(y.error.flatten().fieldErrors);throw o.error("Output validation failed",{fields:S,executionId:i}),g.outputValidationFailed(S)}l=y.data}else l=u}catch(u){p=!0,v=u instanceof Error?u.message:String(u),Z("hasError"),l=$e(u,n,o,i)}finally{qe(a,n,{errored:p,errorMessage:v,handlerResult:h,response:l})}return{response:l,errored:p,errorMessage:v}})}function $e(e,t,r,n){if(e instanceof w)return t.node.res.statusCode=e.statusCode,{error:e.message,statusCode:e.statusCode};if(X(e))return se(t,e);r.error("Unhandled error in handler",{error:e instanceof Error?e.message:String(e),executionId:n}),t.node.res.statusCode=500;let o=process.env.NODE_ENV==="development";return{error:o&&e instanceof Error?e.message:"Internal server error",statusCode:500,...o&&e instanceof Error&&e.stack?{stack:e.stack}:{}}}function qe(e,t,r){e.setAttribute("statusCode",t.node.res.statusCode),!r.errored&&r.handlerResult!==void 0&&E(e,"output",r.handlerResult),E(e,"response",r.response);let n=F.getStore();e.setAttribute("hasAuth",n?.hasAuth??!1),e.setAttribute("hasExternal",n?.hasExternal??!1),e.setAttribute("hasDb",n?.hasDb??!1),e.setAttribute("status",r.errored?"error":"success");let o=r.errored||(n?.hasError??!1);e.setStatus({code:o?ae.ERROR:ae.OK,...r.errorMessage?{message:r.errorMessage}:{}}),e.end()}function Me(e){if(!e)return{inputSchema:void 0,outputSchema:void 0};if(e instanceof Object&&"_def"in e)return{inputSchema:e,outputSchema:void 0};if(typeof e=="object"&&("input"in e||"output"in e)){let t=e;return{inputSchema:t.input,outputSchema:t.output}}return{inputSchema:e,outputSchema:void 0}}function Ze(e,t){let r=e.mode==="lambda",n=(r?e.executionId:de(t,"x-tray-execution-id"))??Le(),o=(r?e.userId:de(t,"x-tray-user-id"))??null,i=r?e.deploymentId??null:null;return{executionId:n,userId:o,deploymentId:i}}function Ke(e,t){if(!(t instanceof Error)||!("statusCode"in t)||!("data"in t))return null;let r=t.statusCode,n=t.data;e.node.res.statusCode=r;let o=n&&typeof n=="object"&&"fieldErrors"in n?n.fieldErrors:void 0;return{error:"Input validation failed",statusCode:r,...o?{fields:Object.keys(o)}:{}}}function Be(e,t,r,n,o,i,d,s,a){return{input:t,method:o,path:i,headers:P(ce(e)),http:Y(),db:f=>ee(f),kv:()=>te(),files:()=>re(),log:n,config:r,status:oe(e),error:ie(),state:{},request:e,executionId:d,deploymentId:s,userId:a,identity:a!==null?{user:{id:a}}:null,traceId:""}}function Ve(e){return e}import{resolve as m}from"path";import{existsSync as _,realpathSync as ue}from"fs";import{loadConfig as Je}from"c12";import{z as c}from"zod";import{z as ze}from"zod";var Ge=["production"],I=ze.string().refine(e=>U(e),e=>({message:`Invalid enum value. Expected ${$().map(t=>`'${t}'`).join(" | ")}, received '${e}'`}));var L=c.string().uuid("projectId must be a valid UUID"),O=c.string().min(1,"workspaceId must be a non-empty string"),Qe=c.object({name:c.string().min(1,"name is required"),projectId:L.optional(),workspaceId:O.optional(),env:I.default("production"),authentications:c.record(c.string()).optional().default({}),config:c.record(c.unknown()).optional().default({})}),fe=c.object({name:c.string().min(1,"name is required"),workspaceId:O.optional(),env:I,authentications:c.record(c.string()),config:c.record(c.unknown())}),Xe=c.discriminatedUnion("kind",[fe.extend({kind:c.literal("provisioned"),projectId:L}),fe.extend({kind:c.literal("unprovisioned")})]),le=["helix","tray"],pe=["ts","mts","cts","js","mjs","cjs"],j="HELIX_CONFIG";function C(e){let t=e?.trim();return t||process.env[j]?.trim()||void 0}function b(e,t){let r=C(t?.configFile);if(r!==void 0){let n=m(e,r);return _(n)?n:void 0}for(let n of le)for(let o of pe){let i=m(e,`${n}.config.${o}`);if(_(i))return i}}function Ye(e){for(let t of le)if(pe.some(r=>_(m(e,`${t}.config.${r}`))))return t;return"helix"}function et(e,t){return m(e,t).replace(/\.(?:m|c)?[jt]s$/,"")}function D(e,t){let r=C(t?.configFile);return{name:Ye(e),cwd:e,...r!==void 0?{configFile:et(e,r)}:{},rcFile:!1,globalRc:!1,packageJson:!1,dotenv:!1,extend:!1}}function H(e,t){if(e!==null&&typeof e=="object"&&"extends"in e)throw g.configLoadFailed(`helix.config.ts at ${t}: 'extends' is not supported \u2014 remove the field`);let r=Qe.safeParse(e);if(!r.success){let f=r.error.issues.map(p=>p.message).join("; ");throw g.configLoadFailed(`Invalid helix.config.ts at ${t}: ${f}`)}let{projectId:n,workspaceId:o,...i}=r.data,d=o!==void 0?{workspaceId:o}:{},s=q(),a=s!==void 0?{...i,env:s}:i;return n!==void 0?{kind:"provisioned",projectId:n,...a,...d}:{kind:"unprovisioned",...a,...d}}async function k(e,t){let r=e??process.cwd(),n=C(t?.configFile),o=n!==void 0?m(r,n):void 0;if(o!==void 0&&!_(o)){let s=t?.configFile?.trim()?"":` (from ${j})`;throw g.configLoadFailed(`Config file not found: ${o}${s}`)}let i;try{i=await Je(D(r,t))}catch(s){throw g.configLoadFailed(`Failed to load helix.config.ts: ${s instanceof Error?s.message:String(s)}`)}let d=i._configFile??b(r,t);if(!d)throw g.configLoadFailed(`helix.config.ts not found at ${m(r,"helix.config.ts")}`);if(o!==void 0&&i._configFile!==void 0){let s=ue(o);if(ue(m(i._configFile))!==s)throw g.configLoadFailed(`Config file conflict: requested ${o} but ${m(i._configFile)} takes extension-resolution precedence \u2014 remove or rename the sibling file`)}return H(i.config,d)}var ge="WORKSPACE_ID_REQUIRED";function tt(e){if(e.workspaceId===void 0)throw Object.assign(new Error("No workspaceId found in helix.config.ts. Discover & choose one \u2014 self-serve fix: run `helix workspace select`; agent-driven fix: call the `tray_list_workspaces` MCP tool. Already know the id? Run `helix workspace set <id>` or call the `tray_set_workspace_id` MCP tool. deploy cannot proceed without a workspace."),{code:ge});return e.workspaceId}import{existsSync as rt,watch as nt,writeSync as ot}from"fs";import{basename as it,dirname as st,join as at,resolve as T}from"path";import{watchConfig as dt}from"c12";function ct(e,t){let r=e instanceof Error?e.message:String(e);ot(2,`[helix-watch] ${t.phase} error: ${r}
|
|
2
|
+
`)}function A(){return{kind:"unprovisioned",name:"",authentications:{},config:{},env:"production"}}var ut=/^(?:helix|tray)\.config\.(?:m|c)?[jt]s$/,me=new Set;function he(e,t){return b(e,{configFile:t})!==void 0}async function ft(e,t,r){if(he(e.cwd,t))try{return{current:await k(e.cwd,{configFile:t}),initialLoadSucceeded:!0}}catch(n){return r(n,{phase:"reload"}),{current:A(),initialLoadSucceeded:!1}}if(e.watch){let n=t!==void 0?T(e.cwd,t):T(e.cwd);me.has(n)||(process.stderr.write(t!==void 0?`warning: config file ${T(e.cwd,t)} not found; tray running with default config
|
|
3
|
+
`:`warning: no helix.config.ts found at ${e.cwd}; tray running with default config \u2014 create one via \`helix init\`
|
|
4
|
+
`),me.add(n))}return{current:A(),initialLoadSucceeded:!1}}var W=class{constructor(t,r,n,o){this.cwd=t;this.configFile=r;this.onError=n;this.emit=o}cwd;configFile;onError;emit;stopped=!1;directoryWatcher=null;c12Unwatch=null;c12AttachPromise=null;updateChain=Promise.resolve();start(t){t?this.attachC12Watcher():this.attachCreationWatcher()}watcherReady(){return this.c12AttachPromise===null?Promise.resolve():this.c12AttachPromise.catch(()=>{})}async stop(){this.stopped=!0,this.directoryWatcher&&(this.directoryWatcher.close(),this.directoryWatcher=null),this.c12AttachPromise&&await this.c12AttachPromise.catch(t=>this.onError(t,{phase:"attach"})),this.c12Unwatch&&(await this.c12Unwatch(),this.c12Unwatch=null)}attachC12Watcher(){this.c12AttachPromise===null&&(this.c12AttachPromise=dt({...D(this.cwd,{configFile:this.configFile}),onUpdate:({newConfig:t})=>{this.stopped||(this.updateChain=this.updateChain.then(async()=>{if(this.stopped)return;let r;try{r=H(t.config,this.cwd)}catch(n){this.onError(n,{phase:"validation"});return}try{this.emit(r)}catch(n){this.onError(n,{phase:"reload"})}}))}}).then(async t=>{if(this.stopped){t.unwatch();return}this.c12Unwatch=t.unwatch;let{watch:r}=await import("chokidar"),n=r(this.cwd,{persistent:!1,depth:0});try{await new Promise((o,i)=>{n.once("ready",o),n.once("error",i)})}finally{await n.close()}}).catch(t=>{this.onError(t,{phase:"attach"})}))}attachCreationWatcher(){let t=this.configFile!==void 0?T(this.cwd,this.configFile):void 0,r=t!==void 0?st(t):this.cwd;try{this.directoryWatcher=nt(r,{persistent:!1},(n,o)=>{if(this.stopped||!o)return;let i=o.toString();if(t!==void 0){if(i!==it(t))return}else if(!ut.test(i))return;rt(at(r,i))&&(this.updateChain=this.updateChain.then(async()=>{if(this.stopped)return;let d;try{d=await k(this.cwd,{configFile:this.configFile})}catch(a){this.onError(a,{phase:"reload"});return}if(this.stopped)return;this.emit(d);let s=this.directoryWatcher;s&&(this.directoryWatcher=null,s.close()),this.attachC12Watcher()}))}),this.directoryWatcher.on("error",n=>{this.onError(n,{phase:"attach"})})}catch(n){this.onError(n,{phase:"attach"})}}};async function lt(e){let t=e.onError??ct,r=new Set,n=C(e.configFile),o=await ft(e,n,t),i=o.current;function d(a){i=a;for(let f of[...r])try{f(a)}catch(p){t(p,{phase:"reload"})}}let s=new W(e.cwd,n,t,d);return e.watch&&s.start(o.initialLoadSucceeded),{get(){return i},async reload(){if(!he(e.cwd,n)){let a=A();return d(a),a}try{let a=await k(e.cwd,{configFile:n});return d(a),a}catch(a){return t(a,{phase:"reload"}),i}},subscribe(a){return r.add(a),()=>{r.delete(a)}},async stop(){r.clear(),await s.stop()},ready(){return s.watcherReady()}}}export{Ge as ENVIRONMENT_VALUES,j as HELIX_CONFIG_ENV,Ee as SCHEDULE_DEFINITION_KIND,we as ScheduleConfigError,ge as WORKSPACE_ID_REQUIRED_CODE,lt as createConfigService,Ve as defineConfig,Ae as defineFunction,ke as defineSchedule,I as environmentSchema,ve as getContext,R as getSdkConfig,Se as isScheduleDefinition,k as loadConfig,Xe as loadedTrayConfigSchema,L as projectIdSchema,tt as requireWorkspaceId,b as resolveConfigFile,C as resolveConfigOverride,Ce as runWithinAppSpan,O as workspaceIdSchema};
|
package/dist/internal/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { L as LoadedTrayConfig } from '../sdk-config-
|
|
2
|
-
export {
|
|
1
|
+
import { L as LoadedTrayConfig } from '../sdk-config-vY_-Reh3.js';
|
|
2
|
+
export { f as SdkConfigInput, s as setSdkConfig } from '../sdk-config-vY_-Reh3.js';
|
|
3
3
|
import 'zod';
|
|
4
4
|
|
|
5
5
|
/** BroadcastChannel name for parent→worker config updates. */
|
|
@@ -101,16 +101,42 @@ type ProvisionedTrayConfig = Extract<LoadedTrayConfig, {
|
|
|
101
101
|
type UnprovisionedTrayConfig = Extract<LoadedTrayConfig, {
|
|
102
102
|
kind: "unprovisioned";
|
|
103
103
|
}>;
|
|
104
|
+
/**
|
|
105
|
+
* Env var naming an alternate config file (relative paths resolve against cwd).
|
|
106
|
+
* Sits between the explicit `configFile` option (the CLI's `--config` flag) and
|
|
107
|
+
* the `helix.config.ts` default in resolution precedence.
|
|
108
|
+
*/
|
|
109
|
+
declare const HELIX_CONFIG_ENV = "HELIX_CONFIG";
|
|
110
|
+
/** Options accepted by every config entry point that resolves the config file. */
|
|
111
|
+
interface ConfigFileOptions {
|
|
112
|
+
/**
|
|
113
|
+
* Explicit config file path (the CLI's `--config` flag). Resolution
|
|
114
|
+
* precedence: this option → `HELIX_CONFIG` env var → `helix.config.ts`
|
|
115
|
+
* (then legacy `tray.config.*`) in cwd.
|
|
116
|
+
*/
|
|
117
|
+
configFile?: string;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* The effective config-file override, if any: the explicit `configFile` option
|
|
121
|
+
* (CLI `--config` flag) wins over the `HELIX_CONFIG` env var. Returns the raw
|
|
122
|
+
* (possibly relative) path — callers resolve it against their cwd.
|
|
123
|
+
*/
|
|
124
|
+
declare function resolveConfigOverride(configFile?: string): string | undefined;
|
|
104
125
|
/**
|
|
105
126
|
* Absolute path of the project config file in `cwd`, preferring `helix.config.*`
|
|
106
127
|
* and falling back to a legacy `tray.config.*`. Returns undefined when neither exists.
|
|
128
|
+
* A config-file override (option or HELIX_CONFIG) replaces the default search
|
|
129
|
+
* entirely: only the named file is considered.
|
|
107
130
|
*/
|
|
108
|
-
declare function resolveConfigFile(cwd: string): string | undefined;
|
|
131
|
+
declare function resolveConfigFile(cwd: string, options?: ConfigFileOptions): string | undefined;
|
|
109
132
|
/**
|
|
110
|
-
* One-shot stateless read of
|
|
111
|
-
*
|
|
133
|
+
* One-shot stateless read of the project config. NO caching — every call hits disk.
|
|
134
|
+
* Reads `helix.config.ts` (legacy `tray.config.*` fallback) unless an override
|
|
135
|
+
* names another file — `options.configFile` (CLI `--config` flag) first, then
|
|
136
|
+
* the `HELIX_CONFIG` env var. For hot-reload or stable snapshot, use
|
|
137
|
+
* {@link createConfigService} instead.
|
|
112
138
|
*/
|
|
113
|
-
declare function loadConfig(cwd?: string): Promise<LoadedTrayConfig>;
|
|
139
|
+
declare function loadConfig(cwd?: string, options?: ConfigFileOptions): Promise<LoadedTrayConfig>;
|
|
114
140
|
/** Phase that surfaced an error inside ConfigService watcher. */
|
|
115
141
|
type WatchErrorPhase = "attach" | "reload" | "validation";
|
|
116
142
|
/** Stable code on errors thrown by {@link requireWorkspaceId}. Narrow by this, not instanceof. */
|
|
@@ -181,4 +207,4 @@ interface SdkConfigInput extends Partial<SdkRuntimeConfig>, BrokerSecrets {
|
|
|
181
207
|
declare function setSdkConfig(options: SdkConfigInput): void;
|
|
182
208
|
declare function getSdkConfig(): Readonly<SdkRuntimeConfig>;
|
|
183
209
|
|
|
184
|
-
export { type LoadedTrayConfig as L, type ProvisionedTrayConfig as P, type SdkRuntimeConfig as S, type TrayConfig as T, type UnprovisionedTrayConfig as U, type WatchErrorPhase as W, WORKSPACE_ID_REQUIRED_CODE as a, type WorkspaceIdRequiredError as b, loadedTrayConfigSchema as c, resolveConfigFile as d, type SdkConfigInput as
|
|
210
|
+
export { type ConfigFileOptions as C, HELIX_CONFIG_ENV as H, type LoadedTrayConfig as L, type ProvisionedTrayConfig as P, type SdkRuntimeConfig as S, type TrayConfig as T, type UnprovisionedTrayConfig as U, type WatchErrorPhase as W, WORKSPACE_ID_REQUIRED_CODE as a, type WorkspaceIdRequiredError as b, loadedTrayConfigSchema as c, resolveConfigFile as d, resolveConfigOverride as e, type SdkConfigInput as f, getSdkConfig as g, loadConfig as l, projectIdSchema as p, requireWorkspaceId as r, setSdkConfig as s, workspaceIdSchema as w };
|