@xova/matrix 0.1.0 → 0.1.1
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/README.md +28 -3
- package/README.zh-CN.md +28 -3
- package/dist/config.js +1 -1
- package/dist/defaults.d.ts +3 -0
- package/dist/defaults.js +1 -1
- package/dist/exec.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/plan.js +1 -1
- package/dist/schema.d.ts +2 -0
- package/dist/schema.js +1 -1
- package/dist/types.d.ts +5 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -121,12 +121,37 @@ export default defineMatrixConfig({
|
|
|
121
121
|
|
|
122
122
|
`defineMatrixEnv()` is syntax sugar for the c12-compatible `$env.<environment>.env` shape. The raw shape remains supported.
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
Ordinary application variables are merged from low to high precedence:
|
|
125
125
|
|
|
126
126
|
```text
|
|
127
|
-
global env/$env < product env/$env < .env layers < process.env
|
|
127
|
+
global env/$env < product env/$env < .env layers < process.env
|
|
128
128
|
```
|
|
129
129
|
|
|
130
|
+
Product identity overrides are resolved from the merged environment before suffixes are applied:
|
|
131
|
+
|
|
132
|
+
```text
|
|
133
|
+
product identity < variant identity < merged identity override < environment suffix
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
`MATRIX_PRODUCT_NAME`, `MATRIX_PRODUCT_SLUG`, and `MATRIX_PRODUCT_APP_ID` may provide identity overrides through the environment. The final values, including suffixes, are written back to both task metadata and the corresponding `MATRIX_PRODUCT_*` variables. `MATRIX_PRODUCT_ID`, `MATRIX_PRODUCT_KEY`, execution-context variables, and `NODE_ENV` are generated by Matrix and cannot be overridden by the environment.
|
|
137
|
+
|
|
138
|
+
Matrix injects the following execution-context variables into every child process:
|
|
139
|
+
|
|
140
|
+
```text
|
|
141
|
+
MATRIX_ENV_NAME
|
|
142
|
+
MATRIX_TARGET
|
|
143
|
+
MATRIX_PRODUCT_KEY
|
|
144
|
+
MATRIX_PRODUCT_ID
|
|
145
|
+
MATRIX_PRODUCT_NAME
|
|
146
|
+
MATRIX_PRODUCT_SLUG
|
|
147
|
+
MATRIX_PRODUCT_APP_ID
|
|
148
|
+
MATRIX_VARIANT
|
|
149
|
+
MATRIX_PROJECT
|
|
150
|
+
NODE_ENV
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
`MATRIX_ENV_NAME` is the selected Matrix configuration environment and may be a custom name such as `staging` or `qa`. `NODE_ENV` describes the target process mode: `dev` uses `development`, while `build` and `preview` use `production`. Therefore a staging build normally receives `MATRIX_ENV_NAME=staging` and `NODE_ENV=production`. `MATRIX_PRODUCT_APP_ID` is emitted only when the resolved product identity has an `appId`; environment suffixes are applied before it is exported.
|
|
154
|
+
|
|
130
155
|
Product-level environment values are resolved independently for each product. This lets two products reuse the same Desktop project while connecting it to different Web variants or services.
|
|
131
156
|
|
|
132
157
|
Custom environment names are supported. Define them with the same helper and pass the name explicitly to the CLI:
|
|
@@ -162,7 +187,7 @@ The built-in targets use these defaults:
|
|
|
162
187
|
| `build` | `production` | No |
|
|
163
188
|
| `preview` | `production` | Yes |
|
|
164
189
|
|
|
165
|
-
Custom targets are non-continuous by default and use `development` unless `--env` is provided. Project roots default to `.`, target output directories default to `dist`, and archive output defaults to `artifacts`. Archives are disabled by default, are only valid for the build target, and use zip when enabled. A configured archive fails the build when its output directory does not exist.
|
|
190
|
+
Custom targets are non-continuous by default and use `development` unless `--env` is provided. The built-in target runtime modes are `development` for `dev` and `production` for `build` and `preview`. Custom targets may set `nodeEnv` to `development`, `production`, or `test`. Project roots default to `.`, target output directories default to `dist`, and archive output defaults to `artifacts`. Archives are disabled by default, are only valid for the build target, and use zip when enabled. A configured archive fails the build when its output directory does not exist.
|
|
166
191
|
|
|
167
192
|
Interactive Variant selection comes before Target selection. Target options are derived from the common targets available to the selected Variants; use `--variant` to provide the scope in non-interactive runs.
|
|
168
193
|
|
package/README.zh-CN.md
CHANGED
|
@@ -121,12 +121,37 @@ export default defineMatrixConfig({
|
|
|
121
121
|
|
|
122
122
|
`defineMatrixEnv()` 是 c12 兼容的 `$env.<environment>.env` 写法的语法糖,底层结构仍然保持不变,也继续支持直接使用原始写法。
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
普通应用变量的覆盖优先级从低到高为:
|
|
125
125
|
|
|
126
126
|
```text
|
|
127
|
-
global env/$env < product env/$env < .env layers < process.env
|
|
127
|
+
global env/$env < product env/$env < .env layers < process.env
|
|
128
128
|
```
|
|
129
129
|
|
|
130
|
+
产品 identity override 会先从合并后的环境变量中解析,然后再应用 suffix:
|
|
131
|
+
|
|
132
|
+
```text
|
|
133
|
+
product identity < variant identity < 合并后的 identity override < environment suffix
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
`MATRIX_PRODUCT_NAME`、`MATRIX_PRODUCT_SLUG` 和 `MATRIX_PRODUCT_APP_ID` 可以通过环境变量提供 identity override。包含 suffix 的最终值会同时写入任务元数据和对应的 `MATRIX_PRODUCT_*` 变量。`MATRIX_PRODUCT_ID`、`MATRIX_PRODUCT_KEY`、执行上下文变量和 `NODE_ENV` 由 Matrix 生成,不能被环境变量覆盖。
|
|
137
|
+
|
|
138
|
+
Matrix 会向每个子进程注入以下执行上下文变量:
|
|
139
|
+
|
|
140
|
+
```text
|
|
141
|
+
MATRIX_ENV_NAME
|
|
142
|
+
MATRIX_TARGET
|
|
143
|
+
MATRIX_PRODUCT_KEY
|
|
144
|
+
MATRIX_PRODUCT_ID
|
|
145
|
+
MATRIX_PRODUCT_NAME
|
|
146
|
+
MATRIX_PRODUCT_SLUG
|
|
147
|
+
MATRIX_PRODUCT_APP_ID
|
|
148
|
+
MATRIX_VARIANT
|
|
149
|
+
MATRIX_PROJECT
|
|
150
|
+
NODE_ENV
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
`MATRIX_ENV_NAME` 是当前选择的 Matrix 配置环境,可以是 `staging`、`qa` 等自定义名称。`NODE_ENV` 表示目标进程的运行模式:`dev` 使用 `development`,`build` 和 `preview` 使用 `production`。因此 staging 构建通常会同时得到 `MATRIX_ENV_NAME=staging` 和 `NODE_ENV=production`。只有最终解析出的产品 identity 配置了 `appId` 时,才会注入 `MATRIX_PRODUCT_APP_ID`;环境 suffix 会在导出前生效。
|
|
154
|
+
|
|
130
155
|
产品级环境变量会为每个产品独立解析。这样多个产品可以复用同一个 Desktop 项目,同时连接不同的 Web 变体或服务。
|
|
131
156
|
|
|
132
157
|
支持自定义环境名称。使用相同的 helper 定义,并在 CLI 中显式传入环境名:
|
|
@@ -162,7 +187,7 @@ dotenv 文件按 `.env`、`.env.local`、`.env.<environment>` 和 `.env.<environ
|
|
|
162
187
|
| `build` | `production` | 否 |
|
|
163
188
|
| `preview` | `production` | 是 |
|
|
164
189
|
|
|
165
|
-
自定义目标默认不会持续运行,未指定 `--env` 时使用 `development`。项目默认使用当前目录,目标输出目录默认为 `dist`,归档输出目录默认为 `artifacts`。归档默认关闭,只允许配置在 build 目标上,启用后默认使用 zip 格式。配置了归档但输出目录不存在时,构建会失败。
|
|
190
|
+
自定义目标默认不会持续运行,未指定 `--env` 时使用 `development`。内置目标的运行模式为:`dev` 使用 `development`,`build` 和 `preview` 使用 `production`。自定义目标也可以将 `nodeEnv` 配置为 `development`、`production` 或 `test`。项目默认使用当前目录,目标输出目录默认为 `dist`,归档输出目录默认为 `artifacts`。归档默认关闭,只允许配置在 build 目标上,启用后默认使用 zip 格式。配置了归档但输出目录不存在时,构建会失败。
|
|
166
191
|
|
|
167
192
|
交互式运行会先选择 Variant,再选择 Target。Target 选项来自已选 Variant 共同支持的目标;非交互式运行可以使用 `--variant` 提前指定执行范围。
|
|
168
193
|
|
package/dist/config.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{MATRIX_DEFAULTS as e,defaultEnvironmentForTarget as t}from"./defaults.js";import{assertMatrixConfig as n}from"./schema.js";import{MATRIX_ENVIRONMENTS as r}from"./types.js";import i from"node:path";import a from"node:process";import{loadConfig as o}from"c12";function s(e){return e}function c(e){return Object.fromEntries(Object.entries(e).map(([e,t])=>[e,{env:t}]))}async function l(e={}){let t=i.resolve(e.cwd??a.cwd()),n=(await o({name:`matrix`,cwd:t,...e.configFile?{configFile:e.configFile}:{},envName:!1,dotenv:!1,omit$Keys:!1,rcFile:!1,packageJson:!1})).config,s=new Set(r);for(let e of Object.keys(n.$env??{}))s.add(e);let c=e.productName?n.products?.[e.productName]:void 0,l=e.productName?c?[c]:[]:Object.values(n.products??{});for(let e of l)for(let t of Object.keys(e.$env??{}))s.add(t);return[...s]}function u(t,n){let r=typeof n==`string`?{command:n}:{...n};if(!r.command)throw Error(`Target ${t} must define a command`);let i=typeof r.archive==`boolean`?{enabled:r.archive,format:e.archive.format}:{enabled:r.archive?.enabled??e.archive.enabled,format:r.archive?.format??e.archive.format},a=e.targets[t];return{...r,name:t,continuous:r.continuous??a?.continuous??!1,outputDir:r.outputDir??e.outputDir,archive:i,dependsOn:(r.dependsOn??[]).map(e=>typeof e==`string`?{variant:e}:e)}}function d(e,t){if(e!==`build`&&typeof t!=`string`&&t.archive!==void 0)throw Error(`Archive is only supported for build targets: ${e}`)}function f(e,t){if(typeof t==`string`)return t;let n=t.readyWhen===void 0?e.readyWhen:{...e.readyWhen,...t.readyWhen},r=typeof e.archive==`object`?e.archive:{enabled:e.archive},i=typeof t.archive==`object`&&t.archive!==null?{...r,...t.archive}:t.archive??r;return{...e,...t,archive:i,...n?{readyWhen:n}:{}}}function p(...e){return Object.assign({},...e.filter(Boolean))}function m(e,t){let n=Object.fromEntries(Object.entries(e.products).map(([e,n])=>{let{$env:r,...i}=n,a=p(n.env,r?.[t]?.env);return[e,{...i,...Object.keys(a).length?{env:a}:{}}]}));return{...e,products:n}}function h(e){let t=Object.fromEntries(Object.entries(e.projects).map(([e,t])=>[e,{...t,id:e,targets:Object.fromEntries(Object.entries(t.targets).map(([e,t])=>(d(e,t),[e,u(e,t)])))}]));return{config:e,projects:t,products:Object.fromEntries(Object.entries(e.products).map(([e,n])=>{let r=Object.fromEntries(Object.entries(n.variants).map(([n,r])=>{let i=typeof r==`string`?{project:r}:r,a=t[i.project];if(!a)throw Error(`Product ${e} variant ${n} references unknown project ${i.project}`);let o=Object.fromEntries(Object.entries(a.targets).map(([e,t])=>{let n=i.targets?.[e];return n!==void 0&&d(e,n),[e,u(e,n===void 0?t:f(t,n))]}));for(let[t,r]of Object.entries(i.targets??{}))if(!(t in o)){if(d(t,r),typeof r==`string`)o[t]=u(t,r);else if(r.command)o[t]=u(t,r);else throw Error(`Variant ${e}/${n} target ${t} must define a command`)}return[n,{...i,id:n,targets:o}]}));return[e,{...n,id:n.id??e,name:n.name??e,slug:n.slug??e,key:e,variants:r}]}))}}async function g(t={}){let r=i.resolve(t.cwd??a.cwd()),s=t.envName??e.environment,c={name:`matrix`,cwd:r,...t.configFile?{configFile:t.configFile}:{},envName:s,dotenv:{fileName:[`.env`,`.env.local`,`.env.${s}`,`.env.${s}.local`]},omit$Keys:!0,rcFile:!1,packageJson:!1},l=await o(c),u=m(n(l.config),s),d=Object.fromEntries(Object.entries(a.env).filter(e=>e[1]!==void 0));return{...h(u),configFile:l.configFile,layers:l.layers,cwd:r,envName:s,externalEnv:d}}export{e as MATRIX_DEFAULTS,t as defaultEnvironmentForTarget,s as defineMatrixConfig,c as defineMatrixEnv,l as listMatrixEnvironments,g as loadMatrixConfig,h as normalizeMatrixConfig};
|
|
1
|
+
import{MATRIX_DEFAULTS as e,defaultEnvironmentForTarget as t}from"./defaults.js";import{assertMatrixConfig as n}from"./schema.js";import{MATRIX_ENVIRONMENTS as r}from"./types.js";import i from"node:path";import a from"node:process";import{loadConfig as o}from"c12";function s(e){return e}function c(e){return Object.fromEntries(Object.entries(e).map(([e,t])=>[e,{env:t}]))}async function l(e={}){let t=i.resolve(e.cwd??a.cwd()),n=(await o({name:`matrix`,cwd:t,...e.configFile?{configFile:e.configFile}:{},envName:!1,dotenv:!1,omit$Keys:!1,rcFile:!1,packageJson:!1})).config,s=new Set(r);for(let e of Object.keys(n.$env??{}))s.add(e);let c=e.productName?n.products?.[e.productName]:void 0,l=e.productName?c?[c]:[]:Object.values(n.products??{});for(let e of l)for(let t of Object.keys(e.$env??{}))s.add(t);return[...s]}function u(t,n){let r=typeof n==`string`?{command:n}:{...n};if(!r.command)throw Error(`Target ${t} must define a command`);let i=typeof r.archive==`boolean`?{enabled:r.archive,format:e.archive.format}:{enabled:r.archive?.enabled??e.archive.enabled,format:r.archive?.format??e.archive.format},a=e.targets[t];return{...r,name:t,continuous:r.continuous??a?.continuous??!1,nodeEnv:r.nodeEnv??a?.nodeEnv??`development`,outputDir:r.outputDir??e.outputDir,archive:i,dependsOn:(r.dependsOn??[]).map(e=>typeof e==`string`?{variant:e}:e)}}function d(e,t){if(e!==`build`&&typeof t!=`string`&&t.archive!==void 0)throw Error(`Archive is only supported for build targets: ${e}`)}function f(e,t){if(typeof t==`string`)return t;let n=t.readyWhen===void 0?e.readyWhen:{...e.readyWhen,...t.readyWhen},r=typeof e.archive==`object`?e.archive:{enabled:e.archive},i=typeof t.archive==`object`&&t.archive!==null?{...r,...t.archive}:t.archive??r;return{...e,...t,archive:i,...n?{readyWhen:n}:{}}}function p(...e){return Object.assign({},...e.filter(Boolean))}function m(e,t){let n=Object.fromEntries(Object.entries(e.products).map(([e,n])=>{let{$env:r,...i}=n,a=p(n.env,r?.[t]?.env);return[e,{...i,...Object.keys(a).length?{env:a}:{}}]}));return{...e,products:n}}function h(e){let t=Object.fromEntries(Object.entries(e.projects).map(([e,t])=>[e,{...t,id:e,targets:Object.fromEntries(Object.entries(t.targets).map(([e,t])=>(d(e,t),[e,u(e,t)])))}]));return{config:e,projects:t,products:Object.fromEntries(Object.entries(e.products).map(([e,n])=>{let r=Object.fromEntries(Object.entries(n.variants).map(([n,r])=>{let i=typeof r==`string`?{project:r}:r,a=t[i.project];if(!a)throw Error(`Product ${e} variant ${n} references unknown project ${i.project}`);let o=Object.fromEntries(Object.entries(a.targets).map(([e,t])=>{let n=i.targets?.[e];return n!==void 0&&d(e,n),[e,u(e,n===void 0?t:f(t,n))]}));for(let[t,r]of Object.entries(i.targets??{}))if(!(t in o)){if(d(t,r),typeof r==`string`)o[t]=u(t,r);else if(r.command)o[t]=u(t,r);else throw Error(`Variant ${e}/${n} target ${t} must define a command`)}return[n,{...i,id:n,targets:o}]}));return[e,{...n,id:n.id??e,name:n.name??e,slug:n.slug??e,key:e,variants:r}]}))}}async function g(t={}){let r=i.resolve(t.cwd??a.cwd()),s=t.envName??e.environment,c={name:`matrix`,cwd:r,...t.configFile?{configFile:t.configFile}:{},envName:s,dotenv:{fileName:[`.env`,`.env.local`,`.env.${s}`,`.env.${s}.local`]},omit$Keys:!0,rcFile:!1,packageJson:!1},l=await o(c),u=m(n(l.config),s),d=Object.fromEntries(Object.entries(a.env).filter(e=>e[1]!==void 0));return{...h(u),configFile:l.configFile,layers:l.layers,cwd:r,envName:s,externalEnv:d}}export{e as MATRIX_DEFAULTS,t as defaultEnvironmentForTarget,s as defineMatrixConfig,c as defineMatrixEnv,l as listMatrixEnvironments,g as loadMatrixConfig,h as normalizeMatrixConfig};
|
package/dist/defaults.d.ts
CHANGED
|
@@ -13,14 +13,17 @@ export declare const MATRIX_DEFAULTS: {
|
|
|
13
13
|
readonly targets: {
|
|
14
14
|
readonly dev: {
|
|
15
15
|
readonly environment: "development";
|
|
16
|
+
readonly nodeEnv: "development";
|
|
16
17
|
readonly continuous: true;
|
|
17
18
|
};
|
|
18
19
|
readonly build: {
|
|
19
20
|
readonly environment: "production";
|
|
21
|
+
readonly nodeEnv: "production";
|
|
20
22
|
readonly continuous: false;
|
|
21
23
|
};
|
|
22
24
|
readonly preview: {
|
|
23
25
|
readonly environment: "production";
|
|
26
|
+
readonly nodeEnv: "production";
|
|
24
27
|
readonly continuous: true;
|
|
25
28
|
};
|
|
26
29
|
};
|
package/dist/defaults.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const e={target:`dev`,environment:`development`,projectRoot:`.`,outputDir:`dist`,artifactsRoot:`artifacts`,archive:{enabled:!1,format:`zip`},targets:{dev:{environment:`development`,continuous:!0},build:{environment:`production`,continuous:!1},preview:{environment:`production`,continuous:!0}}};function t(t){return e.targets[t]?.environment??e.environment}export{e as MATRIX_DEFAULTS,t as defaultEnvironmentForTarget};
|
|
1
|
+
const e={target:`dev`,environment:`development`,projectRoot:`.`,outputDir:`dist`,artifactsRoot:`artifacts`,archive:{enabled:!1,format:`zip`},targets:{dev:{environment:`development`,nodeEnv:`development`,continuous:!0},build:{environment:`production`,nodeEnv:`production`,continuous:!1},preview:{environment:`production`,nodeEnv:`production`,continuous:!0}}};function t(t){return e.targets[t]?.environment??e.environment}export{e as MATRIX_DEFAULTS,t as defaultEnvironmentForTarget};
|
package/dist/exec.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{archiveDirectory as e}from"./archive.js";import t from"node:path";import n from"node:process";import r from"consola";import i from"node:net";import{execaCommand as a}from"execa";const o=new Promise(()=>void 0);function s(e){return new Promise(t=>setTimeout(t,e))}function c(e,t){return t.size?Promise.race([e,...t]):e}function l(e,t,n){return new Promise(r=>{let a=i.createConnection({host:e,port:t}),o=!1,s=e=>{o||(o=!0,a.destroy(),r(e))};a.once(`connect`,()=>s(!0)),a.once(`error`,()=>s(!1)),a.setTimeout(n,()=>s(!1))})}async function u(e,t,n){let r=t.readyWhen;if(!r)return;let i=r.timeout??3e4,a=Date.now()+i;for(;;){let i=a-Date.now();if(i<=0)break;if(await c(l(r.host??`127.0.0.1`,r.port,Math.min(1e3,i)),n))return;if(await c(Promise.race([e.then(e=>e),new Promise(e=>setTimeout(e,Math.min(200,Math.max(1,a-Date.now()))))]),n)!==void 0)throw Error(`${t.id} exited before becoming ready`)}throw Error(`Timed out waiting for ${t.id} on port ${r.port}`)}async function d(e,t,n){for(let[n,r]of e)t.has(n)||(t.add(n),r.kill(`SIGTERM`));await Promise.race([Promise.allSettled([...e.values()]),s(5e3)]);for(let[r,i]of e)n.has(r)||(t.add(r),i.kill(`SIGKILL`));await Promise.allSettled([...e.values()])}async function f(i){let s=/* @__PURE__ */ new Map,l=/* @__PURE__ */ new Set,f=/* @__PURE__ */ new Set,p=/* @__PURE__ */ new Set,m=()=>{for(let[e,t]of s)l.has(e)||(l.add(e),t.kill(`SIGTERM`))};n.on(`SIGINT`,m),n.on(`SIGTERM`,m);try{for(let
|
|
1
|
+
import{archiveDirectory as e}from"./archive.js";import t from"node:path";import n from"node:process";import r from"consola";import i from"node:net";import{execaCommand as a}from"execa";const o=new Promise(()=>void 0);function s(e){return new Promise(t=>setTimeout(t,e))}function c(e,t){return t.size?Promise.race([e,...t]):e}function l(e,t,n){return new Promise(r=>{let a=i.createConnection({host:e,port:t}),o=!1,s=e=>{o||(o=!0,a.destroy(),r(e))};a.once(`connect`,()=>s(!0)),a.once(`error`,()=>s(!1)),a.setTimeout(n,()=>s(!1))})}async function u(e,t,n){let r=t.readyWhen;if(!r)return;let i=r.timeout??3e4,a=Date.now()+i;for(;;){let i=a-Date.now();if(i<=0)break;if(await c(l(r.host??`127.0.0.1`,r.port,Math.min(1e3,i)),n))return;if(await c(Promise.race([e.then(e=>e),new Promise(e=>setTimeout(e,Math.min(200,Math.max(1,a-Date.now()))))]),n)!==void 0)throw Error(`${t.id} exited before becoming ready`)}throw Error(`Timed out waiting for ${t.id} on port ${r.port}`)}async function d(e,t,n){for(let[n,r]of e)t.has(n)||(t.add(n),r.kill(`SIGTERM`));await Promise.race([Promise.allSettled([...e.values()]),s(5e3)]);for(let[r,i]of e)n.has(r)||(t.add(r),i.kill(`SIGKILL`));await Promise.allSettled([...e.values()])}async function f(i){let s=/* @__PURE__ */ new Map,l=/* @__PURE__ */ new Set,f=/* @__PURE__ */ new Set,p=/* @__PURE__ */ new Set,m=()=>{for(let[e,t]of s)l.has(e)||(l.add(e),t.kill(`SIGTERM`))};n.on(`SIGINT`,m),n.on(`SIGTERM`,m);try{for(let n of i.tasks){for(let e of n.dependsOn){let t=s.get(e.id);if(!t)throw Error(`Dependency ${e.id} was not started`);if(e.condition===`completed`){let n=await c(t,p);if(n.exitCode!==0)throw Error(`${e.id} exited with code ${n.exitCode}`)}else{let n=i.tasks.find(t=>t.id===e.id);if(!n)throw Error(`Dependency task ${e.id} is missing`);await u(t,n,p)}}r.info(`${n.id} → ${n.command}`);let d=a(n.command,{cwd:n.cwd,env:Object.fromEntries(Object.entries(n.env).map(([e,t])=>[e,String(t)])),extendEnv:!0,stdio:`inherit`,reject:!1,killDescendants:!0});if(s.set(n.id,d),d.then(()=>f.add(n.id),()=>f.add(n.id)),n.continuous){let e=d.then(e=>{if(l.size||e.exitCode===0)return o;throw Error(`${n.id} exited with code ${e.exitCode}`)});p.add(e),e.catch(()=>void 0)}if(!n.continuous){let r=await c(d,p);if(r.exitCode!==0)throw Error(`${n.id} exited with code ${r.exitCode}`);if(n.target===`build`&&n.archive.enabled){let r=n.archive.format===`zip`?`zip`:`tar.gz`,a=t.join(i.artifactsRoot,n.product,i.envName,`${n.variant}.${r}`);await e(n.outputDir,a,n.archive.format)}}}let n=i.tasks.filter(e=>e.continuous).map(e=>s.get(e.id));return n.length&&await c(Promise.race(n.map(async e=>{let t=await e;if(!l.size&&t.exitCode!==0)throw Error(`Service exited with code ${t.exitCode}`)})),p),{children:s}}finally{i.tasks.some(e=>e.continuous)?await d(s,l,f):await Promise.allSettled([...s.values()]),n.removeListener(`SIGINT`,m),n.removeListener(`SIGTERM`,m)}}export{f as runExecutionPlan};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { CommandTarget, CreateExecutionPlanInput, EnvMap, EnvironmentConfig, ExecutionPlan, ExecutionTask, MATRIX_ENVIRONMENTS, MatrixConfig, MatrixEnvironment, NormalizedProduct, NormalizedProject, NormalizedTarget, NormalizedVariant, ProductConfig, ProjectConfig, Scalar, SuffixConfig, TargetConfig, TargetDependency, TargetOverride, VariantConfig } from "./types.js";
|
|
1
|
+
import { CommandTarget, CreateExecutionPlanInput, EnvMap, EnvironmentConfig, ExecutionPlan, ExecutionTask, MATRIX_ENVIRONMENTS, MatrixConfig, MatrixEnvironment, NodeEnvironment, NormalizedProduct, NormalizedProject, NormalizedTarget, NormalizedVariant, ProductConfig, ProjectConfig, Scalar, SuffixConfig, TargetConfig, TargetDependency, TargetOverride, VariantConfig } from "./types.js";
|
|
2
2
|
import { MATRIX_DEFAULTS, defaultEnvironmentForTarget } from "./defaults.js";
|
|
3
3
|
import { defineMatrixConfig, defineMatrixEnv, listMatrixEnvironments, loadMatrixConfig, normalizeMatrixConfig } from "./config.js";
|
|
4
4
|
import { createExecutionPlan } from "./plan.js";
|
|
5
5
|
import { assertMatrixConfig, matrixConfigSchema } from "./schema.js";
|
|
6
|
-
export { type CommandTarget, type CreateExecutionPlanInput, type EnvMap, type EnvironmentConfig, type ExecutionPlan, type ExecutionTask, MATRIX_DEFAULTS, MATRIX_ENVIRONMENTS, type MatrixConfig, type MatrixEnvironment, type NormalizedProduct, type NormalizedProject, type NormalizedTarget, type NormalizedVariant, type ProductConfig, type ProjectConfig, type Scalar, type SuffixConfig, type TargetConfig, type TargetDependency, type TargetOverride, type VariantConfig, assertMatrixConfig, createExecutionPlan, defaultEnvironmentForTarget, defineMatrixConfig, defineMatrixEnv, listMatrixEnvironments, loadMatrixConfig, matrixConfigSchema, normalizeMatrixConfig };
|
|
6
|
+
export { type CommandTarget, type CreateExecutionPlanInput, type EnvMap, type EnvironmentConfig, type ExecutionPlan, type ExecutionTask, MATRIX_DEFAULTS, MATRIX_ENVIRONMENTS, type MatrixConfig, type MatrixEnvironment, type NodeEnvironment, type NormalizedProduct, type NormalizedProject, type NormalizedTarget, type NormalizedVariant, type ProductConfig, type ProjectConfig, type Scalar, type SuffixConfig, type TargetConfig, type TargetDependency, type TargetOverride, type VariantConfig, assertMatrixConfig, createExecutionPlan, defaultEnvironmentForTarget, defineMatrixConfig, defineMatrixEnv, listMatrixEnvironments, loadMatrixConfig, matrixConfigSchema, normalizeMatrixConfig };
|
package/dist/plan.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{MATRIX_DEFAULTS as e}from"./defaults.js";import t from"node:path";import n from"node:process";function r(...e){return Object.assign({},...e.filter(Boolean))}function i(){return Object.fromEntries(Object.entries(n.env).filter(e=>e[1]!==void 0))}function a(e,t){return e===void 0||t===void 0?e:`${e}${t}`}function o(...e){let t={};for(let n of e)for(let[e,r]of Object.entries(n??{}))t[e]={...t[e],...r};return t}function
|
|
1
|
+
import{MATRIX_DEFAULTS as e}from"./defaults.js";import t from"node:path";import n from"node:process";function r(...e){return Object.assign({},...e.filter(Boolean))}function i(){return Object.fromEntries(Object.entries(n.env).filter(e=>e[1]!==void 0))}function a(e,t){return e===void 0||t===void 0?e:`${e}${t}`}function o(e,t){let n=e[t];return n===void 0?void 0:String(n)}function s(...e){let t={};for(let n of e)for(let[e,r]of Object.entries(n??{}))t[e]={...t[e],...r};return t}function c(e,t,n,r){let i={...e.suffixes?.[n]??{},...t.suffixes?.[n]??{}},s=o(r,`MATRIX_PRODUCT_NAME`)??t.name??e.name,c=o(r,`MATRIX_PRODUCT_SLUG`)??t.slug??e.slug,l=o(r,`MATRIX_PRODUCT_APP_ID`)??t.appId??e.appId;return{id:e.id,name:a(s,i.name),slug:a(c,i.slug),appId:a(l,i.appId)}}function l(e,t){return e.condition??(t.continuous?`ready`:`completed`)}function u(n){let a=/* @__PURE__ */ new Map,o=/* @__PURE__ */ new Set,u=[],d=(f,p,m)=>{let h=`${f}:${p}:${m}`,g=a.get(h);if(g)return g;if(o.has(h))throw Error(`Dependency cycle detected at ${h}`);let _=n.products[f];if(!_)throw Error(`Unknown product: ${f}`);let v=_.variants[p];if(!v)throw Error(`Unknown variant ${p} for product ${f}`);let y=n.projects[v.project];if(!y)throw Error(`Variant ${f}/${p} references unknown project ${v.project}`);let b=v.targets[m];if(!b)throw Error(`Variant ${f}/${p} has no target ${m}`);o.add(h);let x=b.dependsOn.map(e=>{let t=e.target??m,n=_.variants[e.variant];if(!n)throw Error(`Unknown dependency variant ${f}/${e.variant}`);let r=n.targets[t];if(!r)throw Error(`Variant ${f}/${e.variant} has no target ${t}`);return{id:d(f,e.variant,t).id,condition:l(e,r)}});o.delete(h);let S=r(n.config.env,_.env,n.externalEnv??i()),C=c({..._,suffixes:s(n.config.suffixes,_.suffixes)},v,n.envName,S),w=t.resolve(n.cwd,y.root??e.projectRoot),T={id:h,product:f,variant:p,project:v.project,projectRoot:w,target:m,name:C.name,slug:C.slug,...C.appId?{appId:C.appId}:{},command:b.command,cwd:w,env:r(S,{MATRIX_ENV_NAME:n.envName,MATRIX_TARGET:m,MATRIX_PRODUCT_KEY:f,MATRIX_PRODUCT_ID:C.id,MATRIX_PRODUCT_NAME:C.name,MATRIX_PRODUCT_SLUG:C.slug,MATRIX_VARIANT:p,MATRIX_PROJECT:v.project,NODE_ENV:b.nodeEnv,...C.appId?{MATRIX_PRODUCT_APP_ID:C.appId}:{}}),continuous:b.continuous,archive:b.archive,...b.readyWhen?{readyWhen:b.readyWhen}:{},outputDir:t.resolve(w,b.outputDir),dependsOn:x};return a.set(h,T),u.push(T),T};for(let e of n.productNames){let t=n.products[e];if(!t)throw Error(`Unknown product: ${e}`);let r=n.variantNames?.length?n.variantNames:Object.keys(t.variants);for(let t of r)d(e,t,n.target)}return{envName:n.envName,tasks:u,artifactsRoot:t.resolve(n.cwd,n.config.artifacts?.root??e.artifactsRoot)}}function d(e){for(let[t,n]of Object.entries(e.products))for(let[r,i]of Object.entries(n.variants))for(let n of Object.keys(i.targets))u({...e,productNames:[t],variantNames:[r],target:n})}export{u as createExecutionPlan,d as validateExecutionGraph};
|
package/dist/schema.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export declare const matrixConfigSchema: v.ObjectSchema<{
|
|
|
16
16
|
readonly targets: v.RecordSchema<v.StringSchema<undefined>, v.UnionSchema<[v.StringSchema<undefined>, v.ObjectSchema<{
|
|
17
17
|
readonly command: v.StringSchema<undefined>;
|
|
18
18
|
readonly continuous: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
|
|
19
|
+
readonly nodeEnv: v.OptionalSchema<v.PicklistSchema<["development", "production", "test"], undefined>, undefined>;
|
|
19
20
|
readonly readyWhen: v.OptionalSchema<v.ObjectSchema<{
|
|
20
21
|
readonly type: v.LiteralSchema<"port", undefined>;
|
|
21
22
|
readonly host: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
@@ -61,6 +62,7 @@ export declare const matrixConfigSchema: v.ObjectSchema<{
|
|
|
61
62
|
readonly targets: v.OptionalSchema<v.RecordSchema<v.StringSchema<undefined>, v.UnionSchema<[v.StringSchema<undefined>, v.ObjectSchema<{
|
|
62
63
|
readonly command: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
63
64
|
readonly continuous: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
|
|
65
|
+
readonly nodeEnv: v.OptionalSchema<v.PicklistSchema<["development", "production", "test"], undefined>, undefined>;
|
|
64
66
|
readonly readyWhen: v.OptionalSchema<v.ObjectSchema<{
|
|
65
67
|
readonly type: v.LiteralSchema<"port", undefined>;
|
|
66
68
|
readonly host: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
package/dist/schema.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import*as e from"valibot";const t=e.union([e.string(),e.number(),e.boolean()]),n=e.optional(e.record(e.string(),t)),r=e.object({name:e.optional(e.string()),slug:e.optional(e.string()),appId:e.optional(e.string())}),i=e.object({variant:e.string(),target:e.optional(e.string()),condition:e.optional(e.picklist([`completed`,`ready`]))}),a=e.union([e.string(),i]),o=e.pipe(e.number(),e.integer(),e.minValue(1),e.maxValue(65535)),s=e.pipe(e.number(),e.integer(),e.minValue(1)),c=e.union([e.string(),e.object({command:e.string(),continuous:e.optional(e.boolean()),readyWhen:e.optional(e.object({type:e.literal(`port`),host:e.optional(e.string()),port:o,timeout:e.optional(s)})),outputDir:e.optional(e.string()),archive:e.optional(e.union([e.boolean(),e.object({enabled:e.boolean(),format:e.optional(e.picklist([`zip`,`tar.gz`]))})])),dependsOn:e.optional(e.array(a))})]),l=e.union([e.string(),e.object({command:e.optional(e.string()),continuous:e.optional(e.boolean()),readyWhen:e.optional(e.object({type:e.literal(`port`),host:e.optional(e.string()),port:o,timeout:e.optional(s)})),outputDir:e.optional(e.string()),archive:e.optional(e.union([e.boolean(),e.object({enabled:e.boolean(),format:e.optional(e.picklist([`zip`,`tar.gz`]))})])),dependsOn:e.optional(e.array(a))})]),u=e.object({root:e.optional(e.string()),targets:e.record(e.string(),c)}),d=e.object({env:n}),f=e.union([e.string(),e.object({project:e.string(),name:e.optional(e.string()),slug:e.optional(e.string()),appId:e.optional(e.string()),suffixes:e.optional(e.record(e.string(),r)),targets:e.optional(e.record(e.string(),l))})]),p=e.object({id:e.optional(e.string()),name:e.optional(e.string()),slug:e.optional(e.string()),appId:e.optional(e.string()),env:n,$env:e.optional(e.record(e.string(),d)),suffixes:e.optional(e.record(e.string(),r)),variants:e.record(e.string(),f)}),m=e.object({suffixes:e.optional(e.record(e.string(),r)),env:n,$env:e.optional(e.record(e.string(),d)),projects:e.record(e.string(),u),products:e.record(e.string(),p),artifacts:e.optional(e.object({root:e.optional(e.string())}))});function h(t){return e.parse(m,t)}export{h as assertMatrixConfig,m as matrixConfigSchema};
|
|
1
|
+
import*as e from"valibot";const t=e.union([e.string(),e.number(),e.boolean()]),n=e.optional(e.record(e.string(),t)),r=e.object({name:e.optional(e.string()),slug:e.optional(e.string()),appId:e.optional(e.string())}),i=e.object({variant:e.string(),target:e.optional(e.string()),condition:e.optional(e.picklist([`completed`,`ready`]))}),a=e.union([e.string(),i]),o=e.pipe(e.number(),e.integer(),e.minValue(1),e.maxValue(65535)),s=e.pipe(e.number(),e.integer(),e.minValue(1)),c=e.union([e.string(),e.object({command:e.string(),continuous:e.optional(e.boolean()),nodeEnv:e.optional(e.picklist([`development`,`production`,`test`])),readyWhen:e.optional(e.object({type:e.literal(`port`),host:e.optional(e.string()),port:o,timeout:e.optional(s)})),outputDir:e.optional(e.string()),archive:e.optional(e.union([e.boolean(),e.object({enabled:e.boolean(),format:e.optional(e.picklist([`zip`,`tar.gz`]))})])),dependsOn:e.optional(e.array(a))})]),l=e.union([e.string(),e.object({command:e.optional(e.string()),continuous:e.optional(e.boolean()),nodeEnv:e.optional(e.picklist([`development`,`production`,`test`])),readyWhen:e.optional(e.object({type:e.literal(`port`),host:e.optional(e.string()),port:o,timeout:e.optional(s)})),outputDir:e.optional(e.string()),archive:e.optional(e.union([e.boolean(),e.object({enabled:e.boolean(),format:e.optional(e.picklist([`zip`,`tar.gz`]))})])),dependsOn:e.optional(e.array(a))})]),u=e.object({root:e.optional(e.string()),targets:e.record(e.string(),c)}),d=e.object({env:n}),f=e.union([e.string(),e.object({project:e.string(),name:e.optional(e.string()),slug:e.optional(e.string()),appId:e.optional(e.string()),suffixes:e.optional(e.record(e.string(),r)),targets:e.optional(e.record(e.string(),l))})]),p=e.object({id:e.optional(e.string()),name:e.optional(e.string()),slug:e.optional(e.string()),appId:e.optional(e.string()),env:n,$env:e.optional(e.record(e.string(),d)),suffixes:e.optional(e.record(e.string(),r)),variants:e.record(e.string(),f)}),m=e.object({suffixes:e.optional(e.record(e.string(),r)),env:n,$env:e.optional(e.record(e.string(),d)),projects:e.record(e.string(),u),products:e.record(e.string(),p),artifacts:e.optional(e.object({root:e.optional(e.string())}))});function h(t){return e.parse(m,t)}export{h as assertMatrixConfig,m as matrixConfigSchema};
|
package/dist/types.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
//#region src/types.d.ts
|
|
2
2
|
/** Values that can be passed to a process as environment variables. */
|
|
3
3
|
export type Scalar = string | number | boolean;
|
|
4
|
+
/** Node.js runtime mode exported as `NODE_ENV` for child processes. */
|
|
5
|
+
export type NodeEnvironment = 'development' | 'production' | 'test';
|
|
4
6
|
/** A flat environment variable map. */
|
|
5
7
|
export type EnvMap = Record<string, Scalar>;
|
|
6
8
|
/** Configuration for a project command such as `dev`, `build`, or `preview`. */
|
|
@@ -9,6 +11,8 @@ export interface CommandTarget {
|
|
|
9
11
|
command: string;
|
|
10
12
|
/** Whether the command keeps running after it starts. Defaults from the target name. */
|
|
11
13
|
continuous?: boolean;
|
|
14
|
+
/** Node.js runtime mode passed to the target process. */
|
|
15
|
+
nodeEnv?: NodeEnvironment;
|
|
12
16
|
/** Readiness probe used by dependent continuous targets. */
|
|
13
17
|
readyWhen?: {
|
|
14
18
|
/** Probe type. Port probing is currently supported. */
|
|
@@ -123,6 +127,7 @@ export type MatrixEnvironment = typeof MATRIX_ENVIRONMENTS[number];
|
|
|
123
127
|
export type NormalizedTarget = Omit<CommandTarget, 'outputDir' | 'dependsOn'> & {
|
|
124
128
|
name: string;
|
|
125
129
|
continuous: boolean;
|
|
130
|
+
nodeEnv: NodeEnvironment;
|
|
126
131
|
outputDir: string;
|
|
127
132
|
archive: {
|
|
128
133
|
enabled: boolean;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xova/matrix",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.1",
|
|
5
5
|
"description": "A configuration-driven multi-app workspace orchestration engine and CLI.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://github.com/xova-dev/matrix#readme",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
},
|
|
67
67
|
"scripts": {
|
|
68
68
|
"build": "tsdown",
|
|
69
|
-
"release:prepare": "changelogen --release --push",
|
|
69
|
+
"release:prepare": "changelogen --clean --release --push",
|
|
70
70
|
"release:github": "changelogen gh release",
|
|
71
71
|
"test:pack": "node scripts/pack-smoke.mjs",
|
|
72
72
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|