@salsita-ai/sdk 0.50.0-develop.514d19d58c → 0.50.0-develop.7d4723b0a5

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.
Files changed (144) hide show
  1. package/README.md +19 -11
  2. package/dist/application-spec/errors/diagnostic-codes.d.ts +6 -0
  3. package/dist/application-spec/errors/diagnostic-codes.js +1 -1
  4. package/dist/application-spec/errors/diagnostic-messages.d.ts +6 -0
  5. package/dist/application-spec/errors/diagnostic-messages.js +1 -1
  6. package/dist/application-spec/generate-application/code/domain-runtime-files.js +1 -1
  7. package/dist/application-spec/generate-application/code/templates/application-types.js +6 -6
  8. package/dist/application-spec/generate-application/code/templates/configurator-domain-code.d.ts +1 -0
  9. package/dist/application-spec/generate-application/code/templates/configurator-domain-code.js +11 -8
  10. package/dist/application-spec/generate-application/code/templates/domain-spec/generate-domain-block.d.ts +3 -1
  11. package/dist/application-spec/generate-application/code/templates/domain-spec/generate-domain-block.js +70 -66
  12. package/dist/application-spec/generate-application/code/templates/domain-spec/generate-phx-globals.js +3 -0
  13. package/dist/application-spec/generate-application/code/templates/domain-spec.js +5 -5
  14. package/dist/application-spec/generate-application/code/templates/domain.js +2 -2
  15. package/dist/application-spec/generate-application/code/templates/global-definition/generate-action-types.d.ts +11 -0
  16. package/dist/application-spec/generate-application/code/templates/global-definition/generate-action-types.js +8 -0
  17. package/dist/application-spec/generate-application/code/templates/global-definition/index.d.ts +1 -0
  18. package/dist/application-spec/generate-application/code/templates/global-definition/index.js +1 -1
  19. package/dist/application-spec/generate-application/code/templates/globals.js +1 -0
  20. package/dist/application-spec/generate-application/code/templates/skills/phoenix-create-configurator-skill.js +3 -1
  21. package/dist/application-spec/generate-application/code/templates/skills/phoenix-feedback-skill.js +39 -16
  22. package/dist/application-spec/generate-application/code/templates/skills/phoenix-review-skill.js +4 -2
  23. package/dist/application-spec/generate-application/code/templates/skills/sdk-content.d.ts +3 -1
  24. package/dist/application-spec/generate-application/code/templates/skills/sdk-content.js +2 -2
  25. package/dist/application-spec/generate-application/runtime/configurator-runtime/configurator-runtime.d.ts +2 -0
  26. package/dist/application-spec/generate-application/runtime/configurator-runtime/configurator-runtime.js +1 -1
  27. package/dist/application-spec/generate-application/runtime/sections/actions-runtime.d.ts +14 -0
  28. package/dist/application-spec/generate-application/runtime/sections/actions-runtime.js +1 -0
  29. package/dist/application-spec/generate-application/runtime/sections/index.d.ts +1 -0
  30. package/dist/application-spec/generate-application/runtime/sections/index.js +1 -1
  31. package/dist/application-spec/parse-application-files/categorize/categorize-files.d.ts +3 -1
  32. package/dist/application-spec/parse-application-files/categorize/categorize-files.js +1 -1
  33. package/dist/application-spec/parse-application-files/merge/index.d.ts +1 -0
  34. package/dist/application-spec/parse-application-files/merge/index.js +1 -1
  35. package/dist/application-spec/parse-application-files/merge/merged-actions.d.ts +11 -0
  36. package/dist/application-spec/parse-application-files/merge/merged-actions.js +1 -0
  37. package/dist/application-spec/parse-application-files/merge/merged-spec.d.ts +2 -0
  38. package/dist/application-spec/parse-application-files/merge/merged-spec.js +1 -1
  39. package/dist/application-spec/parse-application-files/raw-spec/index.d.ts +1 -0
  40. package/dist/application-spec/parse-application-files/raw-spec/index.js +1 -1
  41. package/dist/application-spec/parse-application-files/raw-spec/raw-actions.d.ts +18 -0
  42. package/dist/application-spec/parse-application-files/raw-spec/raw-actions.js +1 -0
  43. package/dist/application-spec/parse-application-files/raw-spec/raw-assistant.d.ts +14 -2
  44. package/dist/application-spec/parse-application-files/raw-spec/raw-assistant.js +1 -1
  45. package/dist/application-spec/parse-application-files/raw-spec/raw-spec-file.d.ts +2 -0
  46. package/dist/application-spec/parse-application-files/raw-spec/raw-spec-file.js +1 -1
  47. package/dist/application-spec/parse-application-files/resolve/resolve-import-modules.js +1 -1
  48. package/dist/application-spec/parse-application-files/spec/application/application-spec.js +1 -1
  49. package/dist/application-spec/parse-application-files/spec/application/configurator-spec/configurator-spec.d.ts +4 -2
  50. package/dist/application-spec/parse-application-files/spec/application/configurator-spec/configurator-spec.js +1 -1
  51. package/dist/application-spec/parse-application-files/spec/common/index.d.ts +1 -0
  52. package/dist/application-spec/parse-application-files/spec/common/index.js +1 -1
  53. package/dist/application-spec/parse-application-files/spec/common/schema-ref-resolver.d.ts +23 -0
  54. package/dist/application-spec/parse-application-files/spec/common/schema-ref-resolver.js +1 -0
  55. package/dist/application-spec/parse-application-files/spec/model/names/action-name.d.ts +10 -0
  56. package/dist/application-spec/parse-application-files/spec/model/names/action-name.js +1 -0
  57. package/dist/application-spec/parse-application-files/spec/model/names/index.d.ts +1 -0
  58. package/dist/application-spec/parse-application-files/spec/model/names/index.js +1 -1
  59. package/dist/application-spec/parse-application-files/spec/sections/actions.d.ts +35 -0
  60. package/dist/application-spec/parse-application-files/spec/sections/actions.js +1 -0
  61. package/dist/application-spec/parse-application-files/spec/sections/assistant/assistant-field-type.d.ts +3 -14
  62. package/dist/application-spec/parse-application-files/spec/sections/assistant/assistant-field-type.js +1 -1
  63. package/dist/application-spec/parse-application-files/spec/sections/assistant/assistant-schema.d.ts +12 -3
  64. package/dist/application-spec/parse-application-files/spec/sections/assistant/assistant-schema.js +1 -1
  65. package/dist/application-spec/parse-application-files/spec/sections/assistant/assistant.d.ts +5 -3
  66. package/dist/application-spec/parse-application-files/spec/sections/index.d.ts +1 -0
  67. package/dist/application-spec/parse-application-files/spec/sections/index.js +1 -1
  68. package/dist/configurator/application-runtime/extensions/configurator-extension-systems.d.ts +2 -1
  69. package/dist/configurator/application-runtime/extensions/configurator-extension-systems.js +1 -1
  70. package/dist/configurator/application-runtime/extensions/selectors.d.ts +2 -1
  71. package/dist/configurator/application-runtime/extensions/selectors.js +1 -1
  72. package/dist/configurator/application-runtime/extensions/systems/assistant-system/assistant-system.d.ts +35 -0
  73. package/dist/configurator/application-runtime/extensions/systems/assistant-system/assistant-system.js +1 -0
  74. package/dist/configurator/{assistant → application-runtime/extensions/systems/assistant-system}/browser-details.d.ts +1 -1
  75. package/dist/configurator/application-runtime/extensions/systems/assistant-system/browser-details.js +1 -0
  76. package/dist/configurator/{assistant → application-runtime/extensions/systems/assistant-system}/graphql-tools.d.ts +13 -14
  77. package/dist/configurator/application-runtime/extensions/systems/assistant-system/graphql-tools.js +1 -0
  78. package/dist/configurator/application-runtime/extensions/systems/graphql-system/graphql-system.js +1 -1
  79. package/dist/configurator/application-runtime/extensions/systems/index.d.ts +1 -0
  80. package/dist/configurator/application-runtime/extensions/systems/index.js +1 -1
  81. package/dist/configurator/assistant/use-assistant-chat.js +1 -1
  82. package/dist/docs/index.json +12 -2
  83. package/dist/docs/other-features/domain-actions.mdx +418 -0
  84. package/dist/docs/phoenix/best-practices.mdx +1 -0
  85. package/dist/docs/phoenix/phoenix-cli.mdx +59 -19
  86. package/dist/docs/phoenix/project-structure.mdx +4 -1
  87. package/dist/docs/phoenix/sdk-builder-workflow.mdx +5 -4
  88. package/dist/domain/actions/action-factory.d.ts +10 -0
  89. package/dist/domain/actions/action-factory.js +1 -0
  90. package/dist/domain/actions/prepare-action-implementations.d.ts +15 -0
  91. package/dist/domain/actions/prepare-action-implementations.js +5 -0
  92. package/dist/domain/application/application-config.d.ts +3 -1
  93. package/dist/domain/application/assistant-mutations.d.ts +15 -14
  94. package/dist/domain/application/assistant-mutations.js +1 -1
  95. package/dist/domain/core/action.d.ts +67 -0
  96. package/dist/domain/core/action.js +1 -0
  97. package/dist/domain/core/domain-factory.d.ts +4 -0
  98. package/dist/domain/core/index.d.ts +1 -0
  99. package/dist/domain/domain-builder.d.ts +7 -0
  100. package/dist/domain/domain-builder.js +1 -1
  101. package/dist/domain/domain-context/action-system/action-system.d.ts +40 -0
  102. package/dist/domain/domain-context/action-system/action-system.js +1 -0
  103. package/dist/domain/domain-context/domain-context-systems.d.ts +5 -2
  104. package/dist/domain/domain-context/index.d.ts +1 -0
  105. package/dist/domain/domain-context/index.js +1 -1
  106. package/dist/domain/domain-runtime/domain-context/domain-context.js +1 -1
  107. package/dist/domain/domain-runtime/domain-context/selectors.d.ts +4 -0
  108. package/dist/domain/domain-runtime/domain-context/selectors.js +1 -1
  109. package/dist/domain/domain-runtime/domain-runtime.d.ts +12 -0
  110. package/dist/domain/domain-runtime/spec/initialize.js +1 -1
  111. package/dist/domain/domain-runtime/spec/types.d.ts +15 -1
  112. package/dist/domain/index.d.ts +2 -0
  113. package/dist/domain/index.js +1 -1
  114. package/dist/domain/mocks/create-mock-domain-runtime.d.ts +6 -0
  115. package/dist/editor/domain-editors/DomainSpecContext.js +1 -1
  116. package/dist/editor/domain-editors/apply-spec-update.js +1 -1
  117. package/dist/editor/domain-editors/use-implementation-files.d.ts +7 -3
  118. package/dist/editor/domain-editors/use-implementation-files.js +1 -1
  119. package/dist/phoenix/application-spec/generate-assistant-prompt.d.ts +2 -2
  120. package/dist/phoenix/application-spec/generate-assistant-prompt.js +1 -1
  121. package/dist/phoenix/cli.js +1 -1
  122. package/dist/phoenix/commands/build.d.ts +5 -0
  123. package/dist/phoenix/commands/build.js +1 -1
  124. package/dist/phoenix/commands/feedback.d.ts +2 -1
  125. package/dist/phoenix/commands/feedback.js +1 -1
  126. package/dist/phoenix/commands/login.d.ts +24 -6
  127. package/dist/phoenix/commands/login.js +2 -2
  128. package/dist/phoenix/services/index.d.ts +1 -0
  129. package/dist/phoenix/services/index.js +1 -1
  130. package/dist/phoenix/services/phx-banner-plugin.d.ts +32 -0
  131. package/dist/phoenix/services/phx-banner-plugin.js +1 -0
  132. package/dist/phoenix/services/phx-licenses-plugin.d.ts +55 -0
  133. package/dist/phoenix/services/phx-licenses-plugin.js +8 -0
  134. package/dist/phoenix/services/vite-config.d.ts +2 -0
  135. package/dist/phoenix/services/vite-config.js +1 -1
  136. package/dist/phoenix/utils/index.d.ts +1 -0
  137. package/dist/phoenix/utils/index.js +1 -1
  138. package/dist/phoenix/utils/read-git-commit.d.ts +14 -0
  139. package/dist/phoenix/utils/read-git-commit.js +1 -0
  140. package/dist/react-sdk/application-runtime/runtime/create-application-runtime.d.ts +12 -0
  141. package/dist/react-sdk/domain-context/common-hooks/use-domain-context.d.ts +3 -0
  142. package/package.json +3 -3
  143. package/dist/configurator/assistant/browser-details.js +0 -1
  144. package/dist/configurator/assistant/graphql-tools.js +0 -1
@@ -1 +1 @@
1
- import{FEEDBACK_CONTACT_MAX_LENGTH as e,FEEDBACK_MESSAGE_MAX_LENGTH as t}from"@salsita-ai/contracts/domain";import{createApiBaseClient as a}from"#package/client";import{getServerCredentials as r}from"../services/index.js";export const runFeedbackCommand=async(o,{logger:n})=>{let s=o.message.trim(),c=o.contact?.trim(),i=o.agent?.trim();if(0===s.length)throw Error('The feedback message is empty. Pass what you want to tell us: phoenix feedback "<message>".');if(s.length>t)throw Error(`The feedback message has ${s.length} characters, but at most ${t} are accepted. Shorten it and send it again.`);if(c&&c.length>e)throw Error(`The contact detail has ${c.length} characters, but at most ${e} are accepted. Shorten --contact and send the feedback again.`);let h=await r(o.serverUrl);if(!h)throw Error(`You are not signed in to ${o.serverUrl}, so the feedback was not sent. Run \`phoenix login <workspace>\` first, then send it again.`);let{error:d}=await a(o.serverUrl).POST("/feedback",{body:{message:s,type:o.type,clientVersion:o.sdkVersion,clientInfo:o.clientInfo,...c?{contact:c}:{},...i?{agent:i}:{},...o.project?{project:o.project}:{}},headers:{authorization:`Bearer ${h.token}`}});if(d)throw Error(`The server did not accept the feedback: ${d.message}`);n.success(`Feedback sent to the Phoenix team from workspace ${h.customerName}. Thank you.`)};
1
+ import{readFile as e}from"node:fs/promises";import{FEEDBACK_CONTACT_MAX_LENGTH as t,FEEDBACK_MESSAGE_MAX_LENGTH as a}from"@salsita-ai/contracts/domain";import{createApiBaseClient as r}from"#package/client";import{getServerCredentials as o}from"../services/index.js";let s=async t=>{if(void 0!==t.message&&void 0!==t.file)throw Error("Both a message and --file were given, so it is unclear which one to send. Pass the message as an argument or put it in the file, not both.");if(void 0!==t.file)return e(t.file,"utf8").catch(e=>{throw Error(`The feedback file ${t.file} could not be read: ${e instanceof Error?e.message:String(e)}. Check the path passed to --file and send the feedback again.`)});if(void 0===t.message)throw Error('No feedback message was given. Pass it as an argument, phoenix feedback "<message>", or in a text file with --file <path>.');return t.message};export const runFeedbackCommand=async(e,{logger:i})=>{let n=(await s(e)).trim(),c=e.contact?.trim(),h=e.agent?.trim();if(0===n.length)throw Error('The feedback message is empty. Pass what you want to tell us: phoenix feedback "<message>", or a text file with --file <path>.');if(n.length>a)throw Error(`The feedback message has ${n.length} characters, but at most ${a} are accepted. Shorten it and send it again.`);if(c&&c.length>t)throw Error(`The contact detail has ${c.length} characters, but at most ${t} are accepted. Shorten --contact and send the feedback again.`);let d=await o(e.serverUrl);if(!d)throw Error(`You are not signed in to ${e.serverUrl}, so the feedback was not sent. Run \`phoenix login <workspace>\` first, then send it again.`);let{error:f}=await r(e.serverUrl).POST("/feedback",{body:{message:n,type:e.type,clientVersion:e.sdkVersion,clientInfo:e.clientInfo,...c?{contact:c}:{},...h?{agent:h}:{},...e.project?{project:e.project}:{}},headers:{authorization:`Bearer ${d.token}`}});if(f)throw Error(`The server did not accept the feedback: ${f.message}`);i.success(`Feedback sent to the Phoenix team from workspace ${d.customerName}. Thank you.`)};
@@ -5,19 +5,37 @@ export interface LoginCommandProps {
5
5
  readonly studioUrl: string;
6
6
  readonly open: boolean;
7
7
  }
8
+ type LoginOutcome = {
9
+ readonly signedIn: true;
10
+ } | {
11
+ readonly signedIn: false;
12
+ readonly message: string;
13
+ };
14
+ interface LoginCallback {
15
+ readonly code: string;
16
+ readonly tokenName: string | null;
17
+ readonly finish: (outcome: LoginOutcome) => Promise<void>;
18
+ }
8
19
  interface LoginCallbackListener {
9
20
  readonly port: number;
10
- readonly code: Promise<string>;
21
+ readonly callback: Promise<LoginCallback>;
11
22
  readonly close: () => void;
12
23
  }
24
+ interface LoginCallbackListenerProps {
25
+ readonly state: string;
26
+ readonly studioUrl: string;
27
+ readonly customer: string;
28
+ }
13
29
  /**
14
- * Receives the one-time code from Studio on 127.0.0.1. Only a callback carrying this session's state is accepted,
15
- * so another page cannot feed the CLI a code of its own.
30
+ * Receives Studio's answer on 127.0.0.1: the one-time code of an approval, or a denial. Only a callback carrying
31
+ * this session's state is accepted, so another page cannot feed the CLI a code of its own. The browser ends on a
32
+ * Studio page; its address is built from the CLI's own Studio URL, never from the request.
16
33
  */
17
- export declare const startLoginCallbackListener: (state: string) => Promise<LoginCallbackListener>;
34
+ export declare const startLoginCallbackListener: (props: LoginCallbackListenerProps) => Promise<LoginCallbackListener>;
18
35
  /**
19
- * Signs the CLI in to an SDK-builder workspace: the user approves in Studio, Studio returns a one-time code to this
20
- * CLI's loopback listener, and the CLI exchanges it with its PKCE verifier for an API token.
36
+ * Signs the CLI in to an SDK-builder workspace: the user names the token and approves in Studio, Studio returns a
37
+ * one-time code to this CLI's loopback listener, and the CLI exchanges it with its PKCE verifier for an API token.
38
+ * The browser waits on the callback meanwhile and ends on Studio's page for the outcome.
21
39
  */
22
40
  export declare const runLoginCommand: (props: LoginCommandProps, { logger }: CommandContext) => Promise<void>;
23
41
  export {};
@@ -1,4 +1,4 @@
1
- import{spawn as e}from"node:child_process";import{createHash as t,randomBytes as r}from"node:crypto";import{createServer as o}from"node:http";import{hostname as n}from"node:os";import{createApiBaseClient as i}from"#package/client";import{getServerCredentials as s,saveServerCredentials as a}from"../services/index.js";let l=(e,t)=>`<!doctype html><html><head><meta charset="utf-8"><title>${e}</title></head><body style="font-family:system-ui,sans-serif;display:grid;place-items:center;height:100vh;margin:0"><main style="text-align:center"><h1>${e}</h1><p>${t}</p></main></body></html>`;export const startLoginCallbackListener=async e=>{let t=null,r=new Promise((e,r)=>{t={resolve:e,reject:r}}),n=o((r,o)=>{let n=new URL(r.url??"/","http://127.0.0.1"),i=n.searchParams.get("code");if("/callback"!==n.pathname)return void o.writeHead(404).end();if(n.searchParams.get("state")!==e||!i){o.writeHead(400,{"content-type":"text/html"}),o.end(l("Sign-in failed","This response does not belong to the waiting CLI. Run phoenix login again.")),t?.reject(Error("The sign-in response did not match this CLI session. Run `phoenix login` again."));return}o.writeHead(200,{"content-type":"text/html"}),o.end(l("Phoenix CLI signed in","You can close this tab and return to the terminal.")),t?.resolve(i)}),i=await new Promise((e,t)=>{n.once("error",t),n.listen(0,"127.0.0.1",()=>{let r=n.address();null===r||"string"==typeof r?t(Error("The login callback listener did not get a port. Run `phoenix login` again.")):e(r.port)})}),s=setTimeout(()=>{t?.reject(Error("Nobody approved the sign-in within 5 minutes. Run `phoenix login` again."))},3e5);return{port:i,code:r,close:()=>{clearTimeout(s),n.close()}}};let c=async(e,t)=>{try{let{error:r,response:o}=await i(e).DELETE("/auth/token",{headers:{authorization:`Bearer ${t}`,"content-type":null}});return r&&401!==o.status?r.message:null}catch(e){return e instanceof Error?e.message:String(e)}};export const runLoginCommand=async(o,{logger:l})=>{let d=r(32).toString("base64url"),h=t("sha256").update(d).digest("base64url"),u=r(16).toString("base64url"),m=await startLoginCallbackListener(u);try{let t=new URL("/builder/cli-login",o.studioUrl);t.searchParams.set("customer",o.customer),t.searchParams.set("port",String(m.port)),t.searchParams.set("state",u),t.searchParams.set("challenge",h),l.step(`Signing in to workspace "${o.customer}" on ${o.serverUrl}`),l.info(`Approve the CLI in your browser:
1
+ import{spawn as e}from"node:child_process";import{createHash as t,randomBytes as r}from"node:crypto";import{createServer as o}from"node:http";import{hostname as n}from"node:os";import{createApiBaseClient as s}from"#package/client";import{getServerCredentials as i,saveServerCredentials as a}from"../services/index.js";let l={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"},c=(e,t)=>`<!doctype html><html><head><meta charset="utf-8"><title>${e}</title></head><body style="font-family:system-ui,sans-serif;display:grid;place-items:center;height:100vh;margin:0"><main style="text-align:center"><h1>${e}</h1><p>${t.replace(/[&<>"']/g,e=>l[e]??e)}</p></main></body></html>`,d=(e,t,r,o)=>new Promise(n=>{e.destroyed?n():(e.once("close",n),e.writeHead(t,{...r,connection:"close"}).end(o))});export const startLoginCallbackListener=async e=>{let{state:t,studioUrl:r,customer:n}=e,s=(e,t)=>{let o=new URL(e,r);return o.searchParams.set("customer",n),t&&o.searchParams.set("name",t),o.toString()},i=null,a=!1,l=new Promise((e,t)=>{i={resolve:e,reject:t}}),m=o((e,r)=>{let o=new URL(e.url??"/","http://127.0.0.1"),n=o.searchParams.get("code"),l="access_denied"===o.searchParams.get("error");if("/callback"!==o.pathname)return void r.writeHead(404).end();if(o.searchParams.get("state")!==t||a||!n&&!l){d(r,400,{"content-type":"text/html"},c("Sign-in failed","This response does not belong to the waiting CLI. Run phoenix login again.")),a||i?.reject(Error("The sign-in response did not match this CLI session. Run `phoenix login` again."));return}if(a=!0,l||!n)return void d(r,302,{location:s("/builder/cli-login/denied",null)}).then(()=>{i?.reject(Error("The sign-in was denied in Studio. Run `phoenix login` again to sign in."))});let m=o.searchParams.get("name");i?.resolve({code:n,tokenName:m,finish:e=>e.signedIn?d(r,302,{location:s("/builder/cli-login/done",m)}):d(r,200,{"content-type":"text/html"},c("Sign-in failed",e.message))})}),h=await new Promise((e,t)=>{m.once("error",t),m.listen(0,"127.0.0.1",()=>{let r=m.address();null===r||"string"==typeof r?t(Error("The login callback listener did not get a port. Run `phoenix login` again.")):e(r.port)})}),u=setTimeout(()=>{i?.reject(Error("Nobody approved the sign-in within 5 minutes. Run `phoenix login` again."))},3e5);return{port:h,callback:l,close:()=>{clearTimeout(u),m.close()}}};let m=async(e,t)=>{try{let{error:r,response:o}=await s(e).DELETE("/auth/token",{headers:{authorization:`Bearer ${t}`,"content-type":null}});return r&&401!==o.status?r.message:null}catch(e){return e instanceof Error?e.message:String(e)}},h=async(e,t)=>{let{data:r,error:o}=await s(e.serverUrl).POST("/auth/token",{body:t});if(o||!r)throw Error(`The server did not accept the sign-in: ${o?.message??"no response"}`);if(r.customer.slug!==e.customer)throw await m(e.serverUrl,r.token),Error(`The sign-in was approved in workspace "${r.customer.slug}", not "${e.customer}". Check --region or --studioUrl and run \`phoenix login ${e.customer}\` again.`);let n=await i(e.serverUrl),l=await a(e.serverUrl,{token:r.token,userId:r.userId,customerSlug:r.customer.slug,customerName:r.customer.name,studioUrl:e.studioUrl,createdAt:new Date().toISOString()}),c=n?await m(e.serverUrl,n.token):null;return{customerName:r.customer.name,path:l,revokeError:c}};export const runLoginCommand=async(o,{logger:s})=>{let i=r(32).toString("base64url"),a=t("sha256").update(i).digest("base64url"),l=r(16).toString("base64url"),c=n(),d=`phoenix CLI on ${c}`,m=await startLoginCallbackListener({state:l,studioUrl:o.studioUrl,customer:o.customer});try{let t=new URL("/builder/cli-login",o.studioUrl);t.searchParams.set("customer",o.customer),t.searchParams.set("port",String(m.port)),t.searchParams.set("state",l),t.searchParams.set("challenge",a),t.searchParams.set("name",d),t.searchParams.set("host",c),t.searchParams.set("started",String(Date.now())),s.step(`Signing in to workspace "${o.customer}" on ${o.serverUrl}`),s.info(`Approve the CLI in your browser:
2
2
 
3
3
  ${t.toString()}
4
- `),o.open&&(t=>{let[r,o]="darwin"===process.platform?["open",[t]]:"win32"===process.platform?["rundll32",["url.dll,FileProtocolHandler",t]]:["xdg-open",[t]];e(r,o,{stdio:"ignore",detached:!0}).on("error",()=>void 0).unref()})(t.toString());let r=await m.code,{data:p,error:g}=await i(o.serverUrl).POST("/auth/token",{body:{code:r,codeVerifier:d,port:m.port,name:`phoenix CLI on ${n()}`}});if(g||!p)throw Error(`The server did not accept the sign-in: ${g?.message??"no response"}`);if(p.customer.slug!==o.customer)throw await c(o.serverUrl,p.token),Error(`The sign-in was approved in workspace "${p.customer.slug}", not "${o.customer}". Check --region or --studioUrl and run \`phoenix login ${o.customer}\` again.`);let w=await s(o.serverUrl),y=await a(o.serverUrl,{token:p.token,userId:p.userId,customerSlug:p.customer.slug,customerName:p.customer.name,studioUrl:o.studioUrl,createdAt:new Date().toISOString()}),f=w?await c(o.serverUrl,w.token):null;f&&l.warn(`The server did not revoke the token this sign-in replaces (${f}). It stays valid on the server. Contact Phoenix support to revoke it.`),l.success(`Signed in to ${p.customer.name}. The token is stored in ${y}; revoke it with phoenix logout.`)}finally{m.close()}};
4
+ `),o.open&&(t=>{let[r,o]="darwin"===process.platform?["open",[t]]:"win32"===process.platform?["rundll32",["url.dll,FileProtocolHandler",t]]:["xdg-open",[t]];e(r,o,{stdio:"ignore",detached:!0}).on("error",()=>void 0).unref()})(t.toString());let r=await m.callback;try{let e=await h(o,{code:r.code,codeVerifier:i,port:m.port,name:d});e.revokeError&&s.warn(`The server did not revoke the token this sign-in replaces (${e.revokeError}). It stays valid on the server. Contact Phoenix support to revoke it.`),await r.finish({signedIn:!0}),s.success(`Signed in to ${e.customerName}. The token is stored in ${e.path}; revoke it with phoenix logout.`)}catch(e){throw await r.finish({signedIn:!1,message:e instanceof Error?e.message:String(e)}),e}}finally{m.close()}};
@@ -11,6 +11,7 @@ export { patchConsole, type PatchConsoleOptions } from './console-capture.js';
11
11
  export { WebhookTunnelMonitor, type TunnelEvent, type TunnelRequestEntry, type TunnelRequestSummary, type TunnelStatus, type TunnelStatusState, } from './webhook-tunnel-monitor.js';
12
12
  export { compileWithSwc, watchWithSwc } from './swc.js';
13
13
  export { STORY_DEV_MARKER_PATH, readActiveStoryDevMarker, type StoryDevMarker } from './story-dev-marker.js';
14
+ export { composeDefaultBanner } from './phx-banner-plugin.js';
14
15
  export { getViteBuildConfigs, getViteDevConfig, setViteLogSink, type ViteLogSink, type ViteLogSinkOptions, } from './vite-config.js';
15
16
  export { getServerCredentials, removeServerCredentials, saveServerCredentials } from './cli-credentials.js';
16
17
  export { createSourceArchive, extractSourceArchive } from './source-archive.js';
@@ -1 +1 @@
1
- export{PhoenixPubSub}from"./phoenix-pubsub.js";export{MockTemplateWatcher}from"./mock-template-watcher.js";export{ApplicationSpecProjectWatcher}from"./application-spec-project-watcher.js";export{ApplicationSpecStoryWatcher}from"./application-spec-story-watcher.js";export{LinguiWatcher}from"./lingui-watcher.js";export{loadMockTemplates,parseMockTemplateYaml}from"./spec-files.js";export{LOG_STREAMS,LogBus}from"./log-bus.js";export{createBusLogger}from"./bus-logger.js";export{patchConsole}from"./console-capture.js";export{WebhookTunnelMonitor}from"./webhook-tunnel-monitor.js";export{compileWithSwc,watchWithSwc}from"./swc.js";export{STORY_DEV_MARKER_PATH,readActiveStoryDevMarker}from"./story-dev-marker.js";export{getViteBuildConfigs,getViteDevConfig,setViteLogSink}from"./vite-config.js";export{getServerCredentials,removeServerCredentials,saveServerCredentials}from"./cli-credentials.js";export{createSourceArchive,extractSourceArchive}from"./source-archive.js";export{resolveSignedInDevelopmentEnvironment}from"./signed-in-development-environment.js";export{DEFAULT_PHOENIX_REGION,PHOENIX_REGIONS,PUBLIC_PHOENIX_REGIONS,getRegionServerUrl,getRegionStudioUrl}from"./phoenix-regions.js";
1
+ export{PhoenixPubSub}from"./phoenix-pubsub.js";export{MockTemplateWatcher}from"./mock-template-watcher.js";export{ApplicationSpecProjectWatcher}from"./application-spec-project-watcher.js";export{ApplicationSpecStoryWatcher}from"./application-spec-story-watcher.js";export{LinguiWatcher}from"./lingui-watcher.js";export{loadMockTemplates,parseMockTemplateYaml}from"./spec-files.js";export{LOG_STREAMS,LogBus}from"./log-bus.js";export{createBusLogger}from"./bus-logger.js";export{patchConsole}from"./console-capture.js";export{WebhookTunnelMonitor}from"./webhook-tunnel-monitor.js";export{compileWithSwc,watchWithSwc}from"./swc.js";export{STORY_DEV_MARKER_PATH,readActiveStoryDevMarker}from"./story-dev-marker.js";export{composeDefaultBanner}from"./phx-banner-plugin.js";export{getViteBuildConfigs,getViteDevConfig,setViteLogSink}from"./vite-config.js";export{getServerCredentials,removeServerCredentials,saveServerCredentials}from"./cli-credentials.js";export{createSourceArchive,extractSourceArchive}from"./source-archive.js";export{resolveSignedInDevelopmentEnvironment}from"./signed-in-development-environment.js";export{DEFAULT_PHOENIX_REGION,PHOENIX_REGIONS,PUBLIC_PHOENIX_REGIONS,getRegionServerUrl,getRegionStudioUrl}from"./phoenix-regions.js";
@@ -0,0 +1,32 @@
1
+ import type { Plugin } from 'vite';
2
+ export interface BannerBundleChunk {
3
+ readonly type: 'chunk';
4
+ readonly fileName: string;
5
+ code: string;
6
+ }
7
+ export interface BannerBundleAsset {
8
+ readonly type: 'asset';
9
+ readonly fileName: string;
10
+ source: string | Uint8Array;
11
+ }
12
+ export type BannerBundleItem = BannerBundleChunk | BannerBundleAsset;
13
+ export type BannerBundle = Record<string, BannerBundleItem>;
14
+ /** What the comment at the top of every frontend JavaScript file is composed from. */
15
+ export interface BannerCommentProps {
16
+ /** Version of the SDK running the build, named in the Salsita notice. */
17
+ readonly sdkVersion: string;
18
+ /** The caller's `--banner` text, or the default line when the option is not given. It is public in every bundle. */
19
+ readonly banner: string | undefined;
20
+ }
21
+ /** What `phoenix build` knows about the application on its own, for the banner line used when `--banner` is not given. */
22
+ export interface DefaultBannerProps {
23
+ /** `applicationVersion` of the application spec. */
24
+ readonly applicationVersion: string | undefined;
25
+ /** Commit checked out in the project; undefined when the build does not run in a git checkout. */
26
+ readonly commit: string | undefined;
27
+ }
28
+ export declare const composeDefaultBanner: (props: DefaultBannerProps) => string | undefined;
29
+ export declare const composeBannerComment: (props: BannerCommentProps) => string;
30
+ export declare const prependBannerToBundle: (bundle: BannerBundle, comment: string) => void;
31
+ /** Must be the last plugin of a build config: whatever prepends to a bundle after it takes byte 0 from the comment. */
32
+ export declare const phxBannerPlugin: (props: BannerCommentProps) => Plugin;
@@ -0,0 +1 @@
1
+ import{THIRD_PARTY_LICENSES_FILE_NAME as e}from"./phx-licenses-plugin.js";let n=/\r\n|[\n\r\u2028\u2029]/,o=e=>e.split(n).map(e=>e.trim()).filter(e=>e.length>0).map(e=>` * ${e.replaceAll("*/","*\\/")}`);export const composeDefaultBanner=e=>{let n=[...void 0===e.applicationVersion?[]:[`App version: ${e.applicationVersion}`],...void 0===e.commit?[]:[`Commit: ${e.commit}`]];return 0===n.length?void 0:n.join("; ")};export const composeBannerComment=n=>["/*!",...o(n.banner??""),...o(`Built with Salsita SDK ${n.sdkVersion}. Copyright (c) Salsita s.r.o. All rights reserved.`),...o(`Third-party licences: ${e}`)," */",""].join("\n");export const prependBannerToBundle=(e,n)=>{for(let o of Object.values(e))o.fileName.endsWith(".js")&&("chunk"===o.type?o.code=`${n}${o.code}`:"string"==typeof o.source?o.source=`${n}${o.source}`:o.source=Buffer.concat([Buffer.from(n),o.source]))};export const phxBannerPlugin=e=>{let n=composeBannerComment(e);return{name:"vite:phx-banner",enforce:"post",generateBundle(e,o){prependBannerToBundle(o,n)}}};
@@ -0,0 +1,55 @@
1
+ import type { Plugin } from 'vite';
2
+ /** Name of the licences file the frontend build writes next to its bundles; the banner comment points to it. */
3
+ export declare const THIRD_PARTY_LICENSES_FILE_NAME = "THIRD-PARTY-LICENSES.txt";
4
+ /** A third-party package whose code ended up in a frontend bundle, as it is listed in the licences file. */
5
+ export interface LicensedPackage {
6
+ readonly name: string;
7
+ readonly version: string;
8
+ /** Licence declared in the package's `package.json`; `UNKNOWN` when it declares none. */
9
+ readonly license: string;
10
+ /** Content of the package's licence file; undefined when the package ships none. */
11
+ readonly text: string | undefined;
12
+ }
13
+ /**
14
+ * Packages found so far by one `phoenix build`. Its frontend builds share one collection, so the licences file each
15
+ * of them writes into the common output folder is cumulative and the last one is complete.
16
+ */
17
+ export interface LicenseCollection {
18
+ /** Listed packages by `name@version`. */
19
+ readonly packages: Map<string, LicensedPackage>;
20
+ /** Resolution cache by module directory, so a package's files are read once per build command. */
21
+ readonly packageByDirectory: Map<string, LicensedPackage | undefined>;
22
+ }
23
+ export interface LicensesBundleChunk {
24
+ readonly type: 'chunk';
25
+ readonly modules: Readonly<Record<string, {
26
+ readonly renderedLength: number;
27
+ }>>;
28
+ }
29
+ export interface LicensesBundleAsset {
30
+ readonly type: 'asset';
31
+ }
32
+ export type LicensesBundle = Record<string, LicensesBundleChunk | LicensesBundleAsset>;
33
+ /**
34
+ * Finds the package a bundled module belongs to: the nearest named `package.json` above the module's real path that
35
+ * is still under a `node_modules` folder. Project source, workspace-linked packages and virtual modules have none.
36
+ */
37
+ export declare const resolvePackageForModule: (id: string) => LicensedPackage | undefined;
38
+ export declare const createLicenseCollection: () => LicenseCollection;
39
+ /**
40
+ * Adds the packages behind every module that left code or styles in the bundle; tree-shaken modules are not listed.
41
+ *
42
+ * Known gap: only Rollup modules are seen. What a stylesheet pulls in itself (CSS `@import`, Sass `@use`, files behind
43
+ * `url()`) is inlined or emitted by Vite's CSS handling without becoming a module, so those packages are not listed.
44
+ */
45
+ export declare const addBundlePackages: (bundle: LicensesBundle, collection: LicenseCollection) => void;
46
+ export declare const formatLicenses: (packages: readonly LicensedPackage[]) => string;
47
+ /**
48
+ * Lists the third-party packages bundled into the frontend output. Create one instance per `phoenix build` and add it
49
+ * to every frontend build config, so all of them contribute to the one file.
50
+ *
51
+ * Known gap: packages used only inside a web worker are not listed. Vite bundles a worker in a build of its own that
52
+ * runs `worker.plugins` instead of these; registering the plugin there would make every worker build emit a partial
53
+ * file under the same name.
54
+ */
55
+ export declare const phxLicensesPlugin: () => Plugin;
@@ -0,0 +1,8 @@
1
+ import{readFileSync as e,readdirSync as t,realpathSync as s}from"fs";import{dirname as n,isAbsolute as r,join as a}from"path";import{z as i}from"zod";export const THIRD_PARTY_LICENSES_FILE_NAME="THIRD-PARTY-LICENSES.txt";let c=/[\\/]node_modules[\\/]/,o=[/^license/i,/^licence/i,/^copying/i],l="-".repeat(80),p=/\.(css|scss|sass|less|styl|stylus|pcss|postcss|sss)(\?|$)/,u=i.object({name:i.string().min(1),version:i.string().min(1),license:i.unknown(),licenses:i.unknown()}),f=i.object({type:i.string().min(1)}),m=s=>{let n,r;try{n=e(a(s,"package.json"),"utf-8")}catch{return}try{r=JSON.parse(n)}catch{return}let c=u.safeParse(r);if(c.success)return{name:c.data.name,version:c.data.version,license:(e=>{if("string"==typeof e.license&&e.license.length>0)return e.license;let t=i.array(f).safeParse(Array.isArray(e.licenses)?e.licenses:[e.license]);return t.success&&0!==t.data.length?t.data.map(e=>e.type).join(" OR "):"UNKNOWN"})(c.data),text:(s=>{let n=t(s,{withFileTypes:!0}).filter(e=>e.isFile()).map(e=>e.name).sort();for(let t of o){let r=n.find(e=>t.test(e));if(void 0!==r)return e(a(s,r),"utf-8").trim()}})(s)}},d=(e,t)=>e.name!==t.name?e.name<t.name?-1:1:e.version.localeCompare(t.version,"en",{numeric:!0});export const resolvePackageForModule=e=>{let t=e.replace(/[?#].*$/,"");if(e.startsWith("\0")||!r(t))return;let a=n((e=>{try{return s(e)}catch{return e}})(t));for(;c.test(a);){let e=m(a);if(void 0!==e)return e;a=n(a)}};export const createLicenseCollection=()=>({packages:new Map,packageByDirectory:new Map});export const addBundlePackages=(e,t)=>{for(let s of Object.values(e))if("chunk"===s.type)for(let[e,r]of Object.entries(s.modules)){if(0===r.renderedLength&&!p.test(e))continue;let s=n(e);t.packageByDirectory.has(s)||t.packageByDirectory.set(s,resolvePackageForModule(e));let a=t.packageByDirectory.get(s);if(void 0===a)continue;let i=`${a.name}@${a.version}`;t.packages.has(i)||t.packages.set(i,a)}};export const formatLicenses=e=>["Third-party software bundled into the JavaScript files in this folder, with the licence of each package.",...[...e].sort(d).map(e=>{let t=`${e.name} ${e.version}
2
+ License: ${e.license}`;return void 0===e.text?t:`${t}
3
+
4
+ ${e.text}`})].join(`
5
+
6
+ ${l}
7
+
8
+ `).concat("\n");export const phxLicensesPlugin=()=>{let e=createLicenseCollection();return{name:"vite:phx-licenses",enforce:"post",generateBundle(t,s){addBundlePackages(s,e),this.emitFile({type:"asset",fileName:"THIRD-PARTY-LICENSES.txt",source:formatLicenses([...e.packages.values()])})}}};
@@ -17,4 +17,6 @@ export declare const getViteDevConfig: (options?: {
17
17
  export declare const getViteBuildConfigs: (options: {
18
18
  enableEditor?: boolean;
19
19
  templateFiles: readonly string[];
20
+ sdkVersion: string;
21
+ banner: string | undefined;
20
22
  }) => Promise<InlineConfig[]>;
@@ -1 +1 @@
1
- import{lingui as e}from"@lingui/vite-plugin";import i from"@vitejs/plugin-react";import{mkdir as t,readFile as n,writeFile as r}from"fs/promises";import o from"lodash-es/camelCase.js";import{createRequire as a}from"node:module";import{dirname as s,resolve as m}from"path";import{createLogger as l}from"vite";import d from"vite-plugin-css-injected-by-js";import{isUsingLocalSdk as f}from"../utils/package-info.js";import{loadFrontendConfigModule as c}from"./frontend-config.js";import{phxCspNoncePlugin as p}from"./phx-csp-nonce-plugin.js";import{phxGlobalIsolationPlugin as g}from"./phx-global-isolation-plugin.js";let u=null;export const setViteLogSink=(e,i={})=>{u={sink:e,echo:i.echo??!1}};let h=["react","react-dom","three","@react-three/fiber","@react-three/drei","@react-three/postprocessing","its-fine","@emotion/react","@emotion/styled","@mui/material","@mui/system","@tanstack/react-query","rxjs"],S=e=>{let i=e.DEPLOYMENT_ID;if("string"!=typeof i)throw Error("DEPLOYMENT_ID is required in runtime config");return i};export const getViteDevConfig=async t=>{var n,r;let o,d,p=f(),y=c(),O=y.readRuntimeConfig(),b=y.frontendBuildConfig,D=Object.fromEntries((t?.templateFiles??[]).map(e=>[e,`.phoenix/${e}`]));return{...u&&{customLogger:(n=u.sink,r=u.echo,o=l(),{get hasWarned(){return o.hasWarned},info:(e,i)=>{n("info",e),r&&o.info(e,i)},warn:(e,i)=>{n("warn",e),r&&o.warn(e,i)},warnOnce:(e,i)=>{n("warn",e),r&&o.warnOnce(e,i)},error:(e,i)=>{n("error",e),r&&o.error(e,i)},clearScreen:e=>{r&&o.clearScreen(e)},hasErrorLogged:e=>o.hasErrorLogged(e)}),...u.echo?{}:{clearScreen:!1}},mode:"development",optimizeDeps:{exclude:["@swc/wasm-web"],...p&&{entries:["index.html","admin.html",...t?.templateFiles??[]],force:!0}},...p&&{resolve:{alias:(d=a(m(process.cwd(),"package.json")),h.flatMap(e=>{try{let i=s(d.resolve(`${e}/package.json`));return[{find:RegExp(`^${e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}(/|$)`),replacement:`${i}$1`}]}catch{return[]}}))}},plugins:[i({babel:{plugins:["macros"]}}),e(),{name:"vite:runtime-config",apply:(e,i)=>"build"===i.command||"development"===i.mode,config:e=>{let i=y.readRuntimeConfig(),t=y.frontendBuildConfig;return{...e,define:{...e.define,"phx.frontendEnv":JSON.stringify(i),"globalThis.PHX_SCHEMA_VALIDATOR":JSON.stringify(t.jsonSchemaValidator),"globalThis.PHX_GLTF_DECODER":JSON.stringify(t.gltfDecoder)}}}},g({getDeploymentId:()=>S(y.readRuntimeConfig())})],define:{"process.env":{},"import.meta.env.DEV":!0,"import.meta.env.PROD":!1,"phx.frontendEnv":JSON.stringify(O),"globalThis.PHX_SCHEMA_VALIDATOR":JSON.stringify(b.jsonSchemaValidator),"globalThis.PHX_GLTF_DECODER":JSON.stringify(b.gltfDecoder)},build:{rollupOptions:{input:{index:".phoenix/index.html",admin:".phoenix/admin.html",...D}}},root:m(process.cwd(),"./.phoenix"),server:{watch:{ignored:["./**/*.{ts,tsx}","./.swc/**","../spec/**","../src/**"]}}}};export const getViteBuildConfigs=async a=>{let l=c(),f=l.readRuntimeConfig(),u=l.frontendBuildConfig,h=[{name:"salsita-configurator",entryFile:"./configurator.ts",mainScriptPath:"./configurator.ts",htmlFiles:["./index.html",...{templateFiles:a.templateFiles}.templateFiles],main:!0,format:"iife"},{name:"salsita-admin",entryFile:"./admin.ts",mainScriptPath:"./admin.ts",htmlFiles:["./admin.html"],main:!1,format:"iife"},{name:"salsita-embedded-admin",entryFile:"./embedded-admin.ts",mainScriptPath:"./embedded-admin.ts",htmlFiles:[],main:!1,format:"es"},{name:"salsita-embedded-configurator",entryFile:"./embedded-configurator.ts",mainScriptPath:"./embedded-configurator.ts",htmlFiles:[],main:!1,format:"es"}].map(c=>({plugins:[i({babel:{plugins:["macros"]}}),e(),d({useStrictCSP:!0}),p(),g({getDeploymentId:()=>S(l.readRuntimeConfig())}),{name:"vite:lib-index",writeBundle:async()=>{if(!c.htmlFiles.length)return;let e="iife"===c.format?`${c.name}.iife.js`:`${c.name}.js`;for(let i of c.htmlFiles){if(c.main&&a.enableEditor&&"./index.html"===i)continue;let o=m("./.phoenix",i),l=m("./.phoenix/dist/frontend",i);await t(s(l),{recursive:!0});let d=(await n(o,"utf-8")).replace(c.mainScriptPath,e);await r(l,d,"utf-8")}}}],build:{outDir:"./dist/frontend",chunkSizeWarningLimit:4e3,lib:{entry:c.entryFile,name:o(c.name),formats:[c.format],fileName:()=>"iife"===c.format?`${c.name}.iife.js`:`${c.name}.js`},rollupOptions:{external:[],output:{assetFileNames:`${c.name}.[ext]`}},minify:!0,emptyOutDir:!1},root:m(process.cwd(),"./.phoenix"),define:{"process.env":{},"process.env.NODE_ENV":JSON.stringify("production"),"phx.frontendEnv":JSON.stringify(f),"globalThis.PHX_SCHEMA_VALIDATOR":JSON.stringify(u.jsonSchemaValidator),"globalThis.PHX_GLTF_DECODER":JSON.stringify(u.gltfDecoder)}}));return a.enableEditor?[...h,{plugins:[i({babel:{plugins:["macros"]}}),e(),d({useStrictCSP:!0}),p()],build:{outDir:"./dist/frontend",chunkSizeWarningLimit:4e3,rollupOptions:{external:[]},minify:!0,emptyOutDir:!1},root:m(process.cwd(),"./.phoenix"),define:{"process.env":{},"process.env.NODE_ENV":JSON.stringify("production"),"phx.frontendEnv":JSON.stringify(f),"globalThis.PHX_SCHEMA_VALIDATOR":JSON.stringify(u.jsonSchemaValidator),"globalThis.PHX_GLTF_DECODER":JSON.stringify(u.gltfDecoder)}}]:h};
1
+ import{lingui as e}from"@lingui/vite-plugin";import i from"@vitejs/plugin-react";import{mkdir as t,readFile as n,writeFile as r}from"fs/promises";import o from"lodash-es/camelCase.js";import{createRequire as a}from"node:module";import{dirname as s,resolve as m}from"path";import{createLogger as l}from"vite";import d from"vite-plugin-css-injected-by-js";import{isUsingLocalSdk as f}from"../utils/package-info.js";import{loadFrontendConfigModule as p}from"./frontend-config.js";import{phxBannerPlugin as c}from"./phx-banner-plugin.js";import{phxCspNoncePlugin as g}from"./phx-csp-nonce-plugin.js";import{phxGlobalIsolationPlugin as u}from"./phx-global-isolation-plugin.js";import{phxLicensesPlugin as h}from"./phx-licenses-plugin.js";let S=null;export const setViteLogSink=(e,i={})=>{S={sink:e,echo:i.echo??!1}};let b=["react","react-dom","three","@react-three/fiber","@react-three/drei","@react-three/postprocessing","its-fine","@emotion/react","@emotion/styled","@mui/material","@mui/system","@tanstack/react-query","rxjs"],y=e=>{let i=e.DEPLOYMENT_ID;if("string"!=typeof i)throw Error("DEPLOYMENT_ID is required in runtime config");return i};export const getViteDevConfig=async t=>{var n,r;let o,d,c=f(),g=p(),h=g.readRuntimeConfig(),O=g.frontendBuildConfig,x=Object.fromEntries((t?.templateFiles??[]).map(e=>[e,`.phoenix/${e}`]));return{...S&&{customLogger:(n=S.sink,r=S.echo,o=l(),{get hasWarned(){return o.hasWarned},info:(e,i)=>{n("info",e),r&&o.info(e,i)},warn:(e,i)=>{n("warn",e),r&&o.warn(e,i)},warnOnce:(e,i)=>{n("warn",e),r&&o.warnOnce(e,i)},error:(e,i)=>{n("error",e),r&&o.error(e,i)},clearScreen:e=>{r&&o.clearScreen(e)},hasErrorLogged:e=>o.hasErrorLogged(e)}),...S.echo?{}:{clearScreen:!1}},mode:"development",optimizeDeps:{exclude:["@swc/wasm-web"],...c&&{entries:["index.html","admin.html",...t?.templateFiles??[]],force:!0}},...c&&{resolve:{alias:(d=a(m(process.cwd(),"package.json")),b.flatMap(e=>{try{let i=s(d.resolve(`${e}/package.json`));return[{find:RegExp(`^${e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}(/|$)`),replacement:`${i}$1`}]}catch{return[]}}))}},plugins:[i({babel:{plugins:["macros"]}}),e(),{name:"vite:runtime-config",apply:(e,i)=>"build"===i.command||"development"===i.mode,config:e=>{let i=g.readRuntimeConfig(),t=g.frontendBuildConfig;return{...e,define:{...e.define,"phx.frontendEnv":JSON.stringify(i),"globalThis.PHX_SCHEMA_VALIDATOR":JSON.stringify(t.jsonSchemaValidator),"globalThis.PHX_GLTF_DECODER":JSON.stringify(t.gltfDecoder)}}}},u({getDeploymentId:()=>y(g.readRuntimeConfig())})],define:{"process.env":{},"import.meta.env.DEV":!0,"import.meta.env.PROD":!1,"phx.frontendEnv":JSON.stringify(h),"globalThis.PHX_SCHEMA_VALIDATOR":JSON.stringify(O.jsonSchemaValidator),"globalThis.PHX_GLTF_DECODER":JSON.stringify(O.gltfDecoder)},build:{rollupOptions:{input:{index:".phoenix/index.html",admin:".phoenix/admin.html",...x}}},root:m(process.cwd(),"./.phoenix"),server:{watch:{ignored:["./**/*.{ts,tsx}","./.swc/**","../spec/**","../src/**"]}}}};export const getViteBuildConfigs=async a=>{let l=p(),f=l.readRuntimeConfig(),S=l.frontendBuildConfig,b=h(),O=c({sdkVersion:a.sdkVersion,banner:a.banner}),x=[{name:"salsita-configurator",entryFile:"./configurator.ts",mainScriptPath:"./configurator.ts",htmlFiles:["./index.html",...{templateFiles:a.templateFiles}.templateFiles],main:!0,format:"iife"},{name:"salsita-admin",entryFile:"./admin.ts",mainScriptPath:"./admin.ts",htmlFiles:["./admin.html"],main:!1,format:"iife"},{name:"salsita-embedded-admin",entryFile:"./embedded-admin.ts",mainScriptPath:"./embedded-admin.ts",htmlFiles:[],main:!1,format:"es"},{name:"salsita-embedded-configurator",entryFile:"./embedded-configurator.ts",mainScriptPath:"./embedded-configurator.ts",htmlFiles:[],main:!1,format:"es"}].map(p=>({plugins:[i({babel:{plugins:["macros"]}}),e(),d({useStrictCSP:!0}),g(),u({getDeploymentId:()=>y(l.readRuntimeConfig())}),{name:"vite:lib-index",writeBundle:async()=>{if(!p.htmlFiles.length)return;let e="iife"===p.format?`${p.name}.iife.js`:`${p.name}.js`;for(let i of p.htmlFiles){if(p.main&&a.enableEditor&&"./index.html"===i)continue;let o=m("./.phoenix",i),l=m("./.phoenix/dist/frontend",i);await t(s(l),{recursive:!0});let d=(await n(o,"utf-8")).replace(p.mainScriptPath,e);await r(l,d,"utf-8")}}},b,O],build:{outDir:"./dist/frontend",chunkSizeWarningLimit:4e3,lib:{entry:p.entryFile,name:o(p.name),formats:[p.format],fileName:()=>"iife"===p.format?`${p.name}.iife.js`:`${p.name}.js`},rollupOptions:{external:[],output:{assetFileNames:`${p.name}.[ext]`}},minify:!0,emptyOutDir:!1},root:m(process.cwd(),"./.phoenix"),define:{"process.env":{},"process.env.NODE_ENV":JSON.stringify("production"),"phx.frontendEnv":JSON.stringify(f),"globalThis.PHX_SCHEMA_VALIDATOR":JSON.stringify(S.jsonSchemaValidator),"globalThis.PHX_GLTF_DECODER":JSON.stringify(S.gltfDecoder)}}));return a.enableEditor?[...x,{plugins:[i({babel:{plugins:["macros"]}}),e(),d({useStrictCSP:!0}),g(),b,O],build:{outDir:"./dist/frontend",chunkSizeWarningLimit:4e3,rollupOptions:{external:[]},minify:!0,emptyOutDir:!1},root:m(process.cwd(),"./.phoenix"),define:{"process.env":{},"process.env.NODE_ENV":JSON.stringify("production"),"phx.frontendEnv":JSON.stringify(f),"globalThis.PHX_SCHEMA_VALIDATOR":JSON.stringify(S.jsonSchemaValidator),"globalThis.PHX_GLTF_DECODER":JSON.stringify(S.gltfDecoder)}}]:x};
@@ -7,5 +7,6 @@ export { FileLogger } from './file-logger.js';
7
7
  export { type Logger, type CreateLoggerOptions, createLogger } from './logger.js';
8
8
  export { addWorkspaceOverrides, ensureWorkspaceInstallSettings, getLocalOverrides, getSdkPackages, getWorkspaceDir, hasLocalOverrides, readPackageJson, removeWorkspaceOverrides, } from './package-info.js';
9
9
  export { toPascalCase } from './pascal-case.js';
10
+ export { readGitCommit } from './read-git-commit.js';
10
11
  export { type RunCommandProps, runCommand } from './run-command.js';
11
12
  export { type FileUploadInfo, type UploadLinksData, uploadFilesWithPresignedUrls, readUploadLinks, } from './upload-utils.js';
@@ -1 +1 @@
1
- export{askQuestion}from"./ask-question.js";export{checkBundleSizeBudgets}from"./check-bundle-size-budgets.js";export{formatEnvContent,formatEnvLine}from"./env-file.js";export{parseChangelog,getLatestVersion,getVersion,paginateVersions,getVersionNumbers}from"./changelog-parser.js";export{createDirectoryFromYaml,exists,scanFolderForFiles,syncChangedFiles,syncExistingFolders,validateFolders,writeFileIfNotExists,writeFilesIfNotExists,writeRawFiles}from"./file-system.js";export{FileLogger}from"./file-logger.js";export{createLogger}from"./logger.js";export{addWorkspaceOverrides,ensureWorkspaceInstallSettings,getLocalOverrides,getSdkPackages,getWorkspaceDir,hasLocalOverrides,readPackageJson,removeWorkspaceOverrides}from"./package-info.js";export{toPascalCase}from"./pascal-case.js";export{runCommand}from"./run-command.js";export{uploadFilesWithPresignedUrls,readUploadLinks}from"./upload-utils.js";
1
+ export{askQuestion}from"./ask-question.js";export{checkBundleSizeBudgets}from"./check-bundle-size-budgets.js";export{formatEnvContent,formatEnvLine}from"./env-file.js";export{parseChangelog,getLatestVersion,getVersion,paginateVersions,getVersionNumbers}from"./changelog-parser.js";export{createDirectoryFromYaml,exists,scanFolderForFiles,syncChangedFiles,syncExistingFolders,validateFolders,writeFileIfNotExists,writeFilesIfNotExists,writeRawFiles}from"./file-system.js";export{FileLogger}from"./file-logger.js";export{createLogger}from"./logger.js";export{addWorkspaceOverrides,ensureWorkspaceInstallSettings,getLocalOverrides,getSdkPackages,getWorkspaceDir,hasLocalOverrides,readPackageJson,removeWorkspaceOverrides}from"./package-info.js";export{toPascalCase}from"./pascal-case.js";export{readGitCommit}from"./read-git-commit.js";export{runCommand}from"./run-command.js";export{uploadFilesWithPresignedUrls,readUploadLinks}from"./upload-utils.js";
@@ -0,0 +1,14 @@
1
+ export interface ReadGitCommitProps {
2
+ readonly directory: string;
3
+ /**
4
+ * A file of the project, relative to `directory`, that its repository tracks. git looks for a repository in every
5
+ * parent folder, so sources unpacked inside an unrelated checkout would otherwise report that checkout's commit.
6
+ */
7
+ readonly trackedFile: string;
8
+ }
9
+ /**
10
+ * Full SHA of the commit checked out in the project's own repository. Undefined when git cannot read one: no
11
+ * checkout, git missing or refusing the repository, no commit yet, or the project is not tracked by the repository
12
+ * around it. That is normal for a build from uploaded sources.
13
+ */
14
+ export declare const readGitCommit: (props: ReadGitCommitProps) => Promise<string | undefined>;
@@ -0,0 +1 @@
1
+ import{execFile as t}from"node:child_process";import{promisify as e}from"node:util";let r=e(t),i=/^[0-9a-f]{40,64}$/;export const readGitCommit=async t=>{let e={cwd:t.directory,timeout:5e3,windowsHide:!0};try{await r("git",["ls-files","--error-unmatch","--",t.trackedFile],e);let{stdout:o}=await r("git",["rev-parse","HEAD"],e),a=o.trim();return i.test(a)?a:void 0}catch{return}};
@@ -404,6 +404,8 @@ export declare function createApplicationRuntimeSlices<State extends Application
404
404
  getActiveConfigurationId: () => string;
405
405
  getFactory: () => import("#package/domain").AnyDomainFactory;
406
406
  getDomainExpressions: () => import("#package/domain").AnyExpressions;
407
+ getActionDefinitions: () => Readonly<Record<string, import("#package/domain").ActionDefinition>>;
408
+ getActionImplementations: () => Readonly<Record<string, import("#package/domain").AnyActionImplementation>>;
407
409
  getModelVersion: () => number;
408
410
  getAliasMap: () => import("#package/domain").AnyAliasMap;
409
411
  getAllowedRootEntityTypes: () => import("#package/domain").AnyAllowedRootEntityTypes;
@@ -444,6 +446,7 @@ export declare function createApplicationRuntimeSlices<State extends Application
444
446
  getNavigationSystem: () => import("../../../domain/domain-context/navigation-system/navigation-system.js").AnyNavigationSystem;
445
447
  getUndoSystem: () => import("../../../domain/domain-context/undo-system.js").AnyUndoSystem;
446
448
  getRecipeCookerSystem: () => import("#package/domain").AnyRecipeCookerSystem;
449
+ getActionSystem: () => import("#package/domain").AnyActionSystem;
447
450
  getBlenderProjections: () => import("#package/domain").AnyBlenderProjections;
448
451
  getExpressionContext: () => import("#package/domain").AnyExpressionContext;
449
452
  getDomainSystems: () => import("#package/domain").AnyDomainContextSystems;
@@ -510,6 +513,8 @@ export declare function createApplicationRuntimeSlices<State extends Application
510
513
  getActiveConfigurationId: () => string;
511
514
  getFactory: () => import("#package/domain").AnyDomainFactory;
512
515
  getDomainExpressions: () => import("#package/domain").AnyExpressions;
516
+ getActionDefinitions: () => Readonly<Record<string, import("#package/domain").ActionDefinition>>;
517
+ getActionImplementations: () => Readonly<Record<string, import("#package/domain").AnyActionImplementation>>;
513
518
  getModelVersion: () => number;
514
519
  getAliasMap: () => import("#package/domain").AnyAliasMap;
515
520
  getAllowedRootEntityTypes: () => import("#package/domain").AnyAllowedRootEntityTypes;
@@ -550,6 +555,7 @@ export declare function createApplicationRuntimeSlices<State extends Application
550
555
  getNavigationSystem: () => import("../../../domain/domain-context/navigation-system/navigation-system.js").AnyNavigationSystem;
551
556
  getUndoSystem: () => import("../../../domain/domain-context/undo-system.js").AnyUndoSystem;
552
557
  getRecipeCookerSystem: () => import("#package/domain").AnyRecipeCookerSystem;
558
+ getActionSystem: () => import("#package/domain").AnyActionSystem;
553
559
  getBlenderProjections: () => import("#package/domain").AnyBlenderProjections;
554
560
  getExpressionContext: () => import("#package/domain").AnyExpressionContext;
555
561
  getDomainSystems: () => import("#package/domain").AnyDomainContextSystems;
@@ -1058,6 +1064,8 @@ declare const createApplicationRuntimeImpl: <EXTENSIONS extends readonly AnyAppl
1058
1064
  getActiveConfigurationId: () => string;
1059
1065
  getFactory: () => import("#package/domain").AnyDomainFactory;
1060
1066
  getDomainExpressions: () => import("#package/domain").AnyExpressions;
1067
+ getActionDefinitions: () => Readonly<Record<string, import("#package/domain").ActionDefinition>>;
1068
+ getActionImplementations: () => Readonly<Record<string, import("#package/domain").AnyActionImplementation>>;
1061
1069
  getModelVersion: () => number;
1062
1070
  getAliasMap: () => import("#package/domain").AnyAliasMap;
1063
1071
  getAllowedRootEntityTypes: () => import("#package/domain").AnyAllowedRootEntityTypes;
@@ -1098,6 +1106,7 @@ declare const createApplicationRuntimeImpl: <EXTENSIONS extends readonly AnyAppl
1098
1106
  getNavigationSystem: () => import("../../../domain/domain-context/navigation-system/navigation-system.js").AnyNavigationSystem;
1099
1107
  getUndoSystem: () => import("../../../domain/domain-context/undo-system.js").AnyUndoSystem;
1100
1108
  getRecipeCookerSystem: () => import("#package/domain").AnyRecipeCookerSystem;
1109
+ getActionSystem: () => import("#package/domain").AnyActionSystem;
1101
1110
  getBlenderProjections: () => import("#package/domain").AnyBlenderProjections;
1102
1111
  getExpressionContext: () => import("#package/domain").AnyExpressionContext;
1103
1112
  getDomainSystems: () => import("#package/domain").AnyDomainContextSystems;
@@ -1164,6 +1173,8 @@ declare const createApplicationRuntimeImpl: <EXTENSIONS extends readonly AnyAppl
1164
1173
  getActiveConfigurationId: () => string;
1165
1174
  getFactory: () => import("#package/domain").AnyDomainFactory;
1166
1175
  getDomainExpressions: () => import("#package/domain").AnyExpressions;
1176
+ getActionDefinitions: () => Readonly<Record<string, import("#package/domain").ActionDefinition>>;
1177
+ getActionImplementations: () => Readonly<Record<string, import("#package/domain").AnyActionImplementation>>;
1167
1178
  getModelVersion: () => number;
1168
1179
  getAliasMap: () => import("#package/domain").AnyAliasMap;
1169
1180
  getAllowedRootEntityTypes: () => import("#package/domain").AnyAllowedRootEntityTypes;
@@ -1204,6 +1215,7 @@ declare const createApplicationRuntimeImpl: <EXTENSIONS extends readonly AnyAppl
1204
1215
  getNavigationSystem: () => import("../../../domain/domain-context/navigation-system/navigation-system.js").AnyNavigationSystem;
1205
1216
  getUndoSystem: () => import("../../../domain/domain-context/undo-system.js").AnyUndoSystem;
1206
1217
  getRecipeCookerSystem: () => import("#package/domain").AnyRecipeCookerSystem;
1218
+ getActionSystem: () => import("#package/domain").AnyActionSystem;
1207
1219
  getBlenderProjections: () => import("#package/domain").AnyBlenderProjections;
1208
1220
  getExpressionContext: () => import("#package/domain").AnyExpressionContext;
1209
1221
  getDomainSystems: () => import("#package/domain").AnyDomainContextSystems;
@@ -60,6 +60,8 @@ export declare const useDomainContextSelectors: () => {
60
60
  getActiveConfigurationId: () => string;
61
61
  getFactory: () => import("#package/domain").AnyDomainFactory;
62
62
  getDomainExpressions: () => import("#package/domain").AnyExpressions;
63
+ getActionDefinitions: () => Readonly<Record<string, import("#package/domain").ActionDefinition>>;
64
+ getActionImplementations: () => Readonly<Record<string, import("#package/domain").AnyActionImplementation>>;
63
65
  getModelVersion: () => number;
64
66
  getAliasMap: () => import("#package/domain").AnyAliasMap;
65
67
  getAllowedRootEntityTypes: () => import("#package/domain").AnyAllowedRootEntityTypes;
@@ -100,6 +102,7 @@ export declare const useDomainContextSelectors: () => {
100
102
  getNavigationSystem: () => import("../../../domain/domain-context/navigation-system/navigation-system.js").AnyNavigationSystem;
101
103
  getUndoSystem: () => import("../../../domain/domain-context/undo-system.js").AnyUndoSystem;
102
104
  getRecipeCookerSystem: () => import("#package/domain").AnyRecipeCookerSystem;
105
+ getActionSystem: () => import("#package/domain").AnyActionSystem;
103
106
  getBlenderProjections: () => import("#package/domain").AnyBlenderProjections;
104
107
  getExpressionContext: () => import("#package/domain").AnyExpressionContext;
105
108
  getDomainSystems: () => import("#package/domain").AnyDomainContextSystems;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salsita-ai/sdk",
3
- "version": "0.50.0-develop.514d19d58c",
3
+ "version": "0.50.0-develop.7d4723b0a5",
4
4
  "description": "TypeScript SDK and CLI for building, running, and deploying Phoenix configurator applications.",
5
5
  "keywords": [
6
6
  "salsita",
@@ -295,8 +295,8 @@
295
295
  "yaml": "2.9.0",
296
296
  "yargs": "17.7.2",
297
297
  "zod": "3.25.76",
298
- "@salsita-ai/common": "0.50.0-develop.514d19d58c",
299
- "@salsita-ai/contracts": "0.50.0-develop.514d19d58c"
298
+ "@salsita-ai/contracts": "0.50.0-develop.7d4723b0a5",
299
+ "@salsita-ai/common": "0.50.0-develop.7d4723b0a5"
300
300
  },
301
301
  "devDependencies": {
302
302
  "@clerk/clerk-react": "5.61.8",
@@ -1 +0,0 @@
1
- import{browserDetailsTool as e}from"@salsita-ai/contracts/assistant";let t=e=>{let t=[];for(let i=0;i<e.length;i+=64)t.push(e.slice(i,i+64));return t};export const browserDetailsClientTool=e.client(()=>{let e=(()=>{let e=[`User agent: ${navigator.userAgent}`,`Viewport: ${window.innerWidth} x ${window.innerHeight} px`,`Screen: ${window.screen.width} x ${window.screen.height} px`,`Device pixel ratio: ${window.devicePixelRatio}`,`Language: ${navigator.language}`,`Time zone: ${Intl.DateTimeFormat().resolvedOptions().timeZone}`].flatMap(t),i=document.createElement("canvas");i.width=720,i.height=48+26*e.length;let n=i.getContext("2d");if(!n)throw Error("The browser details capture failed because the canvas has no 2D context. Retry in another browser.");return n.fillStyle="#ffffff",n.fillRect(0,0,i.width,i.height),n.fillStyle="#111111",n.font="16px monospace",n.textBaseline="top",e.forEach((e,t)=>n.fillText(e,24,24+26*t)),i.toDataURL("image/png")})();return[{type:"image",source:{type:"data",value:e.slice(e.indexOf(",")+1),mimeType:"image/png"}}]});
@@ -1 +0,0 @@
1
- import{AssistantGraphQLToolInput as e,applyTool as t,queryTool as o}from"@salsita-ai/contracts/assistant";let a=e=>({results:[],committed:!1,message:e}),s=e=>JSON.parse(JSON.stringify(e)),i=t=>{let o=e.safeParse(t);if(!o.success){let e=o.error.issues.map(e=>`${e.path.join(".")}: ${e.message}`).join("; ");return{ok:!1,refusal:a(`Nothing was run, because the tool input is not a list of operations (${e}). Send { "operations": [{ "query": "<one GraphQL operation>" }] } with at least one item.`)}}return{ok:!0,operations:o.data.operations.map(({query:e,variables:t,operationName:o})=>({document:e,variables:t??void 0,operationName:o??void 0}))}};export const queryClientTool=o.client(async(e,{context:t})=>{let o=i(e);return o.ok?t.storeSystem.isInitialized?s({results:(await t.graphQLSystem.prepare(t.storeSystem.stateSlices.committed.value.state,o.operations)).map(({result:e})=>e),committed:!1}):a("Nothing was run, because the configuration is not loaded yet, so there is no state to query. Call the tool again once the configurator has finished loading."):o.refusal});export const applyClientTool=t.client(async(e,{context:t})=>{if("owner"!==t.getActiveMode())return a("Nothing was run, because changes cannot be applied in this session: the user may view this configuration but not change it. The query tool still works here, including mutations, to show what a change would do without keeping it.");let o=i(e);return o.ok?s(await t.graphQLSystem.execute(o.operations)):o.refusal});